From b9db006065ca864bba0499022d31a54fbdb6c98e Mon Sep 17 00:00:00 2001 From: MoonTestUse1 Date: Mon, 23 Dec 2024 20:05:53 +0600 Subject: [PATCH] Initial commit --- .../0001_graceful_ember.sql | 53 - .../0001_royal_fire.sql | 80 - .../0001_still_morning.sql | 56 - .../0001_sweet_shore.sql | 66 - .../0003_proud_cloud.sql | 53 - .../0003_winter_morning.sql | 63 - .../0003_withered_frost.sql | 67 - .../0004_maroon_shore.sql | 46 - .../0004_pink_sun.sql | 46 - .../0007_bronze_castle.sql | 37 - .../0007_fierce_mouse.sql | 166 - .../0007_hidden_king.sql | 51 - .../0007_lively_wind.sql | 66 - .../0007_raspy_rain.sql | 38 - .../0007_snowy_fog.sql | 68 - .../0007_steep_cliff.sql | 112 - .../0007_tiny_trail.sql | 57 - .../0007_wandering_salad.sql | 68 - .../0008_ancient_wind.sql | 50 - .../0008_damp_paper.sql | 56 - .../0008_lingering_breeze.sql | 67 - .../0008_nameless_sea.sql | 53 - .../0008_noisy_prism.sql | 57 - .../0008_old_summit.sql | 80 - .../0008_proud_queen.sql | 85 - .../0008_raspy_cloud.sql | 93 - .../0008_snowy_ember.sql | 89 - .../0008_velvet_bonus.sql | 43 - .../0008_wandering_fire.sql | 50 - .../0008_wooden_star.sql | 27 - .../0009_emerald_dust.sql | 58 - .../0009_fading_grove.sql | 56 - .../0009_mellow_frog.sql | 73 - .../0009_noisy_shadow.sql | 50 - .../0009_snowy_cake.sql | 17 - .../0009_tender_morning.sql | 72 - .../site/python3.11/greenlet/greenlet.h | 164 - .../SQLAlchemy-2.0.27.dist-info/INSTALLER | 1 - .../SQLAlchemy-2.0.27.dist-info/LICENSE | 19 - .../SQLAlchemy-2.0.27.dist-info/METADATA | 242 - .../SQLAlchemy-2.0.27.dist-info/RECORD | 530 - .../SQLAlchemy-2.0.27.dist-info/REQUESTED | 0 .../SQLAlchemy-2.0.27.dist-info/WHEEL | 5 - .../SQLAlchemy-2.0.27.dist-info/top_level.txt | 1 - .../_cffi_backend.cp311-win_amd64.pyd | Bin 178176 -> 0 bytes .../site-packages/_distutils_hack/__init__.py | 222 - .../site-packages/_distutils_hack/override.py | 1 - .../annotated_types-0.7.0.dist-info/INSTALLER | 1 - .../annotated_types-0.7.0.dist-info/METADATA | 295 - .../annotated_types-0.7.0.dist-info/RECORD | 10 - .../annotated_types-0.7.0.dist-info/WHEEL | 4 - .../licenses/LICENSE | 21 - .../site-packages/annotated_types/__init__.py | 432 - .../site-packages/annotated_types/py.typed | 0 .../annotated_types/test_cases.py | 151 - .../anyio-4.7.0.dist-info/INSTALLER | 1 - .../anyio-4.7.0.dist-info/LICENSE | 20 - .../anyio-4.7.0.dist-info/METADATA | 105 - .../anyio-4.7.0.dist-info/RECORD | 84 - .../site-packages/anyio-4.7.0.dist-info/WHEEL | 5 - .../anyio-4.7.0.dist-info/entry_points.txt | 2 - .../anyio-4.7.0.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/anyio/__init__.py | 76 - .../site-packages/anyio/_backends/__init__.py | 0 .../site-packages/anyio/_backends/_asyncio.py | 2851 ------ .../site-packages/anyio/_backends/_trio.py | 1334 --- .../Lib/site-packages/anyio/_core/__init__.py | 0 .../anyio/_core/_asyncio_selector_thread.py | 150 - .../site-packages/anyio/_core/_eventloop.py | 166 - .../site-packages/anyio/_core/_exceptions.py | 89 - .../Lib/site-packages/anyio/_core/_fileio.py | 674 -- .../site-packages/anyio/_core/_resources.py | 18 - .../Lib/site-packages/anyio/_core/_signals.py | 27 - .../Lib/site-packages/anyio/_core/_sockets.py | 787 -- .../Lib/site-packages/anyio/_core/_streams.py | 52 - .../anyio/_core/_subprocesses.py | 196 - .../anyio/_core/_synchronization.py | 733 -- .venv/Lib/site-packages/anyio/_core/_tasks.py | 158 - .../Lib/site-packages/anyio/_core/_testing.py | 78 - .../site-packages/anyio/_core/_typedattr.py | 81 - .venv/Lib/site-packages/anyio/abc/__init__.py | 55 - .../Lib/site-packages/anyio/abc/_eventloop.py | 376 - .../Lib/site-packages/anyio/abc/_resources.py | 33 - .venv/Lib/site-packages/anyio/abc/_sockets.py | 194 - .venv/Lib/site-packages/anyio/abc/_streams.py | 203 - .../site-packages/anyio/abc/_subprocesses.py | 79 - .venv/Lib/site-packages/anyio/abc/_tasks.py | 101 - .venv/Lib/site-packages/anyio/abc/_testing.py | 65 - .venv/Lib/site-packages/anyio/from_thread.py | 527 - .venv/Lib/site-packages/anyio/lowlevel.py | 161 - .venv/Lib/site-packages/anyio/py.typed | 0 .../Lib/site-packages/anyio/pytest_plugin.py | 191 - .../site-packages/anyio/streams/__init__.py | 0 .../site-packages/anyio/streams/buffered.py | 119 - .venv/Lib/site-packages/anyio/streams/file.py | 148 - .../Lib/site-packages/anyio/streams/memory.py | 317 - .../site-packages/anyio/streams/stapled.py | 141 - .venv/Lib/site-packages/anyio/streams/text.py | 147 - .venv/Lib/site-packages/anyio/streams/tls.py | 337 - .venv/Lib/site-packages/anyio/to_process.py | 258 - .venv/Lib/site-packages/anyio/to_thread.py | 69 - .../bcrypt-4.2.1.dist-info/INSTALLER | 1 - .../bcrypt-4.2.1.dist-info/LICENSE | 201 - .../bcrypt-4.2.1.dist-info/METADATA | 322 - .../bcrypt-4.2.1.dist-info/RECORD | 11 - .../bcrypt-4.2.1.dist-info/WHEEL | 5 - .../bcrypt-4.2.1.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/bcrypt/__init__.py | 43 - .venv/Lib/site-packages/bcrypt/__init__.pyi | 10 - .venv/Lib/site-packages/bcrypt/_bcrypt.pyd | Bin 308224 -> 0 bytes .venv/Lib/site-packages/bcrypt/py.typed | 0 .../cffi-1.17.1.dist-info/INSTALLER | 1 - .../cffi-1.17.1.dist-info/LICENSE | 26 - .../cffi-1.17.1.dist-info/METADATA | 40 - .../cffi-1.17.1.dist-info/RECORD | 48 - .../site-packages/cffi-1.17.1.dist-info/WHEEL | 5 - .../cffi-1.17.1.dist-info/entry_points.txt | 2 - .../cffi-1.17.1.dist-info/top_level.txt | 2 - .venv/Lib/site-packages/cffi/__init__.py | 14 - .venv/Lib/site-packages/cffi/_cffi_errors.h | 149 - .venv/Lib/site-packages/cffi/_cffi_include.h | 389 - .venv/Lib/site-packages/cffi/_embedding.h | 550 -- .../Lib/site-packages/cffi/_imp_emulation.py | 83 - .../site-packages/cffi/_shimmed_dist_utils.py | 45 - .venv/Lib/site-packages/cffi/api.py | 967 -- .../Lib/site-packages/cffi/backend_ctypes.py | 1121 --- .venv/Lib/site-packages/cffi/cffi_opcode.py | 187 - .venv/Lib/site-packages/cffi/commontypes.py | 82 - .venv/Lib/site-packages/cffi/cparser.py | 1015 -- .venv/Lib/site-packages/cffi/error.py | 31 - .venv/Lib/site-packages/cffi/ffiplatform.py | 113 - .venv/Lib/site-packages/cffi/lock.py | 30 - .venv/Lib/site-packages/cffi/model.py | 618 -- .venv/Lib/site-packages/cffi/parse_c_type.h | 181 - .venv/Lib/site-packages/cffi/pkgconfig.py | 121 - .venv/Lib/site-packages/cffi/recompiler.py | 1598 --- .../Lib/site-packages/cffi/setuptools_ext.py | 216 - .venv/Lib/site-packages/cffi/vengine_cpy.py | 1084 -- .venv/Lib/site-packages/cffi/vengine_gen.py | 679 -- .venv/Lib/site-packages/cffi/verifier.py | 306 - .../click-8.1.8.dist-info/INSTALLER | 1 - .../click-8.1.8.dist-info/LICENSE.txt | 28 - .../click-8.1.8.dist-info/METADATA | 74 - .../click-8.1.8.dist-info/RECORD | 38 - .../site-packages/click-8.1.8.dist-info/WHEEL | 4 - .venv/Lib/site-packages/click/__init__.py | 75 - .venv/Lib/site-packages/click/_compat.py | 623 -- .venv/Lib/site-packages/click/_termui_impl.py | 788 -- .venv/Lib/site-packages/click/_textwrap.py | 49 - .venv/Lib/site-packages/click/_winconsole.py | 279 - .venv/Lib/site-packages/click/core.py | 3047 ------ .venv/Lib/site-packages/click/decorators.py | 562 -- .venv/Lib/site-packages/click/exceptions.py | 296 - .venv/Lib/site-packages/click/formatting.py | 301 - .venv/Lib/site-packages/click/globals.py | 67 - .venv/Lib/site-packages/click/parser.py | 531 - .venv/Lib/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 603 -- .venv/Lib/site-packages/click/termui.py | 784 -- .venv/Lib/site-packages/click/testing.py | 483 - .venv/Lib/site-packages/click/types.py | 1093 --- .venv/Lib/site-packages/click/utils.py | 624 -- .../colorama-0.4.6.dist-info/INSTALLER | 1 - .../colorama-0.4.6.dist-info/METADATA | 441 - .../colorama-0.4.6.dist-info/RECORD | 31 - .../colorama-0.4.6.dist-info/WHEEL | 5 - .../licenses/LICENSE.txt | 27 - .venv/Lib/site-packages/colorama/__init__.py | 7 - .venv/Lib/site-packages/colorama/ansi.py | 102 - .../Lib/site-packages/colorama/ansitowin32.py | 277 - .../Lib/site-packages/colorama/initialise.py | 121 - .../site-packages/colorama/tests/__init__.py | 1 - .../site-packages/colorama/tests/ansi_test.py | 76 - .../colorama/tests/ansitowin32_test.py | 294 - .../colorama/tests/initialise_test.py | 189 - .../colorama/tests/isatty_test.py | 57 - .../Lib/site-packages/colorama/tests/utils.py | 49 - .../colorama/tests/winterm_test.py | 131 - .venv/Lib/site-packages/colorama/win32.py | 180 - .venv/Lib/site-packages/colorama/winterm.py | 195 - .../cryptography-44.0.0.dist-info/INSTALLER | 1 - .../cryptography-44.0.0.dist-info/METADATA | 140 - .../cryptography-44.0.0.dist-info/RECORD | 182 - .../cryptography-44.0.0.dist-info/WHEEL | 4 - .../licenses/LICENSE | 3 - .../licenses/LICENSE.APACHE | 202 - .../licenses/LICENSE.BSD | 27 - .../site-packages/cryptography/__about__.py | 17 - .../site-packages/cryptography/__init__.py | 26 - .../site-packages/cryptography/exceptions.py | 52 - .../Lib/site-packages/cryptography/fernet.py | 223 - .../cryptography/hazmat/__init__.py | 13 - .../site-packages/cryptography/hazmat/_oid.py | 315 - .../cryptography/hazmat/backends/__init__.py | 13 - .../hazmat/backends/openssl/__init__.py | 9 - .../hazmat/backends/openssl/backend.py | 285 - .../cryptography/hazmat/bindings/__init__.py | 3 - .../cryptography/hazmat/bindings/_rust.pyd | Bin 8292864 -> 0 bytes .../hazmat/bindings/_rust/__init__.pyi | 28 - .../hazmat/bindings/_rust/_openssl.pyi | 8 - .../hazmat/bindings/_rust/asn1.pyi | 7 - .../hazmat/bindings/_rust/exceptions.pyi | 17 - .../hazmat/bindings/_rust/ocsp.pyi | 117 - .../bindings/_rust/openssl/__init__.pyi | 72 - .../hazmat/bindings/_rust/openssl/aead.pyi | 103 - .../hazmat/bindings/_rust/openssl/ciphers.pyi | 38 - .../hazmat/bindings/_rust/openssl/cmac.pyi | 18 - .../hazmat/bindings/_rust/openssl/dh.pyi | 51 - .../hazmat/bindings/_rust/openssl/dsa.pyi | 41 - .../hazmat/bindings/_rust/openssl/ec.pyi | 52 - .../hazmat/bindings/_rust/openssl/ed25519.pyi | 12 - .../hazmat/bindings/_rust/openssl/ed448.pyi | 12 - .../hazmat/bindings/_rust/openssl/hashes.pyi | 19 - .../hazmat/bindings/_rust/openssl/hmac.pyi | 21 - .../hazmat/bindings/_rust/openssl/kdf.pyi | 43 - .../hazmat/bindings/_rust/openssl/keys.pyi | 33 - .../bindings/_rust/openssl/poly1305.pyi | 13 - .../hazmat/bindings/_rust/openssl/rsa.pyi | 55 - .../hazmat/bindings/_rust/openssl/x25519.pyi | 12 - .../hazmat/bindings/_rust/openssl/x448.pyi | 12 - .../hazmat/bindings/_rust/pkcs12.pyi | 46 - .../hazmat/bindings/_rust/pkcs7.pyi | 49 - .../hazmat/bindings/_rust/test_support.pyi | 22 - .../hazmat/bindings/_rust/x509.pyi | 246 - .../hazmat/bindings/openssl/__init__.py | 3 - .../hazmat/bindings/openssl/_conditional.py | 183 - .../hazmat/bindings/openssl/binding.py | 121 - .../cryptography/hazmat/decrepit/__init__.py | 5 - .../hazmat/decrepit/ciphers/__init__.py | 5 - .../hazmat/decrepit/ciphers/algorithms.py | 107 - .../hazmat/primitives/__init__.py | 3 - .../hazmat/primitives/_asymmetric.py | 19 - .../hazmat/primitives/_cipheralgorithm.py | 58 - .../hazmat/primitives/_serialization.py | 169 - .../hazmat/primitives/asymmetric/__init__.py | 3 - .../hazmat/primitives/asymmetric/dh.py | 135 - .../hazmat/primitives/asymmetric/dsa.py | 154 - .../hazmat/primitives/asymmetric/ec.py | 403 - .../hazmat/primitives/asymmetric/ed25519.py | 116 - .../hazmat/primitives/asymmetric/ed448.py | 118 - .../hazmat/primitives/asymmetric/padding.py | 113 - .../hazmat/primitives/asymmetric/rsa.py | 263 - .../hazmat/primitives/asymmetric/types.py | 111 - .../hazmat/primitives/asymmetric/utils.py | 24 - .../hazmat/primitives/asymmetric/x25519.py | 109 - .../hazmat/primitives/asymmetric/x448.py | 112 - .../hazmat/primitives/ciphers/__init__.py | 27 - .../hazmat/primitives/ciphers/aead.py | 23 - .../hazmat/primitives/ciphers/algorithms.py | 183 - .../hazmat/primitives/ciphers/base.py | 145 - .../hazmat/primitives/ciphers/modes.py | 268 - .../cryptography/hazmat/primitives/cmac.py | 10 - .../hazmat/primitives/constant_time.py | 14 - .../cryptography/hazmat/primitives/hashes.py | 242 - .../cryptography/hazmat/primitives/hmac.py | 13 - .../hazmat/primitives/kdf/__init__.py | 23 - .../hazmat/primitives/kdf/argon2.py | 13 - .../hazmat/primitives/kdf/concatkdf.py | 124 - .../hazmat/primitives/kdf/hkdf.py | 101 - .../hazmat/primitives/kdf/kbkdf.py | 302 - .../hazmat/primitives/kdf/pbkdf2.py | 62 - .../hazmat/primitives/kdf/scrypt.py | 19 - .../hazmat/primitives/kdf/x963kdf.py | 61 - .../cryptography/hazmat/primitives/keywrap.py | 177 - .../cryptography/hazmat/primitives/padding.py | 183 - .../hazmat/primitives/poly1305.py | 11 - .../primitives/serialization/__init__.py | 63 - .../hazmat/primitives/serialization/base.py | 14 - .../hazmat/primitives/serialization/pkcs12.py | 156 - .../hazmat/primitives/serialization/pkcs7.py | 369 - .../hazmat/primitives/serialization/ssh.py | 1569 --- .../hazmat/primitives/twofactor/__init__.py | 9 - .../hazmat/primitives/twofactor/hotp.py | 100 - .../hazmat/primitives/twofactor/totp.py | 55 - .venv/Lib/site-packages/cryptography/py.typed | 0 .venv/Lib/site-packages/cryptography/utils.py | 127 - .../cryptography/x509/__init__.py | 267 - .../site-packages/cryptography/x509/base.py | 815 -- .../x509/certificate_transparency.py | 35 - .../cryptography/x509/extensions.py | 2477 ----- .../cryptography/x509/general_name.py | 281 - .../site-packages/cryptography/x509/name.py | 465 - .../site-packages/cryptography/x509/ocsp.py | 344 - .../site-packages/cryptography/x509/oid.py | 35 - .../cryptography/x509/verification.py | 28 - .../site-packages/distutils-precedence.pth | 1 - .../ecdsa-0.19.0.dist-info/INSTALLER | 1 - .../ecdsa-0.19.0.dist-info/LICENSE | 24 - .../ecdsa-0.19.0.dist-info/METADATA | 671 -- .../ecdsa-0.19.0.dist-info/RECORD | 66 - .../ecdsa-0.19.0.dist-info/WHEEL | 6 - .../ecdsa-0.19.0.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/ecdsa/__init__.py | 104 - .venv/Lib/site-packages/ecdsa/_compat.py | 138 - .venv/Lib/site-packages/ecdsa/_rwlock.py | 86 - .venv/Lib/site-packages/ecdsa/_sha3.py | 181 - .venv/Lib/site-packages/ecdsa/_version.py | 21 - .venv/Lib/site-packages/ecdsa/curves.py | 590 -- .venv/Lib/site-packages/ecdsa/der.py | 409 - .venv/Lib/site-packages/ecdsa/ecdh.py | 336 - .venv/Lib/site-packages/ecdsa/ecdsa.py | 1094 --- .venv/Lib/site-packages/ecdsa/eddsa.py | 252 - .../Lib/site-packages/ecdsa/ellipticcurve.py | 1598 --- .venv/Lib/site-packages/ecdsa/errors.py | 4 - .venv/Lib/site-packages/ecdsa/keys.py | 1631 ---- .venv/Lib/site-packages/ecdsa/numbertheory.py | 835 -- .venv/Lib/site-packages/ecdsa/rfc6979.py | 113 - .venv/Lib/site-packages/ecdsa/ssh.py | 83 - .venv/Lib/site-packages/ecdsa/test_curves.py | 361 - .venv/Lib/site-packages/ecdsa/test_der.py | 478 - .venv/Lib/site-packages/ecdsa/test_ecdh.py | 449 - .venv/Lib/site-packages/ecdsa/test_ecdsa.py | 694 -- .venv/Lib/site-packages/ecdsa/test_eddsa.py | 1124 --- .../site-packages/ecdsa/test_ellipticcurve.py | 256 - .venv/Lib/site-packages/ecdsa/test_jacobi.py | 753 -- .venv/Lib/site-packages/ecdsa/test_keys.py | 1138 --- .../ecdsa/test_malformed_sigs.py | 378 - .../site-packages/ecdsa/test_numbertheory.py | 483 - .venv/Lib/site-packages/ecdsa/test_pyecdsa.py | 2522 ----- .venv/Lib/site-packages/ecdsa/test_rw_lock.py | 180 - .venv/Lib/site-packages/ecdsa/test_sha3.py | 111 - .venv/Lib/site-packages/ecdsa/util.py | 519 - .../fastapi-0.110.0.dist-info/INSTALLER | 1 - .../fastapi-0.110.0.dist-info/METADATA | 533 - .../fastapi-0.110.0.dist-info/RECORD | 91 - .../fastapi-0.110.0.dist-info/REQUESTED | 0 .../fastapi-0.110.0.dist-info/WHEEL | 4 - .../licenses/LICENSE | 21 - .venv/Lib/site-packages/fastapi/__init__.py | 25 - .venv/Lib/site-packages/fastapi/_compat.py | 634 -- .../Lib/site-packages/fastapi/applications.py | 4585 --------- .venv/Lib/site-packages/fastapi/background.py | 59 - .../Lib/site-packages/fastapi/concurrency.py | 39 - .../site-packages/fastapi/datastructures.py | 204 - .../fastapi/dependencies/__init__.py | 0 .../fastapi/dependencies/models.py | 58 - .../fastapi/dependencies/utils.py | 816 -- .venv/Lib/site-packages/fastapi/encoders.py | 341 - .../fastapi/exception_handlers.py | 34 - .venv/Lib/site-packages/fastapi/exceptions.py | 176 - .venv/Lib/site-packages/fastapi/logger.py | 3 - .../fastapi/middleware/__init__.py | 1 - .../site-packages/fastapi/middleware/cors.py | 1 - .../site-packages/fastapi/middleware/gzip.py | 1 - .../fastapi/middleware/httpsredirect.py | 3 - .../fastapi/middleware/trustedhost.py | 3 - .../site-packages/fastapi/middleware/wsgi.py | 1 - .../site-packages/fastapi/openapi/__init__.py | 0 .../fastapi/openapi/constants.py | 3 - .../Lib/site-packages/fastapi/openapi/docs.py | 344 - .../site-packages/fastapi/openapi/models.py | 611 -- .../site-packages/fastapi/openapi/utils.py | 530 - .../site-packages/fastapi/param_functions.py | 2360 ----- .venv/Lib/site-packages/fastapi/params.py | 777 -- .venv/Lib/site-packages/fastapi/py.typed | 0 .venv/Lib/site-packages/fastapi/requests.py | 2 - .venv/Lib/site-packages/fastapi/responses.py | 48 - .venv/Lib/site-packages/fastapi/routing.py | 4385 --------- .../fastapi/security/__init__.py | 15 - .../site-packages/fastapi/security/api_key.py | 301 - .../site-packages/fastapi/security/base.py | 6 - .../site-packages/fastapi/security/http.py | 420 - .../site-packages/fastapi/security/oauth2.py | 638 -- .../fastapi/security/open_id_connect_url.py | 84 - .../site-packages/fastapi/security/utils.py | 10 - .../Lib/site-packages/fastapi/staticfiles.py | 1 - .venv/Lib/site-packages/fastapi/templating.py | 1 - .venv/Lib/site-packages/fastapi/testclient.py | 1 - .venv/Lib/site-packages/fastapi/types.py | 10 - .venv/Lib/site-packages/fastapi/utils.py | 229 - .venv/Lib/site-packages/fastapi/websockets.py | 3 - .../greenlet-3.1.1.dist-info/AUTHORS | 51 - .../greenlet-3.1.1.dist-info/INSTALLER | 1 - .../greenlet-3.1.1.dist-info/LICENSE | 30 - .../greenlet-3.1.1.dist-info/LICENSE.PSF | 47 - .../greenlet-3.1.1.dist-info/METADATA | 103 - .../greenlet-3.1.1.dist-info/RECORD | 122 - .../greenlet-3.1.1.dist-info/WHEEL | 5 - .../greenlet-3.1.1.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/greenlet/CObjects.cpp | 157 - .../Lib/site-packages/greenlet/PyGreenlet.cpp | 738 -- .../Lib/site-packages/greenlet/PyGreenlet.hpp | 35 - .../greenlet/PyGreenletUnswitchable.cpp | 147 - .venv/Lib/site-packages/greenlet/PyModule.cpp | 292 - .../greenlet/TBrokenGreenlet.cpp | 45 - .../greenlet/TExceptionState.cpp | 62 - .../Lib/site-packages/greenlet/TGreenlet.cpp | 718 -- .../Lib/site-packages/greenlet/TGreenlet.hpp | 813 -- .../greenlet/TGreenletGlobals.cpp | 94 - .../site-packages/greenlet/TMainGreenlet.cpp | 153 - .../site-packages/greenlet/TPythonState.cpp | 393 - .../site-packages/greenlet/TStackState.cpp | 265 - .../site-packages/greenlet/TThreadState.hpp | 497 - .../greenlet/TThreadStateCreator.hpp | 102 - .../greenlet/TThreadStateDestroy.cpp | 258 - .../site-packages/greenlet/TUserGreenlet.cpp | 662 -- .venv/Lib/site-packages/greenlet/__init__.py | 71 - .../greenlet/_greenlet.cp311-win_amd64.pyd | Bin 218112 -> 0 bytes .venv/Lib/site-packages/greenlet/greenlet.cpp | 320 - .venv/Lib/site-packages/greenlet/greenlet.h | 164 - .../greenlet/greenlet_allocator.hpp | 63 - .../greenlet/greenlet_compiler_compat.hpp | 98 - .../greenlet/greenlet_cpython_add_pending.hpp | 172 - .../greenlet/greenlet_cpython_compat.hpp | 142 - .../greenlet/greenlet_exceptions.hpp | 171 - .../greenlet/greenlet_internal.hpp | 107 - .../site-packages/greenlet/greenlet_refs.hpp | 1118 --- .../greenlet/greenlet_slp_switch.hpp | 99 - .../greenlet/greenlet_thread_support.hpp | 31 - .../greenlet/platform/__init__.py | 0 .../platform/setup_switch_x64_masm.cmd | 2 - .../greenlet/platform/switch_aarch64_gcc.h | 124 - .../greenlet/platform/switch_alpha_unix.h | 30 - .../greenlet/platform/switch_amd64_unix.h | 87 - .../greenlet/platform/switch_arm32_gcc.h | 79 - .../greenlet/platform/switch_arm32_ios.h | 67 - .../greenlet/platform/switch_arm64_masm.asm | 53 - .../greenlet/platform/switch_arm64_masm.obj | Bin 746 -> 0 bytes .../greenlet/platform/switch_arm64_msvc.h | 17 - .../greenlet/platform/switch_csky_gcc.h | 48 - .../platform/switch_loongarch64_linux.h | 31 - .../greenlet/platform/switch_m68k_gcc.h | 38 - .../greenlet/platform/switch_mips_unix.h | 64 - .../greenlet/platform/switch_ppc64_aix.h | 103 - .../greenlet/platform/switch_ppc64_linux.h | 105 - .../greenlet/platform/switch_ppc_aix.h | 87 - .../greenlet/platform/switch_ppc_linux.h | 84 - .../greenlet/platform/switch_ppc_macosx.h | 82 - .../greenlet/platform/switch_ppc_unix.h | 82 - .../greenlet/platform/switch_riscv_unix.h | 36 - .../greenlet/platform/switch_s390_unix.h | 87 - .../greenlet/platform/switch_sh_gcc.h | 36 - .../greenlet/platform/switch_sparc_sun_gcc.h | 92 - .../greenlet/platform/switch_x32_unix.h | 63 - .../greenlet/platform/switch_x64_masm.asm | 111 - .../greenlet/platform/switch_x64_masm.obj | Bin 1078 -> 0 bytes .../greenlet/platform/switch_x64_msvc.h | 60 - .../greenlet/platform/switch_x86_msvc.h | 326 - .../greenlet/platform/switch_x86_unix.h | 105 - .../greenlet/slp_platformselect.h | 75 - .../site-packages/greenlet/tests/__init__.py | 240 - .../greenlet/tests/_test_extension.c | 231 - .../tests/_test_extension.cp311-win_amd64.pyd | Bin 14336 -> 0 bytes .../_test_extension_cpp.cp311-win_amd64.pyd | Bin 15872 -> 0 bytes .../greenlet/tests/_test_extension_cpp.cpp | 226 - .../tests/fail_clearing_run_switches.py | 47 - .../greenlet/tests/fail_cpp_exception.py | 33 - .../tests/fail_initialstub_already_started.py | 78 - .../greenlet/tests/fail_slp_switch.py | 29 - .../tests/fail_switch_three_greenlets.py | 44 - .../tests/fail_switch_three_greenlets2.py | 55 - .../tests/fail_switch_two_greenlets.py | 41 - .../site-packages/greenlet/tests/leakcheck.py | 319 - .../greenlet/tests/test_contextvars.py | 310 - .../site-packages/greenlet/tests/test_cpp.py | 73 - .../tests/test_extension_interface.py | 115 - .../site-packages/greenlet/tests/test_gc.py | 86 - .../greenlet/tests/test_generator.py | 59 - .../greenlet/tests/test_generator_nested.py | 168 - .../greenlet/tests/test_greenlet.py | 1324 --- .../greenlet/tests/test_greenlet_trash.py | 187 - .../greenlet/tests/test_leaks.py | 443 - .../greenlet/tests/test_stack_saved.py | 19 - .../greenlet/tests/test_throw.py | 128 - .../greenlet/tests/test_tracing.py | 291 - .../greenlet/tests/test_version.py | 41 - .../greenlet/tests/test_weakref.py | 35 - .../h11-0.14.0.dist-info/INSTALLER | 1 - .../h11-0.14.0.dist-info/LICENSE.txt | 22 - .../h11-0.14.0.dist-info/METADATA | 193 - .../site-packages/h11-0.14.0.dist-info/RECORD | 52 - .../site-packages/h11-0.14.0.dist-info/WHEEL | 5 - .../h11-0.14.0.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/h11/__init__.py | 62 - .venv/Lib/site-packages/h11/_abnf.py | 132 - .venv/Lib/site-packages/h11/_connection.py | 633 -- .venv/Lib/site-packages/h11/_events.py | 369 - .venv/Lib/site-packages/h11/_headers.py | 278 - .venv/Lib/site-packages/h11/_readers.py | 247 - .venv/Lib/site-packages/h11/_receivebuffer.py | 153 - .venv/Lib/site-packages/h11/_state.py | 367 - .venv/Lib/site-packages/h11/_util.py | 135 - .venv/Lib/site-packages/h11/_version.py | 16 - .venv/Lib/site-packages/h11/_writers.py | 145 - .venv/Lib/site-packages/h11/py.typed | 1 - .venv/Lib/site-packages/h11/tests/__init__.py | 0 .../site-packages/h11/tests/data/test-file | 1 - .venv/Lib/site-packages/h11/tests/helpers.py | 101 - .../h11/tests/test_against_stdlib_http.py | 115 - .../h11/tests/test_connection.py | 1122 --- .../site-packages/h11/tests/test_events.py | 150 - .../site-packages/h11/tests/test_headers.py | 157 - .../site-packages/h11/tests/test_helpers.py | 32 - .venv/Lib/site-packages/h11/tests/test_io.py | 572 -- .../h11/tests/test_receivebuffer.py | 135 - .../Lib/site-packages/h11/tests/test_state.py | 271 - .../Lib/site-packages/h11/tests/test_util.py | 112 - .../idna-3.10.dist-info/INSTALLER | 1 - .../idna-3.10.dist-info/LICENSE.md | 31 - .../idna-3.10.dist-info/METADATA | 250 - .../site-packages/idna-3.10.dist-info/RECORD | 22 - .../site-packages/idna-3.10.dist-info/WHEEL | 4 - .venv/Lib/site-packages/idna/__init__.py | 45 - .venv/Lib/site-packages/idna/codec.py | 122 - .venv/Lib/site-packages/idna/compat.py | 15 - .venv/Lib/site-packages/idna/core.py | 437 - .venv/Lib/site-packages/idna/idnadata.py | 4243 -------- .venv/Lib/site-packages/idna/intranges.py | 57 - .venv/Lib/site-packages/idna/package_data.py | 1 - .venv/Lib/site-packages/idna/py.typed | 0 .venv/Lib/site-packages/idna/uts46data.py | 8681 ----------------- .venv/Lib/site-packages/jose/__init__.py | 10 - .../site-packages/jose/backends/__init__.py | 32 - .../Lib/site-packages/jose/backends/_asn1.py | 83 - .venv/Lib/site-packages/jose/backends/base.py | 89 - .../jose/backends/cryptography_backend.py | 605 -- .../jose/backends/ecdsa_backend.py | 150 - .../Lib/site-packages/jose/backends/native.py | 76 - .../jose/backends/rsa_backend.py | 284 - .venv/Lib/site-packages/jose/constants.py | 98 - .venv/Lib/site-packages/jose/exceptions.py | 59 - .venv/Lib/site-packages/jose/jwe.py | 607 -- .venv/Lib/site-packages/jose/jwk.py | 79 - .venv/Lib/site-packages/jose/jws.py | 266 - .venv/Lib/site-packages/jose/jwt.py | 496 - .venv/Lib/site-packages/jose/utils.py | 108 - .venv/Lib/site-packages/multipart/__init__.py | 16 - .venv/Lib/site-packages/multipart/decoders.py | 171 - .../Lib/site-packages/multipart/exceptions.py | 34 - .../Lib/site-packages/multipart/multipart.py | 1911 ---- .../passlib-1.7.4.dist-info/INSTALLER | 1 - .../passlib-1.7.4.dist-info/LICENSE | 116 - .../passlib-1.7.4.dist-info/METADATA | 40 - .../passlib-1.7.4.dist-info/RECORD | 202 - .../passlib-1.7.4.dist-info/REQUESTED | 0 .../passlib-1.7.4.dist-info/WHEEL | 6 - .../passlib-1.7.4.dist-info/top_level.txt | 1 - .../passlib-1.7.4.dist-info/zip-safe | 1 - .venv/Lib/site-packages/passlib/__init__.py | 3 - .../passlib/_data/wordsets/bip39.txt | 2049 ---- .../passlib/_data/wordsets/eff_long.txt | 7776 --------------- .../passlib/_data/wordsets/eff_prefixed.txt | 1296 --- .../passlib/_data/wordsets/eff_short.txt | 1296 --- .venv/Lib/site-packages/passlib/apache.py | 1255 --- .venv/Lib/site-packages/passlib/apps.py | 245 - .venv/Lib/site-packages/passlib/context.py | 2637 ----- .../site-packages/passlib/crypto/__init__.py | 1 - .../passlib/crypto/_blowfish/__init__.py | 169 - .../passlib/crypto/_blowfish/_gen_files.py | 204 - .../passlib/crypto/_blowfish/base.py | 441 - .../passlib/crypto/_blowfish/unrolled.py | 771 -- .../Lib/site-packages/passlib/crypto/_md4.py | 244 - .venv/Lib/site-packages/passlib/crypto/des.py | 848 -- .../site-packages/passlib/crypto/digest.py | 1057 -- .../passlib/crypto/scrypt/__init__.py | 281 - .../passlib/crypto/scrypt/_builtin.py | 244 - .../passlib/crypto/scrypt/_gen_files.py | 154 - .../passlib/crypto/scrypt/_salsa.py | 170 - .venv/Lib/site-packages/passlib/exc.py | 397 - .../Lib/site-packages/passlib/ext/__init__.py | 1 - .../passlib/ext/django/__init__.py | 6 - .../passlib/ext/django/models.py | 36 - .../site-packages/passlib/ext/django/utils.py | 1276 --- .../passlib/handlers/__init__.py | 1 - .../site-packages/passlib/handlers/argon2.py | 1009 -- .../site-packages/passlib/handlers/bcrypt.py | 1243 --- .../site-packages/passlib/handlers/cisco.py | 440 - .../passlib/handlers/des_crypt.py | 607 -- .../site-packages/passlib/handlers/digests.py | 168 - .../site-packages/passlib/handlers/django.py | 512 - .../site-packages/passlib/handlers/fshp.py | 214 - .../passlib/handlers/ldap_digests.py | 359 - .../passlib/handlers/md5_crypt.py | 346 - .../site-packages/passlib/handlers/misc.py | 269 - .../site-packages/passlib/handlers/mssql.py | 244 - .../site-packages/passlib/handlers/mysql.py | 128 - .../site-packages/passlib/handlers/oracle.py | 172 - .../site-packages/passlib/handlers/pbkdf2.py | 475 - .../site-packages/passlib/handlers/phpass.py | 135 - .../passlib/handlers/postgres.py | 55 - .../site-packages/passlib/handlers/roundup.py | 29 - .../site-packages/passlib/handlers/scram.py | 582 -- .../site-packages/passlib/handlers/scrypt.py | 383 - .../passlib/handlers/sha1_crypt.py | 158 - .../passlib/handlers/sha2_crypt.py | 534 - .../passlib/handlers/sun_md5_crypt.py | 363 - .../site-packages/passlib/handlers/windows.py | 334 - .venv/Lib/site-packages/passlib/hash.py | 68 - .venv/Lib/site-packages/passlib/hosts.py | 106 - .venv/Lib/site-packages/passlib/ifc.py | 353 - .venv/Lib/site-packages/passlib/pwd.py | 809 -- .venv/Lib/site-packages/passlib/registry.py | 547 -- .../site-packages/passlib/tests/__init__.py | 1 - .../site-packages/passlib/tests/__main__.py | 6 - .../passlib/tests/_test_bad_register.py | 15 - .../site-packages/passlib/tests/backports.py | 67 - .../site-packages/passlib/tests/sample1.cfg | 9 - .../site-packages/passlib/tests/sample1b.cfg | 9 - .../site-packages/passlib/tests/sample1c.cfg | Bin 490 -> 0 bytes .../passlib/tests/sample_config_1s.cfg | 8 - .../passlib/tests/test_apache.py | 769 -- .../site-packages/passlib/tests/test_apps.py | 139 - .../passlib/tests/test_context.py | 1786 ---- .../passlib/tests/test_context_deprecated.py | 743 -- .../passlib/tests/test_crypto_builtin_md4.py | 160 - .../passlib/tests/test_crypto_des.py | 194 - .../passlib/tests/test_crypto_digest.py | 544 -- .../passlib/tests/test_crypto_scrypt.py | 634 -- .../passlib/tests/test_ext_django.py | 1080 -- .../passlib/tests/test_ext_django_source.py | 250 - .../passlib/tests/test_handlers.py | 1819 ---- .../passlib/tests/test_handlers_argon2.py | 507 - .../passlib/tests/test_handlers_bcrypt.py | 688 -- .../passlib/tests/test_handlers_cisco.py | 457 - .../passlib/tests/test_handlers_django.py | 413 - .../passlib/tests/test_handlers_pbkdf2.py | 480 - .../passlib/tests/test_handlers_scrypt.py | 111 - .../site-packages/passlib/tests/test_hosts.py | 97 - .../site-packages/passlib/tests/test_pwd.py | 205 - .../passlib/tests/test_registry.py | 228 - .../site-packages/passlib/tests/test_totp.py | 1604 --- .../site-packages/passlib/tests/test_utils.py | 1171 --- .../passlib/tests/test_utils_handlers.py | 870 -- .../passlib/tests/test_utils_md4.py | 41 - .../passlib/tests/test_utils_pbkdf2.py | 323 - .../site-packages/passlib/tests/test_win32.py | 50 - .../passlib/tests/tox_support.py | 83 - .../Lib/site-packages/passlib/tests/utils.py | 3621 ------- .venv/Lib/site-packages/passlib/totp.py | 1908 ---- .../site-packages/passlib/utils/__init__.py | 1220 --- .../Lib/site-packages/passlib/utils/binary.py | 884 -- .../passlib/utils/compat/__init__.py | 474 - .../passlib/utils/compat/_ordered_dict.py | 242 - .../Lib/site-packages/passlib/utils/decor.py | 233 - .venv/Lib/site-packages/passlib/utils/des.py | 46 - .../site-packages/passlib/utils/handlers.py | 2711 ----- .venv/Lib/site-packages/passlib/utils/md4.py | 29 - .../Lib/site-packages/passlib/utils/pbkdf2.py | 193 - .venv/Lib/site-packages/passlib/win32.py | 68 - .../pip-22.3.1.dist-info/INSTALLER | 1 - .../pip-22.3.1.dist-info/LICENSE.txt | 20 - .../pip-22.3.1.dist-info/METADATA | 88 - .../site-packages/pip-22.3.1.dist-info/RECORD | 992 -- .../pip-22.3.1.dist-info/REQUESTED | 0 .../site-packages/pip-22.3.1.dist-info/WHEEL | 5 - .../pip-22.3.1.dist-info/entry_points.txt | 4 - .../pip-22.3.1.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/pip/__init__.py | 13 - .venv/Lib/site-packages/pip/__main__.py | 31 - .venv/Lib/site-packages/pip/__pip-runner__.py | 50 - .../site-packages/pip/_internal/__init__.py | 19 - .../site-packages/pip/_internal/build_env.py | 310 - .../Lib/site-packages/pip/_internal/cache.py | 293 - .../pip/_internal/cli/__init__.py | 4 - .../pip/_internal/cli/autocompletion.py | 171 - .../pip/_internal/cli/base_command.py | 216 - .../pip/_internal/cli/cmdoptions.py | 1049 -- .../pip/_internal/cli/command_context.py | 27 - .../site-packages/pip/_internal/cli/main.py | 70 - .../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 | 502 - .../pip/_internal/cli/spinners.py | 159 - .../pip/_internal/cli/status_codes.py | 6 - .../pip/_internal/commands/__init__.py | 132 - .../pip/_internal/commands/cache.py | 223 - .../pip/_internal/commands/check.py | 53 - .../pip/_internal/commands/completion.py | 126 - .../pip/_internal/commands/configuration.py | 282 - .../pip/_internal/commands/debug.py | 199 - .../pip/_internal/commands/download.py | 149 - .../pip/_internal/commands/freeze.py | 97 - .../pip/_internal/commands/hash.py | 59 - .../pip/_internal/commands/help.py | 41 - .../pip/_internal/commands/index.py | 138 - .../pip/_internal/commands/inspect.py | 97 - .../pip/_internal/commands/install.py | 860 -- .../pip/_internal/commands/list.py | 365 - .../pip/_internal/commands/search.py | 174 - .../pip/_internal/commands/show.py | 183 - .../pip/_internal/commands/uninstall.py | 106 - .../pip/_internal/commands/wheel.py | 203 - .../pip/_internal/configuration.py | 374 - .../pip/_internal/distributions/__init__.py | 21 - .../pip/_internal/distributions/base.py | 39 - .../pip/_internal/distributions/installed.py | 23 - .../pip/_internal/distributions/sdist.py | 150 - .../pip/_internal/distributions/wheel.py | 34 - .../site-packages/pip/_internal/exceptions.py | 660 -- .../pip/_internal/index/__init__.py | 2 - .../pip/_internal/index/collector.py | 505 - .../pip/_internal/index/package_finder.py | 1025 -- .../pip/_internal/index/sources.py | 224 - .../pip/_internal/locations/__init__.py | 528 - .../pip/_internal/locations/_distutils.py | 180 - .../pip/_internal/locations/_sysconfig.py | 218 - .../pip/_internal/locations/base.py | 81 - .venv/Lib/site-packages/pip/_internal/main.py | 12 - .../pip/_internal/metadata/__init__.py | 127 - .../pip/_internal/metadata/_json.py | 84 - .../pip/_internal/metadata/base.py | 688 -- .../_internal/metadata/importlib/__init__.py | 4 - .../_internal/metadata/importlib/_compat.py | 55 - .../_internal/metadata/importlib/_dists.py | 224 - .../pip/_internal/metadata/importlib/_envs.py | 188 - .../pip/_internal/metadata/pkg_resources.py | 270 - .../pip/_internal/models/__init__.py | 2 - .../pip/_internal/models/candidate.py | 34 - .../pip/_internal/models/direct_url.py | 212 - .../pip/_internal/models/format_control.py | 80 - .../pip/_internal/models/index.py | 28 - .../_internal/models/installation_report.py | 53 - .../pip/_internal/models/link.py | 507 - .../pip/_internal/models/scheme.py | 31 - .../pip/_internal/models/search_scope.py | 133 - .../pip/_internal/models/selection_prefs.py | 51 - .../pip/_internal/models/target_python.py | 110 - .../pip/_internal/models/wheel.py | 92 - .../pip/_internal/network/__init__.py | 2 - .../pip/_internal/network/auth.py | 323 - .../pip/_internal/network/cache.py | 69 - .../pip/_internal/network/download.py | 186 - .../pip/_internal/network/lazy_wheel.py | 210 - .../pip/_internal/network/session.py | 518 - .../pip/_internal/network/utils.py | 96 - .../pip/_internal/network/xmlrpc.py | 60 - .../pip/_internal/operations/__init__.py | 0 .../pip/_internal/operations/check.py | 149 - .../pip/_internal/operations/freeze.py | 254 - .../_internal/operations/install/__init__.py | 2 - .../operations/install/editable_legacy.py | 47 - .../_internal/operations/install/legacy.py | 120 - .../pip/_internal/operations/install/wheel.py | 738 -- .../pip/_internal/operations/prepare.py | 667 -- .../site-packages/pip/_internal/pyproject.py | 175 - .../pip/_internal/req/__init__.py | 94 - .../pip/_internal/req/constructors.py | 501 - .../pip/_internal/req/req_file.py | 544 -- .../pip/_internal/req/req_install.py | 942 -- .../pip/_internal/req/req_set.py | 82 - .../pip/_internal/req/req_uninstall.py | 640 -- .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 20 - .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 600 -- .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 141 - .../resolution/resolvelib/candidates.py | 556 -- .../resolution/resolvelib/factory.py | 731 -- .../resolution/resolvelib/found_candidates.py | 155 - .../resolution/resolvelib/provider.py | 248 - .../resolution/resolvelib/reporter.py | 68 - .../resolution/resolvelib/requirements.py | 166 - .../resolution/resolvelib/resolver.py | 296 - .../pip/_internal/self_outdated_check.py | 239 - .../pip/_internal/utils/__init__.py | 0 .../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 | 188 - .../pip/_internal/utils/direct_url_helpers.py | 87 - .../pip/_internal/utils/distutils_args.py | 43 - .../pip/_internal/utils/egg_link.py | 75 - .../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 | 144 - .../_internal/utils/inject_securetransport.py | 35 - .../pip/_internal/utils/logging.py | 348 - .../site-packages/pip/_internal/utils/misc.py | 723 -- .../pip/_internal/utils/models.py | 39 - .../pip/_internal/utils/packaging.py | 57 - .../pip/_internal/utils/setuptools_build.py | 195 - .../pip/_internal/utils/subprocess.py | 260 - .../pip/_internal/utils/temp_dir.py | 246 - .../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 | 136 - .../pip/_internal/vcs/__init__.py | 15 - .../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 | 382 - .../Lib/site-packages/pip/_vendor/__init__.py | 120 - .../pip/_vendor/cachecontrol/__init__.py | 18 - .../pip/_vendor/cachecontrol/_cmd.py | 61 - .../pip/_vendor/cachecontrol/adapter.py | 137 - .../pip/_vendor/cachecontrol/cache.py | 65 - .../_vendor/cachecontrol/caches/__init__.py | 9 - .../_vendor/cachecontrol/caches/file_cache.py | 188 - .../cachecontrol/caches/redis_cache.py | 39 - .../pip/_vendor/cachecontrol/compat.py | 32 - .../pip/_vendor/cachecontrol/controller.py | 439 - .../pip/_vendor/cachecontrol/filewrapper.py | 111 - .../pip/_vendor/cachecontrol/heuristics.py | 139 - .../pip/_vendor/cachecontrol/serialize.py | 190 - .../pip/_vendor/cachecontrol/wrapper.py | 33 - .../pip/_vendor/certifi/__init__.py | 4 - .../pip/_vendor/certifi/__main__.py | 12 - .../pip/_vendor/certifi/cacert.pem | 4708 --------- .../site-packages/pip/_vendor/certifi/core.py | 108 - .../pip/_vendor/chardet/__init__.py | 93 - .../pip/_vendor/chardet/big5freq.py | 386 - .../pip/_vendor/chardet/big5prober.py | 47 - .../pip/_vendor/chardet/chardistribution.py | 259 - .../pip/_vendor/chardet/charsetgroupprober.py | 109 - .../pip/_vendor/chardet/charsetprober.py | 138 - .../pip/_vendor/chardet/cli/__init__.py | 0 .../pip/_vendor/chardet/cli/chardetect.py | 86 - .../pip/_vendor/chardet/codingstatemachine.py | 88 - .../pip/_vendor/chardet/cp949prober.py | 49 - .../pip/_vendor/chardet/enums.py | 82 - .../pip/_vendor/chardet/escprober.py | 102 - .../pip/_vendor/chardet/escsm.py | 260 - .../pip/_vendor/chardet/eucjpprober.py | 95 - .../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 | 302 - .../pip/_vendor/chardet/jisfreq.py | 325 - .../pip/_vendor/chardet/johabfreq.py | 2382 ----- .../pip/_vendor/chardet/johabprober.py | 47 - .../pip/_vendor/chardet/jpcntx.py | 237 - .../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 | 145 - .../pip/_vendor/chardet/mbcharsetprober.py | 95 - .../pip/_vendor/chardet/mbcsgroupprober.py | 56 - .../pip/_vendor/chardet/mbcssm.py | 660 -- .../pip/_vendor/chardet/metadata/__init__.py | 0 .../pip/_vendor/chardet/metadata/languages.py | 351 - .../pip/_vendor/chardet/sbcharsetprober.py | 160 - .../pip/_vendor/chardet/sbcsgroupprober.py | 88 - .../pip/_vendor/chardet/sjisprober.py | 98 - .../pip/_vendor/chardet/universaldetector.py | 328 - .../pip/_vendor/chardet/utf1632prober.py | 223 - .../pip/_vendor/chardet/utf8prober.py | 80 - .../pip/_vendor/chardet/version.py | 9 - .../pip/_vendor/colorama/__init__.py | 6 - .../pip/_vendor/colorama/ansi.py | 102 - .../pip/_vendor/colorama/ansitowin32.py | 266 - .../pip/_vendor/colorama/initialise.py | 80 - .../pip/_vendor/colorama/win32.py | 152 - .../pip/_vendor/colorama/winterm.py | 169 - .../pip/_vendor/distlib/__init__.py | 23 - .../pip/_vendor/distlib/compat.py | 1116 --- .../pip/_vendor/distlib/database.py | 1350 --- .../pip/_vendor/distlib/index.py | 508 - .../pip/_vendor/distlib/locators.py | 1300 --- .../pip/_vendor/distlib/manifest.py | 393 - .../pip/_vendor/distlib/markers.py | 152 - .../pip/_vendor/distlib/metadata.py | 1076 -- .../pip/_vendor/distlib/resources.py | 358 - .../pip/_vendor/distlib/scripts.py | 437 - .../site-packages/pip/_vendor/distlib/t32.exe | Bin 97792 -> 0 bytes .../pip/_vendor/distlib/t64-arm.exe | Bin 182784 -> 0 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 108032 -> 0 bytes .../site-packages/pip/_vendor/distlib/util.py | 1932 ---- .../pip/_vendor/distlib/version.py | 739 -- .../site-packages/pip/_vendor/distlib/w32.exe | Bin 91648 -> 0 bytes .../pip/_vendor/distlib/w64-arm.exe | Bin 168448 -> 0 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 101888 -> 0 bytes .../pip/_vendor/distlib/wheel.py | 1082 -- .../pip/_vendor/distro/__init__.py | 54 - .../pip/_vendor/distro/__main__.py | 4 - .../pip/_vendor/distro/distro.py | 1374 --- .../pip/_vendor/idna/__init__.py | 44 - .../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 | 2151 ---- .../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 - .../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 - .../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/pep517/__init__.py | 6 - .../pip/_vendor/pep517/_compat.py | 8 - .../site-packages/pip/_vendor/pep517/build.py | 126 - .../site-packages/pip/_vendor/pep517/check.py | 207 - .../pip/_vendor/pep517/colorlog.py | 113 - .../pip/_vendor/pep517/dirtools.py | 19 - .../pip/_vendor/pep517/envbuild.py | 170 - .../pip/_vendor/pep517/in_process/__init__.py | 26 - .../_vendor/pep517/in_process/_in_process.py | 351 - .../site-packages/pip/_vendor/pep517/meta.py | 93 - .../pip/_vendor/pep517/wrappers.py | 362 - .../pip/_vendor/pkg_resources/__init__.py | 3296 ------- .../pip/_vendor/pkg_resources/py31compat.py | 23 - .../pip/_vendor/platformdirs/__init__.py | 340 - .../pip/_vendor/platformdirs/__main__.py | 46 - .../pip/_vendor/platformdirs/android.py | 120 - .../pip/_vendor/platformdirs/api.py | 156 - .../pip/_vendor/platformdirs/macos.py | 64 - .../pip/_vendor/platformdirs/unix.py | 181 - .../pip/_vendor/platformdirs/version.py | 4 - .../pip/_vendor/platformdirs/windows.py | 182 - .../pip/_vendor/pygments/__init__.py | 82 - .../pip/_vendor/pygments/__main__.py | 17 - .../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 -- .../pip/_vendor/pygments/formatter.py | 94 - .../_vendor/pygments/formatters/__init__.py | 143 - .../_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 | 179 - .../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 | 882 -- .../pip/_vendor/pygments/lexers/__init__.py | 335 - .../pip/_vendor/pygments/lexers/_mapping.py | 541 - .../pip/_vendor/pygments/lexers/python.py | 1204 --- .../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 | 155 - .../pip/_vendor/pygments/style.py | 197 - .../pip/_vendor/pygments/styles/__init__.py | 97 - .../pip/_vendor/pygments/token.py | 213 - .../pip/_vendor/pygments/unistring.py | 153 - .../pip/_vendor/pygments/util.py | 308 - .../pip/_vendor/pyparsing/__init__.py | 331 - .../pip/_vendor/pyparsing/actions.py | 207 - .../pip/_vendor/pyparsing/common.py | 424 - .../pip/_vendor/pyparsing/core.py | 5814 ----------- .../pip/_vendor/pyparsing/diagram/__init__.py | 642 -- .../pip/_vendor/pyparsing/exceptions.py | 267 - .../pip/_vendor/pyparsing/helpers.py | 1088 --- .../pip/_vendor/pyparsing/results.py | 760 -- .../pip/_vendor/pyparsing/testing.py | 331 - .../pip/_vendor/pyparsing/unicode.py | 352 - .../pip/_vendor/pyparsing/util.py | 235 - .../pip/_vendor/requests/__init__.py | 182 - .../pip/_vendor/requests/__version__.py | 14 - .../pip/_vendor/requests/_internal_utils.py | 48 - .../pip/_vendor/requests/adapters.py | 584 -- .../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 | 831 -- .../pip/_vendor/requests/status_codes.py | 128 - .../pip/_vendor/requests/structures.py | 99 - .../pip/_vendor/requests/utils.py | 1086 --- .../pip/_vendor/resolvelib/__init__.py | 26 - .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../resolvelib/compat/collections_abc.py | 6 - .../pip/_vendor/resolvelib/providers.py | 133 - .../pip/_vendor/resolvelib/reporters.py | 43 - .../pip/_vendor/resolvelib/resolvers.py | 482 - .../pip/_vendor/resolvelib/structs.py | 165 - .../pip/_vendor/rich/__init__.py | 176 - .../pip/_vendor/rich/__main__.py | 282 - .../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 | 78 - .../pip/_vendor/rich/_extension.py | 10 - .../pip/_vendor/rich/_inspect.py | 270 - .../pip/_vendor/rich/_log_render.py | 94 - .../site-packages/pip/_vendor/rich/_loop.py | 43 - .../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 - .../Lib/site-packages/pip/_vendor/rich/abc.py | 33 - .../site-packages/pip/_vendor/rich/align.py | 311 - .../site-packages/pip/_vendor/rich/ansi.py | 237 - .../Lib/site-packages/pip/_vendor/rich/bar.py | 94 - .../Lib/site-packages/pip/_vendor/rich/box.py | 517 - .../site-packages/pip/_vendor/rich/cells.py | 154 - .../site-packages/pip/_vendor/rich/color.py | 615 -- .../pip/_vendor/rich/color_triplet.py | 38 - .../site-packages/pip/_vendor/rich/columns.py | 187 - .../site-packages/pip/_vendor/rich/console.py | 2572 ----- .../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 | 188 - .../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 | 54 - .../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 | 445 - .../site-packages/pip/_vendor/rich/live.py | 373 - .../pip/_vendor/rich/live_render.py | 113 - .../site-packages/pip/_vendor/rich/logging.py | 280 - .../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 | 251 - .../site-packages/pip/_vendor/rich/pretty.py | 1010 -- .../pip/_vendor/rich/progress.py | 1703 ---- .../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 | 152 - .../site-packages/pip/_vendor/rich/rule.py | 134 - .../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 | 136 - .../site-packages/pip/_vendor/rich/status.py | 132 - .../site-packages/pip/_vendor/rich/style.py | 771 -- .../site-packages/pip/_vendor/rich/styled.py | 42 - .../site-packages/pip/_vendor/rich/syntax.py | 934 -- .../site-packages/pip/_vendor/rich/table.py | 996 -- .../pip/_vendor/rich/terminal_theme.py | 153 - .../site-packages/pip/_vendor/rich/text.py | 1286 --- .../site-packages/pip/_vendor/rich/theme.py | 112 - .../site-packages/pip/_vendor/rich/themes.py | 5 - .../pip/_vendor/rich/traceback.py | 679 -- .../site-packages/pip/_vendor/rich/tree.py | 251 - .venv/Lib/site-packages/pip/_vendor/six.py | 998 -- .../pip/_vendor/tenacity/__init__.py | 519 - .../pip/_vendor/tenacity/_asyncio.py | 92 - .../pip/_vendor/tenacity/_utils.py | 68 - .../pip/_vendor/tenacity/after.py | 46 - .../pip/_vendor/tenacity/before.py | 41 - .../pip/_vendor/tenacity/before_sleep.py | 58 - .../site-packages/pip/_vendor/tenacity/nap.py | 43 - .../pip/_vendor/tenacity/retry.py | 240 - .../pip/_vendor/tenacity/stop.py | 96 - .../pip/_vendor/tenacity/tornadoweb.py | 59 - .../pip/_vendor/tenacity/wait.py | 232 - .../pip/_vendor/tomli/__init__.py | 11 - .../pip/_vendor/tomli/_parser.py | 691 -- .../site-packages/pip/_vendor/tomli/_re.py | 107 - .../site-packages/pip/_vendor/tomli/_types.py | 10 - .../pip/_vendor/typing_extensions.py | 2209 ----- .../pip/_vendor/urllib3/__init__.py | 102 - .../pip/_vendor/urllib3/_collections.py | 337 - .../pip/_vendor/urllib3/_version.py | 2 - .../pip/_vendor/urllib3/connection.py | 567 -- .../pip/_vendor/urllib3/connectionpool.py | 1110 --- .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../urllib3/contrib/_appengine_environ.py | 36 - .../contrib/_securetransport/__init__.py | 0 .../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 | 519 - .../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 .../urllib3/packages/backports/__init__.py | 0 .../urllib3/packages/backports/makefile.py | 51 - .../pip/_vendor/urllib3/packages/six.py | 1076 -- .../pip/_vendor/urllib3/poolmanager.py | 537 - .../pip/_vendor/urllib3/request.py | 170 - .../pip/_vendor/urllib3/response.py | 866 -- .../pip/_vendor/urllib3/util/__init__.py | 49 - .../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 | 620 -- .../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 | 268 - .../pip/_vendor/urllib3/util/url.py | 435 - .../pip/_vendor/urllib3/util/wait.py | 152 - .../Lib/site-packages/pip/_vendor/vendor.txt | 23 - .../pip/_vendor/webencodings/__init__.py | 342 - .../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 - .venv/Lib/site-packages/pip/py.typed | 4 - .../site-packages/pkg_resources/__init__.py | 3296 ------- .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 -- .../_vendor/importlib_resources/__init__.py | 36 - .../_vendor/importlib_resources/_adapters.py | 170 - .../_vendor/importlib_resources/_common.py | 104 - .../_vendor/importlib_resources/_compat.py | 98 - .../_vendor/importlib_resources/_itertools.py | 35 - .../_vendor/importlib_resources/_legacy.py | 121 - .../_vendor/importlib_resources/abc.py | 137 - .../_vendor/importlib_resources/readers.py | 122 - .../_vendor/importlib_resources/simple.py | 116 - .../pkg_resources/_vendor/jaraco/__init__.py | 0 .../pkg_resources/_vendor/jaraco/context.py | 213 - .../pkg_resources/_vendor/jaraco/functools.py | 525 - .../_vendor/jaraco/text/__init__.py | 599 -- .../_vendor/more_itertools/__init__.py | 4 - .../_vendor/more_itertools/more.py | 4316 -------- .../_vendor/more_itertools/recipes.py | 698 -- .../_vendor/packaging/__about__.py | 26 - .../_vendor/packaging/__init__.py | 25 - .../_vendor/packaging/_manylinux.py | 301 - .../_vendor/packaging/_musllinux.py | 136 - .../_vendor/packaging/_structures.py | 61 - .../_vendor/packaging/markers.py | 304 - .../_vendor/packaging/requirements.py | 146 - .../_vendor/packaging/specifiers.py | 802 -- .../pkg_resources/_vendor/packaging/tags.py | 487 - .../pkg_resources/_vendor/packaging/utils.py | 136 - .../_vendor/packaging/version.py | 504 - .../_vendor/pyparsing/__init__.py | 331 - .../_vendor/pyparsing/actions.py | 207 - .../pkg_resources/_vendor/pyparsing/common.py | 424 - .../pkg_resources/_vendor/pyparsing/core.py | 5814 ----------- .../_vendor/pyparsing/diagram/__init__.py | 642 -- .../_vendor/pyparsing/exceptions.py | 267 - .../_vendor/pyparsing/helpers.py | 1088 --- .../_vendor/pyparsing/results.py | 760 -- .../_vendor/pyparsing/testing.py | 331 - .../_vendor/pyparsing/unicode.py | 352 - .../pkg_resources/_vendor/pyparsing/util.py | 235 - .../pkg_resources/_vendor/zipp.py | 329 - .../pkg_resources/extern/__init__.py | 76 - .../pyasn1-0.6.1.dist-info/INSTALLER | 1 - .../pyasn1-0.6.1.dist-info/LICENSE.rst | 24 - .../pyasn1-0.6.1.dist-info/METADATA | 228 - .../pyasn1-0.6.1.dist-info/RECORD | 71 - .../pyasn1-0.6.1.dist-info/WHEEL | 5 - .../pyasn1-0.6.1.dist-info/top_level.txt | 1 - .../pyasn1-0.6.1.dist-info/zip-safe | 1 - .venv/Lib/site-packages/pyasn1/__init__.py | 2 - .../site-packages/pyasn1/codec/__init__.py | 1 - .../pyasn1/codec/ber/__init__.py | 1 - .../site-packages/pyasn1/codec/ber/decoder.py | 2189 ----- .../site-packages/pyasn1/codec/ber/encoder.py | 954 -- .../Lib/site-packages/pyasn1/codec/ber/eoo.py | 28 - .../pyasn1/codec/cer/__init__.py | 1 - .../site-packages/pyasn1/codec/cer/decoder.py | 149 - .../site-packages/pyasn1/codec/cer/encoder.py | 331 - .../pyasn1/codec/der/__init__.py | 1 - .../site-packages/pyasn1/codec/der/decoder.py | 120 - .../site-packages/pyasn1/codec/der/encoder.py | 126 - .../pyasn1/codec/native/__init__.py | 1 - .../pyasn1/codec/native/decoder.py | 244 - .../pyasn1/codec/native/encoder.py | 285 - .../site-packages/pyasn1/codec/streaming.py | 234 - .../site-packages/pyasn1/compat/__init__.py | 4 - .../site-packages/pyasn1/compat/integer.py | 13 - .venv/Lib/site-packages/pyasn1/debug.py | 146 - .venv/Lib/site-packages/pyasn1/error.py | 116 - .../Lib/site-packages/pyasn1/type/__init__.py | 1 - .venv/Lib/site-packages/pyasn1/type/base.py | 699 -- .venv/Lib/site-packages/pyasn1/type/char.py | 288 - .../site-packages/pyasn1/type/constraint.py | 751 -- .venv/Lib/site-packages/pyasn1/type/error.py | 11 - .../site-packages/pyasn1/type/namedtype.py | 550 -- .../Lib/site-packages/pyasn1/type/namedval.py | 192 - .../Lib/site-packages/pyasn1/type/opentype.py | 104 - .venv/Lib/site-packages/pyasn1/type/tag.py | 335 - .venv/Lib/site-packages/pyasn1/type/tagmap.py | 96 - .venv/Lib/site-packages/pyasn1/type/univ.py | 3327 ------- .venv/Lib/site-packages/pyasn1/type/useful.py | 189 - .../pycparser-2.22.dist-info/INSTALLER | 1 - .../pycparser-2.22.dist-info/LICENSE | 27 - .../pycparser-2.22.dist-info/METADATA | 28 - .../pycparser-2.22.dist-info/RECORD | 41 - .../pycparser-2.22.dist-info/WHEEL | 5 - .../pycparser-2.22.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/pycparser/__init__.py | 93 - .venv/Lib/site-packages/pycparser/_ast_gen.py | 336 - .../site-packages/pycparser/_build_tables.py | 40 - .venv/Lib/site-packages/pycparser/_c_ast.cfg | 195 - .../site-packages/pycparser/ast_transforms.py | 164 - .venv/Lib/site-packages/pycparser/c_ast.py | 1125 --- .../site-packages/pycparser/c_generator.py | 502 - .venv/Lib/site-packages/pycparser/c_lexer.py | 555 -- .venv/Lib/site-packages/pycparser/c_parser.py | 1950 ---- .venv/Lib/site-packages/pycparser/lextab.py | 10 - .../site-packages/pycparser/ply/__init__.py | 5 - .venv/Lib/site-packages/pycparser/ply/cpp.py | 905 -- .../site-packages/pycparser/ply/ctokens.py | 133 - .venv/Lib/site-packages/pycparser/ply/lex.py | 1099 --- .venv/Lib/site-packages/pycparser/ply/yacc.py | 3494 ------- .venv/Lib/site-packages/pycparser/ply/ygen.py | 74 - .../Lib/site-packages/pycparser/plyparser.py | 133 - .venv/Lib/site-packages/pycparser/yacctab.py | 369 - .../pydantic-2.6.3.dist-info/INSTALLER | 1 - .../pydantic-2.6.3.dist-info/METADATA | 877 -- .../pydantic-2.6.3.dist-info/RECORD | 202 - .../pydantic-2.6.3.dist-info/REQUESTED | 0 .../pydantic-2.6.3.dist-info/WHEEL | 4 - .../pydantic-2.6.3.dist-info/licenses/LICENSE | 21 - .venv/Lib/site-packages/pydantic/__init__.py | 388 - .../pydantic/_internal/__init__.py | 0 .../pydantic/_internal/_config.py | 322 - .../pydantic/_internal/_core_metadata.py | 92 - .../pydantic/_internal/_core_utils.py | 575 -- .../pydantic/_internal/_dataclasses.py | 225 - .../pydantic/_internal/_decorators.py | 791 -- .../pydantic/_internal/_decorators_v1.py | 181 - .../_internal/_discriminated_union.py | 515 - .../pydantic/_internal/_fields.py | 319 - .../pydantic/_internal/_forward_ref.py | 23 - .../pydantic/_internal/_generate_schema.py | 2213 ----- .../pydantic/_internal/_generics.py | 517 - .../site-packages/pydantic/_internal/_git.py | 26 - .../pydantic/_internal/_internal_dataclass.py | 10 - .../_internal/_known_annotated_metadata.py | 410 - .../pydantic/_internal/_mock_val_ser.py | 140 - .../pydantic/_internal/_model_construction.py | 637 -- .../site-packages/pydantic/_internal/_repr.py | 117 - .../_internal/_schema_generation_shared.py | 124 - .../pydantic/_internal/_signature.py | 164 - .../pydantic/_internal/_std_types_schema.py | 714 -- .../pydantic/_internal/_typing_extra.py | 469 - .../pydantic/_internal/_utils.py | 362 - .../pydantic/_internal/_validate_call.py | 84 - .../pydantic/_internal/_validators.py | 278 - .../Lib/site-packages/pydantic/_migration.py | 308 - .../pydantic/alias_generators.py | 50 - .venv/Lib/site-packages/pydantic/aliases.py | 112 - .../pydantic/annotated_handlers.py | 120 - .../pydantic/class_validators.py | 4 - .venv/Lib/site-packages/pydantic/color.py | 603 -- .venv/Lib/site-packages/pydantic/config.py | 912 -- .../Lib/site-packages/pydantic/dataclasses.py | 327 - .../site-packages/pydantic/datetime_parse.py | 4 - .venv/Lib/site-packages/pydantic/decorator.py | 4 - .../pydantic/deprecated/__init__.py | 0 .../pydantic/deprecated/class_validators.py | 253 - .../pydantic/deprecated/config.py | 72 - .../pydantic/deprecated/copy_internals.py | 224 - .../pydantic/deprecated/decorator.py | 279 - .../site-packages/pydantic/deprecated/json.py | 140 - .../pydantic/deprecated/parse.py | 80 - .../pydantic/deprecated/tools.py | 103 - .../site-packages/pydantic/env_settings.py | 4 - .../site-packages/pydantic/error_wrappers.py | 4 - .venv/Lib/site-packages/pydantic/errors.py | 152 - .venv/Lib/site-packages/pydantic/fields.py | 1154 --- .../pydantic/functional_serializers.py | 395 - .../pydantic/functional_validators.py | 706 -- .venv/Lib/site-packages/pydantic/generics.py | 4 - .venv/Lib/site-packages/pydantic/json.py | 4 - .../Lib/site-packages/pydantic/json_schema.py | 2425 ----- .venv/Lib/site-packages/pydantic/main.py | 1500 --- .venv/Lib/site-packages/pydantic/mypy.py | 1273 --- .venv/Lib/site-packages/pydantic/networks.py | 708 -- .venv/Lib/site-packages/pydantic/parse.py | 4 - .../site-packages/pydantic/plugin/__init__.py | 170 - .../site-packages/pydantic/plugin/_loader.py | 50 - .../pydantic/plugin/_schema_validator.py | 138 - .venv/Lib/site-packages/pydantic/py.typed | 0 .../Lib/site-packages/pydantic/root_model.py | 149 - .venv/Lib/site-packages/pydantic/schema.py | 4 - .venv/Lib/site-packages/pydantic/tools.py | 4 - .../site-packages/pydantic/type_adapter.py | 458 - .venv/Lib/site-packages/pydantic/types.py | 2879 ------ .venv/Lib/site-packages/pydantic/typing.py | 4 - .venv/Lib/site-packages/pydantic/utils.py | 4 - .../Lib/site-packages/pydantic/v1/__init__.py | 131 - .../pydantic/v1/_hypothesis_plugin.py | 391 - .../pydantic/v1/annotated_types.py | 72 - .../pydantic/v1/class_validators.py | 361 - .venv/Lib/site-packages/pydantic/v1/color.py | 494 - .venv/Lib/site-packages/pydantic/v1/config.py | 191 - .../site-packages/pydantic/v1/dataclasses.py | 500 - .../pydantic/v1/datetime_parse.py | 248 - .../site-packages/pydantic/v1/decorator.py | 264 - .../site-packages/pydantic/v1/env_settings.py | 350 - .../pydantic/v1/error_wrappers.py | 161 - .venv/Lib/site-packages/pydantic/v1/errors.py | 646 -- .venv/Lib/site-packages/pydantic/v1/fields.py | 1253 --- .../Lib/site-packages/pydantic/v1/generics.py | 400 - .venv/Lib/site-packages/pydantic/v1/json.py | 112 - .venv/Lib/site-packages/pydantic/v1/main.py | 1107 --- .venv/Lib/site-packages/pydantic/v1/mypy.py | 944 -- .../Lib/site-packages/pydantic/v1/networks.py | 747 -- .venv/Lib/site-packages/pydantic/v1/parse.py | 66 - .venv/Lib/site-packages/pydantic/v1/py.typed | 0 .venv/Lib/site-packages/pydantic/v1/schema.py | 1163 --- .venv/Lib/site-packages/pydantic/v1/tools.py | 92 - .venv/Lib/site-packages/pydantic/v1/types.py | 1205 --- .venv/Lib/site-packages/pydantic/v1/typing.py | 603 -- .venv/Lib/site-packages/pydantic/v1/utils.py | 803 -- .../site-packages/pydantic/v1/validators.py | 765 -- .../Lib/site-packages/pydantic/v1/version.py | 38 - .../pydantic/validate_call_decorator.py | 67 - .../Lib/site-packages/pydantic/validators.py | 4 - .venv/Lib/site-packages/pydantic/version.py | 80 - .venv/Lib/site-packages/pydantic/warnings.py | 58 - .../pydantic_core-2.16.3.dist-info/INSTALLER | 1 - .../pydantic_core-2.16.3.dist-info/METADATA | 159 - .../pydantic_core-2.16.3.dist-info/RECORD | 12 - .../pydantic_core-2.16.3.dist-info/WHEEL | 4 - .../license_files/LICENSE | 21 - .../site-packages/pydantic_core/__init__.py | 139 - .../_pydantic_core.cp311-win_amd64.pyd | Bin 5138432 -> 0 bytes .../pydantic_core/_pydantic_core.pyi | 882 -- .../pydantic_core/core_schema.py | 3980 -------- .../Lib/site-packages/pydantic_core/py.typed | 0 .../python_jose-3.3.0.dist-info/INSTALLER | 1 - .../python_jose-3.3.0.dist-info/LICENSE | 22 - .../python_jose-3.3.0.dist-info/METADATA | 148 - .../python_jose-3.3.0.dist-info/RECORD | 37 - .../python_jose-3.3.0.dist-info/REQUESTED | 0 .../python_jose-3.3.0.dist-info/WHEEL | 6 - .../python_jose-3.3.0.dist-info/top_level.txt | 2 - .../INSTALLER | 1 - .../python_multipart-0.0.9.dist-info/METADATA | 70 - .../python_multipart-0.0.9.dist-info/RECORD | 14 - .../REQUESTED | 0 .../python_multipart-0.0.9.dist-info/WHEEL | 4 - .../licenses/LICENSE.txt | 14 - .../site-packages/rsa-4.9.dist-info/INSTALLER | 1 - .../site-packages/rsa-4.9.dist-info/LICENSE | 13 - .../site-packages/rsa-4.9.dist-info/METADATA | 106 - .../site-packages/rsa-4.9.dist-info/RECORD | 41 - .../Lib/site-packages/rsa-4.9.dist-info/WHEEL | 4 - .../rsa-4.9.dist-info/entry_points.txt | 8 - .venv/Lib/site-packages/rsa/__init__.py | 60 - .venv/Lib/site-packages/rsa/asn1.py | 52 - .venv/Lib/site-packages/rsa/cli.py | 321 - .venv/Lib/site-packages/rsa/common.py | 184 - .venv/Lib/site-packages/rsa/core.py | 53 - .venv/Lib/site-packages/rsa/key.py | 858 -- .venv/Lib/site-packages/rsa/parallel.py | 96 - .venv/Lib/site-packages/rsa/pem.py | 134 - .venv/Lib/site-packages/rsa/pkcs1.py | 485 - .venv/Lib/site-packages/rsa/pkcs1_v2.py | 100 - .venv/Lib/site-packages/rsa/prime.py | 198 - .venv/Lib/site-packages/rsa/py.typed | 1 - .venv/Lib/site-packages/rsa/randnum.py | 95 - .venv/Lib/site-packages/rsa/transform.py | 72 - .venv/Lib/site-packages/rsa/util.py | 97 - .venv/Lib/site-packages/rust/Cargo.toml | 35 - .../rust/cryptography-cffi/Cargo.toml | 17 - .../rust/cryptography-keepalive/Cargo.toml | 10 - .../rust/cryptography-key-parsing/Cargo.toml | 17 - .../rust/cryptography-openssl/Cargo.toml | 17 - .../cryptography-x509-verification/Cargo.toml | 16 - .../rust/cryptography-x509/Cargo.toml | 11 - .../setuptools-65.5.0.dist-info/INSTALLER | 1 - .../setuptools-65.5.0.dist-info/LICENSE | 19 - .../setuptools-65.5.0.dist-info/METADATA | 144 - .../setuptools-65.5.0.dist-info/RECORD | 466 - .../setuptools-65.5.0.dist-info/REQUESTED | 0 .../setuptools-65.5.0.dist-info/WHEEL | 5 - .../entry_points.txt | 57 - .../setuptools-65.5.0.dist-info/top_level.txt | 3 - .../Lib/site-packages/setuptools/__init__.py | 247 - .../setuptools/_deprecation_warning.py | 7 - .../setuptools/_distutils/__init__.py | 24 - .../setuptools/_distutils/_collections.py | 56 - .../setuptools/_distutils/_functools.py | 20 - .../setuptools/_distutils/_macos_compat.py | 12 - .../setuptools/_distutils/_msvccompiler.py | 572 -- .../setuptools/_distutils/archive_util.py | 280 - .../setuptools/_distutils/bcppcompiler.py | 408 - .../setuptools/_distutils/ccompiler.py | 1220 --- .../setuptools/_distutils/cmd.py | 436 - .../setuptools/_distutils/command/__init__.py | 25 - .../_distutils/command/_framework_compat.py | 55 - .../setuptools/_distutils/command/bdist.py | 157 - .../_distutils/command/bdist_dumb.py | 144 - .../_distutils/command/bdist_rpm.py | 615 -- .../setuptools/_distutils/command/build.py | 153 - .../_distutils/command/build_clib.py | 208 - .../_distutils/command/build_ext.py | 787 -- .../setuptools/_distutils/command/build_py.py | 407 - .../_distutils/command/build_scripts.py | 173 - .../setuptools/_distutils/command/check.py | 151 - .../setuptools/_distutils/command/clean.py | 76 - .../setuptools/_distutils/command/config.py | 377 - .../setuptools/_distutils/command/install.py | 814 -- .../_distutils/command/install_data.py | 84 - .../_distutils/command/install_egg_info.py | 91 - .../_distutils/command/install_headers.py | 45 - .../_distutils/command/install_lib.py | 238 - .../_distutils/command/install_scripts.py | 61 - .../_distutils/command/py37compat.py | 31 - .../setuptools/_distutils/command/register.py | 319 - .../setuptools/_distutils/command/sdist.py | 531 - .../setuptools/_distutils/command/upload.py | 205 - .../setuptools/_distutils/config.py | 139 - .../setuptools/_distutils/core.py | 291 - .../setuptools/_distutils/cygwinccompiler.py | 364 - .../setuptools/_distutils/debug.py | 5 - .../setuptools/_distutils/dep_util.py | 96 - .../setuptools/_distutils/dir_util.py | 243 - .../setuptools/_distutils/dist.py | 1286 --- .../setuptools/_distutils/errors.py | 127 - .../setuptools/_distutils/extension.py | 248 - .../setuptools/_distutils/fancy_getopt.py | 470 - .../setuptools/_distutils/file_util.py | 249 - .../setuptools/_distutils/filelist.py | 371 - .../setuptools/_distutils/log.py | 80 - .../setuptools/_distutils/msvc9compiler.py | 832 -- .../setuptools/_distutils/msvccompiler.py | 695 -- .../setuptools/_distutils/py38compat.py | 8 - .../setuptools/_distutils/py39compat.py | 22 - .../setuptools/_distutils/spawn.py | 109 - .../setuptools/_distutils/sysconfig.py | 558 -- .../setuptools/_distutils/text_file.py | 287 - .../setuptools/_distutils/unixccompiler.py | 401 - .../setuptools/_distutils/util.py | 513 - .../setuptools/_distutils/version.py | 358 - .../setuptools/_distutils/versionpredicate.py | 175 - .../site-packages/setuptools/_entry_points.py | 86 - .venv/Lib/site-packages/setuptools/_imp.py | 82 - .../site-packages/setuptools/_importlib.py | 47 - .../site-packages/setuptools/_itertools.py | 23 - .venv/Lib/site-packages/setuptools/_path.py | 29 - .venv/Lib/site-packages/setuptools/_reqs.py | 19 - .../setuptools/_vendor/__init__.py | 0 .../_vendor/importlib_metadata/__init__.py | 1047 -- .../_vendor/importlib_metadata/_adapters.py | 68 - .../importlib_metadata/_collections.py | 30 - .../_vendor/importlib_metadata/_compat.py | 71 - .../_vendor/importlib_metadata/_functools.py | 104 - .../_vendor/importlib_metadata/_itertools.py | 73 - .../_vendor/importlib_metadata/_meta.py | 48 - .../_vendor/importlib_metadata/_text.py | 99 - .../_vendor/importlib_resources/__init__.py | 36 - .../_vendor/importlib_resources/_adapters.py | 170 - .../_vendor/importlib_resources/_common.py | 104 - .../_vendor/importlib_resources/_compat.py | 98 - .../_vendor/importlib_resources/_itertools.py | 35 - .../_vendor/importlib_resources/_legacy.py | 121 - .../_vendor/importlib_resources/abc.py | 137 - .../_vendor/importlib_resources/readers.py | 122 - .../_vendor/importlib_resources/simple.py | 116 - .../setuptools/_vendor/jaraco/__init__.py | 0 .../setuptools/_vendor/jaraco/context.py | 213 - .../setuptools/_vendor/jaraco/functools.py | 525 - .../_vendor/jaraco/text/__init__.py | 599 -- .../_vendor/more_itertools/__init__.py | 4 - .../setuptools/_vendor/more_itertools/more.py | 3824 -------- .../_vendor/more_itertools/recipes.py | 620 -- .../setuptools/_vendor/ordered_set.py | 488 - .../setuptools/_vendor/packaging/__about__.py | 26 - .../setuptools/_vendor/packaging/__init__.py | 25 - .../_vendor/packaging/_manylinux.py | 301 - .../_vendor/packaging/_musllinux.py | 136 - .../_vendor/packaging/_structures.py | 61 - .../setuptools/_vendor/packaging/markers.py | 304 - .../_vendor/packaging/requirements.py | 146 - .../_vendor/packaging/specifiers.py | 802 -- .../setuptools/_vendor/packaging/tags.py | 487 - .../setuptools/_vendor/packaging/utils.py | 136 - .../setuptools/_vendor/packaging/version.py | 504 - .../setuptools/_vendor/pyparsing/__init__.py | 331 - .../setuptools/_vendor/pyparsing/actions.py | 207 - .../setuptools/_vendor/pyparsing/common.py | 424 - .../setuptools/_vendor/pyparsing/core.py | 5814 ----------- .../_vendor/pyparsing/diagram/__init__.py | 642 -- .../_vendor/pyparsing/exceptions.py | 267 - .../setuptools/_vendor/pyparsing/helpers.py | 1088 --- .../setuptools/_vendor/pyparsing/results.py | 760 -- .../setuptools/_vendor/pyparsing/testing.py | 331 - .../setuptools/_vendor/pyparsing/unicode.py | 352 - .../setuptools/_vendor/pyparsing/util.py | 235 - .../setuptools/_vendor/tomli/__init__.py | 11 - .../setuptools/_vendor/tomli/_parser.py | 691 -- .../setuptools/_vendor/tomli/_re.py | 107 - .../setuptools/_vendor/tomli/_types.py | 10 - .../setuptools/_vendor/typing_extensions.py | 2296 ----- .../site-packages/setuptools/_vendor/zipp.py | 329 - .../site-packages/setuptools/archive_util.py | 213 - .../site-packages/setuptools/build_meta.py | 511 - .venv/Lib/site-packages/setuptools/cli-32.exe | Bin 65536 -> 0 bytes .venv/Lib/site-packages/setuptools/cli-64.exe | Bin 74752 -> 0 bytes .../site-packages/setuptools/cli-arm64.exe | Bin 137216 -> 0 bytes .venv/Lib/site-packages/setuptools/cli.exe | Bin 65536 -> 0 bytes .../setuptools/command/__init__.py | 12 - .../site-packages/setuptools/command/alias.py | 78 - .../setuptools/command/bdist_egg.py | 457 - .../setuptools/command/bdist_rpm.py | 40 - .../site-packages/setuptools/command/build.py | 146 - .../setuptools/command/build_clib.py | 101 - .../setuptools/command/build_ext.py | 383 - .../setuptools/command/build_py.py | 368 - .../setuptools/command/develop.py | 193 - .../setuptools/command/dist_info.py | 142 - .../setuptools/command/easy_install.py | 2312 ----- .../setuptools/command/editable_wheel.py | 844 -- .../setuptools/command/egg_info.py | 763 -- .../setuptools/command/install.py | 139 - .../setuptools/command/install_egg_info.py | 63 - .../setuptools/command/install_lib.py | 122 - .../setuptools/command/install_scripts.py | 70 - .../setuptools/command/launcher manifest.xml | 15 - .../setuptools/command/py36compat.py | 134 - .../setuptools/command/register.py | 18 - .../setuptools/command/rotate.py | 64 - .../setuptools/command/saveopts.py | 22 - .../site-packages/setuptools/command/sdist.py | 210 - .../setuptools/command/setopt.py | 149 - .../site-packages/setuptools/command/test.py | 251 - .../setuptools/command/upload.py | 17 - .../setuptools/command/upload_docs.py | 213 - .../setuptools/config/__init__.py | 35 - .../setuptools/config/_apply_pyprojecttoml.py | 377 - .../config/_validate_pyproject/__init__.py | 34 - .../_validate_pyproject/error_reporting.py | 318 - .../_validate_pyproject/extra_validations.py | 36 - .../fastjsonschema_exceptions.py | 51 - .../fastjsonschema_validations.py | 1035 -- .../config/_validate_pyproject/formats.py | 259 - .../site-packages/setuptools/config/expand.py | 462 - .../setuptools/config/pyprojecttoml.py | 493 - .../setuptools/config/setupcfg.py | 762 -- .../Lib/site-packages/setuptools/dep_util.py | 25 - .venv/Lib/site-packages/setuptools/depends.py | 176 - .../Lib/site-packages/setuptools/discovery.py | 600 -- .venv/Lib/site-packages/setuptools/dist.py | 1222 --- .venv/Lib/site-packages/setuptools/errors.py | 58 - .../Lib/site-packages/setuptools/extension.py | 148 - .../setuptools/extern/__init__.py | 76 - .venv/Lib/site-packages/setuptools/glob.py | 167 - .venv/Lib/site-packages/setuptools/gui-32.exe | Bin 65536 -> 0 bytes .venv/Lib/site-packages/setuptools/gui-64.exe | Bin 75264 -> 0 bytes .../site-packages/setuptools/gui-arm64.exe | Bin 137728 -> 0 bytes .venv/Lib/site-packages/setuptools/gui.exe | Bin 65536 -> 0 bytes .../Lib/site-packages/setuptools/installer.py | 104 - .venv/Lib/site-packages/setuptools/launch.py | 36 - .venv/Lib/site-packages/setuptools/logging.py | 36 - .venv/Lib/site-packages/setuptools/monkey.py | 165 - .venv/Lib/site-packages/setuptools/msvc.py | 1703 ---- .../site-packages/setuptools/namespaces.py | 107 - .../site-packages/setuptools/package_index.py | 1126 --- .../site-packages/setuptools/py34compat.py | 13 - .venv/Lib/site-packages/setuptools/sandbox.py | 530 - .../setuptools/script (dev).tmpl | 6 - .../Lib/site-packages/setuptools/script.tmpl | 3 - .../site-packages/setuptools/unicode_utils.py | 42 - .venv/Lib/site-packages/setuptools/version.py | 6 - .venv/Lib/site-packages/setuptools/wheel.py | 222 - .../setuptools/windows_support.py | 29 - .../six-1.17.0.dist-info/INSTALLER | 1 - .../six-1.17.0.dist-info/LICENSE | 18 - .../six-1.17.0.dist-info/METADATA | 43 - .../site-packages/six-1.17.0.dist-info/RECORD | 8 - .../site-packages/six-1.17.0.dist-info/WHEEL | 6 - .../six-1.17.0.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/six.py | 1003 -- .../sniffio-1.3.1.dist-info/INSTALLER | 1 - .../sniffio-1.3.1.dist-info/LICENSE | 3 - .../sniffio-1.3.1.dist-info/LICENSE.APACHE2 | 202 - .../sniffio-1.3.1.dist-info/LICENSE.MIT | 20 - .../sniffio-1.3.1.dist-info/METADATA | 104 - .../sniffio-1.3.1.dist-info/RECORD | 19 - .../sniffio-1.3.1.dist-info/WHEEL | 5 - .../sniffio-1.3.1.dist-info/top_level.txt | 1 - .venv/Lib/site-packages/sniffio/__init__.py | 17 - .venv/Lib/site-packages/sniffio/_impl.py | 95 - .../site-packages/sniffio/_tests/__init__.py | 0 .../sniffio/_tests/test_sniffio.py | 84 - .venv/Lib/site-packages/sniffio/_version.py | 3 - .venv/Lib/site-packages/sniffio/py.typed | 0 .../Lib/site-packages/sqlalchemy/__init__.py | 294 - .../sqlalchemy/connectors/__init__.py | 18 - .../sqlalchemy/connectors/aioodbc.py | 174 - .../sqlalchemy/connectors/asyncio.py | 208 - .../sqlalchemy/connectors/pyodbc.py | 249 - .../sqlalchemy/cyextension/__init__.py | 6 - .../collections.cp311-win_amd64.pyd | Bin 175104 -> 0 bytes .../sqlalchemy/cyextension/collections.pyx | 409 - .../immutabledict.cp311-win_amd64.pyd | Bin 72704 -> 0 bytes .../sqlalchemy/cyextension/immutabledict.pxd | 8 - .../sqlalchemy/cyextension/immutabledict.pyx | 133 - .../processors.cp311-win_amd64.pyd | Bin 58368 -> 0 bytes .../sqlalchemy/cyextension/processors.pyx | 68 - .../resultproxy.cp311-win_amd64.pyd | Bin 60928 -> 0 bytes .../sqlalchemy/cyextension/resultproxy.pyx | 102 - .../cyextension/util.cp311-win_amd64.pyd | Bin 72192 -> 0 bytes .../sqlalchemy/cyextension/util.pyx | 91 - .../sqlalchemy/dialects/__init__.py | 61 - .../sqlalchemy/dialects/_typing.py | 25 - .../sqlalchemy/dialects/mssql/__init__.py | 88 - .../sqlalchemy/dialects/mssql/aioodbc.py | 64 - .../sqlalchemy/dialects/mssql/base.py | 4038 -------- .../dialects/mssql/information_schema.py | 254 - .../sqlalchemy/dialects/mssql/json.py | 133 - .../sqlalchemy/dialects/mssql/provision.py | 155 - .../sqlalchemy/dialects/mssql/pymssql.py | 125 - .../sqlalchemy/dialects/mssql/pyodbc.py | 745 -- .../sqlalchemy/dialects/mysql/__init__.py | 101 - .../sqlalchemy/dialects/mysql/aiomysql.py | 332 - .../sqlalchemy/dialects/mysql/asyncmy.py | 337 - .../sqlalchemy/dialects/mysql/base.py | 3447 ------- .../sqlalchemy/dialects/mysql/cymysql.py | 84 - .../sqlalchemy/dialects/mysql/dml.py | 219 - .../sqlalchemy/dialects/mysql/enumerated.py | 244 - .../sqlalchemy/dialects/mysql/expression.py | 141 - .../sqlalchemy/dialects/mysql/json.py | 81 - .../sqlalchemy/dialects/mysql/mariadb.py | 32 - .../dialects/mysql/mariadbconnector.py | 282 - .../dialects/mysql/mysqlconnector.py | 179 - .../sqlalchemy/dialects/mysql/mysqldb.py | 308 - .../sqlalchemy/dialects/mysql/provision.py | 107 - .../sqlalchemy/dialects/mysql/pymysql.py | 137 - .../sqlalchemy/dialects/mysql/pyodbc.py | 138 - .../sqlalchemy/dialects/mysql/reflection.py | 677 -- .../dialects/mysql/reserved_words.py | 567 -- .../sqlalchemy/dialects/mysql/types.py | 773 -- .../sqlalchemy/dialects/oracle/__init__.py | 67 - .../sqlalchemy/dialects/oracle/base.py | 3240 ------ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1492 --- .../sqlalchemy/dialects/oracle/dictionary.py | 507 - .../sqlalchemy/dialects/oracle/oracledb.py | 311 - .../sqlalchemy/dialects/oracle/provision.py | 220 - .../sqlalchemy/dialects/oracle/types.py | 287 - .../dialects/postgresql/__init__.py | 167 - .../dialects/postgresql/_psycopg_common.py | 187 - .../sqlalchemy/dialects/postgresql/array.py | 424 - .../sqlalchemy/dialects/postgresql/asyncpg.py | 1262 --- .../sqlalchemy/dialects/postgresql/base.py | 4920 ---------- .../sqlalchemy/dialects/postgresql/dml.py | 310 - .../sqlalchemy/dialects/postgresql/ext.py | 496 - .../sqlalchemy/dialects/postgresql/hstore.py | 397 - .../sqlalchemy/dialects/postgresql/json.py | 325 - .../dialects/postgresql/named_types.py | 495 - .../dialects/postgresql/operators.py | 129 - .../sqlalchemy/dialects/postgresql/pg8000.py | 662 -- .../dialects/postgresql/pg_catalog.py | 294 - .../dialects/postgresql/provision.py | 175 - .../sqlalchemy/dialects/postgresql/psycopg.py | 749 -- .../dialects/postgresql/psycopg2.py | 876 -- .../dialects/postgresql/psycopg2cffi.py | 61 - .../sqlalchemy/dialects/postgresql/ranges.py | 1029 -- .../sqlalchemy/dialects/postgresql/types.py | 303 - .../sqlalchemy/dialects/sqlite/__init__.py | 57 - .../sqlalchemy/dialects/sqlite/aiosqlite.py | 396 - .../sqlalchemy/dialects/sqlite/base.py | 2782 ------ .../sqlalchemy/dialects/sqlite/dml.py | 240 - .../sqlalchemy/dialects/sqlite/json.py | 92 - .../sqlalchemy/dialects/sqlite/provision.py | 198 - .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 155 - .../sqlalchemy/dialects/sqlite/pysqlite.py | 753 -- .../dialects/type_migration_guidelines.txt | 145 - .../sqlalchemy/engine/__init__.py | 62 - .../sqlalchemy/engine/_py_processors.py | 136 - .../sqlalchemy/engine/_py_row.py | 128 - .../sqlalchemy/engine/_py_util.py | 74 - .../site-packages/sqlalchemy/engine/base.py | 3355 ------- .../sqlalchemy/engine/characteristics.py | 81 - .../site-packages/sqlalchemy/engine/create.py | 864 -- .../site-packages/sqlalchemy/engine/cursor.py | 2150 ---- .../sqlalchemy/engine/default.py | 2339 ----- .../site-packages/sqlalchemy/engine/events.py | 951 -- .../sqlalchemy/engine/interfaces.py | 3391 ------- .../site-packages/sqlalchemy/engine/mock.py | 131 - .../sqlalchemy/engine/processors.py | 61 - .../sqlalchemy/engine/reflection.py | 2089 ---- .../site-packages/sqlalchemy/engine/result.py | 2382 ----- .../site-packages/sqlalchemy/engine/row.py | 401 - .../sqlalchemy/engine/strategies.py | 19 - .../site-packages/sqlalchemy/engine/url.py | 910 -- .../site-packages/sqlalchemy/engine/util.py | 166 - .../sqlalchemy/event/__init__.py | 25 - .../Lib/site-packages/sqlalchemy/event/api.py | 225 - .../site-packages/sqlalchemy/event/attr.py | 655 -- .../site-packages/sqlalchemy/event/base.py | 462 - .../site-packages/sqlalchemy/event/legacy.py | 246 - .../sqlalchemy/event/registry.py | 386 - .venv/Lib/site-packages/sqlalchemy/events.py | 17 - .venv/Lib/site-packages/sqlalchemy/exc.py | 830 -- .../site-packages/sqlalchemy/ext/__init__.py | 11 - .../sqlalchemy/ext/associationproxy.py | 2005 ---- .../sqlalchemy/ext/asyncio/__init__.py | 25 - .../sqlalchemy/ext/asyncio/base.py | 279 - .../sqlalchemy/ext/asyncio/engine.py | 1457 --- .../sqlalchemy/ext/asyncio/exc.py | 21 - .../sqlalchemy/ext/asyncio/result.py | 961 -- .../sqlalchemy/ext/asyncio/scoping.py | 1614 --- .../sqlalchemy/ext/asyncio/session.py | 1927 ---- .../site-packages/sqlalchemy/ext/automap.py | 1652 ---- .../Lib/site-packages/sqlalchemy/ext/baked.py | 574 -- .../site-packages/sqlalchemy/ext/compiler.py | 555 -- .../sqlalchemy/ext/declarative/__init__.py | 65 - .../sqlalchemy/ext/declarative/extensions.py | 548 -- .../sqlalchemy/ext/horizontal_shard.py | 481 - .../site-packages/sqlalchemy/ext/hybrid.py | 1514 --- .../site-packages/sqlalchemy/ext/indexable.py | 341 - .../sqlalchemy/ext/instrumentation.py | 450 - .../site-packages/sqlalchemy/ext/mutable.py | 1073 -- .../sqlalchemy/ext/mypy/__init__.py | 6 - .../sqlalchemy/ext/mypy/apply.py | 320 - .../sqlalchemy/ext/mypy/decl_class.py | 515 - .../sqlalchemy/ext/mypy/infer.py | 590 -- .../sqlalchemy/ext/mypy/names.py | 342 - .../sqlalchemy/ext/mypy/plugin.py | 303 - .../site-packages/sqlalchemy/ext/mypy/util.py | 338 - .../sqlalchemy/ext/orderinglist.py | 416 - .../sqlalchemy/ext/serializer.py | 185 - .../sqlalchemy/future/__init__.py | 16 - .../site-packages/sqlalchemy/future/engine.py | 15 - .../site-packages/sqlalchemy/inspection.py | 174 - .venv/Lib/site-packages/sqlalchemy/log.py | 288 - .../site-packages/sqlalchemy/orm/__init__.py | 170 - .../sqlalchemy/orm/_orm_constructors.py | 2468 ----- .../site-packages/sqlalchemy/orm/_typing.py | 179 - .../sqlalchemy/orm/attributes.py | 2835 ------ .../Lib/site-packages/sqlalchemy/orm/base.py | 970 -- .../sqlalchemy/orm/bulk_persistence.py | 2048 ---- .../sqlalchemy/orm/clsregistry.py | 570 -- .../sqlalchemy/orm/collections.py | 1618 --- .../site-packages/sqlalchemy/orm/context.py | 3243 ------ .../site-packages/sqlalchemy/orm/decl_api.py | 1875 ---- .../site-packages/sqlalchemy/orm/decl_base.py | 2152 ---- .../sqlalchemy/orm/dependency.py | 1304 --- .../sqlalchemy/orm/descriptor_props.py | 1074 -- .../site-packages/sqlalchemy/orm/dynamic.py | 298 - .../site-packages/sqlalchemy/orm/evaluator.py | 368 - .../site-packages/sqlalchemy/orm/events.py | 3259 ------- .venv/Lib/site-packages/sqlalchemy/orm/exc.py | 227 - .../site-packages/sqlalchemy/orm/identity.py | 302 - .../sqlalchemy/orm/instrumentation.py | 754 -- .../sqlalchemy/orm/interfaces.py | 1464 --- .../site-packages/sqlalchemy/orm/loading.py | 1665 ---- .../sqlalchemy/orm/mapped_collection.py | 560 -- .../site-packages/sqlalchemy/orm/mapper.py | 4420 --------- .../sqlalchemy/orm/path_registry.py | 808 -- .../sqlalchemy/orm/persistence.py | 1782 ---- .../sqlalchemy/orm/properties.py | 880 -- .../Lib/site-packages/sqlalchemy/orm/query.py | 3393 ------- .../sqlalchemy/orm/relationships.py | 3468 ------- .../site-packages/sqlalchemy/orm/scoping.py | 2165 ---- .../site-packages/sqlalchemy/orm/session.py | 5238 ---------- .../Lib/site-packages/sqlalchemy/orm/state.py | 1136 --- .../sqlalchemy/orm/state_changes.py | 198 - .../sqlalchemy/orm/strategies.py | 3344 ------- .../sqlalchemy/orm/strategy_options.py | 2530 ----- .../Lib/site-packages/sqlalchemy/orm/sync.py | 164 - .../sqlalchemy/orm/unitofwork.py | 796 -- .../Lib/site-packages/sqlalchemy/orm/util.py | 2405 ----- .../site-packages/sqlalchemy/orm/writeonly.py | 678 -- .../site-packages/sqlalchemy/pool/__init__.py | 44 - .../Lib/site-packages/sqlalchemy/pool/base.py | 1515 --- .../site-packages/sqlalchemy/pool/events.py | 370 - .../Lib/site-packages/sqlalchemy/pool/impl.py | 547 -- .venv/Lib/site-packages/sqlalchemy/py.typed | 0 .venv/Lib/site-packages/sqlalchemy/schema.py | 70 - .../site-packages/sqlalchemy/sql/__init__.py | 145 - .../sqlalchemy/sql/_dml_constructors.py | 140 - .../sqlalchemy/sql/_elements_constructors.py | 1836 ---- .../sqlalchemy/sql/_orm_types.py | 20 - .../site-packages/sqlalchemy/sql/_py_util.py | 75 - .../sql/_selectable_constructors.py | 635 -- .../site-packages/sqlalchemy/sql/_typing.py | 446 - .../sqlalchemy/sql/annotation.py | 585 -- .../Lib/site-packages/sqlalchemy/sql/base.py | 2179 ----- .../site-packages/sqlalchemy/sql/cache_key.py | 1057 -- .../site-packages/sqlalchemy/sql/coercions.py | 1389 --- .../site-packages/sqlalchemy/sql/compiler.py | 7723 --------------- .../Lib/site-packages/sqlalchemy/sql/crud.py | 1669 ---- .venv/Lib/site-packages/sqlalchemy/sql/ddl.py | 1378 --- .../sqlalchemy/sql/default_comparator.py | 552 -- .venv/Lib/site-packages/sqlalchemy/sql/dml.py | 1817 ---- .../site-packages/sqlalchemy/sql/elements.py | 5352 ---------- .../site-packages/sqlalchemy/sql/events.py | 455 - .../sqlalchemy/sql/expression.py | 162 - .../site-packages/sqlalchemy/sql/functions.py | 2052 ---- .../site-packages/sqlalchemy/sql/lambdas.py | 1449 --- .../site-packages/sqlalchemy/sql/naming.py | 212 - .../site-packages/sqlalchemy/sql/operators.py | 2573 ----- .../Lib/site-packages/sqlalchemy/sql/roles.py | 323 - .../site-packages/sqlalchemy/sql/schema.py | 6115 ------------ .../sqlalchemy/sql/selectable.py | 6913 ------------- .../site-packages/sqlalchemy/sql/sqltypes.py | 3811 -------- .../sqlalchemy/sql/traversals.py | 1022 -- .../site-packages/sqlalchemy/sql/type_api.py | 2323 ----- .../Lib/site-packages/sqlalchemy/sql/util.py | 1486 --- .../site-packages/sqlalchemy/sql/visitors.py | 1165 --- .../sqlalchemy/testing/__init__.py | 95 - .../sqlalchemy/testing/assertions.py | 989 -- .../sqlalchemy/testing/assertsql.py | 516 - .../sqlalchemy/testing/asyncio.py | 130 - .../sqlalchemy/testing/config.py | 427 - .../sqlalchemy/testing/engines.py | 467 - .../sqlalchemy/testing/entities.py | 117 - .../sqlalchemy/testing/exclusions.py | 435 - .../sqlalchemy/testing/fixtures/__init__.py | 28 - .../sqlalchemy/testing/fixtures/base.py | 366 - .../sqlalchemy/testing/fixtures/mypy.py | 312 - .../sqlalchemy/testing/fixtures/orm.py | 227 - .../sqlalchemy/testing/fixtures/sql.py | 492 - .../sqlalchemy/testing/pickleable.py | 155 - .../sqlalchemy/testing/plugin/__init__.py | 6 - .../sqlalchemy/testing/plugin/bootstrap.py | 51 - .../sqlalchemy/testing/plugin/plugin_base.py | 779 -- .../sqlalchemy/testing/plugin/pytestplugin.py | 862 -- .../sqlalchemy/testing/profiling.py | 324 - .../sqlalchemy/testing/provision.py | 496 - .../sqlalchemy/testing/requirements.py | 1783 ---- .../sqlalchemy/testing/schema.py | 224 - .../sqlalchemy/testing/suite/__init__.py | 19 - .../sqlalchemy/testing/suite/test_cte.py | 211 - .../sqlalchemy/testing/suite/test_ddl.py | 389 - .../testing/suite/test_deprecations.py | 153 - .../sqlalchemy/testing/suite/test_dialect.py | 740 -- .../sqlalchemy/testing/suite/test_insert.py | 630 -- .../testing/suite/test_reflection.py | 3128 ------ .../sqlalchemy/testing/suite/test_results.py | 468 - .../sqlalchemy/testing/suite/test_rowcount.py | 258 - .../sqlalchemy/testing/suite/test_select.py | 1888 ---- .../sqlalchemy/testing/suite/test_sequence.py | 317 - .../sqlalchemy/testing/suite/test_types.py | 2071 ---- .../testing/suite/test_unicode_ddl.py | 189 - .../testing/suite/test_update_delete.py | 139 - .../site-packages/sqlalchemy/testing/util.py | 519 - .../sqlalchemy/testing/warnings.py | 52 - .venv/Lib/site-packages/sqlalchemy/types.py | 76 - .../site-packages/sqlalchemy/util/__init__.py | 159 - .../sqlalchemy/util/_collections.py | 715 -- .../sqlalchemy/util/_concurrency_py3k.py | 267 - .../site-packages/sqlalchemy/util/_has_cy.py | 40 - .../sqlalchemy/util/_py_collections.py | 541 - .../site-packages/sqlalchemy/util/compat.py | 300 - .../sqlalchemy/util/concurrency.py | 73 - .../sqlalchemy/util/deprecations.py | 401 - .../sqlalchemy/util/langhelpers.py | 2210 ----- .../sqlalchemy/util/preloaded.py | 150 - .../site-packages/sqlalchemy/util/queue.py | 322 - .../sqlalchemy/util/tool_support.py | 201 - .../sqlalchemy/util/topological.py | 120 - .../site-packages/sqlalchemy/util/typing.py | 580 -- .../starlette-0.36.3.dist-info/INSTALLER | 1 - .../starlette-0.36.3.dist-info/METADATA | 175 - .../starlette-0.36.3.dist-info/RECORD | 76 - .../starlette-0.36.3.dist-info/WHEEL | 4 - .../licenses/LICENSE.md | 27 - .venv/Lib/site-packages/starlette/__init__.py | 1 - .venv/Lib/site-packages/starlette/_compat.py | 28 - .../starlette/_exception_handler.py | 87 - .venv/Lib/site-packages/starlette/_utils.py | 99 - .../site-packages/starlette/applications.py | 266 - .../site-packages/starlette/authentication.py | 161 - .../Lib/site-packages/starlette/background.py | 45 - .../site-packages/starlette/concurrency.py | 67 - .venv/Lib/site-packages/starlette/config.py | 151 - .../Lib/site-packages/starlette/convertors.py | 87 - .../site-packages/starlette/datastructures.py | 705 -- .../Lib/site-packages/starlette/endpoints.py | 132 - .../Lib/site-packages/starlette/exceptions.py | 62 - .../site-packages/starlette/formparsers.py | 278 - .../starlette/middleware/__init__.py | 44 - .../starlette/middleware/authentication.py | 52 - .../starlette/middleware/base.py | 217 - .../starlette/middleware/cors.py | 176 - .../starlette/middleware/errors.py | 261 - .../starlette/middleware/exceptions.py | 74 - .../starlette/middleware/gzip.py | 113 - .../starlette/middleware/httpsredirect.py | 19 - .../starlette/middleware/sessions.py | 83 - .../starlette/middleware/trustedhost.py | 60 - .../starlette/middleware/wsgi.py | 154 - .venv/Lib/site-packages/starlette/py.typed | 0 .venv/Lib/site-packages/starlette/requests.py | 320 - .../Lib/site-packages/starlette/responses.py | 359 - .venv/Lib/site-packages/starlette/routing.py | 924 -- .venv/Lib/site-packages/starlette/schemas.py | 150 - .../site-packages/starlette/staticfiles.py | 239 - .venv/Lib/site-packages/starlette/status.py | 200 - .../Lib/site-packages/starlette/templating.py | 237 - .../Lib/site-packages/starlette/testclient.py | 814 -- .venv/Lib/site-packages/starlette/types.py | 30 - .../Lib/site-packages/starlette/websockets.py | 197 - .../INSTALLER | 1 - .../LICENSE | 279 - .../METADATA | 67 - .../typing_extensions-4.12.2.dist-info/RECORD | 7 - .../typing_extensions-4.12.2.dist-info/WHEEL | 4 - .venv/Lib/site-packages/typing_extensions.py | 3641 ------- .../uvicorn-0.27.1.dist-info/INSTALLER | 1 - .../uvicorn-0.27.1.dist-info/METADATA | 181 - .../uvicorn-0.27.1.dist-info/RECORD | 87 - .../uvicorn-0.27.1.dist-info/REQUESTED | 0 .../uvicorn-0.27.1.dist-info/WHEEL | 4 - .../uvicorn-0.27.1.dist-info/entry_points.txt | 2 - .../licenses/LICENSE.md | 27 - .venv/Lib/site-packages/uvicorn/__init__.py | 5 - .venv/Lib/site-packages/uvicorn/__main__.py | 4 - .../Lib/site-packages/uvicorn/_subprocess.py | 78 - .venv/Lib/site-packages/uvicorn/_types.py | 299 - .venv/Lib/site-packages/uvicorn/config.py | 568 -- .venv/Lib/site-packages/uvicorn/importer.py | 38 - .../uvicorn/lifespan/__init__.py | 0 .../Lib/site-packages/uvicorn/lifespan/off.py | 15 - .../Lib/site-packages/uvicorn/lifespan/on.py | 137 - .venv/Lib/site-packages/uvicorn/logging.py | 119 - .../site-packages/uvicorn/loops/__init__.py | 0 .../site-packages/uvicorn/loops/asyncio.py | 10 - .venv/Lib/site-packages/uvicorn/loops/auto.py | 11 - .../Lib/site-packages/uvicorn/loops/uvloop.py | 7 - .venv/Lib/site-packages/uvicorn/main.py | 596 -- .../uvicorn/middleware/__init__.py | 0 .../site-packages/uvicorn/middleware/asgi2.py | 17 - .../uvicorn/middleware/message_logger.py | 87 - .../uvicorn/middleware/proxy_headers.py | 84 - .../site-packages/uvicorn/middleware/wsgi.py | 207 - .../uvicorn/protocols/__init__.py | 0 .../uvicorn/protocols/http/__init__.py | 0 .../uvicorn/protocols/http/auto.py | 15 - .../uvicorn/protocols/http/flow_control.py | 66 - .../uvicorn/protocols/http/h11_impl.py | 554 -- .../uvicorn/protocols/http/httptools_impl.py | 594 -- .../site-packages/uvicorn/protocols/utils.py | 59 - .../uvicorn/protocols/websockets/__init__.py | 0 .../uvicorn/protocols/websockets/auto.py | 19 - .../protocols/websockets/websockets_impl.py | 417 - .../protocols/websockets/wsproto_impl.py | 397 - .venv/Lib/site-packages/uvicorn/py.typed | 1 - .venv/Lib/site-packages/uvicorn/server.py | 336 - .../uvicorn/supervisors/__init__.py | 21 - .../uvicorn/supervisors/basereload.py | 127 - .../uvicorn/supervisors/multiprocess.py | 76 - .../uvicorn/supervisors/statreload.py | 55 - .../uvicorn/supervisors/watchfilesreload.py | 96 - .../uvicorn/supervisors/watchgodreload.py | 163 - .venv/Lib/site-packages/uvicorn/workers.py | 105 - .venv/Scripts/Activate.ps1 | 472 - .venv/Scripts/activate | 69 - .venv/Scripts/activate.bat | 34 - .venv/Scripts/deactivate.bat | 22 - .venv/Scripts/pip.exe | Bin 108425 -> 0 bytes .venv/Scripts/pip3.11.exe | Bin 108425 -> 0 bytes .venv/Scripts/pip3.exe | Bin 108425 -> 0 bytes .venv/Scripts/pyrsa-decrypt.exe | Bin 108416 -> 0 bytes .venv/Scripts/pyrsa-encrypt.exe | Bin 108416 -> 0 bytes .venv/Scripts/pyrsa-keygen.exe | Bin 108414 -> 0 bytes .venv/Scripts/pyrsa-priv2pub.exe | Bin 108437 -> 0 bytes .venv/Scripts/pyrsa-sign.exe | Bin 108410 -> 0 bytes .venv/Scripts/pyrsa-verify.exe | Bin 108414 -> 0 bytes .venv/Scripts/python.exe | Bin 268160 -> 0 bytes .venv/Scripts/pythonw.exe | Bin 256376 -> 0 bytes .venv/Scripts/uvicorn.exe | Bin 108415 -> 0 bytes .venv/pyvenv.cfg | 5 - project-bolt-sb1-fuj7dqxp(1).zip | Bin 52036 -> 0 bytes 1994 files changed, 780188 deletions(-) delete mode 100644 .bolt/supabase_discarded_migrations/0001_graceful_ember.sql delete mode 100644 .bolt/supabase_discarded_migrations/0001_royal_fire.sql delete mode 100644 .bolt/supabase_discarded_migrations/0001_still_morning.sql delete mode 100644 .bolt/supabase_discarded_migrations/0001_sweet_shore.sql delete mode 100644 .bolt/supabase_discarded_migrations/0003_proud_cloud.sql delete mode 100644 .bolt/supabase_discarded_migrations/0003_winter_morning.sql delete mode 100644 .bolt/supabase_discarded_migrations/0003_withered_frost.sql delete mode 100644 .bolt/supabase_discarded_migrations/0004_maroon_shore.sql delete mode 100644 .bolt/supabase_discarded_migrations/0004_pink_sun.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_bronze_castle.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_fierce_mouse.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_hidden_king.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_lively_wind.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_raspy_rain.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_snowy_fog.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_steep_cliff.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_tiny_trail.sql delete mode 100644 .bolt/supabase_discarded_migrations/0007_wandering_salad.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_ancient_wind.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_damp_paper.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_lingering_breeze.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_nameless_sea.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_noisy_prism.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_old_summit.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_proud_queen.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_raspy_cloud.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_snowy_ember.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_velvet_bonus.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_wandering_fire.sql delete mode 100644 .bolt/supabase_discarded_migrations/0008_wooden_star.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_emerald_dust.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_fading_grove.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_mellow_frog.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_noisy_shadow.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_snowy_cake.sql delete mode 100644 .bolt/supabase_discarded_migrations/0009_tender_morning.sql delete mode 100644 .venv/Include/site/python3.11/greenlet/greenlet.h delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/_cffi_backend.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/_distutils_hack/__init__.py delete mode 100644 .venv/Lib/site-packages/_distutils_hack/override.py delete mode 100644 .venv/Lib/site-packages/annotated_types-0.7.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/annotated_types-0.7.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/annotated_types-0.7.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/annotated_types-0.7.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE delete mode 100644 .venv/Lib/site-packages/annotated_types/__init__.py delete mode 100644 .venv/Lib/site-packages/annotated_types/py.typed delete mode 100644 .venv/Lib/site-packages/annotated_types/test_cases.py delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/anyio-4.7.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/anyio/__init__.py delete mode 100644 .venv/Lib/site-packages/anyio/_backends/__init__.py delete mode 100644 .venv/Lib/site-packages/anyio/_backends/_asyncio.py delete mode 100644 .venv/Lib/site-packages/anyio/_backends/_trio.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/__init__.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_eventloop.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_exceptions.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_fileio.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_resources.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_signals.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_sockets.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_streams.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_subprocesses.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_synchronization.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_tasks.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_testing.py delete mode 100644 .venv/Lib/site-packages/anyio/_core/_typedattr.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/__init__.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_eventloop.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_resources.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_sockets.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_streams.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_subprocesses.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_tasks.py delete mode 100644 .venv/Lib/site-packages/anyio/abc/_testing.py delete mode 100644 .venv/Lib/site-packages/anyio/from_thread.py delete mode 100644 .venv/Lib/site-packages/anyio/lowlevel.py delete mode 100644 .venv/Lib/site-packages/anyio/py.typed delete mode 100644 .venv/Lib/site-packages/anyio/pytest_plugin.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/__init__.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/buffered.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/file.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/memory.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/stapled.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/text.py delete mode 100644 .venv/Lib/site-packages/anyio/streams/tls.py delete mode 100644 .venv/Lib/site-packages/anyio/to_process.py delete mode 100644 .venv/Lib/site-packages/anyio/to_thread.py delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/bcrypt-4.2.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/bcrypt/__init__.py delete mode 100644 .venv/Lib/site-packages/bcrypt/__init__.pyi delete mode 100644 .venv/Lib/site-packages/bcrypt/_bcrypt.pyd delete mode 100644 .venv/Lib/site-packages/bcrypt/py.typed delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/cffi-1.17.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/cffi/__init__.py delete mode 100644 .venv/Lib/site-packages/cffi/_cffi_errors.h delete mode 100644 .venv/Lib/site-packages/cffi/_cffi_include.h delete mode 100644 .venv/Lib/site-packages/cffi/_embedding.h delete mode 100644 .venv/Lib/site-packages/cffi/_imp_emulation.py delete mode 100644 .venv/Lib/site-packages/cffi/_shimmed_dist_utils.py delete mode 100644 .venv/Lib/site-packages/cffi/api.py delete mode 100644 .venv/Lib/site-packages/cffi/backend_ctypes.py delete mode 100644 .venv/Lib/site-packages/cffi/cffi_opcode.py delete mode 100644 .venv/Lib/site-packages/cffi/commontypes.py delete mode 100644 .venv/Lib/site-packages/cffi/cparser.py delete mode 100644 .venv/Lib/site-packages/cffi/error.py delete mode 100644 .venv/Lib/site-packages/cffi/ffiplatform.py delete mode 100644 .venv/Lib/site-packages/cffi/lock.py delete mode 100644 .venv/Lib/site-packages/cffi/model.py delete mode 100644 .venv/Lib/site-packages/cffi/parse_c_type.h delete mode 100644 .venv/Lib/site-packages/cffi/pkgconfig.py delete mode 100644 .venv/Lib/site-packages/cffi/recompiler.py delete mode 100644 .venv/Lib/site-packages/cffi/setuptools_ext.py delete mode 100644 .venv/Lib/site-packages/cffi/vengine_cpy.py delete mode 100644 .venv/Lib/site-packages/cffi/vengine_gen.py delete mode 100644 .venv/Lib/site-packages/cffi/verifier.py delete mode 100644 .venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt delete mode 100644 .venv/Lib/site-packages/click-8.1.8.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/click-8.1.8.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/click/__init__.py delete mode 100644 .venv/Lib/site-packages/click/_compat.py delete mode 100644 .venv/Lib/site-packages/click/_termui_impl.py delete mode 100644 .venv/Lib/site-packages/click/_textwrap.py delete mode 100644 .venv/Lib/site-packages/click/_winconsole.py delete mode 100644 .venv/Lib/site-packages/click/core.py delete mode 100644 .venv/Lib/site-packages/click/decorators.py delete mode 100644 .venv/Lib/site-packages/click/exceptions.py delete mode 100644 .venv/Lib/site-packages/click/formatting.py delete mode 100644 .venv/Lib/site-packages/click/globals.py delete mode 100644 .venv/Lib/site-packages/click/parser.py delete mode 100644 .venv/Lib/site-packages/click/py.typed delete mode 100644 .venv/Lib/site-packages/click/shell_completion.py delete mode 100644 .venv/Lib/site-packages/click/termui.py delete mode 100644 .venv/Lib/site-packages/click/testing.py delete mode 100644 .venv/Lib/site-packages/click/types.py delete mode 100644 .venv/Lib/site-packages/click/utils.py delete mode 100644 .venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt delete mode 100644 .venv/Lib/site-packages/colorama/__init__.py delete mode 100644 .venv/Lib/site-packages/colorama/ansi.py delete mode 100644 .venv/Lib/site-packages/colorama/ansitowin32.py delete mode 100644 .venv/Lib/site-packages/colorama/initialise.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/__init__.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/ansi_test.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/ansitowin32_test.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/initialise_test.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/isatty_test.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/utils.py delete mode 100644 .venv/Lib/site-packages/colorama/tests/winterm_test.py delete mode 100644 .venv/Lib/site-packages/colorama/win32.py delete mode 100644 .venv/Lib/site-packages/colorama/winterm.py delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.APACHE delete mode 100644 .venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.BSD delete mode 100644 .venv/Lib/site-packages/cryptography/__about__.py delete mode 100644 .venv/Lib/site-packages/cryptography/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/exceptions.py delete mode 100644 .venv/Lib/site-packages/cryptography/fernet.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/_oid.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/backends/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust.pyd delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/decrepit/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/cmac.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/hashes.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/hmac.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/argon2.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/padding.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py delete mode 100644 .venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py delete mode 100644 .venv/Lib/site-packages/cryptography/py.typed delete mode 100644 .venv/Lib/site-packages/cryptography/utils.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/__init__.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/base.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/certificate_transparency.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/extensions.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/general_name.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/name.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/ocsp.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/oid.py delete mode 100644 .venv/Lib/site-packages/cryptography/x509/verification.py delete mode 100644 .venv/Lib/site-packages/distutils-precedence.pth delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/ecdsa-0.19.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/ecdsa/__init__.py delete mode 100644 .venv/Lib/site-packages/ecdsa/_compat.py delete mode 100644 .venv/Lib/site-packages/ecdsa/_rwlock.py delete mode 100644 .venv/Lib/site-packages/ecdsa/_sha3.py delete mode 100644 .venv/Lib/site-packages/ecdsa/_version.py delete mode 100644 .venv/Lib/site-packages/ecdsa/curves.py delete mode 100644 .venv/Lib/site-packages/ecdsa/der.py delete mode 100644 .venv/Lib/site-packages/ecdsa/ecdh.py delete mode 100644 .venv/Lib/site-packages/ecdsa/ecdsa.py delete mode 100644 .venv/Lib/site-packages/ecdsa/eddsa.py delete mode 100644 .venv/Lib/site-packages/ecdsa/ellipticcurve.py delete mode 100644 .venv/Lib/site-packages/ecdsa/errors.py delete mode 100644 .venv/Lib/site-packages/ecdsa/keys.py delete mode 100644 .venv/Lib/site-packages/ecdsa/numbertheory.py delete mode 100644 .venv/Lib/site-packages/ecdsa/rfc6979.py delete mode 100644 .venv/Lib/site-packages/ecdsa/ssh.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_curves.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_der.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_ecdh.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_ecdsa.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_eddsa.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_ellipticcurve.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_jacobi.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_keys.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_malformed_sigs.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_numbertheory.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_pyecdsa.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_rw_lock.py delete mode 100644 .venv/Lib/site-packages/ecdsa/test_sha3.py delete mode 100644 .venv/Lib/site-packages/ecdsa/util.py delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/fastapi-0.110.0.dist-info/licenses/LICENSE delete mode 100644 .venv/Lib/site-packages/fastapi/__init__.py delete mode 100644 .venv/Lib/site-packages/fastapi/_compat.py delete mode 100644 .venv/Lib/site-packages/fastapi/applications.py delete mode 100644 .venv/Lib/site-packages/fastapi/background.py delete mode 100644 .venv/Lib/site-packages/fastapi/concurrency.py delete mode 100644 .venv/Lib/site-packages/fastapi/datastructures.py delete mode 100644 .venv/Lib/site-packages/fastapi/dependencies/__init__.py delete mode 100644 .venv/Lib/site-packages/fastapi/dependencies/models.py delete mode 100644 .venv/Lib/site-packages/fastapi/dependencies/utils.py delete mode 100644 .venv/Lib/site-packages/fastapi/encoders.py delete mode 100644 .venv/Lib/site-packages/fastapi/exception_handlers.py delete mode 100644 .venv/Lib/site-packages/fastapi/exceptions.py delete mode 100644 .venv/Lib/site-packages/fastapi/logger.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/__init__.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/cors.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/gzip.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/httpsredirect.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/trustedhost.py delete mode 100644 .venv/Lib/site-packages/fastapi/middleware/wsgi.py delete mode 100644 .venv/Lib/site-packages/fastapi/openapi/__init__.py delete mode 100644 .venv/Lib/site-packages/fastapi/openapi/constants.py delete mode 100644 .venv/Lib/site-packages/fastapi/openapi/docs.py delete mode 100644 .venv/Lib/site-packages/fastapi/openapi/models.py delete mode 100644 .venv/Lib/site-packages/fastapi/openapi/utils.py delete mode 100644 .venv/Lib/site-packages/fastapi/param_functions.py delete mode 100644 .venv/Lib/site-packages/fastapi/params.py delete mode 100644 .venv/Lib/site-packages/fastapi/py.typed delete mode 100644 .venv/Lib/site-packages/fastapi/requests.py delete mode 100644 .venv/Lib/site-packages/fastapi/responses.py delete mode 100644 .venv/Lib/site-packages/fastapi/routing.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/__init__.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/api_key.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/base.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/http.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/oauth2.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/open_id_connect_url.py delete mode 100644 .venv/Lib/site-packages/fastapi/security/utils.py delete mode 100644 .venv/Lib/site-packages/fastapi/staticfiles.py delete mode 100644 .venv/Lib/site-packages/fastapi/templating.py delete mode 100644 .venv/Lib/site-packages/fastapi/testclient.py delete mode 100644 .venv/Lib/site-packages/fastapi/types.py delete mode 100644 .venv/Lib/site-packages/fastapi/utils.py delete mode 100644 .venv/Lib/site-packages/fastapi/websockets.py delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/AUTHORS delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE.PSF delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/greenlet-3.1.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/greenlet/CObjects.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/PyGreenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/PyGreenlet.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/PyModule.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TExceptionState.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TGreenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TGreenlet.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TMainGreenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TPythonState.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TStackState.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TThreadState.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/TUserGreenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/__init__.py delete mode 100644 .venv/Lib/site-packages/greenlet/_greenlet.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet.h delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_allocator.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_cpython_add_pending.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_internal.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_refs.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp delete mode 100644 .venv/Lib/site-packages/greenlet/platform/__init__.py delete mode 100644 .venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc64_linux.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc_aix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc_linux.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc_macosx.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_ppc_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_riscv_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_s390_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_sh_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_sparc_sun_gcc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x32_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x64_masm.asm delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x64_masm.obj delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x64_msvc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x86_msvc.h delete mode 100644 .venv/Lib/site-packages/greenlet/platform/switch_x86_unix.h delete mode 100644 .venv/Lib/site-packages/greenlet/slp_platformselect.h delete mode 100644 .venv/Lib/site-packages/greenlet/tests/__init__.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/_test_extension.c delete mode 100644 .venv/Lib/site-packages/greenlet/tests/_test_extension.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cpp delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_clearing_run_switches.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_cpp_exception.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_initialstub_already_started.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_slp_switch.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets2.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/fail_switch_two_greenlets.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/leakcheck.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_contextvars.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_cpp.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_extension_interface.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_gc.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_generator.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_generator_nested.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_greenlet.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_greenlet_trash.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_leaks.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_stack_saved.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_throw.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_tracing.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_version.py delete mode 100644 .venv/Lib/site-packages/greenlet/tests/test_weakref.py delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/LICENSE.txt delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/h11-0.14.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/h11/__init__.py delete mode 100644 .venv/Lib/site-packages/h11/_abnf.py delete mode 100644 .venv/Lib/site-packages/h11/_connection.py delete mode 100644 .venv/Lib/site-packages/h11/_events.py delete mode 100644 .venv/Lib/site-packages/h11/_headers.py delete mode 100644 .venv/Lib/site-packages/h11/_readers.py delete mode 100644 .venv/Lib/site-packages/h11/_receivebuffer.py delete mode 100644 .venv/Lib/site-packages/h11/_state.py delete mode 100644 .venv/Lib/site-packages/h11/_util.py delete mode 100644 .venv/Lib/site-packages/h11/_version.py delete mode 100644 .venv/Lib/site-packages/h11/_writers.py delete mode 100644 .venv/Lib/site-packages/h11/py.typed delete mode 100644 .venv/Lib/site-packages/h11/tests/__init__.py delete mode 100644 .venv/Lib/site-packages/h11/tests/data/test-file delete mode 100644 .venv/Lib/site-packages/h11/tests/helpers.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_connection.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_events.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_headers.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_helpers.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_io.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_receivebuffer.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_state.py delete mode 100644 .venv/Lib/site-packages/h11/tests/test_util.py delete mode 100644 .venv/Lib/site-packages/idna-3.10.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/idna-3.10.dist-info/LICENSE.md delete mode 100644 .venv/Lib/site-packages/idna-3.10.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/idna-3.10.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/idna-3.10.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/idna/__init__.py delete mode 100644 .venv/Lib/site-packages/idna/codec.py delete mode 100644 .venv/Lib/site-packages/idna/compat.py delete mode 100644 .venv/Lib/site-packages/idna/core.py delete mode 100644 .venv/Lib/site-packages/idna/idnadata.py delete mode 100644 .venv/Lib/site-packages/idna/intranges.py delete mode 100644 .venv/Lib/site-packages/idna/package_data.py delete mode 100644 .venv/Lib/site-packages/idna/py.typed delete mode 100644 .venv/Lib/site-packages/idna/uts46data.py delete mode 100644 .venv/Lib/site-packages/jose/__init__.py delete mode 100644 .venv/Lib/site-packages/jose/backends/__init__.py delete mode 100644 .venv/Lib/site-packages/jose/backends/_asn1.py delete mode 100644 .venv/Lib/site-packages/jose/backends/base.py delete mode 100644 .venv/Lib/site-packages/jose/backends/cryptography_backend.py delete mode 100644 .venv/Lib/site-packages/jose/backends/ecdsa_backend.py delete mode 100644 .venv/Lib/site-packages/jose/backends/native.py delete mode 100644 .venv/Lib/site-packages/jose/backends/rsa_backend.py delete mode 100644 .venv/Lib/site-packages/jose/constants.py delete mode 100644 .venv/Lib/site-packages/jose/exceptions.py delete mode 100644 .venv/Lib/site-packages/jose/jwe.py delete mode 100644 .venv/Lib/site-packages/jose/jwk.py delete mode 100644 .venv/Lib/site-packages/jose/jws.py delete mode 100644 .venv/Lib/site-packages/jose/jwt.py delete mode 100644 .venv/Lib/site-packages/jose/utils.py delete mode 100644 .venv/Lib/site-packages/multipart/__init__.py delete mode 100644 .venv/Lib/site-packages/multipart/decoders.py delete mode 100644 .venv/Lib/site-packages/multipart/exceptions.py delete mode 100644 .venv/Lib/site-packages/multipart/multipart.py delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/passlib-1.7.4.dist-info/zip-safe delete mode 100644 .venv/Lib/site-packages/passlib/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/_data/wordsets/bip39.txt delete mode 100644 .venv/Lib/site-packages/passlib/_data/wordsets/eff_long.txt delete mode 100644 .venv/Lib/site-packages/passlib/_data/wordsets/eff_prefixed.txt delete mode 100644 .venv/Lib/site-packages/passlib/_data/wordsets/eff_short.txt delete mode 100644 .venv/Lib/site-packages/passlib/apache.py delete mode 100644 .venv/Lib/site-packages/passlib/apps.py delete mode 100644 .venv/Lib/site-packages/passlib/context.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/_blowfish/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/_blowfish/_gen_files.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/_blowfish/base.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/_blowfish/unrolled.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/_md4.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/des.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/digest.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py delete mode 100644 .venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py delete mode 100644 .venv/Lib/site-packages/passlib/exc.py delete mode 100644 .venv/Lib/site-packages/passlib/ext/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/ext/django/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/ext/django/models.py delete mode 100644 .venv/Lib/site-packages/passlib/ext/django/utils.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/argon2.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/bcrypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/cisco.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/des_crypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/digests.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/django.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/fshp.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/ldap_digests.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/md5_crypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/misc.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/mssql.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/mysql.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/oracle.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/pbkdf2.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/phpass.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/postgres.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/roundup.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/scram.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/scrypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/sha1_crypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/sha2_crypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/sun_md5_crypt.py delete mode 100644 .venv/Lib/site-packages/passlib/handlers/windows.py delete mode 100644 .venv/Lib/site-packages/passlib/hash.py delete mode 100644 .venv/Lib/site-packages/passlib/hosts.py delete mode 100644 .venv/Lib/site-packages/passlib/ifc.py delete mode 100644 .venv/Lib/site-packages/passlib/pwd.py delete mode 100644 .venv/Lib/site-packages/passlib/registry.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/__main__.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/_test_bad_register.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/backports.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/sample1.cfg delete mode 100644 .venv/Lib/site-packages/passlib/tests/sample1b.cfg delete mode 100644 .venv/Lib/site-packages/passlib/tests/sample1c.cfg delete mode 100644 .venv/Lib/site-packages/passlib/tests/sample_config_1s.cfg delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_apache.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_apps.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_context.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_context_deprecated.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_crypto_builtin_md4.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_crypto_des.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_crypto_digest.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_ext_django.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_ext_django_source.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_django.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_hosts.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_pwd.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_registry.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_totp.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_utils.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_utils_handlers.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_utils_md4.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_utils_pbkdf2.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/test_win32.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/tox_support.py delete mode 100644 .venv/Lib/site-packages/passlib/tests/utils.py delete mode 100644 .venv/Lib/site-packages/passlib/totp.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/binary.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/compat/__init__.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/compat/_ordered_dict.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/decor.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/des.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/handlers.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/md4.py delete mode 100644 .venv/Lib/site-packages/passlib/utils/pbkdf2.py delete mode 100644 .venv/Lib/site-packages/passlib/win32.py delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/LICENSE.txt delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/pip-22.3.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/pip/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/__pip-runner__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/build_env.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cache.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/autocompletion.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/base_command.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/command_context.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/main.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/main_parser.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/parser.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/progress_bars.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/req_command.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/spinners.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/cli/status_codes.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/cache.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/check.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/completion.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/configuration.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/debug.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/download.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/freeze.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/hash.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/help.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/index.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/inspect.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/install.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/list.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/search.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/show.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/uninstall.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/commands/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/configuration.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/distributions/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/distributions/base.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/distributions/installed.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/distributions/sdist.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/distributions/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/exceptions.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/index/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/index/collector.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/index/package_finder.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/index/sources.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/locations/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/locations/_distutils.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/locations/base.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/main.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/_json.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/base.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/candidate.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/direct_url.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/format_control.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/index.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/installation_report.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/link.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/scheme.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/search_scope.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/selection_prefs.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/target_python.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/models/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/auth.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/cache.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/download.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/session.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/utils.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/network/xmlrpc.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/check.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/freeze.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/install/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/install/legacy.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/install/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/operations/prepare.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/pyproject.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/constructors.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/req_file.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/req_install.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/req_set.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/req/req_uninstall.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/base.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/legacy/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/self_outdated_check.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/_log.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/appdirs.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/compat.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/datetime.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/deprecation.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/distutils_args.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/egg_link.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/encoding.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/entrypoints.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/filesystem.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/filetypes.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/glibc.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/hashes.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/logging.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/misc.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/models.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/packaging.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/subprocess.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/temp_dir.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/unpacking.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/urls.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/virtualenv.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/utils/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/bazaar.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/git.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/mercurial.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/subversion.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py delete mode 100644 .venv/Lib/site-packages/pip/_internal/wheel_builder.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/certifi/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/certifi/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem delete mode 100644 .venv/Lib/site-packages/pip/_vendor/certifi/core.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/enums.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/escprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/escsm.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/metadata/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/chardet/version.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/ansi.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/initialise.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/win32.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/colorama/winterm.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/database.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/index.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/locators.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/manifest.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/markers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/metadata.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/resources.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/scripts.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/t32.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/t64-arm.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/t64.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/util.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/version.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/w32.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/w64.exe delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distlib/wheel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distro/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distro/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/distro/distro.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/codec.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/core.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/idnadata.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/intranges.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/package_data.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/idna/uts46data.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/msgpack/ext.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/__about__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/_structures.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/markers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/requirements.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/tags.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/utils.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/packaging/version.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/_compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/build.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/check.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/dirtools.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/meta.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/android.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/api.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/version.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/console.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/filter.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatter.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/img.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/irc.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/latex.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/other.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/rtf.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/svg.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/formatters/terminal256.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/lexer.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/lexers/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/lexers/_mapping.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/lexers/python.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/modeline.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/plugin.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/regexopt.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/scanner.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/sphinxext.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/style.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/styles/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/token.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/unistring.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pygments/util.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/actions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/common.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/core.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/diagram/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/results.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/pyparsing/util.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/__version__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/adapters.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/api.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/auth.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/certs.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/compat.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/cookies.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/exceptions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/help.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/hooks.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/models.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/packages.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/sessions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/status_codes.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/structures.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/requests/utils.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/__main__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_export_format.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_extension.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_inspect.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_log_render.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_loop.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_palettes.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_pick.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_ratio.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_spinners.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_stack.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_timer.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_windows.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/_wrap.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/abc.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/align.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/ansi.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/bar.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/box.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/cells.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/color.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/columns.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/console.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/constrain.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/containers.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/control.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/default_styles.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/diagnose.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/emoji.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/errors.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/filesize.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/highlighter.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/json.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/jupyter.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/layout.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/live.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/live_render.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/logging.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/markup.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/measure.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/padding.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/pager.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/palette.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/panel.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/pretty.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/progress.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/progress_bar.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/prompt.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/protocol.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/region.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/repr.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/rule.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/scope.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/screen.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/segment.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/spinner.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/status.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/style.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/styled.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/syntax.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/table.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/terminal_theme.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/text.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/theme.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/themes.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/traceback.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/rich/tree.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/six.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/_asyncio.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/_utils.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/after.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/before.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/before_sleep.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/nap.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/retry.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/stop.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/tornadoweb.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tenacity/wait.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tomli/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tomli/_parser.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tomli/_re.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/tomli/_types.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/typing_extensions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/_collections.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/_version.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/connection.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/securetransport.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/contrib/socks.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/exceptions.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/fields.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/filepost.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/packages/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/packages/six.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/poolmanager.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/request.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/response.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/connection.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/proxy.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/queue.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/request.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/response.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/retry.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/ssltransport.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/timeout.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/url.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/urllib3/util/wait.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/vendor.txt delete mode 100644 .venv/Lib/site-packages/pip/_vendor/webencodings/__init__.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/webencodings/labels.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/webencodings/mklabels.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/webencodings/tests.py delete mode 100644 .venv/Lib/site-packages/pip/_vendor/webencodings/x_user_defined.py delete mode 100644 .venv/Lib/site-packages/pip/py.typed delete mode 100644 .venv/Lib/site-packages/pkg_resources/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/appdirs.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_adapters.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_itertools.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/jaraco/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/jaraco/functools.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/jaraco/text/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/_structures.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/packaging/version.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/_vendor/zipp.py delete mode 100644 .venv/Lib/site-packages/pkg_resources/extern/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/LICENSE.rst delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/pyasn1-0.6.1.dist-info/zip-safe delete mode 100644 .venv/Lib/site-packages/pyasn1/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/ber/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/ber/decoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/ber/encoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/ber/eoo.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/cer/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/cer/decoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/cer/encoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/der/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/der/decoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/der/encoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/native/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/native/decoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/native/encoder.py delete mode 100644 .venv/Lib/site-packages/pyasn1/codec/streaming.py delete mode 100644 .venv/Lib/site-packages/pyasn1/compat/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/compat/integer.py delete mode 100644 .venv/Lib/site-packages/pyasn1/debug.py delete mode 100644 .venv/Lib/site-packages/pyasn1/error.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/__init__.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/base.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/char.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/constraint.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/error.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/namedtype.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/namedval.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/opentype.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/tag.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/tagmap.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/univ.py delete mode 100644 .venv/Lib/site-packages/pyasn1/type/useful.py delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/pycparser-2.22.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/pycparser/__init__.py delete mode 100644 .venv/Lib/site-packages/pycparser/_ast_gen.py delete mode 100644 .venv/Lib/site-packages/pycparser/_build_tables.py delete mode 100644 .venv/Lib/site-packages/pycparser/_c_ast.cfg delete mode 100644 .venv/Lib/site-packages/pycparser/ast_transforms.py delete mode 100644 .venv/Lib/site-packages/pycparser/c_ast.py delete mode 100644 .venv/Lib/site-packages/pycparser/c_generator.py delete mode 100644 .venv/Lib/site-packages/pycparser/c_lexer.py delete mode 100644 .venv/Lib/site-packages/pycparser/c_parser.py delete mode 100644 .venv/Lib/site-packages/pycparser/lextab.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/__init__.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/cpp.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/ctokens.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/lex.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/yacc.py delete mode 100644 .venv/Lib/site-packages/pycparser/ply/ygen.py delete mode 100644 .venv/Lib/site-packages/pycparser/plyparser.py delete mode 100644 .venv/Lib/site-packages/pycparser/yacctab.py delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/pydantic-2.6.3.dist-info/licenses/LICENSE delete mode 100644 .venv/Lib/site-packages/pydantic/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_config.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_core_metadata.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_core_utils.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_dataclasses.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_decorators.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_decorators_v1.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_discriminated_union.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_fields.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_forward_ref.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_generate_schema.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_generics.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_git.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_internal_dataclass.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_known_annotated_metadata.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_mock_val_ser.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_model_construction.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_repr.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_schema_generation_shared.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_signature.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_std_types_schema.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_typing_extra.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_utils.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_validate_call.py delete mode 100644 .venv/Lib/site-packages/pydantic/_internal/_validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/_migration.py delete mode 100644 .venv/Lib/site-packages/pydantic/alias_generators.py delete mode 100644 .venv/Lib/site-packages/pydantic/aliases.py delete mode 100644 .venv/Lib/site-packages/pydantic/annotated_handlers.py delete mode 100644 .venv/Lib/site-packages/pydantic/class_validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/color.py delete mode 100644 .venv/Lib/site-packages/pydantic/config.py delete mode 100644 .venv/Lib/site-packages/pydantic/dataclasses.py delete mode 100644 .venv/Lib/site-packages/pydantic/datetime_parse.py delete mode 100644 .venv/Lib/site-packages/pydantic/decorator.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/class_validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/config.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/copy_internals.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/decorator.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/json.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/parse.py delete mode 100644 .venv/Lib/site-packages/pydantic/deprecated/tools.py delete mode 100644 .venv/Lib/site-packages/pydantic/env_settings.py delete mode 100644 .venv/Lib/site-packages/pydantic/error_wrappers.py delete mode 100644 .venv/Lib/site-packages/pydantic/errors.py delete mode 100644 .venv/Lib/site-packages/pydantic/fields.py delete mode 100644 .venv/Lib/site-packages/pydantic/functional_serializers.py delete mode 100644 .venv/Lib/site-packages/pydantic/functional_validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/generics.py delete mode 100644 .venv/Lib/site-packages/pydantic/json.py delete mode 100644 .venv/Lib/site-packages/pydantic/json_schema.py delete mode 100644 .venv/Lib/site-packages/pydantic/main.py delete mode 100644 .venv/Lib/site-packages/pydantic/mypy.py delete mode 100644 .venv/Lib/site-packages/pydantic/networks.py delete mode 100644 .venv/Lib/site-packages/pydantic/parse.py delete mode 100644 .venv/Lib/site-packages/pydantic/plugin/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic/plugin/_loader.py delete mode 100644 .venv/Lib/site-packages/pydantic/plugin/_schema_validator.py delete mode 100644 .venv/Lib/site-packages/pydantic/py.typed delete mode 100644 .venv/Lib/site-packages/pydantic/root_model.py delete mode 100644 .venv/Lib/site-packages/pydantic/schema.py delete mode 100644 .venv/Lib/site-packages/pydantic/tools.py delete mode 100644 .venv/Lib/site-packages/pydantic/type_adapter.py delete mode 100644 .venv/Lib/site-packages/pydantic/types.py delete mode 100644 .venv/Lib/site-packages/pydantic/typing.py delete mode 100644 .venv/Lib/site-packages/pydantic/utils.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/_hypothesis_plugin.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/annotated_types.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/class_validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/color.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/config.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/dataclasses.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/datetime_parse.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/decorator.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/env_settings.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/error_wrappers.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/errors.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/fields.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/generics.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/json.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/main.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/mypy.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/networks.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/parse.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/py.typed delete mode 100644 .venv/Lib/site-packages/pydantic/v1/schema.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/tools.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/types.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/typing.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/utils.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/v1/version.py delete mode 100644 .venv/Lib/site-packages/pydantic/validate_call_decorator.py delete mode 100644 .venv/Lib/site-packages/pydantic/validators.py delete mode 100644 .venv/Lib/site-packages/pydantic/version.py delete mode 100644 .venv/Lib/site-packages/pydantic/warnings.py delete mode 100644 .venv/Lib/site-packages/pydantic_core-2.16.3.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/pydantic_core-2.16.3.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/pydantic_core-2.16.3.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/pydantic_core-2.16.3.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/pydantic_core-2.16.3.dist-info/license_files/LICENSE delete mode 100644 .venv/Lib/site-packages/pydantic_core/__init__.py delete mode 100644 .venv/Lib/site-packages/pydantic_core/_pydantic_core.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/pydantic_core/_pydantic_core.pyi delete mode 100644 .venv/Lib/site-packages/pydantic_core/core_schema.py delete mode 100644 .venv/Lib/site-packages/pydantic_core/py.typed delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/python_jose-3.3.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/python_multipart-0.0.9.dist-info/licenses/LICENSE.txt delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/rsa-4.9.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/rsa/__init__.py delete mode 100644 .venv/Lib/site-packages/rsa/asn1.py delete mode 100644 .venv/Lib/site-packages/rsa/cli.py delete mode 100644 .venv/Lib/site-packages/rsa/common.py delete mode 100644 .venv/Lib/site-packages/rsa/core.py delete mode 100644 .venv/Lib/site-packages/rsa/key.py delete mode 100644 .venv/Lib/site-packages/rsa/parallel.py delete mode 100644 .venv/Lib/site-packages/rsa/pem.py delete mode 100644 .venv/Lib/site-packages/rsa/pkcs1.py delete mode 100644 .venv/Lib/site-packages/rsa/pkcs1_v2.py delete mode 100644 .venv/Lib/site-packages/rsa/prime.py delete mode 100644 .venv/Lib/site-packages/rsa/py.typed delete mode 100644 .venv/Lib/site-packages/rsa/randnum.py delete mode 100644 .venv/Lib/site-packages/rsa/transform.py delete mode 100644 .venv/Lib/site-packages/rsa/util.py delete mode 100644 .venv/Lib/site-packages/rust/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-cffi/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-keepalive/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-key-parsing/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-openssl/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-x509-verification/Cargo.toml delete mode 100644 .venv/Lib/site-packages/rust/cryptography-x509/Cargo.toml delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/setuptools-65.5.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/setuptools/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_deprecation_warning.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/_collections.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/_functools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/_macos_compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/_msvccompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/archive_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/bcppcompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/ccompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/cmd.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/_framework_compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/bdist.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/build.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/build_clib.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/build_ext.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/build_py.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/build_scripts.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/check.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/clean.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/config.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install_data.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install_egg_info.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install_headers.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install_lib.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/install_scripts.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/py37compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/register.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/sdist.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/command/upload.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/config.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/core.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/debug.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/dep_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/dir_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/dist.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/errors.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/extension.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/fancy_getopt.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/file_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/filelist.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/log.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/msvc9compiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/msvccompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/py38compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/py39compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/spawn.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/sysconfig.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/text_file.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/unixccompiler.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/version.py delete mode 100644 .venv/Lib/site-packages/setuptools/_distutils/versionpredicate.py delete mode 100644 .venv/Lib/site-packages/setuptools/_entry_points.py delete mode 100644 .venv/Lib/site-packages/setuptools/_imp.py delete mode 100644 .venv/Lib/site-packages/setuptools/_importlib.py delete mode 100644 .venv/Lib/site-packages/setuptools/_itertools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_path.py delete mode 100644 .venv/Lib/site-packages/setuptools/_reqs.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_collections.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_functools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_itertools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_metadata/_text.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_adapters.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_itertools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/jaraco/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/jaraco/context.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/jaraco/functools.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/jaraco/text/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/more_itertools/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/more_itertools/more.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/more_itertools/recipes.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/ordered_set.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/__about__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/_structures.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/markers.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/requirements.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/tags.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/utils.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/packaging/version.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/common.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/core.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/results.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/pyparsing/util.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/tomli/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/tomli/_parser.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/tomli/_re.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/tomli/_types.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/typing_extensions.py delete mode 100644 .venv/Lib/site-packages/setuptools/_vendor/zipp.py delete mode 100644 .venv/Lib/site-packages/setuptools/archive_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/build_meta.py delete mode 100644 .venv/Lib/site-packages/setuptools/cli-32.exe delete mode 100644 .venv/Lib/site-packages/setuptools/cli-64.exe delete mode 100644 .venv/Lib/site-packages/setuptools/cli-arm64.exe delete mode 100644 .venv/Lib/site-packages/setuptools/cli.exe delete mode 100644 .venv/Lib/site-packages/setuptools/command/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/alias.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/bdist_egg.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/bdist_rpm.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/build.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/build_clib.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/build_ext.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/build_py.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/develop.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/dist_info.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/easy_install.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/editable_wheel.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/egg_info.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/install.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/install_egg_info.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/install_lib.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/install_scripts.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/launcher manifest.xml delete mode 100644 .venv/Lib/site-packages/setuptools/command/py36compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/register.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/rotate.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/saveopts.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/sdist.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/setopt.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/test.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/upload.py delete mode 100644 .venv/Lib/site-packages/setuptools/command/upload_docs.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/error_reporting.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/extra_validations.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/expand.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/pyprojecttoml.py delete mode 100644 .venv/Lib/site-packages/setuptools/config/setupcfg.py delete mode 100644 .venv/Lib/site-packages/setuptools/dep_util.py delete mode 100644 .venv/Lib/site-packages/setuptools/depends.py delete mode 100644 .venv/Lib/site-packages/setuptools/discovery.py delete mode 100644 .venv/Lib/site-packages/setuptools/dist.py delete mode 100644 .venv/Lib/site-packages/setuptools/errors.py delete mode 100644 .venv/Lib/site-packages/setuptools/extension.py delete mode 100644 .venv/Lib/site-packages/setuptools/extern/__init__.py delete mode 100644 .venv/Lib/site-packages/setuptools/glob.py delete mode 100644 .venv/Lib/site-packages/setuptools/gui-32.exe delete mode 100644 .venv/Lib/site-packages/setuptools/gui-64.exe delete mode 100644 .venv/Lib/site-packages/setuptools/gui-arm64.exe delete mode 100644 .venv/Lib/site-packages/setuptools/gui.exe delete mode 100644 .venv/Lib/site-packages/setuptools/installer.py delete mode 100644 .venv/Lib/site-packages/setuptools/launch.py delete mode 100644 .venv/Lib/site-packages/setuptools/logging.py delete mode 100644 .venv/Lib/site-packages/setuptools/monkey.py delete mode 100644 .venv/Lib/site-packages/setuptools/msvc.py delete mode 100644 .venv/Lib/site-packages/setuptools/namespaces.py delete mode 100644 .venv/Lib/site-packages/setuptools/package_index.py delete mode 100644 .venv/Lib/site-packages/setuptools/py34compat.py delete mode 100644 .venv/Lib/site-packages/setuptools/sandbox.py delete mode 100644 .venv/Lib/site-packages/setuptools/script (dev).tmpl delete mode 100644 .venv/Lib/site-packages/setuptools/script.tmpl delete mode 100644 .venv/Lib/site-packages/setuptools/unicode_utils.py delete mode 100644 .venv/Lib/site-packages/setuptools/version.py delete mode 100644 .venv/Lib/site-packages/setuptools/wheel.py delete mode 100644 .venv/Lib/site-packages/setuptools/windows_support.py delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/six-1.17.0.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/six.py delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.APACHE2 delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/LICENSE.MIT delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/sniffio-1.3.1.dist-info/top_level.txt delete mode 100644 .venv/Lib/site-packages/sniffio/__init__.py delete mode 100644 .venv/Lib/site-packages/sniffio/_impl.py delete mode 100644 .venv/Lib/site-packages/sniffio/_tests/__init__.py delete mode 100644 .venv/Lib/site-packages/sniffio/_tests/test_sniffio.py delete mode 100644 .venv/Lib/site-packages/sniffio/_version.py delete mode 100644 .venv/Lib/site-packages/sniffio/py.typed delete mode 100644 .venv/Lib/site-packages/sqlalchemy/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/connectors/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/connectors/aioodbc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/connectors/asyncio.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/connectors/pyodbc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/collections.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/collections.pyx delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/immutabledict.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/immutabledict.pxd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/immutabledict.pyx delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/processors.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/processors.pyx delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/resultproxy.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/resultproxy.pyx delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/util.cp311-win_amd64.pyd delete mode 100644 .venv/Lib/site-packages/sqlalchemy/cyextension/util.pyx delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/_typing.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/aioodbc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/information_schema.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/json.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/pymssql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mssql/pyodbc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/aiomysql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/asyncmy.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/cymysql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/dml.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/enumerated.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/expression.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/json.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/mariadb.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/mysqldb.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/pymysql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/pyodbc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/reflection.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/reserved_words.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/mysql/types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/dictionary.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/oracledb.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/oracle/types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/_psycopg_common.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/array.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/dml.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ext.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/hstore.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/json.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/named_types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/operators.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg8000.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/pg_catalog.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/ranges.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/postgresql/types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/dml.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/json.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/dialects/type_migration_guidelines.txt delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/_py_processors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/_py_row.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/_py_util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/characteristics.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/create.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/cursor.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/default.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/events.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/interfaces.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/mock.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/processors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/reflection.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/result.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/row.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/strategies.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/url.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/engine/util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/api.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/attr.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/legacy.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/event/registry.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/events.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/exc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/associationproxy.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/engine.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/exc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/result.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/scoping.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/asyncio/session.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/automap.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/baked.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/compiler.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/declarative/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/declarative/extensions.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/horizontal_shard.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/hybrid.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/indexable.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/instrumentation.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mutable.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/apply.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/decl_class.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/infer.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/names.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/plugin.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/mypy/util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/orderinglist.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/ext/serializer.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/future/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/future/engine.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/inspection.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/log.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/_orm_constructors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/_typing.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/attributes.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/bulk_persistence.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/clsregistry.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/collections.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/context.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/decl_api.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/decl_base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/dependency.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/descriptor_props.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/dynamic.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/evaluator.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/events.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/exc.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/identity.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/instrumentation.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/interfaces.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/loading.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/mapped_collection.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/mapper.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/path_registry.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/persistence.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/properties.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/query.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/relationships.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/scoping.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/session.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/state.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/state_changes.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/strategies.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/strategy_options.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/sync.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/unitofwork.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/orm/writeonly.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/pool/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/pool/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/pool/events.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/pool/impl.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/py.typed delete mode 100644 .venv/Lib/site-packages/sqlalchemy/schema.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_dml_constructors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_elements_constructors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_orm_types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_py_util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_selectable_constructors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/_typing.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/annotation.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/cache_key.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/coercions.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/compiler.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/crud.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/ddl.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/default_comparator.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/dml.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/elements.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/events.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/expression.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/functions.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/lambdas.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/naming.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/operators.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/roles.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/schema.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/selectable.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/sqltypes.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/traversals.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/type_api.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/sql/visitors.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/assertions.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/assertsql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/asyncio.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/config.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/engines.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/entities.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/exclusions.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/fixtures/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/fixtures/base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/fixtures/mypy.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/fixtures/orm.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/fixtures/sql.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/pickleable.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/plugin/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/plugin/bootstrap.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/plugin/plugin_base.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/plugin/pytestplugin.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/profiling.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/provision.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/requirements.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/schema.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_cte.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_ddl.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_deprecations.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_dialect.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_insert.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_reflection.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_results.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_rowcount.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_select.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_sequence.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/suite/test_update_delete.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/util.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/testing/warnings.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/types.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/__init__.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/_collections.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/_concurrency_py3k.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/_has_cy.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/_py_collections.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/compat.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/concurrency.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/deprecations.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/langhelpers.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/preloaded.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/queue.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/tool_support.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/topological.py delete mode 100644 .venv/Lib/site-packages/sqlalchemy/util/typing.py delete mode 100644 .venv/Lib/site-packages/starlette-0.36.3.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/starlette-0.36.3.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/starlette-0.36.3.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/starlette-0.36.3.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/starlette-0.36.3.dist-info/licenses/LICENSE.md delete mode 100644 .venv/Lib/site-packages/starlette/__init__.py delete mode 100644 .venv/Lib/site-packages/starlette/_compat.py delete mode 100644 .venv/Lib/site-packages/starlette/_exception_handler.py delete mode 100644 .venv/Lib/site-packages/starlette/_utils.py delete mode 100644 .venv/Lib/site-packages/starlette/applications.py delete mode 100644 .venv/Lib/site-packages/starlette/authentication.py delete mode 100644 .venv/Lib/site-packages/starlette/background.py delete mode 100644 .venv/Lib/site-packages/starlette/concurrency.py delete mode 100644 .venv/Lib/site-packages/starlette/config.py delete mode 100644 .venv/Lib/site-packages/starlette/convertors.py delete mode 100644 .venv/Lib/site-packages/starlette/datastructures.py delete mode 100644 .venv/Lib/site-packages/starlette/endpoints.py delete mode 100644 .venv/Lib/site-packages/starlette/exceptions.py delete mode 100644 .venv/Lib/site-packages/starlette/formparsers.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/__init__.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/authentication.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/base.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/cors.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/errors.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/exceptions.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/gzip.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/httpsredirect.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/sessions.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/trustedhost.py delete mode 100644 .venv/Lib/site-packages/starlette/middleware/wsgi.py delete mode 100644 .venv/Lib/site-packages/starlette/py.typed delete mode 100644 .venv/Lib/site-packages/starlette/requests.py delete mode 100644 .venv/Lib/site-packages/starlette/responses.py delete mode 100644 .venv/Lib/site-packages/starlette/routing.py delete mode 100644 .venv/Lib/site-packages/starlette/schemas.py delete mode 100644 .venv/Lib/site-packages/starlette/staticfiles.py delete mode 100644 .venv/Lib/site-packages/starlette/status.py delete mode 100644 .venv/Lib/site-packages/starlette/templating.py delete mode 100644 .venv/Lib/site-packages/starlette/testclient.py delete mode 100644 .venv/Lib/site-packages/starlette/types.py delete mode 100644 .venv/Lib/site-packages/starlette/websockets.py delete mode 100644 .venv/Lib/site-packages/typing_extensions-4.12.2.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/typing_extensions-4.12.2.dist-info/LICENSE delete mode 100644 .venv/Lib/site-packages/typing_extensions-4.12.2.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/typing_extensions-4.12.2.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/typing_extensions-4.12.2.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/typing_extensions.py delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/INSTALLER delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/METADATA delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/RECORD delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/REQUESTED delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/WHEEL delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/entry_points.txt delete mode 100644 .venv/Lib/site-packages/uvicorn-0.27.1.dist-info/licenses/LICENSE.md delete mode 100644 .venv/Lib/site-packages/uvicorn/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/__main__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/_subprocess.py delete mode 100644 .venv/Lib/site-packages/uvicorn/_types.py delete mode 100644 .venv/Lib/site-packages/uvicorn/config.py delete mode 100644 .venv/Lib/site-packages/uvicorn/importer.py delete mode 100644 .venv/Lib/site-packages/uvicorn/lifespan/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/lifespan/off.py delete mode 100644 .venv/Lib/site-packages/uvicorn/lifespan/on.py delete mode 100644 .venv/Lib/site-packages/uvicorn/logging.py delete mode 100644 .venv/Lib/site-packages/uvicorn/loops/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/loops/asyncio.py delete mode 100644 .venv/Lib/site-packages/uvicorn/loops/auto.py delete mode 100644 .venv/Lib/site-packages/uvicorn/loops/uvloop.py delete mode 100644 .venv/Lib/site-packages/uvicorn/main.py delete mode 100644 .venv/Lib/site-packages/uvicorn/middleware/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/middleware/asgi2.py delete mode 100644 .venv/Lib/site-packages/uvicorn/middleware/message_logger.py delete mode 100644 .venv/Lib/site-packages/uvicorn/middleware/proxy_headers.py delete mode 100644 .venv/Lib/site-packages/uvicorn/middleware/wsgi.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/http/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/http/auto.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/http/flow_control.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/http/h11_impl.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/http/httptools_impl.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/utils.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/websockets/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/websockets/auto.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/websockets/websockets_impl.py delete mode 100644 .venv/Lib/site-packages/uvicorn/protocols/websockets/wsproto_impl.py delete mode 100644 .venv/Lib/site-packages/uvicorn/py.typed delete mode 100644 .venv/Lib/site-packages/uvicorn/server.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/__init__.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/basereload.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/multiprocess.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/statreload.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/watchfilesreload.py delete mode 100644 .venv/Lib/site-packages/uvicorn/supervisors/watchgodreload.py delete mode 100644 .venv/Lib/site-packages/uvicorn/workers.py delete mode 100644 .venv/Scripts/Activate.ps1 delete mode 100644 .venv/Scripts/activate delete mode 100644 .venv/Scripts/activate.bat delete mode 100644 .venv/Scripts/deactivate.bat delete mode 100644 .venv/Scripts/pip.exe delete mode 100644 .venv/Scripts/pip3.11.exe delete mode 100644 .venv/Scripts/pip3.exe delete mode 100644 .venv/Scripts/pyrsa-decrypt.exe delete mode 100644 .venv/Scripts/pyrsa-encrypt.exe delete mode 100644 .venv/Scripts/pyrsa-keygen.exe delete mode 100644 .venv/Scripts/pyrsa-priv2pub.exe delete mode 100644 .venv/Scripts/pyrsa-sign.exe delete mode 100644 .venv/Scripts/pyrsa-verify.exe delete mode 100644 .venv/Scripts/python.exe delete mode 100644 .venv/Scripts/pythonw.exe delete mode 100644 .venv/Scripts/uvicorn.exe delete mode 100644 .venv/pyvenv.cfg delete mode 100644 project-bolt-sb1-fuj7dqxp(1).zip diff --git a/.bolt/supabase_discarded_migrations/0001_graceful_ember.sql b/.bolt/supabase_discarded_migrations/0001_graceful_ember.sql deleted file mode 100644 index 9272d44..0000000 --- a/.bolt/supabase_discarded_migrations/0001_graceful_ember.sql +++ /dev/null @@ -1,53 +0,0 @@ -/* - # Create support requests table - - 1. New Tables - - `support_requests` - - `id` (uuid, primary key) - - `user_id` (text) - - `department` (text) - - `request_type` (text) - for storing request type - - `priority` (text) - - `description` (text) - - `status` (text) - - `created_at` (timestamptz) - - 2. Security - - Enable RLS - - Add policies for users and admins -*/ - --- Create support requests table -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id text NOT NULL, - department text NOT NULL, - request_type text NOT NULL CHECK (request_type IN ('hardware', 'software', 'network', 'access', 'other')), - priority text NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')), - description text, - status text DEFAULT 'new' CHECK (status IN ('new', 'in_progress', 'resolved', 'closed')), - created_at timestamptz DEFAULT now() -); - --- Enable Row Level Security -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (auth.uid()::text = user_id); - -CREATE POLICY "Users can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (auth.uid()::text = user_id OR auth.role() = 'admin'); - -CREATE POLICY "Admins can update requests" - ON support_requests - FOR UPDATE - TO authenticated - USING (auth.role() = 'admin') - WITH CHECK (auth.role() = 'admin'); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0001_royal_fire.sql b/.bolt/supabase_discarded_migrations/0001_royal_fire.sql deleted file mode 100644 index 0e50e70..0000000 --- a/.bolt/supabase_discarded_migrations/0001_royal_fire.sql +++ /dev/null @@ -1,80 +0,0 @@ -/* - # Initial schema setup - - 1. New Tables - - `users` - - `id` (uuid, primary key) - - `first_name` (text) - - `last_name` (text) - - `department` (text) - - `password` (text) - - `created_at` (timestamp) - - - `requests` - - `id` (uuid, primary key) - - `user_id` (uuid, foreign key) - - `first_name` (text) - - `last_name` (text) - - `department` (text) - - `urgency` (text) - - `description` (text) - - `status` (text) - - `created_at` (timestamp) - - 2. Security - - Enable RLS on both tables - - Add policies for authenticated users -*/ - --- Create users table -CREATE TABLE IF NOT EXISTS users ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - first_name text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - password text NOT NULL, - created_at timestamptz DEFAULT now() -); - --- Create requests table -CREATE TABLE IF NOT EXISTS requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id uuid NOT NULL REFERENCES users(id), - first_name text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - urgency text NOT NULL, - description text, - status text NOT NULL DEFAULT 'new', - created_at timestamptz DEFAULT now(), - FOREIGN KEY (user_id) REFERENCES users(id) -); - --- Enable RLS -ALTER TABLE users ENABLE ROW LEVEL SECURITY; -ALTER TABLE requests ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can read own data" - ON users - FOR SELECT - TO authenticated - USING (auth.uid() = id); - -CREATE POLICY "Users can read all requests" - ON requests - FOR SELECT - TO authenticated - USING (true); - -CREATE POLICY "Users can create requests" - ON requests - FOR INSERT - TO authenticated - WITH CHECK (auth.uid() = user_id); - -CREATE POLICY "Users can update own requests" - ON requests - FOR UPDATE - TO authenticated - USING (auth.uid() = user_id); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0001_still_morning.sql b/.bolt/supabase_discarded_migrations/0001_still_morning.sql deleted file mode 100644 index 25a3304..0000000 --- a/.bolt/supabase_discarded_migrations/0001_still_morning.sql +++ /dev/null @@ -1,56 +0,0 @@ -/* - # Support System Database Schema - - 1. New Tables - - `support_requests` - - `id` (uuid, primary key) - - `user_id` (text) - - `department` (text) - - `request_type` (text) - - `priority` (text) - - `description` (text) - - `status` (text) - - `created_at` (timestamp) - - 2. Security - - Enable RLS - - Add policies for users and admins -*/ - --- Drop existing table if it exists -DROP TABLE IF EXISTS support_requests CASCADE; - --- Create support requests table -CREATE TABLE support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id text NOT NULL, - department text NOT NULL, - request_type text NOT NULL CHECK (request_type IN ('hardware', 'software', 'network', 'access', 'other')), - priority text NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')), - description text, - status text DEFAULT 'new' CHECK (status IN ('new', 'in_progress', 'resolved', 'closed')), - created_at timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (auth.uid()::text = user_id); - -CREATE POLICY "Users can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (auth.uid()::text = user_id OR auth.role() = 'admin'); - -CREATE POLICY "Admins can update requests" - ON support_requests - FOR UPDATE - TO authenticated - USING (auth.role() = 'admin') - WITH CHECK (auth.role() = 'admin'); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0001_sweet_shore.sql b/.bolt/supabase_discarded_migrations/0001_sweet_shore.sql deleted file mode 100644 index 35cf445..0000000 --- a/.bolt/supabase_discarded_migrations/0001_sweet_shore.sql +++ /dev/null @@ -1,66 +0,0 @@ -/* - # Support Requests Table - - 1. New Table - - `support_requests` - - `id` (uuid, primary key) - Unique identifier - - `user_id` (text) - ID of the user who created the request - - `department` (text) - Department the request is from - - `request_type` (text) - Type of request (hardware/software/etc) - - `priority` (text) - Request priority level - - `description` (text) - Detailed description of the request - - `status` (text) - Current status of the request - - `created_at` (timestamp) - When the request was created - - 2. Security - - Enable RLS - - Add policies for users and admins -*/ - --- Create support requests table -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - user_id text NOT NULL, - department text NOT NULL, - request_type text NOT NULL CHECK (request_type IN ('hardware', 'software', 'network', 'access', 'other')), - priority text NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')), - description text, - status text DEFAULT 'new' CHECK (status IN ('new', 'in_progress', 'resolved', 'closed')), - created_at timestamptz DEFAULT now() -); - --- Enable Row Level Security -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create policies -DO $$ -BEGIN - IF EXISTS ( - SELECT 1 FROM pg_policies - WHERE schemaname = 'public' - AND tablename = 'support_requests' - ) THEN - DROP POLICY IF EXISTS "Users can create their own requests" ON support_requests; - DROP POLICY IF EXISTS "Users can view their own requests" ON support_requests; - DROP POLICY IF EXISTS "Admins can update requests" ON support_requests; - END IF; -END $$; - -CREATE POLICY "Users can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (auth.uid()::text = user_id); - -CREATE POLICY "Users can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (auth.uid()::text = user_id OR auth.role() = 'admin'); - -CREATE POLICY "Admins can update requests" - ON support_requests - FOR UPDATE - TO authenticated - USING (auth.role() = 'admin') - WITH CHECK (auth.role() = 'admin'); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0003_proud_cloud.sql b/.bolt/supabase_discarded_migrations/0003_proud_cloud.sql deleted file mode 100644 index fa58cef..0000000 --- a/.bolt/supabase_discarded_migrations/0003_proud_cloud.sql +++ /dev/null @@ -1,53 +0,0 @@ -/* - # Fix Authentication Table Structure - - 1. Changes - - Recreate employees table with correct structure - - Add proper indexes - - Insert admin user - - 2. Security - - Enable RLS - - Add proper policies -*/ - --- Recreate the employees table with correct structure -CREATE TABLE IF NOT EXISTS employees ( - username text PRIMARY KEY, - last_name text NOT NULL, - department text NOT NULL, - last_login_timestamp timestamptz, - created_at timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can read own data" - ON employees - FOR SELECT - TO authenticated - USING (auth.uid()::text = username); - -CREATE POLICY "Users can update their own data" - ON employees - FOR UPDATE - TO authenticated - USING (auth.uid()::text = username) - WITH CHECK (auth.uid()::text = username); - --- Add indexes -CREATE INDEX IF NOT EXISTS idx_employees_username -ON employees(username); - -CREATE INDEX IF NOT EXISTS idx_employees_last_login -ON employees(last_login_timestamp); - --- Insert admin user (if not exists) -INSERT INTO employees (username, last_name, department) -VALUES ( - 'admin', - 'Administrator', - 'IT' -) ON CONFLICT (username) DO NOTHING; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0003_winter_morning.sql b/.bolt/supabase_discarded_migrations/0003_winter_morning.sql deleted file mode 100644 index 3d61e2c..0000000 --- a/.bolt/supabase_discarded_migrations/0003_winter_morning.sql +++ /dev/null @@ -1,63 +0,0 @@ -/* - # Employee Management System Schema - - 1. Table Structure - - Creates `employees` table with: - - `username` (text, primary key) - - `last_name` (text, not null) - - `department` (text, not null) - - `last_login_timestamp` (timestamptz) - - `created_at` (timestamptz with default) - - 2. Security - - Enables Row Level Security (RLS) - - Adds policies for: - - Reading own data - - Updating own data - - 3. Performance - - Adds indexes on frequently queried columns -*/ - --- Drop existing table and dependencies if they exist -DROP TABLE IF EXISTS employees CASCADE; - --- Create employees table -CREATE TABLE employees ( - username text PRIMARY KEY, - last_name text NOT NULL, - department text NOT NULL, - last_login_timestamp timestamptz, - created_at timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can read own data" - ON employees - FOR SELECT - TO authenticated - USING (auth.uid()::text = username); - -CREATE POLICY "Users can update their own data" - ON employees - FOR UPDATE - TO authenticated - USING (auth.uid()::text = username) - WITH CHECK (auth.uid()::text = username); - --- Add performance indexes -CREATE INDEX idx_employees_username ON employees(username); -CREATE INDEX idx_employees_last_login ON employees(last_login_timestamp); - --- Insert default admin user -INSERT INTO employees (username, last_name, department) -VALUES ( - 'admin', - 'Administrator', - 'IT' -) ON CONFLICT (username) DO UPDATE SET - last_name = EXCLUDED.last_name, - department = EXCLUDED.department; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0003_withered_frost.sql b/.bolt/supabase_discarded_migrations/0003_withered_frost.sql deleted file mode 100644 index 22a8921..0000000 --- a/.bolt/supabase_discarded_migrations/0003_withered_frost.sql +++ /dev/null @@ -1,67 +0,0 @@ -/* - # Authentication System Setup - - 1. Changes - - Create employees table with all required fields - - Add password field with NOT NULL constraint - - Add timestamp fields for auditing - - 2. Security - - Enable RLS - - Set up read and update policies - - Add performance indexes -*/ - --- Clean up and recreate employees table -CREATE TABLE IF NOT EXISTS employees ( - username text PRIMARY KEY, - password text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - last_login_timestamp timestamptz, - created_at timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Safely handle existing policies -DO $$ -BEGIN - DROP POLICY IF EXISTS "Users can read own data" ON employees; - DROP POLICY IF EXISTS "Users can update their own data" ON employees; -END $$; - --- Create policies -CREATE POLICY "Users can read own data" - ON employees - FOR SELECT - TO authenticated - USING (auth.uid()::text = username); - -CREATE POLICY "Users can update their own data" - ON employees - FOR UPDATE - TO authenticated - USING (auth.uid()::text = username) - WITH CHECK (auth.uid()::text = username); - --- Safely handle existing indexes -DO $$ -BEGIN - DROP INDEX IF EXISTS idx_employees_username; - DROP INDEX IF EXISTS idx_employees_last_login; -END $$; - --- Add indexes -CREATE INDEX idx_employees_username ON employees(username); -CREATE INDEX idx_employees_last_login ON employees(last_login_timestamp); - --- Insert admin user with hashed password for 'admin66' -INSERT INTO employees (username, password, last_name, department) -VALUES ( - 'admin', - '$2a$10$xJ7Yt1UqZKhVkk2mFXgQe.UuB3YH3QQMkj8AfzF8fxMjGlZZYf.Hy', - 'Administrator', - 'IT' -) ON CONFLICT (username) DO NOTHING; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0004_maroon_shore.sql b/.bolt/supabase_discarded_migrations/0004_maroon_shore.sql deleted file mode 100644 index 0d8a398..0000000 --- a/.bolt/supabase_discarded_migrations/0004_maroon_shore.sql +++ /dev/null @@ -1,46 +0,0 @@ -/* - # Update employees table and policies - - 1. Changes - - Ensures employees table exists with required fields - - Safely handles existing RLS policy - - Updates admin user with correct password hash -*/ - --- Create table if it doesn't exist -CREATE TABLE IF NOT EXISTS employees ( - username text PRIMARY KEY, - password text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - created_at timestamptz DEFAULT now() -); - --- Enable RLS (idempotent operation) -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Drop existing policy if it exists -DO $$ -BEGIN - DROP POLICY IF EXISTS "Users can read own data" ON employees; -END $$; - --- Create policy -CREATE POLICY "Users can read own data" - ON employees - FOR SELECT - TO authenticated - USING (auth.uid()::text = username); - --- Update or insert admin user -INSERT INTO employees (username, password, last_name, department) -VALUES ( - 'admin', - '$2a$10$X4kv7j5ZcG39WgkdqhzJXO2/ZZJHNNxt0Bz4Y8DzxfBqL0Q1erqJS', -- hashed 'admin' - 'Administrator', - 'IT' -) ON CONFLICT (username) -DO UPDATE SET - password = EXCLUDED.password, - last_name = EXCLUDED.last_name, - department = EXCLUDED.department; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0004_pink_sun.sql b/.bolt/supabase_discarded_migrations/0004_pink_sun.sql deleted file mode 100644 index bb3a8f5..0000000 --- a/.bolt/supabase_discarded_migrations/0004_pink_sun.sql +++ /dev/null @@ -1,46 +0,0 @@ -/* - # Update employees table and policies - - 1. Changes - - Ensures employees table exists with required fields - - Safely handles existing RLS policy - - Updates admin user with correct password hash for 'admin66' -*/ - --- Create table if it doesn't exist -CREATE TABLE IF NOT EXISTS employees ( - username text PRIMARY KEY, - password text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - created_at timestamptz DEFAULT now() -); - --- Enable RLS (idempotent operation) -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Safely handle existing policy -DO $$ -BEGIN - DROP POLICY IF EXISTS "Users can read own data" ON employees; -END $$; - --- Create policy -CREATE POLICY "Users can read own data" - ON employees - FOR SELECT - TO authenticated - USING (auth.uid()::text = username); - --- Update or insert admin user with hashed password for 'admin66' -INSERT INTO employees (username, password, last_name, department) -VALUES ( - 'admin', - '$2a$10$xJ7Yt1UqZKhVkk2mFXgQe.UuB3YH3QQMkj8AfzF8fxMjGlZZYf.Hy', -- hashed 'admin66' - 'Administrator', - 'IT' -) ON CONFLICT (username) -DO UPDATE SET - password = EXCLUDED.password, - last_name = EXCLUDED.last_name, - department = EXCLUDED.department; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_bronze_castle.sql b/.bolt/supabase_discarded_migrations/0007_bronze_castle.sql deleted file mode 100644 index 2c6dc51..0000000 --- a/.bolt/supabase_discarded_migrations/0007_bronze_castle.sql +++ /dev/null @@ -1,37 +0,0 @@ -/* - # Create Users Table - - 1. New Tables - - `users` - - Basic user information and password storage - - Includes RLS policies for security -*/ - --- Create extension for password hashing -CREATE EXTENSION IF NOT EXISTS pgcrypto; - --- Create users table -CREATE TABLE IF NOT EXISTS users ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - first_name text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - password_hash text NOT NULL, - created_at timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE users ENABLE ROW LEVEL SECURITY; - --- Create basic security policies -CREATE POLICY "Users can view own data" - ON users - FOR SELECT - TO authenticated - USING (id = auth.uid()); - -CREATE POLICY "Admins can manage all users" - ON users - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'email' = 'admin@example.com'); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_fierce_mouse.sql b/.bolt/supabase_discarded_migrations/0007_fierce_mouse.sql deleted file mode 100644 index 6f41131..0000000 --- a/.bolt/supabase_discarded_migrations/0007_fierce_mouse.sql +++ /dev/null @@ -1,166 +0,0 @@ -/* - # Support Request System Tables - - 1. New Tables - - `support_requests` - - `id` (uuid, primary key) - - `employee_id` (uuid, references employees) - - `department` (text) - - `request_type` (enum) - - `priority` (enum) - - `status` (enum) - - `description` (text) - - `created_at` (timestamptz) - - `last_status_change` (timestamptz) - - - `status_history` - - `id` (uuid, primary key) - - `request_id` (uuid, references support_requests) - - `old_status` (enum) - - `new_status` (enum) - - `changed_by` (uuid, references employees) - - `changed_at` (timestamptz) - - 2. Security - - Enable RLS on all tables - - Add policies for employees and admins -*/ - --- Create enum types -DO $$ BEGIN - CREATE TYPE request_type AS ENUM ('hardware', 'software', 'network', 'access', 'other'); - CREATE TYPE request_priority AS ENUM ('low', 'medium', 'high', 'critical'); - CREATE TYPE request_status AS ENUM ('new', 'in_progress', 'resolved', 'closed'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; - --- Create support requests table -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - employee_id uuid REFERENCES employees(id) NOT NULL, - department text NOT NULL, - request_type request_type NOT NULL, - priority request_priority NOT NULL, - status request_status NOT NULL DEFAULT 'new', - description text, - created_at timestamptz NOT NULL DEFAULT now(), - last_status_change timestamptz DEFAULT now() -); - --- Create status history table -CREATE TABLE IF NOT EXISTS status_history ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - request_id uuid REFERENCES support_requests(id) ON DELETE CASCADE, - old_status request_status, - new_status request_status NOT NULL, - changed_by uuid REFERENCES employees(id) NOT NULL, - changed_at timestamptz NOT NULL DEFAULT now() -); - --- Enable RLS -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; -ALTER TABLE status_history ENABLE ROW LEVEL SECURITY; - --- Create policies for support_requests -DO $$ BEGIN - IF NOT EXISTS ( - SELECT FROM pg_policies - WHERE tablename = 'support_requests' - AND policyname = 'Employees can view their own requests' - ) THEN - CREATE POLICY "Employees can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (employee_id = auth.uid()); - END IF; - - IF NOT EXISTS ( - SELECT FROM pg_policies - WHERE tablename = 'support_requests' - AND policyname = 'Employees can create their own requests' - ) THEN - CREATE POLICY "Employees can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (employee_id = auth.uid()); - END IF; - - IF NOT EXISTS ( - SELECT FROM pg_policies - WHERE tablename = 'support_requests' - AND policyname = 'Admins can view all requests' - ) THEN - CREATE POLICY "Admins can view all requests" - ON support_requests - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - END IF; -END $$; - --- Create policies for status_history -DO $$ BEGIN - IF NOT EXISTS ( - SELECT FROM pg_policies - WHERE tablename = 'status_history' - AND policyname = 'Employees can view status history of their requests' - ) THEN - CREATE POLICY "Employees can view status history of their requests" - ON status_history - FOR SELECT - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM support_requests - WHERE id = status_history.request_id - AND employee_id = auth.uid() - ) - ); - END IF; - - IF NOT EXISTS ( - SELECT FROM pg_policies - WHERE tablename = 'status_history' - AND policyname = 'Admins can view all status history' - ) THEN - CREATE POLICY "Admins can view all status history" - ON status_history - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - END IF; -END $$; - --- Create status update trigger -CREATE OR REPLACE FUNCTION update_request_status_history() -RETURNS TRIGGER AS $$ -BEGIN - IF (TG_OP = 'UPDATE' AND OLD.status IS DISTINCT FROM NEW.status) THEN - INSERT INTO status_history ( - request_id, - old_status, - new_status, - changed_by - ) VALUES ( - NEW.id, - OLD.status, - NEW.status, - auth.uid() - ); - - NEW.last_status_change = now(); - END IF; - - RETURN NEW; -END; -$$ LANGUAGE plpgsql SECURITY DEFINER; - --- Create trigger -DROP TRIGGER IF EXISTS track_request_status_changes ON support_requests; -CREATE TRIGGER track_request_status_changes - BEFORE UPDATE ON support_requests - FOR EACH ROW - EXECUTE FUNCTION update_request_status_history(); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_hidden_king.sql b/.bolt/supabase_discarded_migrations/0007_hidden_king.sql deleted file mode 100644 index 568745f..0000000 --- a/.bolt/supabase_discarded_migrations/0007_hidden_king.sql +++ /dev/null @@ -1,51 +0,0 @@ -/* - # Add employee details to support requests - - 1. Changes - - Add employee_last_name and employee_department columns to support_requests - - Add trigger to automatically populate employee details on insert - - Update existing records with employee details - - 2. Security - - Maintain existing RLS policies - - No changes to security policies required as these are derived fields -*/ - --- Add new columns for employee details -ALTER TABLE support_requests -ADD COLUMN IF NOT EXISTS employee_last_name text, -ADD COLUMN IF NOT EXISTS employee_department text; - --- Create function to populate employee details -CREATE OR REPLACE FUNCTION populate_employee_details() -RETURNS TRIGGER AS $$ -BEGIN - SELECT - last_name, - department - INTO - NEW.employee_last_name, - NEW.employee_department - FROM employees - WHERE id = NEW.employee_id; - - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - --- Drop trigger if exists to avoid conflicts -DROP TRIGGER IF EXISTS set_employee_details ON support_requests; - --- Create trigger to automatically populate employee details -CREATE TRIGGER set_employee_details - BEFORE INSERT ON support_requests - FOR EACH ROW - EXECUTE FUNCTION populate_employee_details(); - --- Update existing records with employee details -UPDATE support_requests sr -SET - employee_last_name = e.last_name, - employee_department = e.department -FROM employees e -WHERE sr.employee_id = e.id; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_lively_wind.sql b/.bolt/supabase_discarded_migrations/0007_lively_wind.sql deleted file mode 100644 index 65f62e1..0000000 --- a/.bolt/supabase_discarded_migrations/0007_lively_wind.sql +++ /dev/null @@ -1,66 +0,0 @@ -/* - # Add create_user function - - 1. Changes - - Add function to create new users in employees table - - Function handles first name, last name, department and password - - Returns the created employee record - - 2. Details - - Creates a stored procedure for consistent user creation - - Validates input parameters - - Returns the full employee record after creation - - 3. Security - - Function is SECURITY DEFINER to ensure proper access control - - Input validation to prevent invalid data -*/ - --- Create function to handle user creation -CREATE OR REPLACE FUNCTION create_user( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate inputs - IF p_first_name IS NULL OR p_first_name = '' THEN - RAISE EXCEPTION 'First name cannot be empty'; - END IF; - - IF p_last_name IS NULL OR p_last_name = '' THEN - RAISE EXCEPTION 'Last name cannot be empty'; - END IF; - - IF p_department IS NULL OR p_department = '' THEN - RAISE EXCEPTION 'Department cannot be empty'; - END IF; - - IF p_password IS NULL OR p_password = '' THEN - RAISE EXCEPTION 'Password cannot be empty'; - END IF; - - -- Insert new employee - INSERT INTO employees ( - first_name, - last_name, - department, - email -- Generate email from name - ) VALUES ( - p_first_name, - p_last_name, - p_department, - lower(p_last_name || '@example.com') - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_raspy_rain.sql b/.bolt/supabase_discarded_migrations/0007_raspy_rain.sql deleted file mode 100644 index 680d7a4..0000000 --- a/.bolt/supabase_discarded_migrations/0007_raspy_rain.sql +++ /dev/null @@ -1,38 +0,0 @@ -/* - # Update Employee Table RLS Policies - - 1. Changes - - Drop existing RLS policies - - Create new policies for admin access - - Add policy for employee self-access - - 2. Security - - Enable RLS on employees table - - Admin can manage all employees - - Employees can view their own data -*/ - --- Drop existing policies if they exist -DROP POLICY IF EXISTS "Admins can manage employees" ON employees; -DROP POLICY IF EXISTS "Employees can view own data" ON employees; - --- Enable RLS -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Create admin policy for full access -CREATE POLICY "Admins can manage employees" -ON employees -FOR ALL -TO authenticated -USING ( - auth.jwt() ->> 'email' = 'admin@example.com' -); - --- Create policy for employees to view their own data -CREATE POLICY "Employees can view own data" -ON employees -FOR SELECT -TO authenticated -USING ( - id = auth.uid() -); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_snowy_fog.sql b/.bolt/supabase_discarded_migrations/0007_snowy_fog.sql deleted file mode 100644 index 90e6344..0000000 --- a/.bolt/supabase_discarded_migrations/0007_snowy_fog.sql +++ /dev/null @@ -1,68 +0,0 @@ -/* - # Update support requests schema - - 1. Changes - - Change foreign key reference from auth.users to employees table - - Update RLS policies to use employee_id instead of user_id - - Add indexes for better query performance - - 2. Security - - Enable RLS - - Add policies for employees to manage their requests -*/ - --- First drop the foreign key constraint -ALTER TABLE support_requests - DROP CONSTRAINT IF EXISTS support_requests_user_id_fkey; - --- Then rename the column -ALTER TABLE support_requests - RENAME COLUMN user_id TO employee_id; - --- Add new foreign key constraint -ALTER TABLE support_requests - ADD CONSTRAINT support_requests_employee_id_fkey - FOREIGN KEY (employee_id) REFERENCES employees(id); - --- Create index for better performance -CREATE INDEX IF NOT EXISTS idx_support_requests_employee_id - ON support_requests(employee_id); - --- Update RLS policies -DROP POLICY IF EXISTS "Users can create requests" ON support_requests; -DROP POLICY IF EXISTS "Users can view their own requests" ON support_requests; -DROP POLICY IF EXISTS "Users can update their own requests" ON support_requests; -DROP POLICY IF EXISTS "IT department can manage all requests" ON support_requests; - -CREATE POLICY "Employees can create requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK ( - EXISTS ( - SELECT 1 FROM employees - WHERE id = support_requests.employee_id - ) - ); - -CREATE POLICY "Employees can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING ( - employee_id IN ( - SELECT id FROM employees - WHERE id = support_requests.employee_id - ) - ); - -CREATE POLICY "Employees can update their own requests" - ON support_requests - FOR UPDATE - TO authenticated - USING ( - employee_id IN ( - SELECT id FROM employees - WHERE id = support_requests.employee_id - ) - ); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_steep_cliff.sql b/.bolt/supabase_discarded_migrations/0007_steep_cliff.sql deleted file mode 100644 index 79e114d..0000000 --- a/.bolt/supabase_discarded_migrations/0007_steep_cliff.sql +++ /dev/null @@ -1,112 +0,0 @@ -/* - # Create employees and support requests tables - - 1. New Tables - - `employees` - - `id` (uuid, primary key) - - `first_name` (text) - - `last_name` (text) - - `department` (text) - - `created_at` (timestamptz) - - `support_requests` - - `id` (uuid, primary key) - - `employee_id` (uuid, foreign key) - - `department` (text) - - `request_type` (enum) - - `priority` (enum) - - `status` (enum) - - `description` (text) - - `created_at` (timestamptz) - - `last_status_change` (timestamptz) - - 2. Security - - Enable RLS on both tables - - Add appropriate policies for employees and admins -*/ - --- Create enum types if they don't exist -DO $$ BEGIN - CREATE TYPE request_type AS ENUM ('hardware', 'software', 'network', 'access', 'other'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; - -DO $$ BEGIN - CREATE TYPE request_priority AS ENUM ('low', 'medium', 'high', 'critical'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; - -DO $$ BEGIN - CREATE TYPE request_status AS ENUM ('new', 'in_progress', 'resolved', 'closed'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; - --- Create employees table -CREATE TABLE IF NOT EXISTS employees ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - first_name text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() -); - --- Enable RLS for employees -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Create policies for employees -CREATE POLICY "Employees can view their own data" - ON employees - FOR SELECT - TO authenticated - USING (id = auth.uid()); - -CREATE POLICY "Admins can manage all employees" - ON employees - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - --- Create support requests table -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - employee_id uuid REFERENCES employees(id) NOT NULL, - department text NOT NULL, - request_type request_type NOT NULL, - priority request_priority NOT NULL, - status request_status NOT NULL DEFAULT 'new', - description text, - created_at timestamptz NOT NULL DEFAULT now(), - last_status_change timestamptz DEFAULT now() -); - --- Enable RLS for support requests -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create policies for support requests -CREATE POLICY "Employees can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (employee_id = auth.uid()); - -CREATE POLICY "Employees can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (employee_id = auth.uid()); - -CREATE POLICY "Admins can manage all requests" - ON support_requests - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - --- Add initial test data for employees -INSERT INTO employees (first_name, last_name, department) -VALUES - ('Иван', 'Иванов', 'aho'), - ('Петр', 'Петров', 'gkh'), - ('Сергей', 'Сергеев', 'general') -ON CONFLICT (id) DO NOTHING; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_tiny_trail.sql b/.bolt/supabase_discarded_migrations/0007_tiny_trail.sql deleted file mode 100644 index 28bac5b..0000000 --- a/.bolt/supabase_discarded_migrations/0007_tiny_trail.sql +++ /dev/null @@ -1,57 +0,0 @@ -/* - # Add employee details to support requests - - 1. Changes - - Add employee_last_name and employee_department columns to support_requests - - Create trigger to automatically populate employee details on insert - - Update existing records with employee details - - 2. Details - - Adds columns to store employee information directly in support_requests - - Creates trigger to automatically populate these fields on insert - - Updates existing records with employee information - - Uses user_id to link with employees table - - 3. Security - - Maintains existing RLS policies - - No additional security changes needed as these are derived fields -*/ - --- Add new columns for employee details -ALTER TABLE support_requests -ADD COLUMN IF NOT EXISTS employee_last_name text, -ADD COLUMN IF NOT EXISTS employee_department text; - --- Create function to populate employee details -CREATE OR REPLACE FUNCTION populate_employee_details() -RETURNS TRIGGER AS $$ -BEGIN - SELECT - last_name, - department - INTO - NEW.employee_last_name, - NEW.employee_department - FROM employees - WHERE id = NEW.user_id; - - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - --- Drop trigger if exists to avoid conflicts -DROP TRIGGER IF EXISTS set_employee_details ON support_requests; - --- Create trigger to automatically populate employee details -CREATE TRIGGER set_employee_details - BEFORE INSERT ON support_requests - FOR EACH ROW - EXECUTE FUNCTION populate_employee_details(); - --- Update existing records with employee details -UPDATE support_requests sr -SET - employee_last_name = e.last_name, - employee_department = e.department -FROM employees e -WHERE sr.user_id = e.id; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0007_wandering_salad.sql b/.bolt/supabase_discarded_migrations/0007_wandering_salad.sql deleted file mode 100644 index ea32448..0000000 --- a/.bolt/supabase_discarded_migrations/0007_wandering_salad.sql +++ /dev/null @@ -1,68 +0,0 @@ -/* - # Update support requests policies - - 1. Changes - - Add RLS policies for support requests table - - Allow authenticated users to create and view their requests - - Allow admins to manage all requests - - 2. Security - - Enable RLS on support_requests table - - Add policies for authenticated users - - Add admin policies -*/ - --- Drop existing policies if they exist -DROP POLICY IF EXISTS "Users can create their own requests" ON support_requests; -DROP POLICY IF EXISTS "Users can view their own requests" ON support_requests; -DROP POLICY IF EXISTS "Users can update their own requests" ON support_requests; -DROP POLICY IF EXISTS "Admins can view all requests" ON support_requests; - --- Create new policies -CREATE POLICY "Users can create requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (true); - -CREATE POLICY "Users can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING ( - auth.uid() = user_id OR - EXISTS ( - SELECT 1 FROM users - WHERE users.id = auth.uid() AND department = 'it' - ) - ); - -CREATE POLICY "Users can update their own requests" - ON support_requests - FOR UPDATE - TO authenticated - USING ( - auth.uid() = user_id OR - EXISTS ( - SELECT 1 FROM users - WHERE users.id = auth.uid() AND department = 'it' - ) - ) - WITH CHECK ( - auth.uid() = user_id OR - EXISTS ( - SELECT 1 FROM users - WHERE users.id = auth.uid() AND department = 'it' - ) - ); - -CREATE POLICY "IT department can manage all requests" - ON support_requests - FOR ALL - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM users - WHERE users.id = auth.uid() AND department = 'it' - ) - ); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_ancient_wind.sql b/.bolt/supabase_discarded_migrations/0008_ancient_wind.sql deleted file mode 100644 index 7499a45..0000000 --- a/.bolt/supabase_discarded_migrations/0008_ancient_wind.sql +++ /dev/null @@ -1,50 +0,0 @@ -/* - # Add Employee Creation Function - - 1. Changes - - Add secure function to create new employees with password hashing - - 2. Security - - Uses hash_password function for secure password storage - - SECURITY DEFINER to ensure proper access control - - Returns employee data without password hash -*/ - --- Create function to create employee with password -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate input - IF p_password IS NULL OR length(p_password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - - -- Create employee with hashed password - INSERT INTO employees ( - first_name, - last_name, - department, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - -- Return employee data without password hash - v_employee.password_hash := NULL; - RETURN v_employee; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_damp_paper.sql b/.bolt/supabase_discarded_migrations/0008_damp_paper.sql deleted file mode 100644 index 12d2960..0000000 --- a/.bolt/supabase_discarded_migrations/0008_damp_paper.sql +++ /dev/null @@ -1,56 +0,0 @@ -/* - # Fix employee creation process - - 1. Changes - - Add trigger to create auth user and employee synchronously - - Update create_employee function to handle auth user creation - - Add proper error handling - - 2. Security - - Maintain RLS policies - - Ensure secure password handling -*/ - --- Function to create auth user and employee -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; - v_auth_user uuid; -BEGIN - -- Validate password - PERFORM validate_password(p_password); - - -- Create auth user first - v_auth_user := auth.uid(); - - -- Create employee record - INSERT INTO employees ( - id, - first_name, - last_name, - department, - password_hash - ) VALUES ( - v_auth_user, - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -EXCEPTION - WHEN others THEN - RAISE EXCEPTION 'Failed to create employee: %', SQLERRM; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_lingering_breeze.sql b/.bolt/supabase_discarded_migrations/0008_lingering_breeze.sql deleted file mode 100644 index 70422f5..0000000 --- a/.bolt/supabase_discarded_migrations/0008_lingering_breeze.sql +++ /dev/null @@ -1,67 +0,0 @@ -/* - # Remove email dependency from employees table - - 1. Changes - - Remove email column from employees table - - Update create_employee function to work without email - - Preserve existing data integrity - - 2. Security - - Maintain existing RLS policies -*/ - --- Remove email column and its constraint -ALTER TABLE employees -DROP COLUMN IF EXISTS email; - --- Update create_employee function -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Input validation - IF p_first_name IS NULL OR p_first_name = '' THEN - RAISE EXCEPTION 'First name is required'; - END IF; - - IF p_last_name IS NULL OR p_last_name = '' THEN - RAISE EXCEPTION 'Last name is required'; - END IF; - - IF p_department IS NULL OR p_department = '' THEN - RAISE EXCEPTION 'Department is required'; - END IF; - - IF p_password IS NULL OR length(p_password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - - -- Create employee record - INSERT INTO employees ( - first_name, - last_name, - department, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -EXCEPTION - WHEN others THEN - RAISE EXCEPTION 'Failed to create employee: %', SQLERRM; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_nameless_sea.sql b/.bolt/supabase_discarded_migrations/0008_nameless_sea.sql deleted file mode 100644 index fddefe7..0000000 --- a/.bolt/supabase_discarded_migrations/0008_nameless_sea.sql +++ /dev/null @@ -1,53 +0,0 @@ -/* - # Create status history table - - 1. New Tables - - `status_history` - - `id` (uuid, primary key) - - `request_id` (uuid, foreign key to support_requests.id) - - `old_status` (request_status) - - `new_status` (request_status) - - `changed_by` (uuid, foreign key to employees.id) - - `changed_at` (timestamptz) - - 2. Security - - Enable RLS - - Add policies for status history access -*/ - -CREATE TABLE IF NOT EXISTS status_history ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - request_id uuid REFERENCES support_requests(id) ON DELETE CASCADE NOT NULL, - old_status request_status, - new_status request_status NOT NULL, - changed_by uuid REFERENCES employees(id) ON DELETE CASCADE NOT NULL, - changed_at timestamptz NOT NULL DEFAULT now() -); - --- Enable RLS -ALTER TABLE status_history ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can view status history of their requests" - ON status_history - FOR SELECT - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM support_requests sr - WHERE sr.id = status_history.request_id - AND sr.employee_id = auth.uid() - ) - ); - -CREATE POLICY "Admins can view all status history" - ON status_history - FOR SELECT - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM employees e - WHERE e.id = auth.uid() - AND e.is_admin = true - ) - ); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_noisy_prism.sql b/.bolt/supabase_discarded_migrations/0008_noisy_prism.sql deleted file mode 100644 index 9f24352..0000000 --- a/.bolt/supabase_discarded_migrations/0008_noisy_prism.sql +++ /dev/null @@ -1,57 +0,0 @@ -/* - # Create support requests table - - 1. New Table - - support_requests - - id (uuid, primary key) - - employee_id (uuid, foreign key to employees) - - department (text) - - request_type (request_type enum) - - priority (request_priority enum) - - status (request_status enum) - - description (text) - - created_at (timestamptz) - - 2. Security - - Enable RLS - - Add request viewing, creation, and update policies - - Add performance index for employee lookups -*/ - --- Create support requests table -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - employee_id uuid REFERENCES employees(id), - department text NOT NULL, - request_type request_type NOT NULL, - priority request_priority NOT NULL, - description text, - status request_status NOT NULL DEFAULT 'new', - created_at timestamptz NOT NULL DEFAULT now() -); - --- Enable RLS for support requests -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create index for better query performance -CREATE INDEX IF NOT EXISTS idx_support_requests_employee_id -ON support_requests(employee_id); - --- Create RLS policies for support requests -CREATE POLICY "Employees can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (employee_id = auth.uid()); - -CREATE POLICY "Employees can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (employee_id = auth.uid()); - -CREATE POLICY "Employees can update their own requests" - ON support_requests - FOR UPDATE - TO authenticated - USING (employee_id = auth.uid()); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_old_summit.sql b/.bolt/supabase_discarded_migrations/0008_old_summit.sql deleted file mode 100644 index be820b6..0000000 --- a/.bolt/supabase_discarded_migrations/0008_old_summit.sql +++ /dev/null @@ -1,80 +0,0 @@ -/* - # Fix employee creation functions - - 1. Changes - - Drop and recreate validate_password function with correct return type - - Update create_employee function - - 2. Security - - Maintain SECURITY DEFINER - - Secure password handling -*/ - --- Drop existing functions -DROP FUNCTION IF EXISTS validate_password(text); -DROP FUNCTION IF EXISTS create_employee(text, text, text, text); - --- Recreate validate_password function -CREATE OR REPLACE FUNCTION validate_password(password text) -RETURNS void -LANGUAGE plpgsql -AS $$ -BEGIN - IF length(password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; -END; -$$; - --- Create employee function with auth integration -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate input - IF p_first_name IS NULL OR p_first_name = '' THEN - RAISE EXCEPTION 'First name is required'; - END IF; - - IF p_last_name IS NULL OR p_last_name = '' THEN - RAISE EXCEPTION 'Last name is required'; - END IF; - - IF p_department IS NULL OR p_department = '' THEN - RAISE EXCEPTION 'Department is required'; - END IF; - - -- Validate password - PERFORM validate_password(p_password); - - -- Create employee record - INSERT INTO employees ( - first_name, - last_name, - department, - email, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - lower(p_last_name) || '@example.com', - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -EXCEPTION - WHEN others THEN - RAISE EXCEPTION 'Failed to create employee: %', SQLERRM; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_proud_queen.sql b/.bolt/supabase_discarded_migrations/0008_proud_queen.sql deleted file mode 100644 index b1756bd..0000000 --- a/.bolt/supabase_discarded_migrations/0008_proud_queen.sql +++ /dev/null @@ -1,85 +0,0 @@ -/* - # Fix employee management functions - - 1. Changes - - Drop and recreate validate_password function with proper return type - - Create improved create_employee function with better validation - - 2. Security - - Maintain SECURITY DEFINER for sensitive operations - - Secure password validation and hashing - - Input validation for all fields -*/ - --- Drop existing validate_password function if it exists -DROP FUNCTION IF EXISTS validate_password(text); - --- Recreate validate_password function with better validation -CREATE OR REPLACE FUNCTION validate_password(password text) -RETURNS boolean -LANGUAGE plpgsql -AS $$ -BEGIN - -- Check password length - IF length(password) < 6 THEN - RETURN false; - END IF; - - RETURN true; -END; -$$; - --- Create employee function with improved validation -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate input - IF p_first_name IS NULL OR p_first_name = '' THEN - RAISE EXCEPTION 'First name is required'; - END IF; - - IF p_last_name IS NULL OR p_last_name = '' THEN - RAISE EXCEPTION 'Last name is required'; - END IF; - - IF p_department IS NULL OR p_department = '' THEN - RAISE EXCEPTION 'Department is required'; - END IF; - - -- Validate password - IF NOT validate_password(p_password) THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - - -- Create employee record - INSERT INTO employees ( - first_name, - last_name, - department, - email, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - lower(p_last_name) || '@example.com', - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -EXCEPTION - WHEN others THEN - RAISE EXCEPTION 'Failed to create employee: %', SQLERRM; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_raspy_cloud.sql b/.bolt/supabase_discarded_migrations/0008_raspy_cloud.sql deleted file mode 100644 index 7fd7a63..0000000 --- a/.bolt/supabase_discarded_migrations/0008_raspy_cloud.sql +++ /dev/null @@ -1,93 +0,0 @@ -/* - # Fix employee creation process - - 1. Changes - - Add proper error handling for auth user creation - - Ensure atomic transaction for employee creation - - Add better validation for employee data - - Fix duplicate email handling - - 2. Security - - Maintain RLS policies - - Add proper role checks -*/ - --- Drop existing function if exists -DROP FUNCTION IF EXISTS create_employee(text, text, text, text); - --- Create improved employee creation function -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; - v_email text; -BEGIN - -- Input validation - IF p_first_name IS NULL OR p_first_name = '' THEN - RAISE EXCEPTION 'First name is required'; - END IF; - - IF p_last_name IS NULL OR p_last_name = '' THEN - RAISE EXCEPTION 'Last name is required'; - END IF; - - IF p_department IS NULL OR p_department = '' THEN - RAISE EXCEPTION 'Department is required'; - END IF; - - IF p_password IS NULL OR length(p_password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - - -- Generate unique email - v_email := lower(p_last_name) || '@example.com'; - - -- Create employee record - INSERT INTO employees ( - first_name, - last_name, - department, - email, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - v_email, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - -- Create auth user - INSERT INTO auth.users ( - email, - encrypted_password, - email_confirmed_at, - raw_user_meta_data - ) VALUES ( - v_email, - hash_password(p_password), - now(), - jsonb_build_object( - 'first_name', p_first_name, - 'last_name', p_last_name, - 'department', p_department - ) - ); - - RETURN v_employee; -EXCEPTION - WHEN unique_violation THEN - RAISE EXCEPTION 'Employee with this email already exists'; - WHEN others THEN - RAISE EXCEPTION 'Failed to create employee: %', SQLERRM; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_snowy_ember.sql b/.bolt/supabase_discarded_migrations/0008_snowy_ember.sql deleted file mode 100644 index fb2aaf6..0000000 --- a/.bolt/supabase_discarded_migrations/0008_snowy_ember.sql +++ /dev/null @@ -1,89 +0,0 @@ -/* - # Fix database constraints and password validation - - 1. Changes - - Add password validation function - - Add employee creation function with validation - - Add indexes for performance optimization - - Update RLS policies - - 2. Security - - Maintain RLS policies - - Add proper validation for passwords -*/ - --- Update password validation function -CREATE OR REPLACE FUNCTION validate_password(password text) -RETURNS boolean -LANGUAGE plpgsql -AS $$ -BEGIN - IF length(password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - RETURN true; -END; -$$; - --- Update create_employee function to use password validation -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate password - PERFORM validate_password(p_password); - - -- Create employee - INSERT INTO employees ( - first_name, - last_name, - department, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - RETURN v_employee; -END; -$$; - --- Add indexes for better performance if they don't exist -CREATE INDEX IF NOT EXISTS idx_employees_last_name ON employees(last_name); -CREATE INDEX IF NOT EXISTS idx_support_requests_created_at ON support_requests(created_at DESC); - --- Update RLS policies for support_requests -DROP POLICY IF EXISTS "Users can create their own requests" ON support_requests; -DROP POLICY IF EXISTS "Users can view their own requests" ON support_requests; -DROP POLICY IF EXISTS "Users can update their own requests" ON support_requests; - -CREATE POLICY "Employees can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (user_id = auth.uid()); - -CREATE POLICY "Employees can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (user_id = auth.uid()); - -CREATE POLICY "Employees can update their own requests" - ON support_requests - FOR UPDATE - TO authenticated - USING (user_id = auth.uid()) - WITH CHECK (user_id = auth.uid()); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_velvet_bonus.sql b/.bolt/supabase_discarded_migrations/0008_velvet_bonus.sql deleted file mode 100644 index d2ed9e8..0000000 --- a/.bolt/supabase_discarded_migrations/0008_velvet_bonus.sql +++ /dev/null @@ -1,43 +0,0 @@ -/* - # Update employees table and policies - - 1. Table Creation - - employees - - id (uuid, primary key) - - first_name (text) - - last_name (text) - - department (text) - - created_at (timestamptz) - - 2. Security - - Enable RLS - - Add employee viewing policy (if not exists) -*/ - --- Create employees table -CREATE TABLE IF NOT EXISTS employees ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - first_name text NOT NULL, - last_name text NOT NULL, - department text NOT NULL, - created_at timestamptz DEFAULT now() -); - --- Enable RLS for employees -ALTER TABLE employees ENABLE ROW LEVEL SECURITY; - --- Safely create policy if it doesn't exist -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_policies - WHERE tablename = 'employees' - AND policyname = 'Employees can view their own profile' - ) THEN - CREATE POLICY "Employees can view their own profile" - ON employees - FOR SELECT - TO authenticated - USING (id = auth.uid()); - END IF; -END $$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_wandering_fire.sql b/.bolt/supabase_discarded_migrations/0008_wandering_fire.sql deleted file mode 100644 index 7499a45..0000000 --- a/.bolt/supabase_discarded_migrations/0008_wandering_fire.sql +++ /dev/null @@ -1,50 +0,0 @@ -/* - # Add Employee Creation Function - - 1. Changes - - Add secure function to create new employees with password hashing - - 2. Security - - Uses hash_password function for secure password storage - - SECURITY DEFINER to ensure proper access control - - Returns employee data without password hash -*/ - --- Create function to create employee with password -CREATE OR REPLACE FUNCTION create_employee( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS employees -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_employee employees; -BEGIN - -- Validate input - IF p_password IS NULL OR length(p_password) < 6 THEN - RAISE EXCEPTION 'Password must be at least 6 characters long'; - END IF; - - -- Create employee with hashed password - INSERT INTO employees ( - first_name, - last_name, - department, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_employee; - - -- Return employee data without password hash - v_employee.password_hash := NULL; - RETURN v_employee; -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0008_wooden_star.sql b/.bolt/supabase_discarded_migrations/0008_wooden_star.sql deleted file mode 100644 index 54e5c92..0000000 --- a/.bolt/supabase_discarded_migrations/0008_wooden_star.sql +++ /dev/null @@ -1,27 +0,0 @@ -/* - # Password Management Functions - - 1. New Functions - - Password hashing and verification utilities - - Secure password management -*/ - --- Create password hashing function -CREATE OR REPLACE FUNCTION hash_password(password text) -RETURNS text -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN crypt(password, gen_salt('bf')); -END; -$$; - --- Create password verification function -CREATE OR REPLACE FUNCTION verify_password(stored_hash text, password text) -RETURNS boolean -LANGUAGE plpgsql -AS $$ -BEGIN - RETURN stored_hash = crypt(password, stored_hash); -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_emerald_dust.sql b/.bolt/supabase_discarded_migrations/0009_emerald_dust.sql deleted file mode 100644 index 4ae699f..0000000 --- a/.bolt/supabase_discarded_migrations/0009_emerald_dust.sql +++ /dev/null @@ -1,58 +0,0 @@ -/* - # Создание системы обратной связи - - 1. Новые таблицы - - request_feedback (обратная связь по заявкам) - - id (uuid, первичный ключ) - - request_id (id заявки) - - rating (оценка) - - comment (комментарий) - - created_by (кто создал) - - created_at (дата создания) - - 2. Безопасность - - Включение RLS - - Политики доступа для управления отзывами -*/ - --- Создание таблицы обратной связи -CREATE TABLE IF NOT EXISTS request_feedback ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - request_id uuid REFERENCES support_requests(id) ON DELETE CASCADE, - rating integer NOT NULL CHECK (rating >= 1 AND rating <= 5), - comment text, - created_by uuid REFERENCES employees(id), - created_at timestamptz NOT NULL DEFAULT now() -); - --- Создание индекса -CREATE INDEX IF NOT EXISTS idx_request_feedback_request_id -ON request_feedback(request_id); - --- Включение RLS -ALTER TABLE request_feedback ENABLE ROW LEVEL SECURITY; - --- Создание политик -CREATE POLICY "Сотрудники могут оставлять отзывы о своих заявках" - ON request_feedback - FOR INSERT - TO authenticated - WITH CHECK ( - EXISTS ( - SELECT 1 FROM support_requests - WHERE id = request_id - AND employee_id = auth.uid() - ) - ); - -CREATE POLICY "Сотрудники могут видеть отзывы о своих заявках" - ON request_feedback - FOR SELECT - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM support_requests - WHERE id = request_feedback.request_id - AND employee_id = auth.uid() - ) - ); \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_fading_grove.sql b/.bolt/supabase_discarded_migrations/0009_fading_grove.sql deleted file mode 100644 index 8af4c73..0000000 --- a/.bolt/supabase_discarded_migrations/0009_fading_grove.sql +++ /dev/null @@ -1,56 +0,0 @@ -/* - # Система приоритетов заявок - - 1. Новые таблицы - - `request_priorities` - - `id` (uuid, primary key) - - `name` (text, unique) - - `description` (text) - - `color` (text) - - `sla_hours` (integer) - - `created_at` (timestamptz) - - 2. Безопасность - - Включение RLS - - Политики для чтения и управления -*/ - -DO $$ BEGIN - -- Создание таблицы приоритетов, если она не существует - CREATE TABLE IF NOT EXISTS request_priorities ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL UNIQUE, - description text, - color text NOT NULL, - sla_hours integer NOT NULL, - created_at timestamptz NOT NULL DEFAULT now() - ); - - -- Включение RLS - ALTER TABLE request_priorities ENABLE ROW LEVEL SECURITY; - - -- Безопасное создание политик с проверкой существования - IF NOT EXISTS ( - SELECT 1 FROM pg_policies - WHERE tablename = 'request_priorities' - AND policyname = 'Все могут просматривать приоритеты' - ) THEN - CREATE POLICY "Все могут просматривать приоритеты" - ON request_priorities - FOR SELECT - TO authenticated - USING (true); - END IF; - - IF NOT EXISTS ( - SELECT 1 FROM pg_policies - WHERE tablename = 'request_priorities' - AND policyname = 'Только администраторы могут управлять приоритетами' - ) THEN - CREATE POLICY "Только администраторы могут управлять приоритетами" - ON request_priorities - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - END IF; -END $$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_mellow_frog.sql b/.bolt/supabase_discarded_migrations/0009_mellow_frog.sql deleted file mode 100644 index a857b2e..0000000 --- a/.bolt/supabase_discarded_migrations/0009_mellow_frog.sql +++ /dev/null @@ -1,73 +0,0 @@ -/* - # User Management Functions - - 1. New Functions - - User creation with password validation - - User authentication -*/ - --- Create user management function -CREATE OR REPLACE FUNCTION create_user( - p_first_name text, - p_last_name text, - p_department text, - p_password text -) -RETURNS users -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -DECLARE - v_user users; -BEGIN - -- Validate password length - IF length(p_password) < 4 THEN - RAISE EXCEPTION 'Password must be at least 4 characters long'; - END IF; - - -- Create user - INSERT INTO users ( - first_name, - last_name, - department, - password_hash - ) VALUES ( - p_first_name, - p_last_name, - p_department, - hash_password(p_password) - ) - RETURNING * INTO v_user; - - RETURN v_user; -END; -$$; - --- Create authentication function -CREATE OR REPLACE FUNCTION authenticate_user( - p_last_name text, - p_password text -) -RETURNS TABLE ( - id uuid, - first_name text, - last_name text, - department text, - created_at timestamptz -) -LANGUAGE plpgsql -SECURITY DEFINER -AS $$ -BEGIN - RETURN QUERY - SELECT - u.id, - u.first_name, - u.last_name, - u.department, - u.created_at - FROM users u - WHERE u.last_name = p_last_name - AND verify_password(u.password_hash, p_password); -END; -$$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_noisy_shadow.sql b/.bolt/supabase_discarded_migrations/0009_noisy_shadow.sql deleted file mode 100644 index 96dcbb0..0000000 --- a/.bolt/supabase_discarded_migrations/0009_noisy_shadow.sql +++ /dev/null @@ -1,50 +0,0 @@ -/* - # Добавление системы категорий заявок - - 1. Новые таблицы - - `request_categories` - - `id` (uuid, primary key) - - `name` (text, unique) - - `description` (text) - - `is_active` (boolean) - - `created_at` (timestamp) - - 2. Безопасность - - Включение RLS на таблице request_categories - - Политика для чтения всеми авторизованными пользователями - - Политика для управления только администраторами -*/ - -DO $$ BEGIN - -- Проверяем существование таблицы перед созданием - IF NOT EXISTS ( - SELECT FROM pg_tables - WHERE schemaname = 'public' - AND tablename = 'request_categories' - ) THEN - -- Создание таблицы категорий - CREATE TABLE request_categories ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - name text NOT NULL UNIQUE, - description text, - is_active boolean DEFAULT true, - created_at timestamptz NOT NULL DEFAULT now() - ); - - -- Включение RLS - ALTER TABLE request_categories ENABLE ROW LEVEL SECURITY; - - -- Создание политик - CREATE POLICY "Все могут просматривать категории" - ON request_categories - FOR SELECT - TO authenticated - USING (true); - - CREATE POLICY "Только администраторы могут управлять категориями" - ON request_categories - FOR ALL - TO authenticated - USING (auth.jwt() ->> 'role' = 'admin'); - END IF; -END $$; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_snowy_cake.sql b/.bolt/supabase_discarded_migrations/0009_snowy_cake.sql deleted file mode 100644 index 9ddab9f..0000000 --- a/.bolt/supabase_discarded_migrations/0009_snowy_cake.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* - # Update employees table - - 1. Changes - - Add `is_admin` column for admin access control - - Add email generation function - - Update existing records -*/ - --- Add admin flag -ALTER TABLE employees -ADD COLUMN IF NOT EXISTS is_admin boolean NOT NULL DEFAULT false; - --- Update existing admin users -UPDATE employees -SET is_admin = true -WHERE email LIKE '%admin%'; \ No newline at end of file diff --git a/.bolt/supabase_discarded_migrations/0009_tender_morning.sql b/.bolt/supabase_discarded_migrations/0009_tender_morning.sql deleted file mode 100644 index c5222fd..0000000 --- a/.bolt/supabase_discarded_migrations/0009_tender_morning.sql +++ /dev/null @@ -1,72 +0,0 @@ -/* - # Create support requests table with employee relationship - - 1. Changes - - Create support_requests table with proper foreign keys - - Add RLS policies for access control - - Handle existing enum types safely - - 2. Security - - Enable RLS on support_requests table - - Add policies for authenticated users and admins -*/ - --- Safely create enum types if they don't exist -DO $$ BEGIN - CREATE TYPE request_type AS ENUM ('hardware', 'software', 'network', 'access', 'other'); -EXCEPTION - WHEN duplicate_object THEN NULL; -END $$; - -DO $$ BEGIN - CREATE TYPE request_priority AS ENUM ('low', 'medium', 'high', 'critical'); -EXCEPTION - WHEN duplicate_object THEN NULL; -END $$; - -DO $$ BEGIN - CREATE TYPE request_status AS ENUM ('new', 'in_progress', 'resolved', 'closed'); -EXCEPTION - WHEN duplicate_object THEN NULL; -END $$; - --- Create support requests table with proper foreign key -CREATE TABLE IF NOT EXISTS support_requests ( - id uuid PRIMARY KEY DEFAULT gen_random_uuid(), - employee_id uuid REFERENCES employees(id) ON DELETE CASCADE NOT NULL, - department text NOT NULL, - request_type request_type NOT NULL, - priority request_priority NOT NULL, - status request_status NOT NULL DEFAULT 'new', - description text NOT NULL DEFAULT '', - created_at timestamptz NOT NULL DEFAULT now(), - last_status_change timestamptz DEFAULT now() -); - --- Enable RLS -ALTER TABLE support_requests ENABLE ROW LEVEL SECURITY; - --- Create policies -CREATE POLICY "Users can view their own requests" - ON support_requests - FOR SELECT - TO authenticated - USING (employee_id = auth.uid()); - -CREATE POLICY "Users can create their own requests" - ON support_requests - FOR INSERT - TO authenticated - WITH CHECK (employee_id = auth.uid()); - -CREATE POLICY "Admins can view all requests" - ON support_requests - FOR ALL - TO authenticated - USING ( - EXISTS ( - SELECT 1 FROM employees e - WHERE e.id = auth.uid() - AND e.is_admin = true - ) - ); \ No newline at end of file diff --git a/.venv/Include/site/python3.11/greenlet/greenlet.h b/.venv/Include/site/python3.11/greenlet/greenlet.h deleted file mode 100644 index d02a16e..0000000 --- a/.venv/Include/site/python3.11/greenlet/greenlet.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ - -/* Greenlet object interface */ - -#ifndef Py_GREENLETOBJECT_H -#define Py_GREENLETOBJECT_H - - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is deprecated and undocumented. It does not change. */ -#define GREENLET_VERSION "1.0.0" - -#ifndef GREENLET_MODULE -#define implementation_ptr_t void* -#endif - -typedef struct _greenlet { - PyObject_HEAD - PyObject* weakreflist; - PyObject* dict; - implementation_ptr_t pimpl; -} PyGreenlet; - -#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) - - -/* C API functions */ - -/* Total number of symbols that are exported */ -#define PyGreenlet_API_pointers 12 - -#define PyGreenlet_Type_NUM 0 -#define PyExc_GreenletError_NUM 1 -#define PyExc_GreenletExit_NUM 2 - -#define PyGreenlet_New_NUM 3 -#define PyGreenlet_GetCurrent_NUM 4 -#define PyGreenlet_Throw_NUM 5 -#define PyGreenlet_Switch_NUM 6 -#define PyGreenlet_SetParent_NUM 7 - -#define PyGreenlet_MAIN_NUM 8 -#define PyGreenlet_STARTED_NUM 9 -#define PyGreenlet_ACTIVE_NUM 10 -#define PyGreenlet_GET_PARENT_NUM 11 - -#ifndef GREENLET_MODULE -/* This section is used by modules that uses the greenlet C API */ -static void** _PyGreenlet_API = NULL; - -# define PyGreenlet_Type \ - (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) - -# define PyExc_GreenletError \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) - -# define PyExc_GreenletExit \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) - -/* - * PyGreenlet_New(PyObject *args) - * - * greenlet.greenlet(run, parent=None) - */ -# define PyGreenlet_New \ - (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ - _PyGreenlet_API[PyGreenlet_New_NUM]) - -/* - * PyGreenlet_GetCurrent(void) - * - * greenlet.getcurrent() - */ -# define PyGreenlet_GetCurrent \ - (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) - -/* - * PyGreenlet_Throw( - * PyGreenlet *greenlet, - * PyObject *typ, - * PyObject *val, - * PyObject *tb) - * - * g.throw(...) - */ -# define PyGreenlet_Throw \ - (*(PyObject * (*)(PyGreenlet * self, \ - PyObject * typ, \ - PyObject * val, \ - PyObject * tb)) \ - _PyGreenlet_API[PyGreenlet_Throw_NUM]) - -/* - * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) - * - * g.switch(*args, **kwargs) - */ -# define PyGreenlet_Switch \ - (*(PyObject * \ - (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ - _PyGreenlet_API[PyGreenlet_Switch_NUM]) - -/* - * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) - * - * g.parent = new_parent - */ -# define PyGreenlet_SetParent \ - (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ - _PyGreenlet_API[PyGreenlet_SetParent_NUM]) - -/* - * PyGreenlet_GetParent(PyObject* greenlet) - * - * return greenlet.parent; - * - * This could return NULL even if there is no exception active. - * If it does not return NULL, you are responsible for decrementing the - * reference count. - */ -# define PyGreenlet_GetParent \ - (*(PyGreenlet* (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) - -/* - * deprecated, undocumented alias. - */ -# define PyGreenlet_GET_PARENT PyGreenlet_GetParent - -# define PyGreenlet_MAIN \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_MAIN_NUM]) - -# define PyGreenlet_STARTED \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_STARTED_NUM]) - -# define PyGreenlet_ACTIVE \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) - - - - -/* Macro that imports greenlet and initializes C API */ -/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we - keep the older definition to be sure older code that might have a copy of - the header still works. */ -# define PyGreenlet_Import() \ - { \ - _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ - } - -#endif /* GREENLET_MODULE */ - -#ifdef __cplusplus -} -#endif -#endif /* !Py_GREENLETOBJECT_H */ diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/INSTALLER b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/LICENSE b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/LICENSE deleted file mode 100644 index 967cdc5..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright 2005-2024 SQLAlchemy authors and contributors . - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/METADATA b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/METADATA deleted file mode 100644 index e43a459..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/METADATA +++ /dev/null @@ -1,242 +0,0 @@ -Metadata-Version: 2.1 -Name: SQLAlchemy -Version: 2.0.27 -Summary: Database Abstraction Library -Home-page: https://www.sqlalchemy.org -Author: Mike Bayer -Author-email: mike_mp@zzzcomputing.com -License: MIT -Project-URL: Documentation, https://docs.sqlalchemy.org -Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/ -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Database :: Front-Ends -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE -Requires-Dist: typing-extensions >=4.6.0 -Requires-Dist: greenlet !=0.4.17 ; platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32"))))) -Requires-Dist: importlib-metadata ; python_version < "3.8" -Provides-Extra: aiomysql -Requires-Dist: greenlet !=0.4.17 ; extra == 'aiomysql' -Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql' -Provides-Extra: aioodbc -Requires-Dist: greenlet !=0.4.17 ; extra == 'aioodbc' -Requires-Dist: aioodbc ; extra == 'aioodbc' -Provides-Extra: aiosqlite -Requires-Dist: greenlet !=0.4.17 ; extra == 'aiosqlite' -Requires-Dist: aiosqlite ; extra == 'aiosqlite' -Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite' -Provides-Extra: asyncio -Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncio' -Provides-Extra: asyncmy -Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncmy' -Requires-Dist: asyncmy !=0.2.4,!=0.2.6,>=0.2.3 ; extra == 'asyncmy' -Provides-Extra: mariadb_connector -Requires-Dist: mariadb !=1.1.2,!=1.1.5,>=1.0.1 ; extra == 'mariadb_connector' -Provides-Extra: mssql -Requires-Dist: pyodbc ; extra == 'mssql' -Provides-Extra: mssql_pymssql -Requires-Dist: pymssql ; extra == 'mssql_pymssql' -Provides-Extra: mssql_pyodbc -Requires-Dist: pyodbc ; extra == 'mssql_pyodbc' -Provides-Extra: mypy -Requires-Dist: mypy >=0.910 ; extra == 'mypy' -Provides-Extra: mysql -Requires-Dist: mysqlclient >=1.4.0 ; extra == 'mysql' -Provides-Extra: mysql_connector -Requires-Dist: mysql-connector-python ; extra == 'mysql_connector' -Provides-Extra: oracle -Requires-Dist: cx-oracle >=8 ; extra == 'oracle' -Provides-Extra: oracle_oracledb -Requires-Dist: oracledb >=1.0.1 ; extra == 'oracle_oracledb' -Provides-Extra: postgresql -Requires-Dist: psycopg2 >=2.7 ; extra == 'postgresql' -Provides-Extra: postgresql_asyncpg -Requires-Dist: greenlet !=0.4.17 ; extra == 'postgresql_asyncpg' -Requires-Dist: asyncpg ; extra == 'postgresql_asyncpg' -Provides-Extra: postgresql_pg8000 -Requires-Dist: pg8000 >=1.29.1 ; extra == 'postgresql_pg8000' -Provides-Extra: postgresql_psycopg -Requires-Dist: psycopg >=3.0.7 ; extra == 'postgresql_psycopg' -Provides-Extra: postgresql_psycopg2binary -Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary' -Provides-Extra: postgresql_psycopg2cffi -Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi' -Provides-Extra: postgresql_psycopgbinary -Requires-Dist: psycopg[binary] >=3.0.7 ; extra == 'postgresql_psycopgbinary' -Provides-Extra: pymysql -Requires-Dist: pymysql ; extra == 'pymysql' -Provides-Extra: sqlcipher -Requires-Dist: sqlcipher3-binary ; extra == 'sqlcipher' - -SQLAlchemy -========== - -|PyPI| |Python| |Downloads| - -.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy - :target: https://pypi.org/project/sqlalchemy - :alt: PyPI - -.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy - :target: https://pypi.org/project/sqlalchemy - :alt: PyPI - Python Version - -.. |Downloads| image:: https://static.pepy.tech/badge/sqlalchemy/month - :target: https://pepy.tech/project/sqlalchemy - :alt: PyPI - Downloads - - -The Python SQL Toolkit and Object Relational Mapper - -Introduction -------------- - -SQLAlchemy is the Python SQL toolkit and Object Relational Mapper -that gives application developers the full power and -flexibility of SQL. SQLAlchemy provides a full suite -of well known enterprise-level persistence patterns, -designed for efficient and high-performing database -access, adapted into a simple and Pythonic domain -language. - -Major SQLAlchemy features include: - -* An industrial strength ORM, built - from the core on the identity map, unit of work, - and data mapper patterns. These patterns - allow transparent persistence of objects - using a declarative configuration system. - Domain models - can be constructed and manipulated naturally, - and changes are synchronized with the - current transaction automatically. -* A relationally-oriented query system, exposing - the full range of SQL's capabilities - explicitly, including joins, subqueries, - correlation, and most everything else, - in terms of the object model. - Writing queries with the ORM uses the same - techniques of relational composition you use - when writing SQL. While you can drop into - literal SQL at any time, it's virtually never - needed. -* A comprehensive and flexible system - of eager loading for related collections and objects. - Collections are cached within a session, - and can be loaded on individual access, all - at once using joins, or by query per collection - across the full result set. -* A Core SQL construction system and DBAPI - interaction layer. The SQLAlchemy Core is - separate from the ORM and is a full database - abstraction layer in its own right, and includes - an extensible Python-based SQL expression - language, schema metadata, connection pooling, - type coercion, and custom types. -* All primary and foreign key constraints are - assumed to be composite and natural. Surrogate - integer primary keys are of course still the - norm, but SQLAlchemy never assumes or hardcodes - to this model. -* Database introspection and generation. Database - schemas can be "reflected" in one step into - Python structures representing database metadata; - those same structures can then generate - CREATE statements right back out - all within - the Core, independent of the ORM. - -SQLAlchemy's philosophy: - -* SQL databases behave less and less like object - collections the more size and performance start to - matter; object collections behave less and less like - tables and rows the more abstraction starts to matter. - SQLAlchemy aims to accommodate both of these - principles. -* An ORM doesn't need to hide the "R". A relational - database provides rich, set-based functionality - that should be fully exposed. SQLAlchemy's - ORM provides an open-ended set of patterns - that allow a developer to construct a custom - mediation layer between a domain model and - a relational schema, turning the so-called - "object relational impedance" issue into - a distant memory. -* The developer, in all cases, makes all decisions - regarding the design, structure, and naming conventions - of both the object model as well as the relational - schema. SQLAlchemy only provides the means - to automate the execution of these decisions. -* With SQLAlchemy, there's no such thing as - "the ORM generated a bad query" - you - retain full control over the structure of - queries, including how joins are organized, - how subqueries and correlation is used, what - columns are requested. Everything SQLAlchemy - does is ultimately the result of a developer-initiated - decision. -* Don't use an ORM if the problem doesn't need one. - SQLAlchemy consists of a Core and separate ORM - component. The Core offers a full SQL expression - language that allows Pythonic construction - of SQL constructs that render directly to SQL - strings for a target database, returning - result sets that are essentially enhanced DBAPI - cursors. -* Transactions should be the norm. With SQLAlchemy's - ORM, nothing goes to permanent storage until - commit() is called. SQLAlchemy encourages applications - to create a consistent means of delineating - the start and end of a series of operations. -* Never render a literal value in a SQL statement. - Bound parameters are used to the greatest degree - possible, allowing query optimizers to cache - query plans effectively and making SQL injection - attacks a non-issue. - -Documentation -------------- - -Latest documentation is at: - -https://www.sqlalchemy.org/docs/ - -Installation / Requirements ---------------------------- - -Full documentation for installation is at -`Installation `_. - -Getting Help / Development / Bug reporting ------------------------------------------- - -Please refer to the `SQLAlchemy Community Guide `_. - -Code of Conduct ---------------- - -Above all, SQLAlchemy places great emphasis on polite, thoughtful, and -constructive communication between users and developers. -Please see our current Code of Conduct at -`Code of Conduct `_. - -License -------- - -SQLAlchemy is distributed under the `MIT license -`_. - diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/RECORD b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/RECORD deleted file mode 100644 index d270522..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/RECORD +++ /dev/null @@ -1,530 +0,0 @@ -SQLAlchemy-2.0.27.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -SQLAlchemy-2.0.27.dist-info/LICENSE,sha256=eYQKk6tEYK_iQW6ePf95YIdsg66dK-JwXoOhBNSXQOs,1119 -SQLAlchemy-2.0.27.dist-info/METADATA,sha256=ySjjHZ2y0chER0KC_8dWxR1tJNJ6ZFKSsKn5NpUGQ4c,9844 -SQLAlchemy-2.0.27.dist-info/RECORD,, -SQLAlchemy-2.0.27.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -SQLAlchemy-2.0.27.dist-info/WHEEL,sha256=ircjsfhzblqgSzO8ow7-0pXK-RVqDqNRGQ8F650AUNM,102 -SQLAlchemy-2.0.27.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 -sqlalchemy/__init__.py,sha256=hU_2Jtxg7KtVE1AdYJaSNwubWJozVVoKzMBzda_lewc,13327 -sqlalchemy/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/__pycache__/events.cpython-311.pyc,, -sqlalchemy/__pycache__/exc.cpython-311.pyc,, -sqlalchemy/__pycache__/inspection.cpython-311.pyc,, -sqlalchemy/__pycache__/log.cpython-311.pyc,, -sqlalchemy/__pycache__/schema.cpython-311.pyc,, -sqlalchemy/__pycache__/types.cpython-311.pyc,, -sqlalchemy/connectors/__init__.py,sha256=A2AI8p63aT0jT5CsVX33xlTfiGWliOcGahlK0RyTLXg,494 -sqlalchemy/connectors/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/connectors/__pycache__/aioodbc.cpython-311.pyc,, -sqlalchemy/connectors/__pycache__/asyncio.cpython-311.pyc,, -sqlalchemy/connectors/__pycache__/pyodbc.cpython-311.pyc,, -sqlalchemy/connectors/aioodbc.py,sha256=fg3xfG-5gLsy-DSyVonNNKYhOf0_lzHmixRFa5edtWI,5462 -sqlalchemy/connectors/asyncio.py,sha256=DOy84rX4l0U5Nfn9dYLY8ETFE2tRiuSAZTJVPZpEl0E,6163 -sqlalchemy/connectors/pyodbc.py,sha256=IG5lLCyFbnv1wB85HQuMO3S5piWHaB660OBWvBIQhbg,8750 -sqlalchemy/cyextension/__init__.py,sha256=Hlfk91RinbOuNF_fybR5R2UtiIcTeUOXS66QOfSSCV0,250 -sqlalchemy/cyextension/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/cyextension/collections.cp311-win_amd64.pyd,sha256=sMUnGsAsxmktVQxumYz7FORlt4p0fkcXpNvtdQl0Bfw,175104 -sqlalchemy/cyextension/collections.pyx,sha256=GXPkr9cHRLW3Vcu-ik3dVBZMR-zf0Q5_K4J-_8yV-gk,12980 -sqlalchemy/cyextension/immutabledict.cp311-win_amd64.pyd,sha256=Md9KeQlBLAJ9k671YhqKNg0oxyY-gGH_izUWkMV0ciE,72704 -sqlalchemy/cyextension/immutabledict.pxd,sha256=5iGndSbJCgCkNmRbJ_z14RANs2dSSnAzyiRPUTBk58Y,299 -sqlalchemy/cyextension/immutabledict.pyx,sha256=IhB2pR49CrORXQ3LXMFpuCIRc6I08QNvIylE1cPQA5o,3668 -sqlalchemy/cyextension/processors.cp311-win_amd64.pyd,sha256=ocBdpnIueLk1-UrvAt1ieovUjd7c94wCy-eh5Bsd384,58368 -sqlalchemy/cyextension/processors.pyx,sha256=V9gzqXiNHWsa5DBgYl-3KzclFHY8kXGF_TD1xHFE7eM,1860 -sqlalchemy/cyextension/resultproxy.cp311-win_amd64.pyd,sha256=b8Sw6akLIwneSdTRYvga8VuWhc-zz7aX1pa3EACVuwE,60928 -sqlalchemy/cyextension/resultproxy.pyx,sha256=h_RrKasbLtKK3LqUh6UiWtkumBlKtcN5eeB_1bZROMA,2827 -sqlalchemy/cyextension/util.cp311-win_amd64.pyd,sha256=HZ220KIKcvLl-qn23YNUtRGL6kLMKMPOnKhtyRzR7qA,72192 -sqlalchemy/cyextension/util.pyx,sha256=50QYpSAKgLSUfhFEQgSN2e1qHWCMh_b6ZNlErDUS7ec,2621 -sqlalchemy/dialects/__init__.py,sha256=SJfQyxMhOL58EB-S6GQv_0jf2oP7MMfmVdlV2UxGWQo,1831 -sqlalchemy/dialects/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/__pycache__/_typing.cpython-311.pyc,, -sqlalchemy/dialects/_typing.py,sha256=mN2r8mU8z-mRh4YS3VeK8Nv_IKJmE0Mb1CrJ-ptILas,913 -sqlalchemy/dialects/mssql/__init__.py,sha256=r3oTfX2LLbJAGhM57wdPLWxaZBzunkcmyaTbW0FjLuY,1968 -sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/base.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/json.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-311.pyc,, -sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-311.pyc,, -sqlalchemy/dialects/mssql/aioodbc.py,sha256=b9bhUKcVj4NzoqJIDfECeE_Rmt51sRy8OOUFz_R3vpg,2086 -sqlalchemy/dialects/mssql/base.py,sha256=ERGOPdJHLCF4n9L9-nBeiVQ3X-nl8ryt1FEFLxUwj1o,137336 -sqlalchemy/dialects/mssql/information_schema.py,sha256=A1UJAoFb3UtE8YCY3heBgeTMkzWq3j7C2caZ3gcMGZk,8338 -sqlalchemy/dialects/mssql/json.py,sha256=nZVVsgmR4Z4dNn9cv5Gucq596gsQ0MvASPuEEtz-Gek,4949 -sqlalchemy/dialects/mssql/provision.py,sha256=pa-b74Xr2qsto3BFG1O0I_B25TUT3TOecg6cAKuRcf8,5517 -sqlalchemy/dialects/mssql/pymssql.py,sha256=f7xqRif9Dp64de9G8yuC4OyWArwXy_oVq5X0oiwIX4E,4163 -sqlalchemy/dialects/mssql/pyodbc.py,sha256=YVI19AnrqxPCBwDqcjrO_rqUUWbV2re7E8iLuV1ilqE,27801 -sqlalchemy/dialects/mysql/__init__.py,sha256=PPQDwNqcpxWMt3nFQ66KefX9T9iz7d8lybEwKlfXB1U,2254 -sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/base.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/dml.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/expression.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/json.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-311.pyc,, -sqlalchemy/dialects/mysql/__pycache__/types.cpython-311.pyc,, -sqlalchemy/dialects/mysql/aiomysql.py,sha256=-YFqFQEx0M2pww3xvsOlaVKflTeoUfIsM8BgfhP1MP0,10296 -sqlalchemy/dialects/mysql/asyncmy.py,sha256=h9BBhGsqPG2LWdoh0lPieRLhaQ_BJrhxwsR_G5yPLDQ,10370 -sqlalchemy/dialects/mysql/base.py,sha256=LQ-nvj7HIQSntwAycof93th3kpMBatHl402HNQ8z9hc,124297 -sqlalchemy/dialects/mysql/cymysql.py,sha256=0mRP3gFe2t7iJYQqJz1Os_TztFwMAF34w2MmXe-4B_w,2384 -sqlalchemy/dialects/mysql/dml.py,sha256=n31-m4vfOIL0MdHpUdIfTLgaMzusfQ-yHYoJWO_ndEc,7864 -sqlalchemy/dialects/mysql/enumerated.py,sha256=Nz9Sv3ENX-1T18aEoOY8QfZlAcwRf65lIOse7vwjil8,8692 -sqlalchemy/dialects/mysql/expression.py,sha256=uxD1fICubfGh8BhAn6WoeS8AF6hAVEvreDShXqRZTqM,4238 -sqlalchemy/dialects/mysql/json.py,sha256=i0Lrd_7VKTd3fNm6kQKzrtPERuW0JeSw7XSUWnl1HQI,2350 -sqlalchemy/dialects/mysql/mariadb.py,sha256=WoNxkjiPfIbWAkrVEU9MTM7mePeLHZ2uiJsyfvcpv1s,885 -sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=_XZ60dSn8-iQP-qyn1Utk-lswGciuoDgpW7m7GguSVA,9016 -sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=1ga6IV7lVLH9BKsMh2M2wSz78l5a82BZnyRqJMaS5Qw,5854 -sqlalchemy/dialects/mysql/mysqldb.py,sha256=KdSkQXgCTHfOWzaM9dlQnmb77FR9X8Io6PVWTYRb1XQ,9966 -sqlalchemy/dialects/mysql/provision.py,sha256=2ecdVRnZSXy5GF3hpLtQp3T8QW-oFjtTSQgbEePDH1k,3581 -sqlalchemy/dialects/mysql/pymysql.py,sha256=Kxi_A34-nbQ5UEFSmy14TXc1v43-1SZ8gE628REGTFo,4220 -sqlalchemy/dialects/mysql/pyodbc.py,sha256=CZCEnhyLIgbuiAW32Cw7N1m1aiQv1eBB34pV-txOs70,4435 -sqlalchemy/dialects/mysql/reflection.py,sha256=wn8qKHxDb9Dnr8zC_uEgAVjk2lVuObvqPOEiad8568c,23499 -sqlalchemy/dialects/mysql/reserved_words.py,sha256=s59cfdGTmlXn3GFCnevugDsc3qiiZn8tL31lief0tvo,9721 -sqlalchemy/dialects/mysql/types.py,sha256=2du4p4PnuJgLHvdofFYP5dtkicIDJYeEdrFBhe7uotQ,25040 -sqlalchemy/dialects/oracle/__init__.py,sha256=_yFT_k0R6yc7MKQG-Al9QZt8wYZsiCtpkhNlba5xqn8,1560 -sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/base.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/dialects/oracle/__pycache__/types.cpython-311.pyc,, -sqlalchemy/dialects/oracle/base.py,sha256=haztCHFbuhnhpcBUr8h1PDLXAavdwtpIjgOg_PSDI_A,121486 -sqlalchemy/dialects/oracle/cx_oracle.py,sha256=3Tx3DKvqcCKyXupBuCiCL4B8D5TDO934Q7LYsLJjlkk,57058 -sqlalchemy/dialects/oracle/dictionary.py,sha256=tmAZLEACqBAPBE0SEV2jr1R4aPcpNOrbomJl-UmgiR4,20026 -sqlalchemy/dialects/oracle/oracledb.py,sha256=kuw08rp-tXKPOtGGutqcs5o2gvRptQXAzNBqPVZoLxg,9798 -sqlalchemy/dialects/oracle/provision.py,sha256=KKlXDQnC8n6BjLJWA7AJg3lwXluH1OyStqfP2Uf9rq0,8524 -sqlalchemy/dialects/oracle/types.py,sha256=U9EReFRcr0PiwOxT9vg2cA7WOix8LQ2sVp0gRkMHcPo,8518 -sqlalchemy/dialects/postgresql/__init__.py,sha256=nfP-oId_1wzvRKo5TM6f2xrXry-IxoMkVd4Dga7LDg8,4062 -sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/array.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/base.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/json.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/__pycache__/types.cpython-311.pyc,, -sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=fYFqLVxNxAqh3nOvzGOv3Pfpm2BsclHrk71MJZrpJKo,5883 -sqlalchemy/dialects/postgresql/array.py,sha256=E69zMO2h7NQFjnWEeFX4fGYDymTbqHAI-laLfy7WrEA,14137 -sqlalchemy/dialects/postgresql/asyncpg.py,sha256=CpFNMxOqJUvviWxbEyMiJKnBXTMcvN0IFwaka2gH4R0,41445 -sqlalchemy/dialects/postgresql/base.py,sha256=x3xx5650ZKYAq9ShqDGQNCkj5nt3HNQXv0-Xq0ZT1Ro,181406 -sqlalchemy/dialects/postgresql/dml.py,sha256=uMiqxEkji-UXqk8gO1ramQEvEfCugYmy8Cv1cnG7DQs,11522 -sqlalchemy/dialects/postgresql/ext.py,sha256=ct6NQfMAfBnLYhybpF2wPEq-p8-U0tEpy-aq8NwqJLw,16758 -sqlalchemy/dialects/postgresql/hstore.py,sha256=4jAZQMPWl3VE4weDRZrgrbVDRZJTM3X0Xj4twr5znYQ,11938 -sqlalchemy/dialects/postgresql/json.py,sha256=TCw4qYTeLhxjDrF1sq3k-yk7UFEhryweKEj8AelBfRc,11537 -sqlalchemy/dialects/postgresql/named_types.py,sha256=aaH5fNMZp8ZdmLI1ag9g0UgDGVvX0dpE0PbTu3XVUYc,17595 -sqlalchemy/dialects/postgresql/operators.py,sha256=iyZuyx_daRyJjiS5rw-XnZlaWj1bmRiHdy5MXzBrFZw,2937 -sqlalchemy/dialects/postgresql/pg8000.py,sha256=TPJXX078vW0FSwZ-DlWNkEOXg7Z4xk8IFwi1droMhPw,19302 -sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=rNWjbOOC3SB2jmFAwz5VkbdZub7BCTp60YUmJt3eeRI,9178 -sqlalchemy/dialects/postgresql/provision.py,sha256=Uo_6vxVzFryFjLqsrvesRO55VqHrnsAs_pBH_8JtFcA,5937 -sqlalchemy/dialects/postgresql/psycopg.py,sha256=zRoaG1ggAKnMBo6fxe6xJa52BxyU2OrOGX-_jDrH2Zk,23113 -sqlalchemy/dialects/postgresql/psycopg2.py,sha256=xau5nXatjvPNbzqTF8GF_wrwU4Q5T2zkIMGZkhHvrI0,32483 -sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=hFg-9GH08ApPy3foVPUdJKwCEzNSv2zD5l4nH97AqgI,1817 -sqlalchemy/dialects/postgresql/ranges.py,sha256=oiTmnZ-hd5WqqGNsXbuOJfoNxpbso_M_49gky8dlCrE,33978 -sqlalchemy/dialects/postgresql/types.py,sha256=pd1QmuGwJFLqpY2tK-Ql3FNjtT1Ha-lVvfaR9dimvHc,7603 -sqlalchemy/dialects/sqlite/__init__.py,sha256=MmQfjHun1U_4q-Dq_yhs9RzAX0VLixSwWeY5xWiDwag,1239 -sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/base.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/json.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-311.pyc,, -sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=TgobCILLu2mjGvDgMTxX3-CPxkj_c5LDYRDJHo5W5qg,12701 -sqlalchemy/dialects/sqlite/base.py,sha256=8Ft5tZeT1lHiWliTwoJaNslf8PRITVKKWhjhhRCeVDk,99576 -sqlalchemy/dialects/sqlite/dml.py,sha256=8JV6Ise7WtmFniy590X5b19AYZcE51M6N5hef7d9JoA,8683 -sqlalchemy/dialects/sqlite/json.py,sha256=-9afZnBt07vInCX20CKzjlTG85wHTO5_cxhcYU4phDc,2869 -sqlalchemy/dialects/sqlite/provision.py,sha256=nAXZPEjXFrb6a1LxXZMqKmkQoXgl3MPsSHuMyBQ76NU,5830 -sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=p0KfzHBwANDMwKTKEJCjR5RxMYqQwS4E8KXjl3Bx6Fw,5511 -sqlalchemy/dialects/sqlite/pysqlite.py,sha256=s1eL04d6fqPeUNSTnpxXSjWR8OdCuYcy9ilk7716wzU,28653 -sqlalchemy/dialects/type_migration_guidelines.txt,sha256=gyh3JCauAIFi_9XEfqm3vYv_jb2Eqcz2HjpmC9ZEPMM,8384 -sqlalchemy/engine/__init__.py,sha256=93FWhb62dLCidc6e4FE65wq_P8GeoWQG1OG6RZMBqhM,2880 -sqlalchemy/engine/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/_py_processors.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/_py_row.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/_py_util.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/base.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/characteristics.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/create.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/cursor.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/default.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/events.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/interfaces.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/mock.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/processors.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/reflection.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/result.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/row.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/strategies.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/url.cpython-311.pyc,, -sqlalchemy/engine/__pycache__/util.cpython-311.pyc,, -sqlalchemy/engine/_py_processors.py,sha256=-jlAYPM6etmuKeViiI7BD41kqY0Pr8nzaox22TPqCCQ,3880 -sqlalchemy/engine/_py_row.py,sha256=UEGCjAeRsggcUn0QB0PdFC82kuykrOiOZ1KGq_Gf_qQ,3915 -sqlalchemy/engine/_py_util.py,sha256=nh1XoVq1b-eGgkdzbqFqzje0RNSmVWotoa6yaB7J5Sw,2558 -sqlalchemy/engine/base.py,sha256=0VnqL5IK8rTjs7wanSKgOjuzbKdNjs_8TvzJgUqfNCw,125393 -sqlalchemy/engine/characteristics.py,sha256=q_l1-KybkM5Y-5hG74NuQBxHRkidBstqpRBlFLxJPqs,2671 -sqlalchemy/engine/create.py,sha256=IC_D9X-t-MN78Ro3shJl4RL0fP9eTvskl4j1bAjagco,33736 -sqlalchemy/engine/cursor.py,sha256=nCEzaLslgar67EDE4mqowuJsuiBaCbs5ix7sgD9iWTI,76593 -sqlalchemy/engine/default.py,sha256=oB9T6i1k7rMxat1HkDGCf-_i_3eAOymV1yPU4k0tBeU,87011 -sqlalchemy/engine/events.py,sha256=e0VHj69fH20sB7gocBhr5Rs2FjR8ioY4iE8VQt70oJg,38332 -sqlalchemy/engine/interfaces.py,sha256=G29dfsTCNayoIiRnCQ64y9y9w9wtrGwxnm5t102ApvA,116079 -sqlalchemy/engine/mock.py,sha256=wInBRiHwydTc5ELQLivdezDd1ikbSMVXgLVzZrSC0iQ,4310 -sqlalchemy/engine/processors.py,sha256=w4MiVMlU6VvfhIW49nygbHcwX8FteGpz7g3IGEqtZb8,2440 -sqlalchemy/engine/reflection.py,sha256=TO-tymk7BsfAzc6Fi0GmwtYyOWjfMGkyvytJyMVz_oY,77216 -sqlalchemy/engine/result.py,sha256=U245Q3kGUOugqjmv-qSkx8eyDn27fLYV5agIoBHXQCA,79985 -sqlalchemy/engine/row.py,sha256=g7ZqmsqX_BtRUzY-zfXoZZ4-5xZ_KJEVbvqKHUIlqRg,12433 -sqlalchemy/engine/strategies.py,sha256=fD4DJn0AD371wlUa7s5Sy4j7QtgGyP7gMy_kUyqCLDQ,461 -sqlalchemy/engine/url.py,sha256=tOCRmKkqrpsIfNeSDoy6KKTLtQAMtoIn9xa5kmJQebk,31694 -sqlalchemy/engine/util.py,sha256=GDoT1xzoCL_CaBvWpBigzXIpUsFAFtjHH8v2e_au7qo,5833 -sqlalchemy/event/__init__.py,sha256=09qZzHwt0PkIDsPwuPUVJvNakjtCBjuUJeY0AEJ9j7k,1022 -sqlalchemy/event/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/event/__pycache__/api.cpython-311.pyc,, -sqlalchemy/event/__pycache__/attr.cpython-311.pyc,, -sqlalchemy/event/__pycache__/base.cpython-311.pyc,, -sqlalchemy/event/__pycache__/legacy.cpython-311.pyc,, -sqlalchemy/event/__pycache__/registry.cpython-311.pyc,, -sqlalchemy/event/api.py,sha256=74Z-EXtNsv8fvsf8m8bgCkVzJf7QzhMaVXJhCylegLo,8452 -sqlalchemy/event/attr.py,sha256=-SHjzXMOs7IICPSgNwpgRS3FIEeLIpB5PyvVlpw8Gp8,21406 -sqlalchemy/event/base.py,sha256=haAsH-KuvvY52A6OjwATfvCXy3hdYGgZxkDZiOqbMvI,15416 -sqlalchemy/event/legacy.py,sha256=a8VEvS83PvgbomNnaSa3okZmTkxl_buZ7Lfilechjh8,8473 -sqlalchemy/event/registry.py,sha256=f31k0FLqIlWpOK9tksiYXnv-yuZPPz9iLQqvKEYV7ko,11221 -sqlalchemy/events.py,sha256=OAy8TK21lWzSe8bDUnAbmsP82bsBYy0LL19hR6y3BrM,542 -sqlalchemy/exc.py,sha256=k01TD2xp2BM3DrXdo2U5r8yuRfsoqBND4kwvtD1SVN0,24806 -sqlalchemy/ext/__init__.py,sha256=YbMQmRS_9HxRyWM-KA_F76WOss1_Em1ZcrnQDIDXoOc,333 -sqlalchemy/ext/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/associationproxy.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/automap.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/baked.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/compiler.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/horizontal_shard.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/hybrid.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/indexable.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/instrumentation.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/mutable.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/orderinglist.cpython-311.pyc,, -sqlalchemy/ext/__pycache__/serializer.cpython-311.pyc,, -sqlalchemy/ext/associationproxy.py,sha256=MBtGwISA4wwT9i6op8jfY6u9lCUYn_4JCqtxZpjggok,67776 -sqlalchemy/ext/asyncio/__init__.py,sha256=tKYIrERYf8hov9m8DuKWRO_53qhrvj2jRmIYjSGQ2Po,1342 -sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/base.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/engine.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/exc.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/result.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-311.pyc,, -sqlalchemy/ext/asyncio/__pycache__/session.cpython-311.pyc,, -sqlalchemy/ext/asyncio/base.py,sha256=slWQTFdgQQlkzrnx3m5a9xT8IRg4iM0gkEbypXr_YXQ,9184 -sqlalchemy/ext/asyncio/engine.py,sha256=VKp3nRnmzl7cEIWSxJNJQMK8CbQbigmrvyqpIiVUdgY,49398 -sqlalchemy/ext/asyncio/exc.py,sha256=0awLfUB4PhEPVVTKYluyor1tW91GPZZnvdQ-GGSOmJY,660 -sqlalchemy/ext/asyncio/result.py,sha256=MtKAqA7hwYIdkpRxlCgHNYYzlB7dvqCtEp-aoDdFjDA,31370 -sqlalchemy/ext/asyncio/scoping.py,sha256=CiMQ7ewPNsyEtl9aGOiEZOrUaNYOTP_LrR0_xkdV3r8,54211 -sqlalchemy/ext/asyncio/session.py,sha256=gVdecaNFy8fo4YXhfhgwmMpezGaNKsIKL5kSI_j4GoI,64821 -sqlalchemy/ext/automap.py,sha256=8mu3_-s4DUnWfmOAZsWFFaADZALSIe1J3NQS2BvUGi8,63041 -sqlalchemy/ext/baked.py,sha256=jc6vPocoXXsvdZsOsqgT4kG6guWSZD1TdPjoRBmkbRU,18381 -sqlalchemy/ext/compiler.py,sha256=PbvelWqZdzL6y1C6rEc8ledF79t_04MtYV26RUwNhik,20946 -sqlalchemy/ext/declarative/__init__.py,sha256=MHSOffOS4MWcqshAuLNQv0vDXpK_Z3lpGXTm1riyLls,1883 -sqlalchemy/ext/declarative/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/ext/declarative/__pycache__/extensions.cpython-311.pyc,, -sqlalchemy/ext/declarative/extensions.py,sha256=aPpW0PvTKH3CoSMhsOY5GcUMZOVq-OFsV1hflxmb3Lw,20095 -sqlalchemy/ext/horizontal_shard.py,sha256=V8vXEt5ZQb_PM39agZD2IyoQNGSqVI1MhY-6mNV5MRY,17231 -sqlalchemy/ext/hybrid.py,sha256=Fc73iUTCJuHcz3McvD3FBbjEvDag1Jw8THY6rL-phA8,53946 -sqlalchemy/ext/indexable.py,sha256=aDlVpN4rilRrer9qKg3kO7fqnqB5NX4M5qzYuYM8pvw,11373 -sqlalchemy/ext/instrumentation.py,sha256=lFsJECWlN1oc1E0r9TaQDZcxAx4VOz6PSHYrl5fLk9Y,16157 -sqlalchemy/ext/mutable.py,sha256=nAz3_lF2xkYSARt7GAWQh-OUMcnpe6s1ocjvQGxCPkc,38428 -sqlalchemy/ext/mypy/__init__.py,sha256=aqT8_9sNwzC8PIaEZ4zkCYGBvYPaDD3eCgJtJuk3g6A,247 -sqlalchemy/ext/mypy/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/apply.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/infer.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/names.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/plugin.cpython-311.pyc,, -sqlalchemy/ext/mypy/__pycache__/util.cpython-311.pyc,, -sqlalchemy/ext/mypy/apply.py,sha256=1Qb-_FpQ_0LVB2KFA5hVjfPv6DDMIcxXe86Ts1X9GBk,10870 -sqlalchemy/ext/mypy/decl_class.py,sha256=f2iWiFVlDFqGb_IoGGotI3IEOUErh25sLT7B_cMfx0g,17899 -sqlalchemy/ext/mypy/infer.py,sha256=O-3IjELDSBEAwGGxRM7lr0NWwGD0HMK4vda_iY6iwjs,19959 -sqlalchemy/ext/mypy/names.py,sha256=TWsrp5ftD5p0NeyAipgYIir9SUbA4U0BAk0W2FXA3VA,10972 -sqlalchemy/ext/mypy/plugin.py,sha256=TDTziLsYFRqyX8UcQMtBBa6TFR4z9N-XNO8wRkHlEOI,10053 -sqlalchemy/ext/mypy/util.py,sha256=3iQ1zVpXSUoj2aHa-Kkg4O83JOzqVd8TDEwpZj3SWWs,9786 -sqlalchemy/ext/orderinglist.py,sha256=r7La_3nZlGevIgsBL1IB30FvWO_tZHlTKo_FWwid-aY,14800 -sqlalchemy/ext/serializer.py,sha256=_7gottqRCI-qkW4Go4o2EnOSnieKDCQ8jQ6muHXw-RM,6363 -sqlalchemy/future/__init__.py,sha256=6-qPdjMHX-V-kAPjTQgNuHztmYiwKlJhKhhljuETvoQ,528 -sqlalchemy/future/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/future/__pycache__/engine.cpython-311.pyc,, -sqlalchemy/future/engine.py,sha256=N_5W2ab5-ueedWzqNdgLPzTW9audT1IbxF6FCDLRZOc,510 -sqlalchemy/inspection.py,sha256=GpmMuSAZ53u4W__iGpvzQKCBMFnTxnHt4Lo7Nq1FSKM,5237 -sqlalchemy/log.py,sha256=Sg6PGR_wmseiCCpJfRDEkaMs08XTPPsf0X_iYJLvzS0,8895 -sqlalchemy/orm/__init__.py,sha256=I-XesvuyjkAAwnsiF5FnXRLNV6W2nW70EnGAIt2GAjU,8633 -sqlalchemy/orm/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/_orm_constructors.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/_typing.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/attributes.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/base.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/bulk_persistence.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/clsregistry.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/collections.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/context.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/decl_api.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/decl_base.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/dependency.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/descriptor_props.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/dynamic.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/evaluator.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/events.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/exc.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/identity.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/instrumentation.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/interfaces.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/loading.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/mapped_collection.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/mapper.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/path_registry.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/persistence.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/properties.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/query.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/relationships.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/scoping.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/session.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/state.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/state_changes.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/strategies.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/strategy_options.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/sync.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/unitofwork.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/util.cpython-311.pyc,, -sqlalchemy/orm/__pycache__/writeonly.cpython-311.pyc,, -sqlalchemy/orm/_orm_constructors.py,sha256=qOQLU_Rly-AYjGDotwZv65PCjjLgdAwqHGPUeZrTYfE,101822 -sqlalchemy/orm/_typing.py,sha256=Z9GZT8Vb-wFwvHeOeVE37dvmCWdItLZnqI_pLin4cMc,5152 -sqlalchemy/orm/attributes.py,sha256=devlqbjcICNLnG0HEg4wSP2sgm2m8sW9RpoQZuaU0KM,95355 -sqlalchemy/orm/base.py,sha256=JWzk2w1k-xT5BW8T7K0DRhMg7lDKFf4T6KrFey_6c4A,28394 -sqlalchemy/orm/bulk_persistence.py,sha256=z7yTdor_Nea7R0UMu8kloKvN-t0z2AZ-P-q5FE-oabc,72070 -sqlalchemy/orm/clsregistry.py,sha256=ZumBI7I2O-l93LbA4eyMKm0w6al-nNS2QV1VDcJxGko,18528 -sqlalchemy/orm/collections.py,sha256=lHjP6uDz0WdwedTqyh_8R2_nzRAK_5ONCIoisHrsb94,53797 -sqlalchemy/orm/context.py,sha256=bBieTIPsM10lt5z5Feq4tDjBqpznbsssaiXh7OrTh18,115244 -sqlalchemy/orm/decl_api.py,sha256=Xd9s8A7V_jlypP-u-tnpQ5o6Oq-v1A4H1f3hSEj4-Bc,65549 -sqlalchemy/orm/decl_base.py,sha256=g6bhTg5M9hsf3JVnMaqn1NS7MTdBJY--L-qf9aQabgs,83753 -sqlalchemy/orm/dependency.py,sha256=glstmbB4t-PIRA47u9NgTyyxbENfyQuG9Uzj2iezB_s,48935 -sqlalchemy/orm/descriptor_props.py,sha256=PpDt83EX72AhCbBjixQDpYG1P1MqWqGdA-bJpXMSxSw,38254 -sqlalchemy/orm/dynamic.py,sha256=m7V2GPS5__4y_hP7BQjD66b6BVEre1pzPwaj2bmRFRM,10084 -sqlalchemy/orm/evaluator.py,sha256=gQIDxuoB5Ut2hiFdN768U67JusVkbFt-Bdna_T8npPA,12293 -sqlalchemy/orm/events.py,sha256=EeIAGfdSQX0XQLZyURz94KYOlsjwI-ANWjPfyN_-jcQ,130956 -sqlalchemy/orm/exc.py,sha256=90xWOIIAmzPguaVH6mxr2xUSGW44aGIPz5WytJSwmR4,7583 -sqlalchemy/orm/identity.py,sha256=fOpANTf73r12F_w9DhVoyjkAdh8ldgJcNnwxx0GY8YM,9551 -sqlalchemy/orm/instrumentation.py,sha256=a8vi3qEAyO7Z9PYksLkFi_YzxqQhzB-anblegiAtsFw,25075 -sqlalchemy/orm/interfaces.py,sha256=IKCWZFHamXIkpBCaEh7YIYy-Jz2hV4-SQt6kGbfOuE8,49842 -sqlalchemy/orm/loading.py,sha256=sp7VaIoc9gzrtAOekU_2EWYN3L_9lKuiKkfn6f4VlQQ,59202 -sqlalchemy/orm/mapped_collection.py,sha256=AeSzQwj56cLr1tVMC0B-3JsC74IAP7_gbr-EPc4_2uw,20250 -sqlalchemy/orm/mapper.py,sha256=13goncHEJueS73Z7EWovVdp1W-xBKSWB5cjqtpyIk5s,175479 -sqlalchemy/orm/path_registry.py,sha256=KqS4yYe__beUSpdEjL8qxzL_z7V-FJkolm4AMCBHhGg,26658 -sqlalchemy/orm/persistence.py,sha256=MKb7TuSLJUQpyqnHxf6uNmGXSznmZgkkFTD04nHbNUQ,63483 -sqlalchemy/orm/properties.py,sha256=xtSDAAeUgDAmZF_OTBd4F18q9h3h7JWnPtQnWU0-aU4,30007 -sqlalchemy/orm/query.py,sha256=d2aENAsXkiZuXOxZ5WNKMqsrNHT7_d-_BgXqNa2BtsA,120948 -sqlalchemy/orm/relationships.py,sha256=jtIA6Y7jhlSzee-MGu_0YMmtH8Kr4lwanjCBfB_eV_I,131177 -sqlalchemy/orm/scoping.py,sha256=aAQMIAAZ-M_m6UGndmkUiDazcphE-klw6wZjFT2Az7E,80842 -sqlalchemy/orm/session.py,sha256=QRC0WQSjNFIhyInWnZD-picMoKWS79oIlQAVAcRon_4,198419 -sqlalchemy/orm/state.py,sha256=9opH8AR6LnbCRmW1lN2RxEQyxnEi1rcDXlySqrDeUiw,38656 -sqlalchemy/orm/state_changes.py,sha256=4i90vDgBGvVGUzhlonlBkZBAZFOWaAXij2X8OEA3-BA,7013 -sqlalchemy/orm/strategies.py,sha256=GFppPikNxZJdsi4DW1HcU6pv-EvIeFvTI8K_LDY5nmk,117550 -sqlalchemy/orm/strategy_options.py,sha256=cBzotMlredMZ8vM6T04mTnskiS0qSoRkHEQKSLZNARA,86734 -sqlalchemy/orm/sync.py,sha256=aMEMhYTj2rtJZJvjqm-cUx2CoQxYl8P6YddCLpLelhM,5943 -sqlalchemy/orm/unitofwork.py,sha256=THggzzAaqmYh5PBDob5dHTP_YyHXYdscs3fIxtRV-gE,27829 -sqlalchemy/orm/util.py,sha256=xIaltctFxy-u4xdLk1mqdHYsq_bvg4xsaTRQTev7I80,82756 -sqlalchemy/orm/writeonly.py,sha256=j5DcpZKOv1tLGQLhKfk-Uw-B0yEG7LezwJWNTq0FtWQ,22983 -sqlalchemy/pool/__init__.py,sha256=ZKUPMKdBU57mhu677UsvRs5Aq9s9BwIbMmSNRoTRPoY,1848 -sqlalchemy/pool/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/pool/__pycache__/base.cpython-311.pyc,, -sqlalchemy/pool/__pycache__/events.cpython-311.pyc,, -sqlalchemy/pool/__pycache__/impl.cpython-311.pyc,, -sqlalchemy/pool/base.py,sha256=D0sKTRla6wpIFbELyGY2JEHUHR324rveIl93qjjmYr8,53751 -sqlalchemy/pool/events.py,sha256=ysyFh0mNDpL4N4rQ-o_BC6tpo_zt0_au_QLBgJqaKY8,13517 -sqlalchemy/pool/impl.py,sha256=8VcM4JSUnu4FcSrC5TUzTWT0FYFxfNouKyuXCKnD6KM,18264 -sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -sqlalchemy/schema.py,sha256=UFhZjGmYoqN3zkId7M4CbVCd8KaeZUfKUjdlk0sHQ_E,3264 -sqlalchemy/sql/__init__.py,sha256=T16ZB3Za0Tq1LQGXeJuuxDkyu2t-XHR2t-8QH1mE1Uw,5965 -sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/base.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/crud.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/dml.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/elements.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/events.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/expression.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/functions.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/naming.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/operators.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/roles.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/schema.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/util.cpython-311.pyc,, -sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc,, -sqlalchemy/sql/_dml_constructors.py,sha256=1xMH5Kd6SLhlFwfIs_lOXGC8GTrqW8mQM7Kc3cKyLuw,4007 -sqlalchemy/sql/_elements_constructors.py,sha256=DN5B84YTp9K5cfp1qjEpg_2d5WpHujlNBJ-pG7bsKoI,64386 -sqlalchemy/sql/_orm_types.py,sha256=_bzlAh3-vTIZoLvAM2ry1SF7rsYRM3-jupfhGWZZn5Y,645 -sqlalchemy/sql/_py_util.py,sha256=VzThcXk7fKqT9_mZmXrkxePdwyyl_wIciCftzl2Z_-g,2248 -sqlalchemy/sql/_selectable_constructors.py,sha256=mRgtlGyctlb1LMBqFxgn0eGzIXMbyZtQafjUuJWhYjs,19415 -sqlalchemy/sql/_typing.py,sha256=i4COgky9Gv0ArXdOpp9xfxpvKqP3lj0I_vJqinvWEck,12940 -sqlalchemy/sql/annotation.py,sha256=PslN1KQV9hN8Ji4k8I3-W-cDuRMCCLwMmJcg-n86Yy4,18830 -sqlalchemy/sql/base.py,sha256=Twa1DYB1fBGGzU0kIVS0h30-0j67gfduuw9GU6RxGao,75935 -sqlalchemy/sql/cache_key.py,sha256=vUWB-pqAtgt8SBRMHEkzo8BQ_1yXjMeGt-aaDB1ieek,34605 -sqlalchemy/sql/coercions.py,sha256=O6PA7Gzhr9XQedJs3zIngCivN1vcrNyEhFueN5CqriI,41882 -sqlalchemy/sql/compiler.py,sha256=PslsNi8ND2FXVPcFIS3UBMpVthfM47MAu4c_rpBpMHE,278910 -sqlalchemy/sql/crud.py,sha256=I5nPPnujtNKHC5C2v1vW4A0mbyomwChT21IYOX3z5fw,58190 -sqlalchemy/sql/ddl.py,sha256=NbW8F3UT4BTnda5w5TNPGxXPtv0wHSNB51hhr4gBSJM,46980 -sqlalchemy/sql/default_comparator.py,sha256=lXmd8yAUzfyeP5w4vebrQG99oC0bTrmdGc0crBq1GKw,17259 -sqlalchemy/sql/dml.py,sha256=lt5FC6BbJNotE65U-fmvEovBxkADfKBnVcnkVYYQxUM,67431 -sqlalchemy/sql/elements.py,sha256=tIgio7vC-0gftEddMtUu35cXIWwPqqomat0ufAIFrMM,177377 -sqlalchemy/sql/events.py,sha256=pG3jqJbPX18N9Amp47aXiQYMYD_HL_lOXHk-0m8m7Hw,18745 -sqlalchemy/sql/expression.py,sha256=T-AgCPp30tgKQYLKeSyqQg_VoJFE69m2yDTz6fn-u1E,7748 -sqlalchemy/sql/functions.py,sha256=vxYsWwzQpYhfQ_EwfdA-lGlbh2pkQ30AXGjvHEvVBWo,65741 -sqlalchemy/sql/lambdas.py,sha256=i3F6TZEAHSPqTV704LAybfNOMyUJ52x2YE2eCHTlYi4,50738 -sqlalchemy/sql/naming.py,sha256=ERVjqo6fBHBw2BwNgpbb5cvsCkq1jjdztczP9BKzVt8,7070 -sqlalchemy/sql/operators.py,sha256=6rpSbuFon7iIUCT4SDowYctDyOmFpe2-FdLu2HIX3x8,78508 -sqlalchemy/sql/roles.py,sha256=8nO4y1hbP1cA8IzeOn6uPgNZNVILb3E-IMeJWOIScu8,7985 -sqlalchemy/sql/schema.py,sha256=iIurzYqmZNRi_wBN-tXFKIM-jt07DIvVuzV_IVsfsTo,234377 -sqlalchemy/sql/selectable.py,sha256=fWcddtd9UM3QMcS-3Pg6E98mK9uAZGkhmyOLWlNEigI,239761 -sqlalchemy/sql/sqltypes.py,sha256=AJvAe9Nt3Bweic9eC__NVnkVAbIgb_exoajEfTij1R4,130912 -sqlalchemy/sql/traversals.py,sha256=p2iXAQc0FvV-l1Q3NNMxIhRYTm8U3Ul630jG3Ys6qCI,34611 -sqlalchemy/sql/type_api.py,sha256=zRtzrf5sLjDWnSUvl_vAnG6X8fhY8vuln4jG_Jx4zKY,86164 -sqlalchemy/sql/util.py,sha256=ftTiyNGeJK0MIRMqWMV7Xf8iZuiRGocoJRp3MIO3F3Y,49563 -sqlalchemy/sql/visitors.py,sha256=oudlabsf9qleuC78GFe_iflRSAD8H-HjaM7T8Frc538,37482 -sqlalchemy/testing/__init__.py,sha256=8iT66v5k4J9RmquaH4GLI2DjEA7c_JZSTVig-uuBNw8,3221 -sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/config.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/engines.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/entities.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/provision.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/schema.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/util.cpython-311.pyc,, -sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc,, -sqlalchemy/testing/assertions.py,sha256=bBn2Ep89FF-WBmzh0VkvnJ9gNMKuqk8OXq7ALpUwar4,32428 -sqlalchemy/testing/assertsql.py,sha256=gj4YRBR9cjOtS1WgR3nsyIze1tmqctsNs1uCV8N2Q4w,17333 -sqlalchemy/testing/asyncio.py,sha256=GvWrQFrL3xz7rub61oGOS2PXVvw7D9Id3gtkXQjZJLY,3858 -sqlalchemy/testing/config.py,sha256=jfFVUiAOm8im6SlqyAdZVSaA51kmADgfBDqrHnngH7c,12517 -sqlalchemy/testing/engines.py,sha256=U3FkWECbghiK2_Yv5uKMjco377xoFsi75WZgRZroGWA,13814 -sqlalchemy/testing/entities.py,sha256=Um-DFSz81p06DhTK899ZRUOZRw3FtUDeNMVHcIg3eLc,3471 -sqlalchemy/testing/exclusions.py,sha256=8kjsaFfjCvPlLsQLD_LIDwuqvVlIVbD5qTWBlKdtNkM,12895 -sqlalchemy/testing/fixtures/__init__.py,sha256=B1IFCzEVdCqhEvFrLmgxZ_Fr08jDus5FddSA-lnnAAU,1226 -sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc,, -sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc,, -sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc,, -sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc,, -sqlalchemy/testing/fixtures/base.py,sha256=S0ODuph0jA2Za4GN3NNhYVIqN9jAa3Q9Vd1N4O4rcTc,12622 -sqlalchemy/testing/fixtures/mypy.py,sha256=2H8QxvGvwsb_Z3alRtvCvfXeqGjOb8aemfoYxQiuGMc,12285 -sqlalchemy/testing/fixtures/orm.py,sha256=6JvQpIfmgmSTH3Hie4nhmUFfvH0pseujIFA9Lup2Dzw,6322 -sqlalchemy/testing/fixtures/sql.py,sha256=Joqh4q1vE3wCaE3eDZUnSobeLNUE-pabIy58ZMURFto,16196 -sqlalchemy/testing/pickleable.py,sha256=uYLl557iNep6jSOVl0vK1GwaLHUKidALoPJc-QIrC08,2988 -sqlalchemy/testing/plugin/__init__.py,sha256=bbtVIt7LzVnUCcVxHWRH2owOQD067bQwwhyMf_whqHs,253 -sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc,, -sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc,, -sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc,, -sqlalchemy/testing/plugin/bootstrap.py,sha256=USn6pE-JcE5pSmnEd2wad3goKLx2hdJS3AUUFpXHm-I,1736 -sqlalchemy/testing/plugin/plugin_base.py,sha256=CgrNj2wj9KNALu9YfnGSaHX2fXfTtiim_cfx0CPVoy8,22357 -sqlalchemy/testing/plugin/pytestplugin.py,sha256=xFbgBkv92U7_nYSyq87MG6OZSg_NR2HOo7CG7IC1cpY,28416 -sqlalchemy/testing/profiling.py,sha256=o8_V3TpF_WytudMQQLm1UxlfNDrLCWxUvkH-Kd0unKU,10472 -sqlalchemy/testing/provision.py,sha256=ciWoXf3P9ql4hh1yBp0RNEtPr5vyvPbd8RD_DYxNG9U,15115 -sqlalchemy/testing/requirements.py,sha256=L_DKVqVxVMbB3JveC_6UhD5oVry2KjBHPxfQd35hrWQ,53600 -sqlalchemy/testing/schema.py,sha256=z2Z5rm3iJ1-vgifUxwzxEjt1qu7QOyr3TeDnQdCHlWE,6737 -sqlalchemy/testing/suite/__init__.py,sha256=YvTEqUNHaBlgLgWDAWn79mQrUR4VBGUHtprywJlmDT8,741 -sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc,, -sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc,, -sqlalchemy/testing/suite/test_cte.py,sha256=C_viXJKClFAm91rtPb42tiAA7gYJwKkqGYVJYap0cLM,6662 -sqlalchemy/testing/suite/test_ddl.py,sha256=k6D6RreLkDSSpRUM2hQz-_CA48qV2PYx_2LNyUSoZzE,12420 -sqlalchemy/testing/suite/test_deprecations.py,sha256=SKRFZDteBO1rw9-BQjDic5nh7fdyw2ypVOewR2pj7-Q,5490 -sqlalchemy/testing/suite/test_dialect.py,sha256=ftOWRXWOotB2_jMJJqwoH9f3X2ucc1HwwOiXp573GwM,23663 -sqlalchemy/testing/suite/test_insert.py,sha256=v3zrUZaGlke3cI4vabHg7xaI4gNqcHhtMPgYuf0mOxc,19454 -sqlalchemy/testing/suite/test_reflection.py,sha256=C6P9ccG5Eog5uiIHO4s6M7ThnBbEUZKh83CmOMn-KSo,109594 -sqlalchemy/testing/suite/test_results.py,sha256=1SlvhdioM1_ZrkQX2IJbJgXNHuleizwAge6-XvHtA0s,16405 -sqlalchemy/testing/suite/test_rowcount.py,sha256=DCEGxorDcrT5JCLd3_SNQeZmxT6sKIcuKxX1r6vK4Mg,8158 -sqlalchemy/testing/suite/test_select.py,sha256=NwHUSVc4UptVYMGjp3QVLr0OpGxpz2qJG4cNWZW8vTo,60462 -sqlalchemy/testing/suite/test_sequence.py,sha256=sIqkfgVqPIgl4lm75EPdag9gK-rTHfUm3pWX-JijPy4,10240 -sqlalchemy/testing/suite/test_types.py,sha256=i1fCIXERdtGABdp_T3l1vaPH9AhQ80DJvbjOPbeng1c,67748 -sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=juF_KTK1nGrSlsL8z0Ky0rFSNkPGheLB3e0Kq3yRqss,6330 -sqlalchemy/testing/suite/test_update_delete.py,sha256=TnJI5U_ZEuu3bni4sH-S6CENxvSZwDgZL-FKSV45bAo,4133 -sqlalchemy/testing/util.py,sha256=jX9jlUHSH-7_2OCypZUvitP8JkJbNdr5_ZxU6Aa8DPY,14599 -sqlalchemy/testing/warnings.py,sha256=3EhbTlPe4gJnoydj-OKueNOOtGwIRF2kV4XvlFwFYOA,1598 -sqlalchemy/types.py,sha256=unCm_O8qKxU3LjLbqeqSNQSsK5k5R5POsyEx2gH6CF4,3244 -sqlalchemy/util/__init__.py,sha256=3-O9j9qPk-gTx6hlyLsISc_JOW5MhjV0J_L5nV19qI8,8436 -sqlalchemy/util/__pycache__/__init__.cpython-311.pyc,, -sqlalchemy/util/__pycache__/_collections.cpython-311.pyc,, -sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-311.pyc,, -sqlalchemy/util/__pycache__/_has_cy.cpython-311.pyc,, -sqlalchemy/util/__pycache__/_py_collections.cpython-311.pyc,, -sqlalchemy/util/__pycache__/compat.cpython-311.pyc,, -sqlalchemy/util/__pycache__/concurrency.cpython-311.pyc,, -sqlalchemy/util/__pycache__/deprecations.cpython-311.pyc,, -sqlalchemy/util/__pycache__/langhelpers.cpython-311.pyc,, -sqlalchemy/util/__pycache__/preloaded.cpython-311.pyc,, -sqlalchemy/util/__pycache__/queue.cpython-311.pyc,, -sqlalchemy/util/__pycache__/tool_support.cpython-311.pyc,, -sqlalchemy/util/__pycache__/topological.cpython-311.pyc,, -sqlalchemy/util/__pycache__/typing.cpython-311.pyc,, -sqlalchemy/util/_collections.py,sha256=O3iqq0R9TbcXNyFk8nG4QLwkUzdWkCFmqBYcDrajvl8,20778 -sqlalchemy/util/_concurrency_py3k.py,sha256=HQ5tLleQd5cR4BOoXKFWVTK7p4fCDW105QxRLW52_ko,8841 -sqlalchemy/util/_has_cy.py,sha256=IHGc5hUFbXQuv1a1z2P8yVwz0yGbCYXyQM2qsdcBTyg,1287 -sqlalchemy/util/_py_collections.py,sha256=2PUqiKIsF8d-gNDAAqYI8WE6XPyRf1flRLkVsJeXuOo,17255 -sqlalchemy/util/compat.py,sha256=ojCAtKHlkqNdYB33PXbAP0zTH1ZXYdTZkJl32cqGnMQ,9014 -sqlalchemy/util/concurrency.py,sha256=AOLQUBm9Hm4jDArP8vBYL39FzckyH9S4NsKRvWaYzEE,2500 -sqlalchemy/util/deprecations.py,sha256=AnHpDWHi7g2gv_QUTGStQTnr0J94lIF-3aFLOsv9yzg,12372 -sqlalchemy/util/langhelpers.py,sha256=1meF9IffDMmz50uxNdUO15FUL0TARzwFcPjwbpOQRX8,67115 -sqlalchemy/util/preloaded.py,sha256=78Sl7VjzTOPajbovvARxNeuZb-iYRpEvL5k8m5Bz4vQ,6054 -sqlalchemy/util/queue.py,sha256=4SbSbVamUECjCDpMPR035N1ooVHt9W5GjbqkxfZmH5k,10507 -sqlalchemy/util/tool_support.py,sha256=DuurikYgDUIIxk3gubUKl6rs-etXt3eeHaZ4ZkIyJXQ,6336 -sqlalchemy/util/topological.py,sha256=_NdtAghZjhZ4e2fwWHmn25erP5cvtGgOUMplsCa_VCE,3578 -sqlalchemy/util/typing.py,sha256=DG9V94Mh63cqObr_G5X19wH4H3hhWMqZXufVEZ2wtiw,17221 diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/REQUESTED b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/WHEEL b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/WHEEL deleted file mode 100644 index d60b004..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.42.0) -Root-Is-Purelib: false -Tag: cp311-cp311-win_amd64 - diff --git a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/top_level.txt b/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/top_level.txt deleted file mode 100644 index 39fb2be..0000000 --- a/.venv/Lib/site-packages/SQLAlchemy-2.0.27.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -sqlalchemy diff --git a/.venv/Lib/site-packages/_cffi_backend.cp311-win_amd64.pyd b/.venv/Lib/site-packages/_cffi_backend.cp311-win_amd64.pyd deleted file mode 100644 index 9bb0309f5c6679c8bfcfdb344d76aa6eaadc0585..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178176 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%0DM@piAzIw{3haY~75#-xHr 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 E2N9XoMgRZ+ diff --git a/.venv/Lib/site-packages/_distutils_hack/__init__.py b/.venv/Lib/site-packages/_distutils_hack/__init__.py deleted file mode 100644 index f987a53..0000000 --- a/.venv/Lib/site-packages/_distutils_hack/__init__.py +++ /dev/null @@ -1,222 +0,0 @@ -# don't import any costly modules -import sys -import os - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - import warnings - - warnings.warn( - "Distutils was imported before Setuptools, but importing Setuptools " - "also replaces the `distutils` module in `sys.modules`. This may lead " - "to undesirable behaviors or errors. To avoid these issues, avoid " - "using distutils directly, ensure that setuptools is installed in the " - "traditional way (e.g. not an editable install), and/or make sure " - "that setuptools is always imported before distutils." - ) - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - import warnings - - warnings.warn("Setuptools is replacing distutils.") - mods = [ - name - for name in sys.modules - if name == "distutils" or name.startswith("distutils.") - ] - for name in mods: - del sys.modules[name] - - -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') - return which == 'local' - - -def ensure_local_distutils(): - import importlib - - clear_distutils() - - # With the DistutilsMetaFinder in place, - # perform an import to cause distutils to be - # loaded from setuptools._distutils. Ref #2906. - with shim(): - importlib.import_module('distutils') - - # check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - assert 'setuptools._distutils.log' not in sys.modules - - -def do_override(): - """ - Ensure that the local copy of distutils is preferred over stdlib. - - See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 - for more motivation. - """ - if enabled(): - warn_distutils_present() - ensure_local_distutils() - - -class _TrivialRe: - def __init__(self, *patterns): - self._patterns = patterns - - def match(self, string): - return all(pat in string for pat in self._patterns) - - -class DistutilsMetaFinder: - def find_spec(self, fullname, path, target=None): - # optimization: only consider top level modules and those - # found in the CPython test suite. - if path is not None and not fullname.startswith('test.'): - return - - method_name = 'spec_for_{fullname}'.format(**locals()) - method = getattr(self, method_name, lambda: None) - return method() - - def spec_for_distutils(self): - if self.is_cpython(): - return - - import importlib - import importlib.abc - import importlib.util - - try: - mod = importlib.import_module('setuptools._distutils') - except Exception: - # There are a couple of cases where setuptools._distutils - # may not be present: - # - An older Setuptools without a local distutils is - # taking precedence. Ref #2957. - # - Path manipulation during sitecustomize removes - # setuptools from the path but only after the hook - # has been loaded. Ref #2980. - # In either case, fall back to stdlib behavior. - return - - class DistutilsLoader(importlib.abc.Loader): - def create_module(self, spec): - mod.__name__ = 'distutils' - return mod - - def exec_module(self, module): - pass - - return importlib.util.spec_from_loader( - 'distutils', DistutilsLoader(), origin=mod.__file__ - ) - - @staticmethod - def is_cpython(): - """ - Suppress supplying distutils for CPython (build and tests). - Ref #2965 and #3007. - """ - return os.path.isfile('pybuilddir.txt') - - def spec_for_pip(self): - """ - Ensure stdlib distutils when running under pip. - See pypa/pip#8761 for rationale. - """ - if self.pip_imported_during_build(): - return - clear_distutils() - self.spec_for_distutils = lambda: None - - @classmethod - def pip_imported_during_build(cls): - """ - Detect if pip is being imported in a build script. Ref #2355. - """ - import traceback - - return any( - cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) - ) - - @staticmethod - def frame_file_is_setup(frame): - """ - Return True if the indicated frame suggests a setup.py file. - """ - # some frames may not have __file__ (#2940) - return frame.f_globals.get('__file__', '').endswith('setup.py') - - def spec_for_sensitive_tests(self): - """ - Ensure stdlib distutils when running select tests under CPython. - - python/cpython#91169 - """ - clear_distutils() - self.spec_for_distutils = lambda: None - - sensitive_tests = ( - [ - 'test.test_distutils', - 'test.test_peg_generator', - 'test.test_importlib', - ] - if sys.version_info < (3, 10) - else [ - 'test.test_distutils', - ] - ) - - -for name in DistutilsMetaFinder.sensitive_tests: - setattr( - DistutilsMetaFinder, - f'spec_for_{name}', - DistutilsMetaFinder.spec_for_sensitive_tests, - ) - - -DISTUTILS_FINDER = DistutilsMetaFinder() - - -def add_shim(): - DISTUTILS_FINDER in sys.meta_path or insert_shim() - - -class shim: - def __enter__(self): - insert_shim() - - def __exit__(self, exc, value, tb): - remove_shim() - - -def insert_shim(): - sys.meta_path.insert(0, DISTUTILS_FINDER) - - -def remove_shim(): - try: - sys.meta_path.remove(DISTUTILS_FINDER) - except ValueError: - pass diff --git a/.venv/Lib/site-packages/_distutils_hack/override.py b/.venv/Lib/site-packages/_distutils_hack/override.py deleted file mode 100644 index 2cc433a..0000000 --- a/.venv/Lib/site-packages/_distutils_hack/override.py +++ /dev/null @@ -1 +0,0 @@ -__import__('_distutils_hack').do_override() diff --git a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/INSTALLER b/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/METADATA b/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/METADATA deleted file mode 100644 index 3ac05cf..0000000 --- a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/METADATA +++ /dev/null @@ -1,295 +0,0 @@ -Metadata-Version: 2.3 -Name: annotated-types -Version: 0.7.0 -Summary: Reusable constraint types to use with typing.Annotated -Project-URL: Homepage, https://github.com/annotated-types/annotated-types -Project-URL: Source, https://github.com/annotated-types/annotated-types -Project-URL: Changelog, https://github.com/annotated-types/annotated-types/releases -Author-email: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Samuel Colvin , Zac Hatfield-Dodds -License-File: LICENSE -Classifier: Development Status :: 4 - Beta -Classifier: Environment :: Console -Classifier: Environment :: MacOS X -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Information Technology -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: Unix -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Typing :: Typed -Requires-Python: >=3.8 -Requires-Dist: typing-extensions>=4.0.0; python_version < '3.9' -Description-Content-Type: text/markdown - -# annotated-types - -[![CI](https://github.com/annotated-types/annotated-types/workflows/CI/badge.svg?event=push)](https://github.com/annotated-types/annotated-types/actions?query=event%3Apush+branch%3Amain+workflow%3ACI) -[![pypi](https://img.shields.io/pypi/v/annotated-types.svg)](https://pypi.python.org/pypi/annotated-types) -[![versions](https://img.shields.io/pypi/pyversions/annotated-types.svg)](https://github.com/annotated-types/annotated-types) -[![license](https://img.shields.io/github/license/annotated-types/annotated-types.svg)](https://github.com/annotated-types/annotated-types/blob/main/LICENSE) - -[PEP-593](https://peps.python.org/pep-0593/) added `typing.Annotated` as a way of -adding context-specific metadata to existing types, and specifies that -`Annotated[T, x]` _should_ be treated as `T` by any tool or library without special -logic for `x`. - -This package provides metadata objects which can be used to represent common -constraints such as upper and lower bounds on scalar values and collection sizes, -a `Predicate` marker for runtime checks, and -descriptions of how we intend these metadata to be interpreted. In some cases, -we also note alternative representations which do not require this package. - -## Install - -```bash -pip install annotated-types -``` - -## Examples - -```python -from typing import Annotated -from annotated_types import Gt, Len, Predicate - -class MyClass: - age: Annotated[int, Gt(18)] # Valid: 19, 20, ... - # Invalid: 17, 18, "19", 19.0, ... - factors: list[Annotated[int, Predicate(is_prime)]] # Valid: 2, 3, 5, 7, 11, ... - # Invalid: 4, 8, -2, 5.0, "prime", ... - - my_list: Annotated[list[int], Len(0, 10)] # Valid: [], [10, 20, 30, 40, 50] - # Invalid: (1, 2), ["abc"], [0] * 20 -``` - -## Documentation - -_While `annotated-types` avoids runtime checks for performance, users should not -construct invalid combinations such as `MultipleOf("non-numeric")` or `Annotated[int, Len(3)]`. -Downstream implementors may choose to raise an error, emit a warning, silently ignore -a metadata item, etc., if the metadata objects described below are used with an -incompatible type - or for any other reason!_ - -### Gt, Ge, Lt, Le - -Express inclusive and/or exclusive bounds on orderable values - which may be numbers, -dates, times, strings, sets, etc. Note that the boundary value need not be of the -same type that was annotated, so long as they can be compared: `Annotated[int, Gt(1.5)]` -is fine, for example, and implies that the value is an integer x such that `x > 1.5`. - -We suggest that implementors may also interpret `functools.partial(operator.le, 1.5)` -as being equivalent to `Gt(1.5)`, for users who wish to avoid a runtime dependency on -the `annotated-types` package. - -To be explicit, these types have the following meanings: - -* `Gt(x)` - value must be "Greater Than" `x` - equivalent to exclusive minimum -* `Ge(x)` - value must be "Greater than or Equal" to `x` - equivalent to inclusive minimum -* `Lt(x)` - value must be "Less Than" `x` - equivalent to exclusive maximum -* `Le(x)` - value must be "Less than or Equal" to `x` - equivalent to inclusive maximum - -### Interval - -`Interval(gt, ge, lt, le)` allows you to specify an upper and lower bound with a single -metadata object. `None` attributes should be ignored, and non-`None` attributes -treated as per the single bounds above. - -### MultipleOf - -`MultipleOf(multiple_of=x)` might be interpreted in two ways: - -1. Python semantics, implying `value % multiple_of == 0`, or -2. [JSONschema semantics](https://json-schema.org/draft/2020-12/json-schema-validation.html#rfc.section.6.2.1), - where `int(value / multiple_of) == value / multiple_of`. - -We encourage users to be aware of these two common interpretations and their -distinct behaviours, especially since very large or non-integer numbers make -it easy to cause silent data corruption due to floating-point imprecision. - -We encourage libraries to carefully document which interpretation they implement. - -### MinLen, MaxLen, Len - -`Len()` implies that `min_length <= len(value) <= max_length` - lower and upper bounds are inclusive. - -As well as `Len()` which can optionally include upper and lower bounds, we also -provide `MinLen(x)` and `MaxLen(y)` which are equivalent to `Len(min_length=x)` -and `Len(max_length=y)` respectively. - -`Len`, `MinLen`, and `MaxLen` may be used with any type which supports `len(value)`. - -Examples of usage: - -* `Annotated[list, MaxLen(10)]` (or `Annotated[list, Len(max_length=10))`) - list must have a length of 10 or less -* `Annotated[str, MaxLen(10)]` - string must have a length of 10 or less -* `Annotated[list, MinLen(3))` (or `Annotated[list, Len(min_length=3))`) - list must have a length of 3 or more -* `Annotated[list, Len(4, 6)]` - list must have a length of 4, 5, or 6 -* `Annotated[list, Len(8, 8)]` - list must have a length of exactly 8 - -#### Changed in v0.4.0 - -* `min_inclusive` has been renamed to `min_length`, no change in meaning -* `max_exclusive` has been renamed to `max_length`, upper bound is now **inclusive** instead of **exclusive** -* The recommendation that slices are interpreted as `Len` has been removed due to ambiguity and different semantic - meaning of the upper bound in slices vs. `Len` - -See [issue #23](https://github.com/annotated-types/annotated-types/issues/23) for discussion. - -### Timezone - -`Timezone` can be used with a `datetime` or a `time` to express which timezones -are allowed. `Annotated[datetime, Timezone(None)]` must be a naive datetime. -`Timezone[...]` ([literal ellipsis](https://docs.python.org/3/library/constants.html#Ellipsis)) -expresses that any timezone-aware datetime is allowed. You may also pass a specific -timezone string or [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) -object such as `Timezone(timezone.utc)` or `Timezone("Africa/Abidjan")` to express that you only -allow a specific timezone, though we note that this is often a symptom of fragile design. - -#### Changed in v0.x.x - -* `Timezone` accepts [`tzinfo`](https://docs.python.org/3/library/datetime.html#tzinfo-objects) objects instead of - `timezone`, extending compatibility to [`zoneinfo`](https://docs.python.org/3/library/zoneinfo.html) and third party libraries. - -### Unit - -`Unit(unit: str)` expresses that the annotated numeric value is the magnitude of -a quantity with the specified unit. For example, `Annotated[float, Unit("m/s")]` -would be a float representing a velocity in meters per second. - -Please note that `annotated_types` itself makes no attempt to parse or validate -the unit string in any way. That is left entirely to downstream libraries, -such as [`pint`](https://pint.readthedocs.io) or -[`astropy.units`](https://docs.astropy.org/en/stable/units/). - -An example of how a library might use this metadata: - -```python -from annotated_types import Unit -from typing import Annotated, TypeVar, Callable, Any, get_origin, get_args - -# given a type annotated with a unit: -Meters = Annotated[float, Unit("m")] - - -# you can cast the annotation to a specific unit type with any -# callable that accepts a string and returns the desired type -T = TypeVar("T") -def cast_unit(tp: Any, unit_cls: Callable[[str], T]) -> T | None: - if get_origin(tp) is Annotated: - for arg in get_args(tp): - if isinstance(arg, Unit): - return unit_cls(arg.unit) - return None - - -# using `pint` -import pint -pint_unit = cast_unit(Meters, pint.Unit) - - -# using `astropy.units` -import astropy.units as u -astropy_unit = cast_unit(Meters, u.Unit) -``` - -### Predicate - -`Predicate(func: Callable)` expresses that `func(value)` is truthy for valid values. -Users should prefer the statically inspectable metadata above, but if you need -the full power and flexibility of arbitrary runtime predicates... here it is. - -For some common constraints, we provide generic types: - -* `IsLower = Annotated[T, Predicate(str.islower)]` -* `IsUpper = Annotated[T, Predicate(str.isupper)]` -* `IsDigit = Annotated[T, Predicate(str.isdigit)]` -* `IsFinite = Annotated[T, Predicate(math.isfinite)]` -* `IsNotFinite = Annotated[T, Predicate(Not(math.isfinite))]` -* `IsNan = Annotated[T, Predicate(math.isnan)]` -* `IsNotNan = Annotated[T, Predicate(Not(math.isnan))]` -* `IsInfinite = Annotated[T, Predicate(math.isinf)]` -* `IsNotInfinite = Annotated[T, Predicate(Not(math.isinf))]` - -so that you can write e.g. `x: IsFinite[float] = 2.0` instead of the longer -(but exactly equivalent) `x: Annotated[float, Predicate(math.isfinite)] = 2.0`. - -Some libraries might have special logic to handle known or understandable predicates, -for example by checking for `str.isdigit` and using its presence to both call custom -logic to enforce digit-only strings, and customise some generated external schema. -Users are therefore encouraged to avoid indirection like `lambda s: s.lower()`, in -favor of introspectable methods such as `str.lower` or `re.compile("pattern").search`. - -To enable basic negation of commonly used predicates like `math.isnan` without introducing introspection that makes it impossible for implementers to introspect the predicate we provide a `Not` wrapper that simply negates the predicate in an introspectable manner. Several of the predicates listed above are created in this manner. - -We do not specify what behaviour should be expected for predicates that raise -an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently -skip invalid constraints, or statically raise an error; or it might try calling it -and then propagate or discard the resulting -`TypeError: descriptor 'isdigit' for 'str' objects doesn't apply to a 'int' object` -exception. We encourage libraries to document the behaviour they choose. - -### Doc - -`doc()` can be used to add documentation information in `Annotated`, for function and method parameters, variables, class attributes, return types, and any place where `Annotated` can be used. - -It expects a value that can be statically analyzed, as the main use case is for static analysis, editors, documentation generators, and similar tools. - -It returns a `DocInfo` class with a single attribute `documentation` containing the value passed to `doc()`. - -This is the early adopter's alternative form of the [`typing-doc` proposal](https://github.com/tiangolo/fastapi/blob/typing-doc/typing_doc.md). - -### Integrating downstream types with `GroupedMetadata` - -Implementers may choose to provide a convenience wrapper that groups multiple pieces of metadata. -This can help reduce verbosity and cognitive overhead for users. -For example, an implementer like Pydantic might provide a `Field` or `Meta` type that accepts keyword arguments and transforms these into low-level metadata: - -```python -from dataclasses import dataclass -from typing import Iterator -from annotated_types import GroupedMetadata, Ge - -@dataclass -class Field(GroupedMetadata): - ge: int | None = None - description: str | None = None - - def __iter__(self) -> Iterator[object]: - # Iterating over a GroupedMetadata object should yield annotated-types - # constraint metadata objects which describe it as fully as possible, - # and may include other unknown objects too. - if self.ge is not None: - yield Ge(self.ge) - if self.description is not None: - yield Description(self.description) -``` - -Libraries consuming annotated-types constraints should check for `GroupedMetadata` and unpack it by iterating over the object and treating the results as if they had been "unpacked" in the `Annotated` type. The same logic should be applied to the [PEP 646 `Unpack` type](https://peps.python.org/pep-0646/), so that `Annotated[T, Field(...)]`, `Annotated[T, Unpack[Field(...)]]` and `Annotated[T, *Field(...)]` are all treated consistently. - -Libraries consuming annotated-types should also ignore any metadata they do not recongize that came from unpacking a `GroupedMetadata`, just like they ignore unrecognized metadata in `Annotated` itself. - -Our own `annotated_types.Interval` class is a `GroupedMetadata` which unpacks itself into `Gt`, `Lt`, etc., so this is not an abstract concern. Similarly, `annotated_types.Len` is a `GroupedMetadata` which unpacks itself into `MinLen` (optionally) and `MaxLen`. - -### Consuming metadata - -We intend to not be prescriptive as to _how_ the metadata and constraints are used, but as an example of how one might parse constraints from types annotations see our [implementation in `test_main.py`](https://github.com/annotated-types/annotated-types/blob/f59cf6d1b5255a0fe359b93896759a180bec30ae/tests/test_main.py#L94-L103). - -It is up to the implementer to determine how this metadata is used. -You could use the metadata for runtime type checking, for generating schemas or to generate example data, amongst other use cases. - -## Design & History - -This package was designed at the PyCon 2022 sprints by the maintainers of Pydantic -and Hypothesis, with the goal of making it as easy as possible for end-users to -provide more informative annotations for use by runtime libraries. - -It is deliberately minimal, and following PEP-593 allows considerable downstream -discretion in what (if anything!) they choose to support. Nonetheless, we expect -that staying simple and covering _only_ the most common use-cases will give users -and maintainers the best experience we can. If you'd like more constraints for your -types - follow our lead, by defining them and documenting them downstream! diff --git a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/RECORD b/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/RECORD deleted file mode 100644 index 568bd52..0000000 --- a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/RECORD +++ /dev/null @@ -1,10 +0,0 @@ -annotated_types-0.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -annotated_types-0.7.0.dist-info/METADATA,sha256=7ltqxksJJ0wCYFGBNIQCWTlWQGeAH0hRFdnK3CB895E,15046 -annotated_types-0.7.0.dist-info/RECORD,, -annotated_types-0.7.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87 -annotated_types-0.7.0.dist-info/licenses/LICENSE,sha256=_hBJiEsaDZNCkB6I4H8ykl0ksxIdmXK2poBfuYJLCV0,1083 -annotated_types/__init__.py,sha256=RynLsRKUEGI0KimXydlD1fZEfEzWwDo0Uon3zOKhG1Q,13819 -annotated_types/__pycache__/__init__.cpython-311.pyc,, -annotated_types/__pycache__/test_cases.cpython-311.pyc,, -annotated_types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -annotated_types/test_cases.py,sha256=zHFX6EpcMbGJ8FzBYDbO56bPwx_DYIVSKbZM-4B3_lg,6421 diff --git a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/WHEEL b/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/WHEEL deleted file mode 100644 index 516596c..0000000 --- a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.24.2 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE b/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE deleted file mode 100644 index d99323a..0000000 --- a/.venv/Lib/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2022 the contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/Lib/site-packages/annotated_types/__init__.py b/.venv/Lib/site-packages/annotated_types/__init__.py deleted file mode 100644 index 74e0dee..0000000 --- a/.venv/Lib/site-packages/annotated_types/__init__.py +++ /dev/null @@ -1,432 +0,0 @@ -import math -import sys -import types -from dataclasses import dataclass -from datetime import tzinfo -from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, SupportsFloat, SupportsIndex, TypeVar, Union - -if sys.version_info < (3, 8): - from typing_extensions import Protocol, runtime_checkable -else: - from typing import Protocol, runtime_checkable - -if sys.version_info < (3, 9): - from typing_extensions import Annotated, Literal -else: - from typing import Annotated, Literal - -if sys.version_info < (3, 10): - EllipsisType = type(Ellipsis) - KW_ONLY = {} - SLOTS = {} -else: - from types import EllipsisType - - KW_ONLY = {"kw_only": True} - SLOTS = {"slots": True} - - -__all__ = ( - 'BaseMetadata', - 'GroupedMetadata', - 'Gt', - 'Ge', - 'Lt', - 'Le', - 'Interval', - 'MultipleOf', - 'MinLen', - 'MaxLen', - 'Len', - 'Timezone', - 'Predicate', - 'LowerCase', - 'UpperCase', - 'IsDigits', - 'IsFinite', - 'IsNotFinite', - 'IsNan', - 'IsNotNan', - 'IsInfinite', - 'IsNotInfinite', - 'doc', - 'DocInfo', - '__version__', -) - -__version__ = '0.7.0' - - -T = TypeVar('T') - - -# arguments that start with __ are considered -# positional only -# see https://peps.python.org/pep-0484/#positional-only-arguments - - -class SupportsGt(Protocol): - def __gt__(self: T, __other: T) -> bool: - ... - - -class SupportsGe(Protocol): - def __ge__(self: T, __other: T) -> bool: - ... - - -class SupportsLt(Protocol): - def __lt__(self: T, __other: T) -> bool: - ... - - -class SupportsLe(Protocol): - def __le__(self: T, __other: T) -> bool: - ... - - -class SupportsMod(Protocol): - def __mod__(self: T, __other: T) -> T: - ... - - -class SupportsDiv(Protocol): - def __div__(self: T, __other: T) -> T: - ... - - -class BaseMetadata: - """Base class for all metadata. - - This exists mainly so that implementers - can do `isinstance(..., BaseMetadata)` while traversing field annotations. - """ - - __slots__ = () - - -@dataclass(frozen=True, **SLOTS) -class Gt(BaseMetadata): - """Gt(gt=x) implies that the value must be greater than x. - - It can be used with any type that supports the ``>`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - gt: SupportsGt - - -@dataclass(frozen=True, **SLOTS) -class Ge(BaseMetadata): - """Ge(ge=x) implies that the value must be greater than or equal to x. - - It can be used with any type that supports the ``>=`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - ge: SupportsGe - - -@dataclass(frozen=True, **SLOTS) -class Lt(BaseMetadata): - """Lt(lt=x) implies that the value must be less than x. - - It can be used with any type that supports the ``<`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - lt: SupportsLt - - -@dataclass(frozen=True, **SLOTS) -class Le(BaseMetadata): - """Le(le=x) implies that the value must be less than or equal to x. - - It can be used with any type that supports the ``<=`` operator, - including numbers, dates and times, strings, sets, and so on. - """ - - le: SupportsLe - - -@runtime_checkable -class GroupedMetadata(Protocol): - """A grouping of multiple objects, like typing.Unpack. - - `GroupedMetadata` on its own is not metadata and has no meaning. - All of the constraints and metadata should be fully expressable - in terms of the `BaseMetadata`'s returned by `GroupedMetadata.__iter__()`. - - Concrete implementations should override `GroupedMetadata.__iter__()` - to add their own metadata. - For example: - - >>> @dataclass - >>> class Field(GroupedMetadata): - >>> gt: float | None = None - >>> description: str | None = None - ... - >>> def __iter__(self) -> Iterable[object]: - >>> if self.gt is not None: - >>> yield Gt(self.gt) - >>> if self.description is not None: - >>> yield Description(self.gt) - - Also see the implementation of `Interval` below for an example. - - Parsers should recognize this and unpack it so that it can be used - both with and without unpacking: - - - `Annotated[int, Field(...)]` (parser must unpack Field) - - `Annotated[int, *Field(...)]` (PEP-646) - """ # noqa: trailing-whitespace - - @property - def __is_annotated_types_grouped_metadata__(self) -> Literal[True]: - return True - - def __iter__(self) -> Iterator[object]: - ... - - if not TYPE_CHECKING: - __slots__ = () # allow subclasses to use slots - - def __init_subclass__(cls, *args: Any, **kwargs: Any) -> None: - # Basic ABC like functionality without the complexity of an ABC - super().__init_subclass__(*args, **kwargs) - if cls.__iter__ is GroupedMetadata.__iter__: - raise TypeError("Can't subclass GroupedMetadata without implementing __iter__") - - def __iter__(self) -> Iterator[object]: # noqa: F811 - raise NotImplementedError # more helpful than "None has no attribute..." type errors - - -@dataclass(frozen=True, **KW_ONLY, **SLOTS) -class Interval(GroupedMetadata): - """Interval can express inclusive or exclusive bounds with a single object. - - It accepts keyword arguments ``gt``, ``ge``, ``lt``, and/or ``le``, which - are interpreted the same way as the single-bound constraints. - """ - - gt: Union[SupportsGt, None] = None - ge: Union[SupportsGe, None] = None - lt: Union[SupportsLt, None] = None - le: Union[SupportsLe, None] = None - - def __iter__(self) -> Iterator[BaseMetadata]: - """Unpack an Interval into zero or more single-bounds.""" - if self.gt is not None: - yield Gt(self.gt) - if self.ge is not None: - yield Ge(self.ge) - if self.lt is not None: - yield Lt(self.lt) - if self.le is not None: - yield Le(self.le) - - -@dataclass(frozen=True, **SLOTS) -class MultipleOf(BaseMetadata): - """MultipleOf(multiple_of=x) might be interpreted in two ways: - - 1. Python semantics, implying ``value % multiple_of == 0``, or - 2. JSONschema semantics, where ``int(value / multiple_of) == value / multiple_of`` - - We encourage users to be aware of these two common interpretations, - and libraries to carefully document which they implement. - """ - - multiple_of: Union[SupportsDiv, SupportsMod] - - -@dataclass(frozen=True, **SLOTS) -class MinLen(BaseMetadata): - """ - MinLen() implies minimum inclusive length, - e.g. ``len(value) >= min_length``. - """ - - min_length: Annotated[int, Ge(0)] - - -@dataclass(frozen=True, **SLOTS) -class MaxLen(BaseMetadata): - """ - MaxLen() implies maximum inclusive length, - e.g. ``len(value) <= max_length``. - """ - - max_length: Annotated[int, Ge(0)] - - -@dataclass(frozen=True, **SLOTS) -class Len(GroupedMetadata): - """ - Len() implies that ``min_length <= len(value) <= max_length``. - - Upper bound may be omitted or ``None`` to indicate no upper length bound. - """ - - min_length: Annotated[int, Ge(0)] = 0 - max_length: Optional[Annotated[int, Ge(0)]] = None - - def __iter__(self) -> Iterator[BaseMetadata]: - """Unpack a Len into zone or more single-bounds.""" - if self.min_length > 0: - yield MinLen(self.min_length) - if self.max_length is not None: - yield MaxLen(self.max_length) - - -@dataclass(frozen=True, **SLOTS) -class Timezone(BaseMetadata): - """Timezone(tz=...) requires a datetime to be aware (or ``tz=None``, naive). - - ``Annotated[datetime, Timezone(None)]`` must be a naive datetime. - ``Timezone[...]`` (the ellipsis literal) expresses that the datetime must be - tz-aware but any timezone is allowed. - - You may also pass a specific timezone string or tzinfo object such as - ``Timezone(timezone.utc)`` or ``Timezone("Africa/Abidjan")`` to express that - you only allow a specific timezone, though we note that this is often - a symptom of poor design. - """ - - tz: Union[str, tzinfo, EllipsisType, None] - - -@dataclass(frozen=True, **SLOTS) -class Unit(BaseMetadata): - """Indicates that the value is a physical quantity with the specified unit. - - It is intended for usage with numeric types, where the value represents the - magnitude of the quantity. For example, ``distance: Annotated[float, Unit('m')]`` - or ``speed: Annotated[float, Unit('m/s')]``. - - Interpretation of the unit string is left to the discretion of the consumer. - It is suggested to follow conventions established by python libraries that work - with physical quantities, such as - - - ``pint`` : - - ``astropy.units``: - - For indicating a quantity with a certain dimensionality but without a specific unit - it is recommended to use square brackets, e.g. `Annotated[float, Unit('[time]')]`. - Note, however, ``annotated_types`` itself makes no use of the unit string. - """ - - unit: str - - -@dataclass(frozen=True, **SLOTS) -class Predicate(BaseMetadata): - """``Predicate(func: Callable)`` implies `func(value)` is truthy for valid values. - - Users should prefer statically inspectable metadata, but if you need the full - power and flexibility of arbitrary runtime predicates... here it is. - - We provide a few predefined predicates for common string constraints: - ``IsLower = Predicate(str.islower)``, ``IsUpper = Predicate(str.isupper)``, and - ``IsDigits = Predicate(str.isdigit)``. Users are encouraged to use methods which - can be given special handling, and avoid indirection like ``lambda s: s.lower()``. - - Some libraries might have special logic to handle certain predicates, e.g. by - checking for `str.isdigit` and using its presence to both call custom logic to - enforce digit-only strings, and customise some generated external schema. - - We do not specify what behaviour should be expected for predicates that raise - an exception. For example `Annotated[int, Predicate(str.isdigit)]` might silently - skip invalid constraints, or statically raise an error; or it might try calling it - and then propagate or discard the resulting exception. - """ - - func: Callable[[Any], bool] - - def __repr__(self) -> str: - if getattr(self.func, "__name__", "") == "": - return f"{self.__class__.__name__}({self.func!r})" - if isinstance(self.func, (types.MethodType, types.BuiltinMethodType)) and ( - namespace := getattr(self.func.__self__, "__name__", None) - ): - return f"{self.__class__.__name__}({namespace}.{self.func.__name__})" - if isinstance(self.func, type(str.isascii)): # method descriptor - return f"{self.__class__.__name__}({self.func.__qualname__})" - return f"{self.__class__.__name__}({self.func.__name__})" - - -@dataclass -class Not: - func: Callable[[Any], bool] - - def __call__(self, __v: Any) -> bool: - return not self.func(__v) - - -_StrType = TypeVar("_StrType", bound=str) - -LowerCase = Annotated[_StrType, Predicate(str.islower)] -""" -Return True if the string is a lowercase string, False otherwise. - -A string is lowercase if all cased characters in the string are lowercase and there is at least one cased character in the string. -""" # noqa: E501 -UpperCase = Annotated[_StrType, Predicate(str.isupper)] -""" -Return True if the string is an uppercase string, False otherwise. - -A string is uppercase if all cased characters in the string are uppercase and there is at least one cased character in the string. -""" # noqa: E501 -IsDigit = Annotated[_StrType, Predicate(str.isdigit)] -IsDigits = IsDigit # type: ignore # plural for backwards compatibility, see #63 -""" -Return True if the string is a digit string, False otherwise. - -A string is a digit string if all characters in the string are digits and there is at least one character in the string. -""" # noqa: E501 -IsAscii = Annotated[_StrType, Predicate(str.isascii)] -""" -Return True if all characters in the string are ASCII, False otherwise. - -ASCII characters have code points in the range U+0000-U+007F. Empty string is ASCII too. -""" - -_NumericType = TypeVar('_NumericType', bound=Union[SupportsFloat, SupportsIndex]) -IsFinite = Annotated[_NumericType, Predicate(math.isfinite)] -"""Return True if x is neither an infinity nor a NaN, and False otherwise.""" -IsNotFinite = Annotated[_NumericType, Predicate(Not(math.isfinite))] -"""Return True if x is one of infinity or NaN, and False otherwise""" -IsNan = Annotated[_NumericType, Predicate(math.isnan)] -"""Return True if x is a NaN (not a number), and False otherwise.""" -IsNotNan = Annotated[_NumericType, Predicate(Not(math.isnan))] -"""Return True if x is anything but NaN (not a number), and False otherwise.""" -IsInfinite = Annotated[_NumericType, Predicate(math.isinf)] -"""Return True if x is a positive or negative infinity, and False otherwise.""" -IsNotInfinite = Annotated[_NumericType, Predicate(Not(math.isinf))] -"""Return True if x is neither a positive or negative infinity, and False otherwise.""" - -try: - from typing_extensions import DocInfo, doc # type: ignore [attr-defined] -except ImportError: - - @dataclass(frozen=True, **SLOTS) - class DocInfo: # type: ignore [no-redef] - """ " - The return value of doc(), mainly to be used by tools that want to extract the - Annotated documentation at runtime. - """ - - documentation: str - """The documentation string passed to doc().""" - - def doc( - documentation: str, - ) -> DocInfo: - """ - Add documentation to a type annotation inside of Annotated. - - For example: - - >>> def hi(name: Annotated[int, doc("The name of the user")]) -> None: ... - """ - return DocInfo(documentation) diff --git a/.venv/Lib/site-packages/annotated_types/py.typed b/.venv/Lib/site-packages/annotated_types/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/annotated_types/test_cases.py b/.venv/Lib/site-packages/annotated_types/test_cases.py deleted file mode 100644 index d9164d6..0000000 --- a/.venv/Lib/site-packages/annotated_types/test_cases.py +++ /dev/null @@ -1,151 +0,0 @@ -import math -import sys -from datetime import date, datetime, timedelta, timezone -from decimal import Decimal -from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Set, Tuple - -if sys.version_info < (3, 9): - from typing_extensions import Annotated -else: - from typing import Annotated - -import annotated_types as at - - -class Case(NamedTuple): - """ - A test case for `annotated_types`. - """ - - annotation: Any - valid_cases: Iterable[Any] - invalid_cases: Iterable[Any] - - -def cases() -> Iterable[Case]: - # Gt, Ge, Lt, Le - yield Case(Annotated[int, at.Gt(4)], (5, 6, 1000), (4, 0, -1)) - yield Case(Annotated[float, at.Gt(0.5)], (0.6, 0.7, 0.8, 0.9), (0.5, 0.0, -0.1)) - yield Case( - Annotated[datetime, at.Gt(datetime(2000, 1, 1))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(2000, 1, 1), datetime(1999, 12, 31)], - ) - yield Case( - Annotated[datetime, at.Gt(date(2000, 1, 1))], - [date(2000, 1, 2), date(2000, 1, 3)], - [date(2000, 1, 1), date(1999, 12, 31)], - ) - yield Case( - Annotated[datetime, at.Gt(Decimal('1.123'))], - [Decimal('1.1231'), Decimal('123')], - [Decimal('1.123'), Decimal('0')], - ) - - yield Case(Annotated[int, at.Ge(4)], (4, 5, 6, 1000, 4), (0, -1)) - yield Case(Annotated[float, at.Ge(0.5)], (0.5, 0.6, 0.7, 0.8, 0.9), (0.4, 0.0, -0.1)) - yield Case( - Annotated[datetime, at.Ge(datetime(2000, 1, 1))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(1998, 1, 1), datetime(1999, 12, 31)], - ) - - yield Case(Annotated[int, at.Lt(4)], (0, -1), (4, 5, 6, 1000, 4)) - yield Case(Annotated[float, at.Lt(0.5)], (0.4, 0.0, -0.1), (0.5, 0.6, 0.7, 0.8, 0.9)) - yield Case( - Annotated[datetime, at.Lt(datetime(2000, 1, 1))], - [datetime(1999, 12, 31), datetime(1999, 12, 31)], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - ) - - yield Case(Annotated[int, at.Le(4)], (4, 0, -1), (5, 6, 1000)) - yield Case(Annotated[float, at.Le(0.5)], (0.5, 0.0, -0.1), (0.6, 0.7, 0.8, 0.9)) - yield Case( - Annotated[datetime, at.Le(datetime(2000, 1, 1))], - [datetime(2000, 1, 1), datetime(1999, 12, 31)], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - ) - - # Interval - yield Case(Annotated[int, at.Interval(gt=4)], (5, 6, 1000), (4, 0, -1)) - yield Case(Annotated[int, at.Interval(gt=4, lt=10)], (5, 6), (4, 10, 1000, 0, -1)) - yield Case(Annotated[float, at.Interval(ge=0.5, le=1)], (0.5, 0.9, 1), (0.49, 1.1)) - yield Case( - Annotated[datetime, at.Interval(gt=datetime(2000, 1, 1), le=datetime(2000, 1, 3))], - [datetime(2000, 1, 2), datetime(2000, 1, 3)], - [datetime(2000, 1, 1), datetime(2000, 1, 4)], - ) - - yield Case(Annotated[int, at.MultipleOf(multiple_of=3)], (0, 3, 9), (1, 2, 4)) - yield Case(Annotated[float, at.MultipleOf(multiple_of=0.5)], (0, 0.5, 1, 1.5), (0.4, 1.1)) - - # lengths - - yield Case(Annotated[str, at.MinLen(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) - yield Case(Annotated[str, at.Len(3)], ('123', '1234', 'x' * 10), ('', '1', '12')) - yield Case(Annotated[List[int], at.MinLen(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) - yield Case(Annotated[List[int], at.Len(3)], ([1, 2, 3], [1, 2, 3, 4], [1] * 10), ([], [1], [1, 2])) - - yield Case(Annotated[str, at.MaxLen(4)], ('', '1234'), ('12345', 'x' * 10)) - yield Case(Annotated[str, at.Len(0, 4)], ('', '1234'), ('12345', 'x' * 10)) - yield Case(Annotated[List[str], at.MaxLen(4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) - yield Case(Annotated[List[str], at.Len(0, 4)], ([], ['a', 'bcdef'], ['a', 'b', 'c']), (['a'] * 5, ['b'] * 10)) - - yield Case(Annotated[str, at.Len(3, 5)], ('123', '12345'), ('', '1', '12', '123456', 'x' * 10)) - yield Case(Annotated[str, at.Len(3, 3)], ('123',), ('12', '1234')) - - yield Case(Annotated[Dict[int, int], at.Len(2, 3)], [{1: 1, 2: 2}], [{}, {1: 1}, {1: 1, 2: 2, 3: 3, 4: 4}]) - yield Case(Annotated[Set[int], at.Len(2, 3)], ({1, 2}, {1, 2, 3}), (set(), {1}, {1, 2, 3, 4})) - yield Case(Annotated[Tuple[int, ...], at.Len(2, 3)], ((1, 2), (1, 2, 3)), ((), (1,), (1, 2, 3, 4))) - - # Timezone - - yield Case( - Annotated[datetime, at.Timezone(None)], [datetime(2000, 1, 1)], [datetime(2000, 1, 1, tzinfo=timezone.utc)] - ) - yield Case( - Annotated[datetime, at.Timezone(...)], [datetime(2000, 1, 1, tzinfo=timezone.utc)], [datetime(2000, 1, 1)] - ) - yield Case( - Annotated[datetime, at.Timezone(timezone.utc)], - [datetime(2000, 1, 1, tzinfo=timezone.utc)], - [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], - ) - yield Case( - Annotated[datetime, at.Timezone('Europe/London')], - [datetime(2000, 1, 1, tzinfo=timezone(timedelta(0), name='Europe/London'))], - [datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=timezone(timedelta(hours=6)))], - ) - - # Quantity - - yield Case(Annotated[float, at.Unit(unit='m')], (5, 4.2), ('5m', '4.2m')) - - # predicate types - - yield Case(at.LowerCase[str], ['abc', 'foobar'], ['', 'A', 'Boom']) - yield Case(at.UpperCase[str], ['ABC', 'DEFO'], ['', 'a', 'abc', 'AbC']) - yield Case(at.IsDigit[str], ['123'], ['', 'ab', 'a1b2']) - yield Case(at.IsAscii[str], ['123', 'foo bar'], ['£100', '😊', 'whatever 👀']) - - yield Case(Annotated[int, at.Predicate(lambda x: x % 2 == 0)], [0, 2, 4], [1, 3, 5]) - - yield Case(at.IsFinite[float], [1.23], [math.nan, math.inf, -math.inf]) - yield Case(at.IsNotFinite[float], [math.nan, math.inf], [1.23]) - yield Case(at.IsNan[float], [math.nan], [1.23, math.inf]) - yield Case(at.IsNotNan[float], [1.23, math.inf], [math.nan]) - yield Case(at.IsInfinite[float], [math.inf], [math.nan, 1.23]) - yield Case(at.IsNotInfinite[float], [math.nan, 1.23], [math.inf]) - - # check stacked predicates - yield Case(at.IsInfinite[Annotated[float, at.Predicate(lambda x: x > 0)]], [math.inf], [-math.inf, 1.23, math.nan]) - - # doc - yield Case(Annotated[int, at.doc("A number")], [1, 2], []) - - # custom GroupedMetadata - class MyCustomGroupedMetadata(at.GroupedMetadata): - def __iter__(self) -> Iterator[at.Predicate]: - yield at.Predicate(lambda x: float(x).is_integer()) - - yield Case(Annotated[float, MyCustomGroupedMetadata()], [0, 2.0], [0.01, 1.5]) diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/INSTALLER b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/LICENSE b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/LICENSE deleted file mode 100644 index 104eebf..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Alex Grönholm - -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/site-packages/anyio-4.7.0.dist-info/METADATA b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/METADATA deleted file mode 100644 index 5d0f7c9..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/METADATA +++ /dev/null @@ -1,105 +0,0 @@ -Metadata-Version: 2.1 -Name: anyio -Version: 4.7.0 -Summary: High level compatibility layer for multiple asynchronous event loop implementations -Author-email: Alex Grönholm -License: MIT -Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/ -Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html -Project-URL: Source code, https://github.com/agronholm/anyio -Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Framework :: AnyIO -Classifier: Typing :: Typed -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -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 :: 3.13 -Requires-Python: >=3.9 -Description-Content-Type: text/x-rst -License-File: LICENSE -Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11" -Requires-Dist: idna>=2.8 -Requires-Dist: sniffio>=1.1 -Requires-Dist: typing_extensions>=4.5; python_version < "3.13" -Provides-Extra: trio -Requires-Dist: trio>=0.26.1; extra == "trio" -Provides-Extra: test -Requires-Dist: anyio[trio]; extra == "test" -Requires-Dist: coverage[toml]>=7; extra == "test" -Requires-Dist: exceptiongroup>=1.2.0; extra == "test" -Requires-Dist: hypothesis>=4.0; extra == "test" -Requires-Dist: psutil>=5.9; extra == "test" -Requires-Dist: pytest>=7.0; extra == "test" -Requires-Dist: pytest-mock>=3.6.1; extra == "test" -Requires-Dist: trustme; extra == "test" -Requires-Dist: truststore>=0.9.1; python_version >= "3.10" and extra == "test" -Requires-Dist: uvloop>=0.21; (platform_python_implementation == "CPython" and platform_system != "Windows") and extra == "test" -Provides-Extra: doc -Requires-Dist: packaging; extra == "doc" -Requires-Dist: Sphinx~=7.4; extra == "doc" -Requires-Dist: sphinx_rtd_theme; extra == "doc" -Requires-Dist: sphinx-autodoc-typehints>=1.2.0; extra == "doc" - -.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg - :target: https://github.com/agronholm/anyio/actions/workflows/test.yml - :alt: Build Status -.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master - :target: https://coveralls.io/github/agronholm/anyio?branch=master - :alt: Code Coverage -.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest - :target: https://anyio.readthedocs.io/en/latest/?badge=latest - :alt: Documentation -.. image:: https://badges.gitter.im/gitterHQ/gitter.svg - :target: https://gitter.im/python-trio/AnyIO - :alt: Gitter chat - -AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or -trio_. It implements trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony -with the native SC of trio itself. - -Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or -trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full -refactoring necessary. It will blend in with the native libraries of your chosen backend. - -Documentation -------------- - -View full documentation at: https://anyio.readthedocs.io/ - -Features --------- - -AnyIO offers the following functionality: - -* Task groups (nurseries_ in trio terminology) -* High-level networking (TCP, UDP and UNIX sockets) - - * `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python - 3.8) - * async/await style UDP sockets (unlike asyncio where you still have to use Transports and - Protocols) - -* A versatile API for byte streams and object streams -* Inter-task synchronization and communication (locks, conditions, events, semaphores, object - streams) -* Worker threads -* Subprocesses -* Asynchronous file I/O (using worker threads) -* Signal handling - -AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures. -It even works with the popular Hypothesis_ library. - -.. _asyncio: https://docs.python.org/3/library/asyncio.html -.. _trio: https://github.com/python-trio/trio -.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency -.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning -.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs -.. _pytest: https://docs.pytest.org/en/latest/ -.. _Hypothesis: https://hypothesis.works/ diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/RECORD b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/RECORD deleted file mode 100644 index eac883c..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/RECORD +++ /dev/null @@ -1,84 +0,0 @@ -anyio-4.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -anyio-4.7.0.dist-info/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081 -anyio-4.7.0.dist-info/METADATA,sha256=A-A-n0m-esMw8lYv8a9kqsr84J2aFh8MvqcTq2Xx_so,4653 -anyio-4.7.0.dist-info/RECORD,, -anyio-4.7.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91 -anyio-4.7.0.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39 -anyio-4.7.0.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6 -anyio/__init__.py,sha256=5NCKQNJueCeIJqVbOpAQdho2HIQrQvcnfQjuEhAiZcc,4433 -anyio/__pycache__/__init__.cpython-311.pyc,, -anyio/__pycache__/from_thread.cpython-311.pyc,, -anyio/__pycache__/lowlevel.cpython-311.pyc,, -anyio/__pycache__/pytest_plugin.cpython-311.pyc,, -anyio/__pycache__/to_process.cpython-311.pyc,, -anyio/__pycache__/to_thread.cpython-311.pyc,, -anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/_backends/__pycache__/__init__.cpython-311.pyc,, -anyio/_backends/__pycache__/_asyncio.cpython-311.pyc,, -anyio/_backends/__pycache__/_trio.cpython-311.pyc,, -anyio/_backends/_asyncio.py,sha256=i5Qe4IBdiWRlww0qIUAVsF-K0z30bgZakuMePpNbdro,94051 -anyio/_backends/_trio.py,sha256=7oGxbqeveiesGm2pAnCRBydqy-Gbistn_xfsmKhSLLg,40371 -anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/_core/__pycache__/__init__.cpython-311.pyc,, -anyio/_core/__pycache__/_asyncio_selector_thread.cpython-311.pyc,, -anyio/_core/__pycache__/_eventloop.cpython-311.pyc,, -anyio/_core/__pycache__/_exceptions.cpython-311.pyc,, -anyio/_core/__pycache__/_fileio.cpython-311.pyc,, -anyio/_core/__pycache__/_resources.cpython-311.pyc,, -anyio/_core/__pycache__/_signals.cpython-311.pyc,, -anyio/_core/__pycache__/_sockets.cpython-311.pyc,, -anyio/_core/__pycache__/_streams.cpython-311.pyc,, -anyio/_core/__pycache__/_subprocesses.cpython-311.pyc,, -anyio/_core/__pycache__/_synchronization.cpython-311.pyc,, -anyio/_core/__pycache__/_tasks.cpython-311.pyc,, -anyio/_core/__pycache__/_testing.cpython-311.pyc,, -anyio/_core/__pycache__/_typedattr.cpython-311.pyc,, -anyio/_core/_asyncio_selector_thread.py,sha256=vTdZBWaxRgVcgUaRb5uBwQ_VGgY3qPKF7l91IJ5Mqzo,4773 -anyio/_core/_eventloop.py,sha256=t_tAwBFPjF8jrZGjlJ6bbYy6KA3bjsbZxV9mvh9t1i0,4695 -anyio/_core/_exceptions.py,sha256=bKPr2QbkYG7nIb425L5JePUie9bGc9XfkY0y4JKWvFM,2488 -anyio/_core/_fileio.py,sha256=DqnG_zvQFMqiIFaUeDRC1Ts3LT0FWHkWtGgm-684hvQ,20957 -anyio/_core/_resources.py,sha256=NbmU5O5UX3xEyACnkmYX28Fmwdl-f-ny0tHym26e0w0,435 -anyio/_core/_signals.py,sha256=vulT1M1xdLYtAR-eY5TamIgaf1WTlOwOrMGwswlTTr8,905 -anyio/_core/_sockets.py,sha256=vQ5GnSDLHjEhHhV2yvsdiPs5wmPxxb1kRsv3RM5lbQk,26951 -anyio/_core/_streams.py,sha256=OnaKgoDD-FcMSwLvkoAUGP51sG2ZdRvMpxt9q2w1gYA,1804 -anyio/_core/_subprocesses.py,sha256=WquR6sHrnaZofaeqnL8U4Yv___msVW_WqivleLHK4zI,7760 -anyio/_core/_synchronization.py,sha256=tct5FJFdgYjiEMtUeg5NGG15tf-2Qd7VaWuSgzS5dIU,20347 -anyio/_core/_tasks.py,sha256=pvVEX2Fw159sf0ypAPerukKsZgRRwvFFedVW52nR2Vk,4764 -anyio/_core/_testing.py,sha256=YUGwA5cgFFbUTv4WFd7cv_BSVr4ryTtPp8owQA3JdWE,2118 -anyio/_core/_typedattr.py,sha256=P4ozZikn3-DbpoYcvyghS_FOYAgbmUxeoU8-L_07pZM,2508 -anyio/abc/__init__.py,sha256=c2OQbTCS_fQowviMXanLPh8m29ccwkXmpDr7uyNZYOo,2652 -anyio/abc/__pycache__/__init__.cpython-311.pyc,, -anyio/abc/__pycache__/_eventloop.cpython-311.pyc,, -anyio/abc/__pycache__/_resources.cpython-311.pyc,, -anyio/abc/__pycache__/_sockets.cpython-311.pyc,, -anyio/abc/__pycache__/_streams.cpython-311.pyc,, -anyio/abc/__pycache__/_subprocesses.cpython-311.pyc,, -anyio/abc/__pycache__/_tasks.cpython-311.pyc,, -anyio/abc/__pycache__/_testing.cpython-311.pyc,, -anyio/abc/_eventloop.py,sha256=Wd_3C3hLm0ex5z_eHHWGqvLle2OKCSexJSZVnwQNGV4,9658 -anyio/abc/_resources.py,sha256=DrYvkNN1hH6Uvv5_5uKySvDsnknGVDe8FCKfko0VtN8,783 -anyio/abc/_sockets.py,sha256=KhWtJxan8jpBXKwPaFeQzI4iRXdFaOIn0HXtDZnaO7U,6262 -anyio/abc/_streams.py,sha256=GzST5Q2zQmxVzdrAqtbSyHNxkPlIC9AzeZJg_YyPAXw,6598 -anyio/abc/_subprocesses.py,sha256=cumAPJTktOQtw63IqG0lDpyZqu_l1EElvQHMiwJgL08,2067 -anyio/abc/_tasks.py,sha256=yJWbMwowvqjlAX4oJ3l9Is1w-zwynr2lX1Z02AWJqsY,3080 -anyio/abc/_testing.py,sha256=tBJUzkSfOXJw23fe8qSJ03kJlShOYjjaEyFB6k6MYT8,1821 -anyio/from_thread.py,sha256=dbi5TUH45_Sg_jZ8Vv1NJWVohe0WeQ_OaCvXIKveAGg,17478 -anyio/lowlevel.py,sha256=nkgmW--SdxGVp0cmLUYazjkigveRm5HY7-gW8Bpp9oY,4169 -anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/pytest_plugin.py,sha256=vjGhGRHD31OyMgJRFQrMvExhx3Ea8KbyDqYKmiSDdXA,6712 -anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -anyio/streams/__pycache__/__init__.cpython-311.pyc,, -anyio/streams/__pycache__/buffered.cpython-311.pyc,, -anyio/streams/__pycache__/file.cpython-311.pyc,, -anyio/streams/__pycache__/memory.cpython-311.pyc,, -anyio/streams/__pycache__/stapled.cpython-311.pyc,, -anyio/streams/__pycache__/text.cpython-311.pyc,, -anyio/streams/__pycache__/tls.cpython-311.pyc,, -anyio/streams/buffered.py,sha256=UCldKC168YuLvT7n3HtNPnQ2iWAMSTYQWbZvzLwMwkM,4500 -anyio/streams/file.py,sha256=6uoTNb5KbMoj-6gS3_xrrL8uZN8Q4iIvOS1WtGyFfKw,4383 -anyio/streams/memory.py,sha256=j8AyOExK4-UPaon_Xbhwax25Vqs0DwFg3ZXc-EIiHjY,10550 -anyio/streams/stapled.py,sha256=U09pCrmOw9kkNhe6tKopsm1QIMT1lFTFvtb-A7SIe4k,4302 -anyio/streams/text.py,sha256=6x8w8xlfCZKTUWQoJiMPoMhSSJFUBRKgoBNSBtbd9yg,5094 -anyio/streams/tls.py,sha256=m3AE2LVSpoRHSIwSoSCupiOVL54EvOFoY3CcwTxcZfg,12742 -anyio/to_process.py,sha256=cR4n7TssbbJowE_9cWme49zaeuoBuMzqgZ6cBIs0YIs,9571 -anyio/to_thread.py,sha256=WM2JQ2MbVsd5D5CM08bQiTwzZIvpsGjfH1Fy247KoDQ,2396 diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/WHEEL b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/WHEEL deleted file mode 100644 index ae527e7..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (75.6.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/entry_points.txt b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/entry_points.txt deleted file mode 100644 index 44dd9bd..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[pytest11] -anyio = anyio.pytest_plugin diff --git a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/top_level.txt b/.venv/Lib/site-packages/anyio-4.7.0.dist-info/top_level.txt deleted file mode 100644 index c77c069..0000000 --- a/.venv/Lib/site-packages/anyio-4.7.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -anyio diff --git a/.venv/Lib/site-packages/anyio/__init__.py b/.venv/Lib/site-packages/anyio/__init__.py deleted file mode 100644 index 0738e59..0000000 --- a/.venv/Lib/site-packages/anyio/__init__.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import annotations - -from ._core._eventloop import current_time as current_time -from ._core._eventloop import get_all_backends as get_all_backends -from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class -from ._core._eventloop import run as run -from ._core._eventloop import sleep as sleep -from ._core._eventloop import sleep_forever as sleep_forever -from ._core._eventloop import sleep_until as sleep_until -from ._core._exceptions import BrokenResourceError as BrokenResourceError -from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess -from ._core._exceptions import BusyResourceError as BusyResourceError -from ._core._exceptions import ClosedResourceError as ClosedResourceError -from ._core._exceptions import DelimiterNotFound as DelimiterNotFound -from ._core._exceptions import EndOfStream as EndOfStream -from ._core._exceptions import IncompleteRead as IncompleteRead -from ._core._exceptions import TypedAttributeLookupError as TypedAttributeLookupError -from ._core._exceptions import WouldBlock as WouldBlock -from ._core._fileio import AsyncFile as AsyncFile -from ._core._fileio import Path as Path -from ._core._fileio import open_file as open_file -from ._core._fileio import wrap_file as wrap_file -from ._core._resources import aclose_forcefully as aclose_forcefully -from ._core._signals import open_signal_receiver as open_signal_receiver -from ._core._sockets import connect_tcp as connect_tcp -from ._core._sockets import connect_unix as connect_unix -from ._core._sockets import create_connected_udp_socket as create_connected_udp_socket -from ._core._sockets import ( - create_connected_unix_datagram_socket as create_connected_unix_datagram_socket, -) -from ._core._sockets import create_tcp_listener as create_tcp_listener -from ._core._sockets import create_udp_socket as create_udp_socket -from ._core._sockets import create_unix_datagram_socket as create_unix_datagram_socket -from ._core._sockets import create_unix_listener as create_unix_listener -from ._core._sockets import getaddrinfo as getaddrinfo -from ._core._sockets import getnameinfo as getnameinfo -from ._core._sockets import wait_readable as wait_readable -from ._core._sockets import wait_socket_readable as wait_socket_readable -from ._core._sockets import wait_socket_writable as wait_socket_writable -from ._core._sockets import wait_writable as wait_writable -from ._core._streams import create_memory_object_stream as create_memory_object_stream -from ._core._subprocesses import open_process as open_process -from ._core._subprocesses import run_process as run_process -from ._core._synchronization import CapacityLimiter as CapacityLimiter -from ._core._synchronization import ( - CapacityLimiterStatistics as CapacityLimiterStatistics, -) -from ._core._synchronization import Condition as Condition -from ._core._synchronization import ConditionStatistics as ConditionStatistics -from ._core._synchronization import Event as Event -from ._core._synchronization import EventStatistics as EventStatistics -from ._core._synchronization import Lock as Lock -from ._core._synchronization import LockStatistics as LockStatistics -from ._core._synchronization import ResourceGuard as ResourceGuard -from ._core._synchronization import Semaphore as Semaphore -from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics -from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED -from ._core._tasks import CancelScope as CancelScope -from ._core._tasks import create_task_group as create_task_group -from ._core._tasks import current_effective_deadline as current_effective_deadline -from ._core._tasks import fail_after as fail_after -from ._core._tasks import move_on_after as move_on_after -from ._core._testing import TaskInfo as TaskInfo -from ._core._testing import get_current_task as get_current_task -from ._core._testing import get_running_tasks as get_running_tasks -from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked -from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider -from ._core._typedattr import TypedAttributeSet as TypedAttributeSet -from ._core._typedattr import typed_attribute as typed_attribute - -# Re-export imports so they look like they live directly in this package -for __value in list(locals().values()): - if getattr(__value, "__module__", "").startswith("anyio."): - __value.__module__ = __name__ - -del __value diff --git a/.venv/Lib/site-packages/anyio/_backends/__init__.py b/.venv/Lib/site-packages/anyio/_backends/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/anyio/_backends/_asyncio.py b/.venv/Lib/site-packages/anyio/_backends/_asyncio.py deleted file mode 100644 index 0b7479d..0000000 --- a/.venv/Lib/site-packages/anyio/_backends/_asyncio.py +++ /dev/null @@ -1,2851 +0,0 @@ -from __future__ import annotations - -import array -import asyncio -import concurrent.futures -import math -import os -import socket -import sys -import threading -import weakref -from asyncio import ( - AbstractEventLoop, - CancelledError, - all_tasks, - create_task, - current_task, - get_running_loop, - sleep, -) -from asyncio.base_events import _run_until_complete_cb # type: ignore[attr-defined] -from collections import OrderedDict, deque -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Iterator, - MutableMapping, - Sequence, -) -from concurrent.futures import Future -from contextlib import AbstractContextManager, suppress -from contextvars import Context, copy_context -from dataclasses import dataclass -from functools import partial, wraps -from inspect import ( - CORO_RUNNING, - CORO_SUSPENDED, - getcoroutinestate, - iscoroutine, -) -from io import IOBase -from os import PathLike -from queue import Queue -from signal import Signals -from socket import AddressFamily, SocketKind -from threading import Thread -from types import TracebackType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Optional, - TypeVar, - cast, -) -from weakref import WeakKeyDictionary - -import sniffio - -from .. import ( - CapacityLimiterStatistics, - EventStatistics, - LockStatistics, - TaskInfo, - abc, -) -from .._core._eventloop import claim_worker_thread, threadlocals -from .._core._exceptions import ( - BrokenResourceError, - BusyResourceError, - ClosedResourceError, - EndOfStream, - WouldBlock, - iterate_exceptions, -) -from .._core._sockets import convert_ipv6_sockaddr -from .._core._streams import create_memory_object_stream -from .._core._synchronization import ( - CapacityLimiter as BaseCapacityLimiter, -) -from .._core._synchronization import Event as BaseEvent -from .._core._synchronization import Lock as BaseLock -from .._core._synchronization import ( - ResourceGuard, - SemaphoreStatistics, -) -from .._core._synchronization import Semaphore as BaseSemaphore -from .._core._tasks import CancelScope as BaseCancelScope -from ..abc import ( - AsyncBackend, - IPSockAddrType, - SocketListener, - UDPPacketType, - UNIXDatagramPacketType, -) -from ..abc._eventloop import StrOrBytesPath -from ..lowlevel import RunVar -from ..streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike -else: - FileDescriptorLike = object - -if sys.version_info >= (3, 10): - from typing import ParamSpec -else: - from typing_extensions import ParamSpec - -if sys.version_info >= (3, 11): - from asyncio import Runner - from typing import TypeVarTuple, Unpack -else: - import contextvars - import enum - import signal - from asyncio import coroutines, events, exceptions, tasks - - from exceptiongroup import BaseExceptionGroup - from typing_extensions import TypeVarTuple, Unpack - - class _State(enum.Enum): - CREATED = "created" - INITIALIZED = "initialized" - CLOSED = "closed" - - class Runner: - # Copied from CPython 3.11 - def __init__( - self, - *, - debug: bool | None = None, - loop_factory: Callable[[], AbstractEventLoop] | None = None, - ): - self._state = _State.CREATED - self._debug = debug - self._loop_factory = loop_factory - self._loop: AbstractEventLoop | None = None - self._context = None - self._interrupt_count = 0 - self._set_event_loop = False - - def __enter__(self) -> Runner: - self._lazy_init() - return self - - def __exit__( - self, - exc_type: type[BaseException], - exc_val: BaseException, - exc_tb: TracebackType, - ) -> None: - self.close() - - def close(self) -> None: - """Shutdown and close event loop.""" - if self._state is not _State.INITIALIZED: - return - try: - loop = self._loop - _cancel_all_tasks(loop) - loop.run_until_complete(loop.shutdown_asyncgens()) - if hasattr(loop, "shutdown_default_executor"): - loop.run_until_complete(loop.shutdown_default_executor()) - else: - loop.run_until_complete(_shutdown_default_executor(loop)) - finally: - if self._set_event_loop: - events.set_event_loop(None) - loop.close() - self._loop = None - self._state = _State.CLOSED - - def get_loop(self) -> AbstractEventLoop: - """Return embedded event loop.""" - self._lazy_init() - return self._loop - - def run(self, coro: Coroutine[T_Retval], *, context=None) -> T_Retval: - """Run a coroutine inside the embedded event loop.""" - if not coroutines.iscoroutine(coro): - raise ValueError(f"a coroutine was expected, got {coro!r}") - - if events._get_running_loop() is not None: - # fail fast with short traceback - raise RuntimeError( - "Runner.run() cannot be called from a running event loop" - ) - - self._lazy_init() - - if context is None: - context = self._context - task = context.run(self._loop.create_task, coro) - - if ( - threading.current_thread() is threading.main_thread() - and signal.getsignal(signal.SIGINT) is signal.default_int_handler - ): - sigint_handler = partial(self._on_sigint, main_task=task) - try: - signal.signal(signal.SIGINT, sigint_handler) - except ValueError: - # `signal.signal` may throw if `threading.main_thread` does - # not support signals (e.g. embedded interpreter with signals - # not registered - see gh-91880) - sigint_handler = None - else: - sigint_handler = None - - self._interrupt_count = 0 - try: - return self._loop.run_until_complete(task) - except exceptions.CancelledError: - if self._interrupt_count > 0: - uncancel = getattr(task, "uncancel", None) - if uncancel is not None and uncancel() == 0: - raise KeyboardInterrupt() - raise # CancelledError - finally: - if ( - sigint_handler is not None - and signal.getsignal(signal.SIGINT) is sigint_handler - ): - signal.signal(signal.SIGINT, signal.default_int_handler) - - def _lazy_init(self) -> None: - if self._state is _State.CLOSED: - raise RuntimeError("Runner is closed") - if self._state is _State.INITIALIZED: - return - if self._loop_factory is None: - self._loop = events.new_event_loop() - if not self._set_event_loop: - # Call set_event_loop only once to avoid calling - # attach_loop multiple times on child watchers - events.set_event_loop(self._loop) - self._set_event_loop = True - else: - self._loop = self._loop_factory() - if self._debug is not None: - self._loop.set_debug(self._debug) - self._context = contextvars.copy_context() - self._state = _State.INITIALIZED - - def _on_sigint(self, signum, frame, main_task: asyncio.Task) -> None: - self._interrupt_count += 1 - if self._interrupt_count == 1 and not main_task.done(): - main_task.cancel() - # wakeup loop if it is blocked by select() with long timeout - self._loop.call_soon_threadsafe(lambda: None) - return - raise KeyboardInterrupt() - - def _cancel_all_tasks(loop: AbstractEventLoop) -> None: - to_cancel = tasks.all_tasks(loop) - if not to_cancel: - return - - for task in to_cancel: - task.cancel() - - loop.run_until_complete(tasks.gather(*to_cancel, return_exceptions=True)) - - for task in to_cancel: - if task.cancelled(): - continue - if task.exception() is not None: - loop.call_exception_handler( - { - "message": "unhandled exception during asyncio.run() shutdown", - "exception": task.exception(), - "task": task, - } - ) - - async def _shutdown_default_executor(loop: AbstractEventLoop) -> None: - """Schedule the shutdown of the default executor.""" - - def _do_shutdown(future: asyncio.futures.Future) -> None: - try: - loop._default_executor.shutdown(wait=True) # type: ignore[attr-defined] - loop.call_soon_threadsafe(future.set_result, None) - except Exception as ex: - loop.call_soon_threadsafe(future.set_exception, ex) - - loop._executor_shutdown_called = True - if loop._default_executor is None: - return - future = loop.create_future() - thread = threading.Thread(target=_do_shutdown, args=(future,)) - thread.start() - try: - await future - finally: - thread.join() - - -T_Retval = TypeVar("T_Retval") -T_contra = TypeVar("T_contra", contravariant=True) -PosArgsT = TypeVarTuple("PosArgsT") -P = ParamSpec("P") - -_root_task: RunVar[asyncio.Task | None] = RunVar("_root_task") - - -def find_root_task() -> asyncio.Task: - root_task = _root_task.get(None) - if root_task is not None and not root_task.done(): - return root_task - - # Look for a task that has been started via run_until_complete() - for task in all_tasks(): - if task._callbacks and not task.done(): - callbacks = [cb for cb, context in task._callbacks] - for cb in callbacks: - if ( - cb is _run_until_complete_cb - or getattr(cb, "__module__", None) == "uvloop.loop" - ): - _root_task.set(task) - return task - - # Look up the topmost task in the AnyIO task tree, if possible - task = cast(asyncio.Task, current_task()) - state = _task_states.get(task) - if state: - cancel_scope = state.cancel_scope - while cancel_scope and cancel_scope._parent_scope is not None: - cancel_scope = cancel_scope._parent_scope - - if cancel_scope is not None: - return cast(asyncio.Task, cancel_scope._host_task) - - return task - - -def get_callable_name(func: Callable) -> str: - module = getattr(func, "__module__", None) - qualname = getattr(func, "__qualname__", None) - return ".".join([x for x in (module, qualname) if x]) - - -# -# Event loop -# - -_run_vars: WeakKeyDictionary[asyncio.AbstractEventLoop, Any] = WeakKeyDictionary() - - -def _task_started(task: asyncio.Task) -> bool: - """Return ``True`` if the task has been started and has not finished.""" - # The task coro should never be None here, as we never add finished tasks to the - # task list - coro = task.get_coro() - assert coro is not None - try: - return getcoroutinestate(coro) in (CORO_RUNNING, CORO_SUSPENDED) - except AttributeError: - # task coro is async_genenerator_asend https://bugs.python.org/issue37771 - raise Exception(f"Cannot determine if task {task} has started or not") from None - - -# -# Timeouts and cancellation -# - - -def is_anyio_cancellation(exc: CancelledError) -> bool: - # Sometimes third party frameworks catch a CancelledError and raise a new one, so as - # a workaround we have to look at the previous ones in __context__ too for a - # matching cancel message - while True: - if ( - exc.args - and isinstance(exc.args[0], str) - and exc.args[0].startswith("Cancelled by cancel scope ") - ): - return True - - if isinstance(exc.__context__, CancelledError): - exc = exc.__context__ - continue - - return False - - -class CancelScope(BaseCancelScope): - def __new__( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return object.__new__(cls) - - def __init__(self, deadline: float = math.inf, shield: bool = False): - self._deadline = deadline - self._shield = shield - self._parent_scope: CancelScope | None = None - self._child_scopes: set[CancelScope] = set() - self._cancel_called = False - self._cancelled_caught = False - self._active = False - self._timeout_handle: asyncio.TimerHandle | None = None - self._cancel_handle: asyncio.Handle | None = None - self._tasks: set[asyncio.Task] = set() - self._host_task: asyncio.Task | None = None - if sys.version_info >= (3, 11): - self._pending_uncancellations: int | None = 0 - else: - self._pending_uncancellations = None - - def __enter__(self) -> CancelScope: - if self._active: - raise RuntimeError( - "Each CancelScope may only be used for a single 'with' block" - ) - - self._host_task = host_task = cast(asyncio.Task, current_task()) - self._tasks.add(host_task) - try: - task_state = _task_states[host_task] - except KeyError: - task_state = TaskState(None, self) - _task_states[host_task] = task_state - else: - self._parent_scope = task_state.cancel_scope - task_state.cancel_scope = self - if self._parent_scope is not None: - # If using an eager task factory, the parent scope may not even contain - # the host task - self._parent_scope._child_scopes.add(self) - self._parent_scope._tasks.discard(host_task) - - self._timeout() - self._active = True - - # Start cancelling the host task if the scope was cancelled before entering - if self._cancel_called: - self._deliver_cancellation(self) - - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - del exc_tb - - if not self._active: - raise RuntimeError("This cancel scope is not active") - if current_task() is not self._host_task: - raise RuntimeError( - "Attempted to exit cancel scope in a different task than it was " - "entered in" - ) - - assert self._host_task is not None - host_task_state = _task_states.get(self._host_task) - if host_task_state is None or host_task_state.cancel_scope is not self: - raise RuntimeError( - "Attempted to exit a cancel scope that isn't the current tasks's " - "current cancel scope" - ) - - try: - self._active = False - if self._timeout_handle: - self._timeout_handle.cancel() - self._timeout_handle = None - - self._tasks.remove(self._host_task) - if self._parent_scope is not None: - self._parent_scope._child_scopes.remove(self) - self._parent_scope._tasks.add(self._host_task) - - host_task_state.cancel_scope = self._parent_scope - - # Restart the cancellation effort in the closest visible, cancelled parent - # scope if necessary - self._restart_cancellation_in_parent() - - # We only swallow the exception iff it was an AnyIO CancelledError, either - # directly as exc_val or inside an exception group and there are no cancelled - # parent cancel scopes visible to us here - if self._cancel_called and not self._parent_cancellation_is_visible_to_us: - # For each level-cancel() call made on the host task, call uncancel() - while self._pending_uncancellations: - self._host_task.uncancel() - self._pending_uncancellations -= 1 - - # Update cancelled_caught and check for exceptions we must not swallow - cannot_swallow_exc_val = False - if exc_val is not None: - for exc in iterate_exceptions(exc_val): - if isinstance(exc, CancelledError) and is_anyio_cancellation( - exc - ): - self._cancelled_caught = True - else: - cannot_swallow_exc_val = True - - return self._cancelled_caught and not cannot_swallow_exc_val - else: - if self._pending_uncancellations: - assert self._parent_scope is not None - assert self._parent_scope._pending_uncancellations is not None - self._parent_scope._pending_uncancellations += ( - self._pending_uncancellations - ) - self._pending_uncancellations = 0 - - return False - finally: - self._host_task = None - del exc_val - - @property - def _effectively_cancelled(self) -> bool: - cancel_scope: CancelScope | None = self - while cancel_scope is not None: - if cancel_scope._cancel_called: - return True - - if cancel_scope.shield: - return False - - cancel_scope = cancel_scope._parent_scope - - return False - - @property - def _parent_cancellation_is_visible_to_us(self) -> bool: - return ( - self._parent_scope is not None - and not self.shield - and self._parent_scope._effectively_cancelled - ) - - def _timeout(self) -> None: - if self._deadline != math.inf: - loop = get_running_loop() - if loop.time() >= self._deadline: - self.cancel() - else: - self._timeout_handle = loop.call_at(self._deadline, self._timeout) - - def _deliver_cancellation(self, origin: CancelScope) -> bool: - """ - Deliver cancellation to directly contained tasks and nested cancel scopes. - - Schedule another run at the end if we still have tasks eligible for - cancellation. - - :param origin: the cancel scope that originated the cancellation - :return: ``True`` if the delivery needs to be retried on the next cycle - - """ - should_retry = False - current = current_task() - for task in self._tasks: - should_retry = True - if task._must_cancel: # type: ignore[attr-defined] - continue - - # The task is eligible for cancellation if it has started - if task is not current and (task is self._host_task or _task_started(task)): - waiter = task._fut_waiter # type: ignore[attr-defined] - if not isinstance(waiter, asyncio.Future) or not waiter.done(): - task.cancel(f"Cancelled by cancel scope {id(origin):x}") - if ( - task is origin._host_task - and origin._pending_uncancellations is not None - ): - origin._pending_uncancellations += 1 - - # Deliver cancellation to child scopes that aren't shielded or running their own - # cancellation callbacks - for scope in self._child_scopes: - if not scope._shield and not scope.cancel_called: - should_retry = scope._deliver_cancellation(origin) or should_retry - - # Schedule another callback if there are still tasks left - if origin is self: - if should_retry: - self._cancel_handle = get_running_loop().call_soon( - self._deliver_cancellation, origin - ) - else: - self._cancel_handle = None - - return should_retry - - def _restart_cancellation_in_parent(self) -> None: - """ - Restart the cancellation effort in the closest directly cancelled parent scope. - - """ - scope = self._parent_scope - while scope is not None: - if scope._cancel_called: - if scope._cancel_handle is None: - scope._deliver_cancellation(scope) - - break - - # No point in looking beyond any shielded scope - if scope._shield: - break - - scope = scope._parent_scope - - def cancel(self) -> None: - if not self._cancel_called: - if self._timeout_handle: - self._timeout_handle.cancel() - self._timeout_handle = None - - self._cancel_called = True - if self._host_task is not None: - self._deliver_cancellation(self) - - @property - def deadline(self) -> float: - return self._deadline - - @deadline.setter - def deadline(self, value: float) -> None: - self._deadline = float(value) - if self._timeout_handle is not None: - self._timeout_handle.cancel() - self._timeout_handle = None - - if self._active and not self._cancel_called: - self._timeout() - - @property - def cancel_called(self) -> bool: - return self._cancel_called - - @property - def cancelled_caught(self) -> bool: - return self._cancelled_caught - - @property - def shield(self) -> bool: - return self._shield - - @shield.setter - def shield(self, value: bool) -> None: - if self._shield != value: - self._shield = value - if not value: - self._restart_cancellation_in_parent() - - -# -# Task states -# - - -class TaskState: - """ - Encapsulates auxiliary task information that cannot be added to the Task instance - itself because there are no guarantees about its implementation. - """ - - __slots__ = "parent_id", "cancel_scope", "__weakref__" - - def __init__(self, parent_id: int | None, cancel_scope: CancelScope | None): - self.parent_id = parent_id - self.cancel_scope = cancel_scope - - -class TaskStateStore(MutableMapping["Awaitable[Any] | asyncio.Task", TaskState]): - def __init__(self) -> None: - self._task_states = WeakKeyDictionary[asyncio.Task, TaskState]() - self._preliminary_task_states: dict[Awaitable[Any], TaskState] = {} - - def __getitem__(self, key: Awaitable[Any] | asyncio.Task, /) -> TaskState: - assert isinstance(key, asyncio.Task) - try: - return self._task_states[key] - except KeyError: - if coro := key.get_coro(): - if state := self._preliminary_task_states.get(coro): - return state - - raise KeyError(key) - - def __setitem__( - self, key: asyncio.Task | Awaitable[Any], value: TaskState, / - ) -> None: - if isinstance(key, asyncio.Task): - self._task_states[key] = value - else: - self._preliminary_task_states[key] = value - - def __delitem__(self, key: asyncio.Task | Awaitable[Any], /) -> None: - if isinstance(key, asyncio.Task): - del self._task_states[key] - else: - del self._preliminary_task_states[key] - - def __len__(self) -> int: - return len(self._task_states) + len(self._preliminary_task_states) - - def __iter__(self) -> Iterator[Awaitable[Any] | asyncio.Task]: - yield from self._task_states - yield from self._preliminary_task_states - - -_task_states = TaskStateStore() - - -# -# Task groups -# - - -class _AsyncioTaskStatus(abc.TaskStatus): - def __init__(self, future: asyncio.Future, parent_id: int): - self._future = future - self._parent_id = parent_id - - def started(self, value: T_contra | None = None) -> None: - try: - self._future.set_result(value) - except asyncio.InvalidStateError: - if not self._future.cancelled(): - raise RuntimeError( - "called 'started' twice on the same task status" - ) from None - - task = cast(asyncio.Task, current_task()) - _task_states[task].parent_id = self._parent_id - - -async def _wait(tasks: Iterable[asyncio.Task[object]]) -> None: - tasks = set(tasks) - waiter = get_running_loop().create_future() - - def on_completion(task: asyncio.Task[object]) -> None: - tasks.discard(task) - if not tasks and not waiter.done(): - waiter.set_result(None) - - for task in tasks: - task.add_done_callback(on_completion) - del task - - try: - await waiter - finally: - while tasks: - tasks.pop().remove_done_callback(on_completion) - - -class TaskGroup(abc.TaskGroup): - def __init__(self) -> None: - self.cancel_scope: CancelScope = CancelScope() - self._active = False - self._exceptions: list[BaseException] = [] - self._tasks: set[asyncio.Task] = set() - - async def __aenter__(self) -> TaskGroup: - self.cancel_scope.__enter__() - self._active = True - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - try: - if exc_val is not None: - self.cancel_scope.cancel() - if not isinstance(exc_val, CancelledError): - self._exceptions.append(exc_val) - - try: - if self._tasks: - with CancelScope() as wait_scope: - while self._tasks: - try: - await _wait(self._tasks) - except CancelledError as exc: - # Shield the scope against further cancellation attempts, - # as they're not productive (#695) - wait_scope.shield = True - self.cancel_scope.cancel() - - # Set exc_val from the cancellation exception if it was - # previously unset. However, we should not replace a native - # cancellation exception with one raise by a cancel scope. - if exc_val is None or ( - isinstance(exc_val, CancelledError) - and not is_anyio_cancellation(exc) - ): - exc_val = exc - else: - # If there are no child tasks to wait on, run at least one checkpoint - # anyway - await AsyncIOBackend.cancel_shielded_checkpoint() - - self._active = False - if self._exceptions: - raise BaseExceptionGroup( - "unhandled errors in a TaskGroup", self._exceptions - ) - elif exc_val: - raise exc_val - except BaseException as exc: - if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__): - return True - - raise - - return self.cancel_scope.__exit__(exc_type, exc_val, exc_tb) - finally: - del exc_val, exc_tb, self._exceptions - - def _spawn( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], - args: tuple[Unpack[PosArgsT]], - name: object, - task_status_future: asyncio.Future | None = None, - ) -> asyncio.Task: - def task_done(_task: asyncio.Task) -> None: - # task_state = _task_states[_task] - assert task_state.cancel_scope is not None - assert _task in task_state.cancel_scope._tasks - task_state.cancel_scope._tasks.remove(_task) - self._tasks.remove(task) - del _task_states[_task] - - try: - exc = _task.exception() - except CancelledError as e: - while isinstance(e.__context__, CancelledError): - e = e.__context__ - - exc = e - - if exc is not None: - # The future can only be in the cancelled state if the host task was - # cancelled, so return immediately instead of adding one more - # CancelledError to the exceptions list - if task_status_future is not None and task_status_future.cancelled(): - return - - if task_status_future is None or task_status_future.done(): - if not isinstance(exc, CancelledError): - self._exceptions.append(exc) - - if not self.cancel_scope._effectively_cancelled: - self.cancel_scope.cancel() - else: - task_status_future.set_exception(exc) - elif task_status_future is not None and not task_status_future.done(): - task_status_future.set_exception( - RuntimeError("Child exited without calling task_status.started()") - ) - - if not self._active: - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - kwargs = {} - if task_status_future: - parent_id = id(current_task()) - kwargs["task_status"] = _AsyncioTaskStatus( - task_status_future, id(self.cancel_scope._host_task) - ) - else: - parent_id = id(self.cancel_scope._host_task) - - coro = func(*args, **kwargs) - if not iscoroutine(coro): - prefix = f"{func.__module__}." if hasattr(func, "__module__") else "" - raise TypeError( - f"Expected {prefix}{func.__qualname__}() to return a coroutine, but " - f"the return value ({coro!r}) is not a coroutine object" - ) - - # Make the spawned task inherit the task group's cancel scope - _task_states[coro] = task_state = TaskState( - parent_id=parent_id, cancel_scope=self.cancel_scope - ) - name = get_callable_name(func) if name is None else str(name) - try: - task = create_task(coro, name=name) - finally: - del _task_states[coro] - - _task_states[task] = task_state - self.cancel_scope._tasks.add(task) - self._tasks.add(task) - - if task.done(): - # This can happen with eager task factories - task_done(task) - else: - task.add_done_callback(task_done) - - return task - - def start_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> None: - self._spawn(func, args, name) - - async def start( - self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None - ) -> Any: - future: asyncio.Future = asyncio.Future() - task = self._spawn(func, args, name, future) - - # If the task raises an exception after sending a start value without a switch - # point between, the task group is cancelled and this method never proceeds to - # process the completed future. That's why we have to have a shielded cancel - # scope here. - try: - return await future - except CancelledError: - # Cancel the task and wait for it to exit before returning - task.cancel() - with CancelScope(shield=True), suppress(CancelledError): - await task - - raise - - -# -# Threads -# - -_Retval_Queue_Type = tuple[Optional[T_Retval], Optional[BaseException]] - - -class WorkerThread(Thread): - MAX_IDLE_TIME = 10 # seconds - - def __init__( - self, - root_task: asyncio.Task, - workers: set[WorkerThread], - idle_workers: deque[WorkerThread], - ): - super().__init__(name="AnyIO worker thread") - self.root_task = root_task - self.workers = workers - self.idle_workers = idle_workers - self.loop = root_task._loop - self.queue: Queue[ - tuple[Context, Callable, tuple, asyncio.Future, CancelScope] | None - ] = Queue(2) - self.idle_since = AsyncIOBackend.current_time() - self.stopping = False - - def _report_result( - self, future: asyncio.Future, result: Any, exc: BaseException | None - ) -> None: - self.idle_since = AsyncIOBackend.current_time() - if not self.stopping: - self.idle_workers.append(self) - - if not future.cancelled(): - if exc is not None: - if isinstance(exc, StopIteration): - new_exc = RuntimeError("coroutine raised StopIteration") - new_exc.__cause__ = exc - exc = new_exc - - future.set_exception(exc) - else: - future.set_result(result) - - def run(self) -> None: - with claim_worker_thread(AsyncIOBackend, self.loop): - while True: - item = self.queue.get() - if item is None: - # Shutdown command received - return - - context, func, args, future, cancel_scope = item - if not future.cancelled(): - result = None - exception: BaseException | None = None - threadlocals.current_cancel_scope = cancel_scope - try: - result = context.run(func, *args) - except BaseException as exc: - exception = exc - finally: - del threadlocals.current_cancel_scope - - if not self.loop.is_closed(): - self.loop.call_soon_threadsafe( - self._report_result, future, result, exception - ) - - self.queue.task_done() - - def stop(self, f: asyncio.Task | None = None) -> None: - self.stopping = True - self.queue.put_nowait(None) - self.workers.discard(self) - try: - self.idle_workers.remove(self) - except ValueError: - pass - - -_threadpool_idle_workers: RunVar[deque[WorkerThread]] = RunVar( - "_threadpool_idle_workers" -) -_threadpool_workers: RunVar[set[WorkerThread]] = RunVar("_threadpool_workers") - - -class BlockingPortal(abc.BlockingPortal): - def __new__(cls) -> BlockingPortal: - return object.__new__(cls) - - def __init__(self) -> None: - super().__init__() - self._loop = get_running_loop() - - def _spawn_task_from_thread( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - name: object, - future: Future[T_Retval], - ) -> None: - AsyncIOBackend.run_sync_from_thread( - partial(self._task_group.start_soon, name=name), - (self._call_func, func, args, kwargs, future), - self._loop, - ) - - -# -# Subprocesses -# - - -@dataclass(eq=False) -class StreamReaderWrapper(abc.ByteReceiveStream): - _stream: asyncio.StreamReader - - async def receive(self, max_bytes: int = 65536) -> bytes: - data = await self._stream.read(max_bytes) - if data: - return data - else: - raise EndOfStream - - async def aclose(self) -> None: - self._stream.set_exception(ClosedResourceError()) - await AsyncIOBackend.checkpoint() - - -@dataclass(eq=False) -class StreamWriterWrapper(abc.ByteSendStream): - _stream: asyncio.StreamWriter - - async def send(self, item: bytes) -> None: - self._stream.write(item) - await self._stream.drain() - - async def aclose(self) -> None: - self._stream.close() - await AsyncIOBackend.checkpoint() - - -@dataclass(eq=False) -class Process(abc.Process): - _process: asyncio.subprocess.Process - _stdin: StreamWriterWrapper | None - _stdout: StreamReaderWrapper | None - _stderr: StreamReaderWrapper | None - - async def aclose(self) -> None: - with CancelScope(shield=True) as scope: - if self._stdin: - await self._stdin.aclose() - if self._stdout: - await self._stdout.aclose() - if self._stderr: - await self._stderr.aclose() - - scope.shield = False - try: - await self.wait() - except BaseException: - scope.shield = True - self.kill() - await self.wait() - raise - - async def wait(self) -> int: - return await self._process.wait() - - def terminate(self) -> None: - self._process.terminate() - - def kill(self) -> None: - self._process.kill() - - def send_signal(self, signal: int) -> None: - self._process.send_signal(signal) - - @property - def pid(self) -> int: - return self._process.pid - - @property - def returncode(self) -> int | None: - return self._process.returncode - - @property - def stdin(self) -> abc.ByteSendStream | None: - return self._stdin - - @property - def stdout(self) -> abc.ByteReceiveStream | None: - return self._stdout - - @property - def stderr(self) -> abc.ByteReceiveStream | None: - return self._stderr - - -def _forcibly_shutdown_process_pool_on_exit( - workers: set[Process], _task: object -) -> None: - """ - Forcibly shuts down worker processes belonging to this event loop.""" - child_watcher: asyncio.AbstractChildWatcher | None = None - if sys.version_info < (3, 12): - try: - child_watcher = asyncio.get_event_loop_policy().get_child_watcher() - except NotImplementedError: - pass - - # Close as much as possible (w/o async/await) to avoid warnings - for process in workers: - if process.returncode is None: - continue - - process._stdin._stream._transport.close() # type: ignore[union-attr] - process._stdout._stream._transport.close() # type: ignore[union-attr] - process._stderr._stream._transport.close() # type: ignore[union-attr] - process.kill() - if child_watcher: - child_watcher.remove_child_handler(process.pid) - - -async def _shutdown_process_pool_on_exit(workers: set[abc.Process]) -> None: - """ - Shuts down worker processes belonging to this event loop. - - NOTE: this only works when the event loop was started using asyncio.run() or - anyio.run(). - - """ - process: abc.Process - try: - await sleep(math.inf) - except asyncio.CancelledError: - for process in workers: - if process.returncode is None: - process.kill() - - for process in workers: - await process.aclose() - - -# -# Sockets and networking -# - - -class StreamProtocol(asyncio.Protocol): - read_queue: deque[bytes] - read_event: asyncio.Event - write_event: asyncio.Event - exception: Exception | None = None - is_at_eof: bool = False - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - self.read_queue = deque() - self.read_event = asyncio.Event() - self.write_event = asyncio.Event() - self.write_event.set() - cast(asyncio.Transport, transport).set_write_buffer_limits(0) - - def connection_lost(self, exc: Exception | None) -> None: - if exc: - self.exception = BrokenResourceError() - self.exception.__cause__ = exc - - self.read_event.set() - self.write_event.set() - - def data_received(self, data: bytes) -> None: - # ProactorEventloop sometimes sends bytearray instead of bytes - self.read_queue.append(bytes(data)) - self.read_event.set() - - def eof_received(self) -> bool | None: - self.is_at_eof = True - self.read_event.set() - return True - - def pause_writing(self) -> None: - self.write_event = asyncio.Event() - - def resume_writing(self) -> None: - self.write_event.set() - - -class DatagramProtocol(asyncio.DatagramProtocol): - read_queue: deque[tuple[bytes, IPSockAddrType]] - read_event: asyncio.Event - write_event: asyncio.Event - exception: Exception | None = None - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - self.read_queue = deque(maxlen=100) # arbitrary value - self.read_event = asyncio.Event() - self.write_event = asyncio.Event() - self.write_event.set() - - def connection_lost(self, exc: Exception | None) -> None: - self.read_event.set() - self.write_event.set() - - def datagram_received(self, data: bytes, addr: IPSockAddrType) -> None: - addr = convert_ipv6_sockaddr(addr) - self.read_queue.append((data, addr)) - self.read_event.set() - - def error_received(self, exc: Exception) -> None: - self.exception = exc - - def pause_writing(self) -> None: - self.write_event.clear() - - def resume_writing(self) -> None: - self.write_event.set() - - -class SocketStream(abc.SocketStream): - def __init__(self, transport: asyncio.Transport, protocol: StreamProtocol): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def receive(self, max_bytes: int = 65536) -> bytes: - with self._receive_guard: - if ( - not self._protocol.read_event.is_set() - and not self._transport.is_closing() - and not self._protocol.is_at_eof - ): - self._transport.resume_reading() - await self._protocol.read_event.wait() - self._transport.pause_reading() - else: - await AsyncIOBackend.checkpoint() - - try: - chunk = self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - elif self._protocol.exception: - raise self._protocol.exception from None - else: - raise EndOfStream from None - - if len(chunk) > max_bytes: - # Split the oversized chunk - chunk, leftover = chunk[:max_bytes], chunk[max_bytes:] - self._protocol.read_queue.appendleft(leftover) - - # If the read queue is empty, clear the flag so that the next call will - # block until data is available - if not self._protocol.read_queue: - self._protocol.read_event.clear() - - return chunk - - async def send(self, item: bytes) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - - if self._closed: - raise ClosedResourceError - elif self._protocol.exception is not None: - raise self._protocol.exception - - try: - self._transport.write(item) - except RuntimeError as exc: - if self._transport.is_closing(): - raise BrokenResourceError from exc - else: - raise - - await self._protocol.write_event.wait() - - async def send_eof(self) -> None: - try: - self._transport.write_eof() - except OSError: - pass - - async def aclose(self) -> None: - if not self._transport.is_closing(): - self._closed = True - try: - self._transport.write_eof() - except OSError: - pass - - self._transport.close() - await sleep(0) - self._transport.abort() - - -class _RawSocketMixin: - _receive_future: asyncio.Future | None = None - _send_future: asyncio.Future | None = None - _closing = False - - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - def _wait_until_readable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: - def callback(f: object) -> None: - del self._receive_future - loop.remove_reader(self.__raw_socket) - - f = self._receive_future = asyncio.Future() - loop.add_reader(self.__raw_socket, f.set_result, None) - f.add_done_callback(callback) - return f - - def _wait_until_writable(self, loop: asyncio.AbstractEventLoop) -> asyncio.Future: - def callback(f: object) -> None: - del self._send_future - loop.remove_writer(self.__raw_socket) - - f = self._send_future = asyncio.Future() - loop.add_writer(self.__raw_socket, f.set_result, None) - f.add_done_callback(callback) - return f - - async def aclose(self) -> None: - if not self._closing: - self._closing = True - if self.__raw_socket.fileno() != -1: - self.__raw_socket.close() - - if self._receive_future: - self._receive_future.set_result(None) - if self._send_future: - self._send_future.set_result(None) - - -class UNIXSocketStream(_RawSocketMixin, abc.UNIXSocketStream): - async def send_eof(self) -> None: - with self._send_guard: - self._raw_socket.shutdown(socket.SHUT_WR) - - async def receive(self, max_bytes: int = 65536) -> bytes: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recv(max_bytes) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - if not data: - raise EndOfStream - - return data - - async def send(self, item: bytes) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - view = memoryview(item) - while view: - try: - bytes_sent = self._raw_socket.send(view) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - view = view[bytes_sent:] - - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - if not isinstance(msglen, int) or msglen < 0: - raise ValueError("msglen must be a non-negative integer") - if not isinstance(maxfds, int) or maxfds < 1: - raise ValueError("maxfds must be a positive integer") - - loop = get_running_loop() - fds = array.array("i") - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - message, ancdata, flags, addr = self._raw_socket.recvmsg( - msglen, socket.CMSG_LEN(maxfds * fds.itemsize) - ) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - if not message and not ancdata: - raise EndOfStream - - break - - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: - raise RuntimeError( - f"Received unexpected ancillary data; message = {message!r}, " - f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" - ) - - fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - - return message, list(fds) - - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - if not message: - raise ValueError("message must not be empty") - if not fds: - raise ValueError("fds must not be empty") - - loop = get_running_loop() - filenos: list[int] = [] - for fd in fds: - if isinstance(fd, int): - filenos.append(fd) - elif isinstance(fd, IOBase): - filenos.append(fd.fileno()) - - fdarray = array.array("i", filenos) - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - # The ignore can be removed after mypy picks up - # https://github.com/python/typeshed/pull/5545 - self._raw_socket.sendmsg( - [message], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fdarray)] - ) - break - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - - -class TCPSocketListener(abc.SocketListener): - _accept_scope: CancelScope | None = None - _closed = False - - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._loop = cast(asyncio.BaseEventLoop, get_running_loop()) - self._accept_guard = ResourceGuard("accepting connections from") - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - async def accept(self) -> abc.SocketStream: - if self._closed: - raise ClosedResourceError - - with self._accept_guard: - await AsyncIOBackend.checkpoint() - with CancelScope() as self._accept_scope: - try: - client_sock, _addr = await self._loop.sock_accept(self._raw_socket) - except asyncio.CancelledError: - # Workaround for https://bugs.python.org/issue41317 - try: - self._loop.remove_reader(self._raw_socket) - except (ValueError, NotImplementedError): - pass - - if self._closed: - raise ClosedResourceError from None - - raise - finally: - self._accept_scope = None - - client_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - transport, protocol = await self._loop.connect_accepted_socket( - StreamProtocol, client_sock - ) - return SocketStream(transport, protocol) - - async def aclose(self) -> None: - if self._closed: - return - - self._closed = True - if self._accept_scope: - # Workaround for https://bugs.python.org/issue41317 - try: - self._loop.remove_reader(self._raw_socket) - except (ValueError, NotImplementedError): - pass - - self._accept_scope.cancel() - await sleep(0) - - self._raw_socket.close() - - -class UNIXSocketListener(abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - self.__raw_socket = raw_socket - self._loop = get_running_loop() - self._accept_guard = ResourceGuard("accepting connections from") - self._closed = False - - async def accept(self) -> abc.SocketStream: - await AsyncIOBackend.checkpoint() - with self._accept_guard: - while True: - try: - client_sock, _ = self.__raw_socket.accept() - client_sock.setblocking(False) - return UNIXSocketStream(client_sock) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - self._loop.add_reader(self.__raw_socket, f.set_result, None) - f.add_done_callback( - lambda _: self._loop.remove_reader(self.__raw_socket) - ) - await f - except OSError as exc: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - - async def aclose(self) -> None: - self._closed = True - self.__raw_socket.close() - - @property - def _raw_socket(self) -> socket.socket: - return self.__raw_socket - - -class UDPSocket(abc.UDPSocket): - def __init__( - self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol - ): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def aclose(self) -> None: - if not self._transport.is_closing(): - self._closed = True - self._transport.close() - - async def receive(self) -> tuple[bytes, IPSockAddrType]: - with self._receive_guard: - await AsyncIOBackend.checkpoint() - - # If the buffer is empty, ask for more data - if not self._protocol.read_queue and not self._transport.is_closing(): - self._protocol.read_event.clear() - await self._protocol.read_event.wait() - - try: - return self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from None - - async def send(self, item: UDPPacketType) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - await self._protocol.write_event.wait() - if self._closed: - raise ClosedResourceError - elif self._transport.is_closing(): - raise BrokenResourceError - else: - self._transport.sendto(*item) - - -class ConnectedUDPSocket(abc.ConnectedUDPSocket): - def __init__( - self, transport: asyncio.DatagramTransport, protocol: DatagramProtocol - ): - self._transport = transport - self._protocol = protocol - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - self._closed = False - - @property - def _raw_socket(self) -> socket.socket: - return self._transport.get_extra_info("socket") - - async def aclose(self) -> None: - if not self._transport.is_closing(): - self._closed = True - self._transport.close() - - async def receive(self) -> bytes: - with self._receive_guard: - await AsyncIOBackend.checkpoint() - - # If the buffer is empty, ask for more data - if not self._protocol.read_queue and not self._transport.is_closing(): - self._protocol.read_event.clear() - await self._protocol.read_event.wait() - - try: - packet = self._protocol.read_queue.popleft() - except IndexError: - if self._closed: - raise ClosedResourceError from None - else: - raise BrokenResourceError from None - - return packet[0] - - async def send(self, item: bytes) -> None: - with self._send_guard: - await AsyncIOBackend.checkpoint() - await self._protocol.write_event.wait() - if self._closed: - raise ClosedResourceError - elif self._transport.is_closing(): - raise BrokenResourceError - else: - self._transport.sendto(item) - - -class UNIXDatagramSocket(_RawSocketMixin, abc.UNIXDatagramSocket): - async def receive(self) -> UNIXDatagramPacketType: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recvfrom(65536) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return data - - async def send(self, item: UNIXDatagramPacketType) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - self._raw_socket.sendto(*item) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return - - -class ConnectedUNIXDatagramSocket(_RawSocketMixin, abc.ConnectedUNIXDatagramSocket): - async def receive(self) -> bytes: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._receive_guard: - while True: - try: - data = self._raw_socket.recv(65536) - except BlockingIOError: - await self._wait_until_readable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return data - - async def send(self, item: bytes) -> None: - loop = get_running_loop() - await AsyncIOBackend.checkpoint() - with self._send_guard: - while True: - try: - self._raw_socket.send(item) - except BlockingIOError: - await self._wait_until_writable(loop) - except OSError as exc: - if self._closing: - raise ClosedResourceError from None - else: - raise BrokenResourceError from exc - else: - return - - -_read_events: RunVar[dict[int, asyncio.Event]] = RunVar("read_events") -_write_events: RunVar[dict[int, asyncio.Event]] = RunVar("write_events") - - -# -# Synchronization -# - - -class Event(BaseEvent): - def __new__(cls) -> Event: - return object.__new__(cls) - - def __init__(self) -> None: - self._event = asyncio.Event() - - def set(self) -> None: - self._event.set() - - def is_set(self) -> bool: - return self._event.is_set() - - async def wait(self) -> None: - if self.is_set(): - await AsyncIOBackend.checkpoint() - else: - await self._event.wait() - - def statistics(self) -> EventStatistics: - return EventStatistics(len(self._event._waiters)) - - -class Lock(BaseLock): - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False) -> None: - self._fast_acquire = fast_acquire - self._owner_task: asyncio.Task | None = None - self._waiters: deque[tuple[asyncio.Task, asyncio.Future]] = deque() - - async def acquire(self) -> None: - task = cast(asyncio.Task, current_task()) - if self._owner_task is None and not self._waiters: - await AsyncIOBackend.checkpoint_if_cancelled() - self._owner_task = task - - # Unless on the "fast path", yield control of the event loop so that other - # tasks can run too - if not self._fast_acquire: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except CancelledError: - self.release() - raise - - return - - if self._owner_task == task: - raise RuntimeError("Attempted to acquire an already held Lock") - - fut: asyncio.Future[None] = asyncio.Future() - item = task, fut - self._waiters.append(item) - try: - await fut - except CancelledError: - self._waiters.remove(item) - if self._owner_task is task: - self.release() - - raise - - self._waiters.remove(item) - - def acquire_nowait(self) -> None: - task = cast(asyncio.Task, current_task()) - if self._owner_task is None and not self._waiters: - self._owner_task = task - return - - if self._owner_task is task: - raise RuntimeError("Attempted to acquire an already held Lock") - - raise WouldBlock - - def locked(self) -> bool: - return self._owner_task is not None - - def release(self) -> None: - if self._owner_task != current_task(): - raise RuntimeError("The current task is not holding this lock") - - for task, fut in self._waiters: - if not fut.cancelled(): - self._owner_task = task - fut.set_result(None) - return - - self._owner_task = None - - def statistics(self) -> LockStatistics: - task_info = AsyncIOTaskInfo(self._owner_task) if self._owner_task else None - return LockStatistics(self.locked(), task_info, len(self._waiters)) - - -class Semaphore(BaseSemaphore): - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ): - super().__init__(initial_value, max_value=max_value) - self._value = initial_value - self._max_value = max_value - self._fast_acquire = fast_acquire - self._waiters: deque[asyncio.Future[None]] = deque() - - async def acquire(self) -> None: - if self._value > 0 and not self._waiters: - await AsyncIOBackend.checkpoint_if_cancelled() - self._value -= 1 - - # Unless on the "fast path", yield control of the event loop so that other - # tasks can run too - if not self._fast_acquire: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except CancelledError: - self.release() - raise - - return - - fut: asyncio.Future[None] = asyncio.Future() - self._waiters.append(fut) - try: - await fut - except CancelledError: - try: - self._waiters.remove(fut) - except ValueError: - self.release() - - raise - - def acquire_nowait(self) -> None: - if self._value == 0: - raise WouldBlock - - self._value -= 1 - - def release(self) -> None: - if self._max_value is not None and self._value == self._max_value: - raise ValueError("semaphore released too many times") - - for fut in self._waiters: - if not fut.cancelled(): - fut.set_result(None) - self._waiters.remove(fut) - return - - self._value += 1 - - @property - def value(self) -> int: - return self._value - - @property - def max_value(self) -> int | None: - return self._max_value - - def statistics(self) -> SemaphoreStatistics: - return SemaphoreStatistics(len(self._waiters)) - - -class CapacityLimiter(BaseCapacityLimiter): - _total_tokens: float = 0 - - def __new__(cls, total_tokens: float) -> CapacityLimiter: - return object.__new__(cls) - - def __init__(self, total_tokens: float): - self._borrowers: set[Any] = set() - self._wait_queue: OrderedDict[Any, asyncio.Event] = OrderedDict() - self.total_tokens = total_tokens - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - @property - def total_tokens(self) -> float: - return self._total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - if not isinstance(value, int) and not math.isinf(value): - raise TypeError("total_tokens must be an int or math.inf") - if value < 1: - raise ValueError("total_tokens must be >= 1") - - waiters_to_notify = max(value - self._total_tokens, 0) - self._total_tokens = value - - # Notify waiting tasks that they have acquired the limiter - while self._wait_queue and waiters_to_notify: - event = self._wait_queue.popitem(last=False)[1] - event.set() - waiters_to_notify -= 1 - - @property - def borrowed_tokens(self) -> int: - return len(self._borrowers) - - @property - def available_tokens(self) -> float: - return self._total_tokens - len(self._borrowers) - - def acquire_nowait(self) -> None: - self.acquire_on_behalf_of_nowait(current_task()) - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - if borrower in self._borrowers: - raise RuntimeError( - "this borrower is already holding one of this CapacityLimiter's " - "tokens" - ) - - if self._wait_queue or len(self._borrowers) >= self._total_tokens: - raise WouldBlock - - self._borrowers.add(borrower) - - async def acquire(self) -> None: - return await self.acquire_on_behalf_of(current_task()) - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await AsyncIOBackend.checkpoint_if_cancelled() - try: - self.acquire_on_behalf_of_nowait(borrower) - except WouldBlock: - event = asyncio.Event() - self._wait_queue[borrower] = event - try: - await event.wait() - except BaseException: - self._wait_queue.pop(borrower, None) - raise - - self._borrowers.add(borrower) - else: - try: - await AsyncIOBackend.cancel_shielded_checkpoint() - except BaseException: - self.release() - raise - - def release(self) -> None: - self.release_on_behalf_of(current_task()) - - def release_on_behalf_of(self, borrower: object) -> None: - try: - self._borrowers.remove(borrower) - except KeyError: - raise RuntimeError( - "this borrower isn't holding any of this CapacityLimiter's tokens" - ) from None - - # Notify the next task in line if this limiter has free capacity now - if self._wait_queue and len(self._borrowers) < self._total_tokens: - event = self._wait_queue.popitem(last=False)[1] - event.set() - - def statistics(self) -> CapacityLimiterStatistics: - return CapacityLimiterStatistics( - self.borrowed_tokens, - self.total_tokens, - tuple(self._borrowers), - len(self._wait_queue), - ) - - -_default_thread_limiter: RunVar[CapacityLimiter] = RunVar("_default_thread_limiter") - - -# -# Operating system signals -# - - -class _SignalReceiver: - def __init__(self, signals: tuple[Signals, ...]): - self._signals = signals - self._loop = get_running_loop() - self._signal_queue: deque[Signals] = deque() - self._future: asyncio.Future = asyncio.Future() - self._handled_signals: set[Signals] = set() - - def _deliver(self, signum: Signals) -> None: - self._signal_queue.append(signum) - if not self._future.done(): - self._future.set_result(None) - - def __enter__(self) -> _SignalReceiver: - for sig in set(self._signals): - self._loop.add_signal_handler(sig, self._deliver, sig) - self._handled_signals.add(sig) - - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - for sig in self._handled_signals: - self._loop.remove_signal_handler(sig) - return None - - def __aiter__(self) -> _SignalReceiver: - return self - - async def __anext__(self) -> Signals: - await AsyncIOBackend.checkpoint() - if not self._signal_queue: - self._future = asyncio.Future() - await self._future - - return self._signal_queue.popleft() - - -# -# Testing and debugging -# - - -class AsyncIOTaskInfo(TaskInfo): - def __init__(self, task: asyncio.Task): - task_state = _task_states.get(task) - if task_state is None: - parent_id = None - else: - parent_id = task_state.parent_id - - coro = task.get_coro() - assert coro is not None, "created TaskInfo from a completed Task" - super().__init__(id(task), parent_id, task.get_name(), coro) - self._task = weakref.ref(task) - - def has_pending_cancellation(self) -> bool: - if not (task := self._task()): - # If the task isn't around anymore, it won't have a pending cancellation - return False - - if task._must_cancel: # type: ignore[attr-defined] - return True - elif ( - isinstance(task._fut_waiter, asyncio.Future) # type: ignore[attr-defined] - and task._fut_waiter.cancelled() # type: ignore[attr-defined] - ): - return True - - if task_state := _task_states.get(task): - if cancel_scope := task_state.cancel_scope: - return cancel_scope._effectively_cancelled - - return False - - -class TestRunner(abc.TestRunner): - _send_stream: MemoryObjectSendStream[tuple[Awaitable[Any], asyncio.Future[Any]]] - - def __init__( - self, - *, - debug: bool | None = None, - use_uvloop: bool = False, - loop_factory: Callable[[], AbstractEventLoop] | None = None, - ) -> None: - if use_uvloop and loop_factory is None: - import uvloop - - loop_factory = uvloop.new_event_loop - - self._runner = Runner(debug=debug, loop_factory=loop_factory) - self._exceptions: list[BaseException] = [] - self._runner_task: asyncio.Task | None = None - - def __enter__(self) -> TestRunner: - self._runner.__enter__() - self.get_loop().set_exception_handler(self._exception_handler) - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self._runner.__exit__(exc_type, exc_val, exc_tb) - - def get_loop(self) -> AbstractEventLoop: - return self._runner.get_loop() - - def _exception_handler( - self, loop: asyncio.AbstractEventLoop, context: dict[str, Any] - ) -> None: - if isinstance(context.get("exception"), Exception): - self._exceptions.append(context["exception"]) - else: - loop.default_exception_handler(context) - - def _raise_async_exceptions(self) -> None: - # Re-raise any exceptions raised in asynchronous callbacks - if self._exceptions: - exceptions, self._exceptions = self._exceptions, [] - if len(exceptions) == 1: - raise exceptions[0] - elif exceptions: - raise BaseExceptionGroup( - "Multiple exceptions occurred in asynchronous callbacks", exceptions - ) - - async def _run_tests_and_fixtures( - self, - receive_stream: MemoryObjectReceiveStream[ - tuple[Awaitable[T_Retval], asyncio.Future[T_Retval]] - ], - ) -> None: - from _pytest.outcomes import OutcomeException - - with receive_stream, self._send_stream: - async for coro, future in receive_stream: - try: - retval = await coro - except CancelledError as exc: - if not future.cancelled(): - future.cancel(*exc.args) - - raise - except BaseException as exc: - if not future.cancelled(): - future.set_exception(exc) - - if not isinstance(exc, (Exception, OutcomeException)): - raise - else: - if not future.cancelled(): - future.set_result(retval) - - async def _call_in_runner_task( - self, - func: Callable[P, Awaitable[T_Retval]], - *args: P.args, - **kwargs: P.kwargs, - ) -> T_Retval: - if not self._runner_task: - self._send_stream, receive_stream = create_memory_object_stream[ - tuple[Awaitable[Any], asyncio.Future] - ](1) - self._runner_task = self.get_loop().create_task( - self._run_tests_and_fixtures(receive_stream) - ) - - coro = func(*args, **kwargs) - future: asyncio.Future[T_Retval] = self.get_loop().create_future() - self._send_stream.send_nowait((coro, future)) - return await future - - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], - kwargs: dict[str, Any], - ) -> Iterable[T_Retval]: - asyncgen = fixture_func(**kwargs) - fixturevalue: T_Retval = self.get_loop().run_until_complete( - self._call_in_runner_task(asyncgen.asend, None) - ) - self._raise_async_exceptions() - - yield fixturevalue - - try: - self.get_loop().run_until_complete( - self._call_in_runner_task(asyncgen.asend, None) - ) - except StopAsyncIteration: - self._raise_async_exceptions() - else: - self.get_loop().run_until_complete(asyncgen.aclose()) - raise RuntimeError("Async generator fixture did not stop") - - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], - kwargs: dict[str, Any], - ) -> T_Retval: - retval = self.get_loop().run_until_complete( - self._call_in_runner_task(fixture_func, **kwargs) - ) - self._raise_async_exceptions() - return retval - - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - try: - self.get_loop().run_until_complete( - self._call_in_runner_task(test_func, **kwargs) - ) - except Exception as exc: - self._exceptions.append(exc) - - self._raise_async_exceptions() - - -class AsyncIOBackend(AsyncBackend): - @classmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - @wraps(func) - async def wrapper() -> T_Retval: - task = cast(asyncio.Task, current_task()) - task.set_name(get_callable_name(func)) - _task_states[task] = TaskState(None, None) - - try: - return await func(*args) - finally: - del _task_states[task] - - debug = options.get("debug", None) - loop_factory = options.get("loop_factory", None) - if loop_factory is None and options.get("use_uvloop", False): - import uvloop - - loop_factory = uvloop.new_event_loop - - with Runner(debug=debug, loop_factory=loop_factory) as runner: - return runner.run(wrapper()) - - @classmethod - def current_token(cls) -> object: - return get_running_loop() - - @classmethod - def current_time(cls) -> float: - return get_running_loop().time() - - @classmethod - def cancelled_exception_class(cls) -> type[BaseException]: - return CancelledError - - @classmethod - async def checkpoint(cls) -> None: - await sleep(0) - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - task = current_task() - if task is None: - return - - try: - cancel_scope = _task_states[task].cancel_scope - except KeyError: - return - - while cancel_scope: - if cancel_scope.cancel_called: - await sleep(0) - elif cancel_scope.shield: - break - else: - cancel_scope = cancel_scope._parent_scope - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - with CancelScope(shield=True): - await sleep(0) - - @classmethod - async def sleep(cls, delay: float) -> None: - await sleep(delay) - - @classmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return CancelScope(deadline=deadline, shield=shield) - - @classmethod - def current_effective_deadline(cls) -> float: - if (task := current_task()) is None: - return math.inf - - try: - cancel_scope = _task_states[task].cancel_scope - except KeyError: - return math.inf - - deadline = math.inf - while cancel_scope: - deadline = min(deadline, cancel_scope.deadline) - if cancel_scope._cancel_called: - deadline = -math.inf - break - elif cancel_scope.shield: - break - else: - cancel_scope = cancel_scope._parent_scope - - return deadline - - @classmethod - def create_task_group(cls) -> abc.TaskGroup: - return TaskGroup() - - @classmethod - def create_event(cls) -> abc.Event: - return Event() - - @classmethod - def create_lock(cls, *, fast_acquire: bool) -> abc.Lock: - return Lock(fast_acquire=fast_acquire) - - @classmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> abc.Semaphore: - return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) - - @classmethod - def create_capacity_limiter(cls, total_tokens: float) -> abc.CapacityLimiter: - return CapacityLimiter(total_tokens) - - @classmethod - async def run_sync_in_worker_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: abc.CapacityLimiter | None = None, - ) -> T_Retval: - await cls.checkpoint() - - # If this is the first run in this event loop thread, set up the necessary - # variables - try: - idle_workers = _threadpool_idle_workers.get() - workers = _threadpool_workers.get() - except LookupError: - idle_workers = deque() - workers = set() - _threadpool_idle_workers.set(idle_workers) - _threadpool_workers.set(workers) - - async with limiter or cls.current_default_thread_limiter(): - with CancelScope(shield=not abandon_on_cancel) as scope: - future: asyncio.Future = asyncio.Future() - root_task = find_root_task() - if not idle_workers: - worker = WorkerThread(root_task, workers, idle_workers) - worker.start() - workers.add(worker) - root_task.add_done_callback(worker.stop) - else: - worker = idle_workers.pop() - - # Prune any other workers that have been idle for MAX_IDLE_TIME - # seconds or longer - now = cls.current_time() - while idle_workers: - if ( - now - idle_workers[0].idle_since - < WorkerThread.MAX_IDLE_TIME - ): - break - - expired_worker = idle_workers.popleft() - expired_worker.root_task.remove_done_callback( - expired_worker.stop - ) - expired_worker.stop() - - context = copy_context() - context.run(sniffio.current_async_library_cvar.set, None) - if abandon_on_cancel or scope._parent_scope is None: - worker_scope = scope - else: - worker_scope = scope._parent_scope - - worker.queue.put_nowait((context, func, args, future, worker_scope)) - return await future - - @classmethod - def check_cancelled(cls) -> None: - scope: CancelScope | None = threadlocals.current_cancel_scope - while scope is not None: - if scope.cancel_called: - raise CancelledError(f"Cancelled by cancel scope {id(scope):x}") - - if scope.shield: - return - - scope = scope._parent_scope - - @classmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - async def task_wrapper(scope: CancelScope) -> T_Retval: - __tracebackhide__ = True - task = cast(asyncio.Task, current_task()) - _task_states[task] = TaskState(None, scope) - scope._tasks.add(task) - try: - return await func(*args) - except CancelledError as exc: - raise concurrent.futures.CancelledError(str(exc)) from None - finally: - scope._tasks.discard(task) - - loop = cast(AbstractEventLoop, token) - context = copy_context() - context.run(sniffio.current_async_library_cvar.set, "asyncio") - wrapper = task_wrapper(threadlocals.current_cancel_scope) - f: concurrent.futures.Future[T_Retval] = context.run( - asyncio.run_coroutine_threadsafe, wrapper, loop - ) - return f.result() - - @classmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - @wraps(func) - def wrapper() -> None: - try: - sniffio.current_async_library_cvar.set("asyncio") - f.set_result(func(*args)) - except BaseException as exc: - f.set_exception(exc) - if not isinstance(exc, Exception): - raise - - f: concurrent.futures.Future[T_Retval] = Future() - loop = cast(AbstractEventLoop, token) - loop.call_soon_threadsafe(wrapper) - return f.result() - - @classmethod - def create_blocking_portal(cls) -> abc.BlockingPortal: - return BlockingPortal() - - @classmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - await cls.checkpoint() - if isinstance(command, PathLike): - command = os.fspath(command) - - if isinstance(command, (str, bytes)): - process = await asyncio.create_subprocess_shell( - command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - **kwargs, - ) - else: - process = await asyncio.create_subprocess_exec( - *command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - **kwargs, - ) - - stdin_stream = StreamWriterWrapper(process.stdin) if process.stdin else None - stdout_stream = StreamReaderWrapper(process.stdout) if process.stdout else None - stderr_stream = StreamReaderWrapper(process.stderr) if process.stderr else None - return Process(process, stdin_stream, stdout_stream, stderr_stream) - - @classmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: - create_task( - _shutdown_process_pool_on_exit(workers), - name="AnyIO process pool shutdown task", - ) - find_root_task().add_done_callback( - partial(_forcibly_shutdown_process_pool_on_exit, workers) # type:ignore[arg-type] - ) - - @classmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> abc.SocketStream: - transport, protocol = cast( - tuple[asyncio.Transport, StreamProtocol], - await get_running_loop().create_connection( - StreamProtocol, host, port, local_addr=local_address - ), - ) - transport.pause_reading() - return SocketStream(transport, protocol) - - @classmethod - async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: - await cls.checkpoint() - loop = get_running_loop() - raw_socket = socket.socket(socket.AF_UNIX) - raw_socket.setblocking(False) - while True: - try: - raw_socket.connect(path) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - loop.add_writer(raw_socket, f.set_result, None) - f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) - await f - except BaseException: - raw_socket.close() - raise - else: - return UNIXSocketStream(raw_socket) - - @classmethod - def create_tcp_listener(cls, sock: socket.socket) -> SocketListener: - return TCPSocketListener(sock) - - @classmethod - def create_unix_listener(cls, sock: socket.socket) -> SocketListener: - return UNIXSocketListener(sock) - - @classmethod - async def create_udp_socket( - cls, - family: AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - transport, protocol = await get_running_loop().create_datagram_endpoint( - DatagramProtocol, - local_addr=local_address, - remote_addr=remote_address, - family=family, - reuse_port=reuse_port, - ) - if protocol.exception: - transport.close() - raise protocol.exception - - if not remote_address: - return UDPSocket(transport, protocol) - else: - return ConnectedUDPSocket(transport, protocol) - - @classmethod - async def create_unix_datagram_socket( # type: ignore[override] - cls, raw_socket: socket.socket, remote_path: str | bytes | None - ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: - await cls.checkpoint() - loop = get_running_loop() - - if remote_path: - while True: - try: - raw_socket.connect(remote_path) - except BlockingIOError: - f: asyncio.Future = asyncio.Future() - loop.add_writer(raw_socket, f.set_result, None) - f.add_done_callback(lambda _: loop.remove_writer(raw_socket)) - await f - except BaseException: - raw_socket.close() - raise - else: - return ConnectedUNIXDatagramSocket(raw_socket) - else: - return UNIXDatagramSocket(raw_socket) - - @classmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> list[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int], - ] - ]: - return await get_running_loop().getaddrinfo( - host, port, family=family, type=type, proto=proto, flags=flags - ) - - @classmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - return await get_running_loop().getnameinfo(sockaddr, flags) - - @classmethod - async def wait_readable(cls, obj: FileDescriptorLike) -> None: - await cls.checkpoint() - try: - read_events = _read_events.get() - except LookupError: - read_events = {} - _read_events.set(read_events) - - if not isinstance(obj, int): - obj = obj.fileno() - - if read_events.get(obj): - raise BusyResourceError("reading from") - - loop = get_running_loop() - event = asyncio.Event() - try: - loop.add_reader(obj, event.set) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - selector = get_selector() - selector.add_reader(obj, event.set) - remove_reader = selector.remove_reader - else: - remove_reader = loop.remove_reader - - read_events[obj] = event - try: - await event.wait() - finally: - remove_reader(obj) - del read_events[obj] - - @classmethod - async def wait_writable(cls, obj: FileDescriptorLike) -> None: - await cls.checkpoint() - try: - write_events = _write_events.get() - except LookupError: - write_events = {} - _write_events.set(write_events) - - if not isinstance(obj, int): - obj = obj.fileno() - - if write_events.get(obj): - raise BusyResourceError("writing to") - - loop = get_running_loop() - event = asyncio.Event() - try: - loop.add_writer(obj, event.set) - except NotImplementedError: - from anyio._core._asyncio_selector_thread import get_selector - - selector = get_selector() - selector.add_writer(obj, event.set) - remove_writer = selector.remove_writer - else: - remove_writer = loop.remove_writer - - write_events[obj] = event - try: - await event.wait() - finally: - del write_events[obj] - remove_writer(obj) - - @classmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - try: - return _default_thread_limiter.get() - except LookupError: - limiter = CapacityLimiter(40) - _default_thread_limiter.set(limiter) - return limiter - - @classmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - return _SignalReceiver(signals) - - @classmethod - def get_current_task(cls) -> TaskInfo: - return AsyncIOTaskInfo(current_task()) # type: ignore[arg-type] - - @classmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - return [AsyncIOTaskInfo(task) for task in all_tasks() if not task.done()] - - @classmethod - async def wait_all_tasks_blocked(cls) -> None: - await cls.checkpoint() - this_task = current_task() - while True: - for task in all_tasks(): - if task is this_task: - continue - - waiter = task._fut_waiter # type: ignore[attr-defined] - if waiter is None or waiter.done(): - await sleep(0.1) - break - else: - return - - @classmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - return TestRunner(**options) - - -backend_class = AsyncIOBackend diff --git a/.venv/Lib/site-packages/anyio/_backends/_trio.py b/.venv/Lib/site-packages/anyio/_backends/_trio.py deleted file mode 100644 index 70a0a60..0000000 --- a/.venv/Lib/site-packages/anyio/_backends/_trio.py +++ /dev/null @@ -1,1334 +0,0 @@ -from __future__ import annotations - -import array -import math -import os -import socket -import sys -import types -import weakref -from collections.abc import ( - AsyncGenerator, - AsyncIterator, - Awaitable, - Callable, - Collection, - Coroutine, - Iterable, - Sequence, -) -from concurrent.futures import Future -from contextlib import AbstractContextManager -from dataclasses import dataclass -from functools import partial -from io import IOBase -from os import PathLike -from signal import Signals -from socket import AddressFamily, SocketKind -from types import TracebackType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Generic, - NoReturn, - TypeVar, - cast, - overload, -) - -import trio.from_thread -import trio.lowlevel -from outcome import Error, Outcome, Value -from trio.lowlevel import ( - current_root_task, - current_task, - wait_readable, - wait_writable, -) -from trio.socket import SocketType as TrioSocketType -from trio.to_thread import run_sync - -from .. import ( - CapacityLimiterStatistics, - EventStatistics, - LockStatistics, - TaskInfo, - WouldBlock, - abc, -) -from .._core._eventloop import claim_worker_thread -from .._core._exceptions import ( - BrokenResourceError, - BusyResourceError, - ClosedResourceError, - EndOfStream, -) -from .._core._sockets import convert_ipv6_sockaddr -from .._core._streams import create_memory_object_stream -from .._core._synchronization import ( - CapacityLimiter as BaseCapacityLimiter, -) -from .._core._synchronization import Event as BaseEvent -from .._core._synchronization import Lock as BaseLock -from .._core._synchronization import ( - ResourceGuard, - SemaphoreStatistics, -) -from .._core._synchronization import Semaphore as BaseSemaphore -from .._core._tasks import CancelScope as BaseCancelScope -from ..abc import IPSockAddrType, UDPPacketType, UNIXDatagramPacketType -from ..abc._eventloop import AsyncBackend, StrOrBytesPath -from ..streams.memory import MemoryObjectSendStream - -if TYPE_CHECKING: - from _typeshed import HasFileno - -if sys.version_info >= (3, 10): - from typing import ParamSpec -else: - from typing_extensions import ParamSpec - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from exceptiongroup import BaseExceptionGroup - from typing_extensions import TypeVarTuple, Unpack - -T = TypeVar("T") -T_Retval = TypeVar("T_Retval") -T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType) -PosArgsT = TypeVarTuple("PosArgsT") -P = ParamSpec("P") - - -# -# Event loop -# - -RunVar = trio.lowlevel.RunVar - - -# -# Timeouts and cancellation -# - - -class CancelScope(BaseCancelScope): - def __new__( - cls, original: trio.CancelScope | None = None, **kwargs: object - ) -> CancelScope: - return object.__new__(cls) - - def __init__(self, original: trio.CancelScope | None = None, **kwargs: Any) -> None: - self.__original = original or trio.CancelScope(**kwargs) - - def __enter__(self) -> CancelScope: - self.__original.__enter__() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - # https://github.com/python-trio/trio-typing/pull/79 - return self.__original.__exit__(exc_type, exc_val, exc_tb) - - def cancel(self) -> None: - self.__original.cancel() - - @property - def deadline(self) -> float: - return self.__original.deadline - - @deadline.setter - def deadline(self, value: float) -> None: - self.__original.deadline = value - - @property - def cancel_called(self) -> bool: - return self.__original.cancel_called - - @property - def cancelled_caught(self) -> bool: - return self.__original.cancelled_caught - - @property - def shield(self) -> bool: - return self.__original.shield - - @shield.setter - def shield(self, value: bool) -> None: - self.__original.shield = value - - -# -# Task groups -# - - -class TaskGroup(abc.TaskGroup): - def __init__(self) -> None: - self._active = False - self._nursery_manager = trio.open_nursery(strict_exception_groups=True) - self.cancel_scope = None # type: ignore[assignment] - - async def __aenter__(self) -> TaskGroup: - self._active = True - self._nursery = await self._nursery_manager.__aenter__() - self.cancel_scope = CancelScope(self._nursery.cancel_scope) - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - try: - return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb) - except BaseExceptionGroup as exc: - if not exc.split(trio.Cancelled)[1]: - raise trio.Cancelled._create() from exc - - raise - finally: - del exc_val, exc_tb - self._active = False - - def start_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> None: - if not self._active: - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - self._nursery.start_soon(func, *args, name=name) - - async def start( - self, func: Callable[..., Awaitable[Any]], *args: object, name: object = None - ) -> Any: - if not self._active: - raise RuntimeError( - "This task group is not active; no new tasks can be started." - ) - - return await self._nursery.start(func, *args, name=name) - - -# -# Threads -# - - -class BlockingPortal(abc.BlockingPortal): - def __new__(cls) -> BlockingPortal: - return object.__new__(cls) - - def __init__(self) -> None: - super().__init__() - self._token = trio.lowlevel.current_trio_token() - - def _spawn_task_from_thread( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - name: object, - future: Future[T_Retval], - ) -> None: - trio.from_thread.run_sync( - partial(self._task_group.start_soon, name=name), - self._call_func, - func, - args, - kwargs, - future, - trio_token=self._token, - ) - - -# -# Subprocesses -# - - -@dataclass(eq=False) -class ReceiveStreamWrapper(abc.ByteReceiveStream): - _stream: trio.abc.ReceiveStream - - async def receive(self, max_bytes: int | None = None) -> bytes: - try: - data = await self._stream.receive_some(max_bytes) - except trio.ClosedResourceError as exc: - raise ClosedResourceError from exc.__cause__ - except trio.BrokenResourceError as exc: - raise BrokenResourceError from exc.__cause__ - - if data: - return data - else: - raise EndOfStream - - async def aclose(self) -> None: - await self._stream.aclose() - - -@dataclass(eq=False) -class SendStreamWrapper(abc.ByteSendStream): - _stream: trio.abc.SendStream - - async def send(self, item: bytes) -> None: - try: - await self._stream.send_all(item) - except trio.ClosedResourceError as exc: - raise ClosedResourceError from exc.__cause__ - except trio.BrokenResourceError as exc: - raise BrokenResourceError from exc.__cause__ - - async def aclose(self) -> None: - await self._stream.aclose() - - -@dataclass(eq=False) -class Process(abc.Process): - _process: trio.Process - _stdin: abc.ByteSendStream | None - _stdout: abc.ByteReceiveStream | None - _stderr: abc.ByteReceiveStream | None - - async def aclose(self) -> None: - with CancelScope(shield=True): - if self._stdin: - await self._stdin.aclose() - if self._stdout: - await self._stdout.aclose() - if self._stderr: - await self._stderr.aclose() - - try: - await self.wait() - except BaseException: - self.kill() - with CancelScope(shield=True): - await self.wait() - raise - - async def wait(self) -> int: - return await self._process.wait() - - def terminate(self) -> None: - self._process.terminate() - - def kill(self) -> None: - self._process.kill() - - def send_signal(self, signal: Signals) -> None: - self._process.send_signal(signal) - - @property - def pid(self) -> int: - return self._process.pid - - @property - def returncode(self) -> int | None: - return self._process.returncode - - @property - def stdin(self) -> abc.ByteSendStream | None: - return self._stdin - - @property - def stdout(self) -> abc.ByteReceiveStream | None: - return self._stdout - - @property - def stderr(self) -> abc.ByteReceiveStream | None: - return self._stderr - - -class _ProcessPoolShutdownInstrument(trio.abc.Instrument): - def after_run(self) -> None: - super().after_run() - - -current_default_worker_process_limiter: trio.lowlevel.RunVar = RunVar( - "current_default_worker_process_limiter" -) - - -async def _shutdown_process_pool(workers: set[abc.Process]) -> None: - try: - await trio.sleep(math.inf) - except trio.Cancelled: - for process in workers: - if process.returncode is None: - process.kill() - - with CancelScope(shield=True): - for process in workers: - await process.aclose() - - -# -# Sockets and networking -# - - -class _TrioSocketMixin(Generic[T_SockAddr]): - def __init__(self, trio_socket: TrioSocketType) -> None: - self._trio_socket = trio_socket - self._closed = False - - def _check_closed(self) -> None: - if self._closed: - raise ClosedResourceError - if self._trio_socket.fileno() < 0: - raise BrokenResourceError - - @property - def _raw_socket(self) -> socket.socket: - return self._trio_socket._sock # type: ignore[attr-defined] - - async def aclose(self) -> None: - if self._trio_socket.fileno() >= 0: - self._closed = True - self._trio_socket.close() - - def _convert_socket_error(self, exc: BaseException) -> NoReturn: - if isinstance(exc, trio.ClosedResourceError): - raise ClosedResourceError from exc - elif self._trio_socket.fileno() < 0 and self._closed: - raise ClosedResourceError from None - elif isinstance(exc, OSError): - raise BrokenResourceError from exc - else: - raise exc - - -class SocketStream(_TrioSocketMixin, abc.SocketStream): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self, max_bytes: int = 65536) -> bytes: - with self._receive_guard: - try: - data = await self._trio_socket.recv(max_bytes) - except BaseException as exc: - self._convert_socket_error(exc) - - if data: - return data - else: - raise EndOfStream - - async def send(self, item: bytes) -> None: - with self._send_guard: - view = memoryview(item) - while view: - try: - bytes_sent = await self._trio_socket.send(view) - except BaseException as exc: - self._convert_socket_error(exc) - - view = view[bytes_sent:] - - async def send_eof(self) -> None: - self._trio_socket.shutdown(socket.SHUT_WR) - - -class UNIXSocketStream(SocketStream, abc.UNIXSocketStream): - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - if not isinstance(msglen, int) or msglen < 0: - raise ValueError("msglen must be a non-negative integer") - if not isinstance(maxfds, int) or maxfds < 1: - raise ValueError("maxfds must be a positive integer") - - fds = array.array("i") - await trio.lowlevel.checkpoint() - with self._receive_guard: - while True: - try: - message, ancdata, flags, addr = await self._trio_socket.recvmsg( - msglen, socket.CMSG_LEN(maxfds * fds.itemsize) - ) - except BaseException as exc: - self._convert_socket_error(exc) - else: - if not message and not ancdata: - raise EndOfStream - - break - - for cmsg_level, cmsg_type, cmsg_data in ancdata: - if cmsg_level != socket.SOL_SOCKET or cmsg_type != socket.SCM_RIGHTS: - raise RuntimeError( - f"Received unexpected ancillary data; message = {message!r}, " - f"cmsg_level = {cmsg_level}, cmsg_type = {cmsg_type}" - ) - - fds.frombytes(cmsg_data[: len(cmsg_data) - (len(cmsg_data) % fds.itemsize)]) - - return message, list(fds) - - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - if not message: - raise ValueError("message must not be empty") - if not fds: - raise ValueError("fds must not be empty") - - filenos: list[int] = [] - for fd in fds: - if isinstance(fd, int): - filenos.append(fd) - elif isinstance(fd, IOBase): - filenos.append(fd.fileno()) - - fdarray = array.array("i", filenos) - await trio.lowlevel.checkpoint() - with self._send_guard: - while True: - try: - await self._trio_socket.sendmsg( - [message], - [ - ( - socket.SOL_SOCKET, - socket.SCM_RIGHTS, - fdarray, - ) - ], - ) - break - except BaseException as exc: - self._convert_socket_error(exc) - - -class TCPSocketListener(_TrioSocketMixin, abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - super().__init__(trio.socket.from_stdlib_socket(raw_socket)) - self._accept_guard = ResourceGuard("accepting connections from") - - async def accept(self) -> SocketStream: - with self._accept_guard: - try: - trio_socket, _addr = await self._trio_socket.accept() - except BaseException as exc: - self._convert_socket_error(exc) - - trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - return SocketStream(trio_socket) - - -class UNIXSocketListener(_TrioSocketMixin, abc.SocketListener): - def __init__(self, raw_socket: socket.socket): - super().__init__(trio.socket.from_stdlib_socket(raw_socket)) - self._accept_guard = ResourceGuard("accepting connections from") - - async def accept(self) -> UNIXSocketStream: - with self._accept_guard: - try: - trio_socket, _addr = await self._trio_socket.accept() - except BaseException as exc: - self._convert_socket_error(exc) - - return UNIXSocketStream(trio_socket) - - -class UDPSocket(_TrioSocketMixin[IPSockAddrType], abc.UDPSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> tuple[bytes, IPSockAddrType]: - with self._receive_guard: - try: - data, addr = await self._trio_socket.recvfrom(65536) - return data, convert_ipv6_sockaddr(addr) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: UDPPacketType) -> None: - with self._send_guard: - try: - await self._trio_socket.sendto(*item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class ConnectedUDPSocket(_TrioSocketMixin[IPSockAddrType], abc.ConnectedUDPSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> bytes: - with self._receive_guard: - try: - return await self._trio_socket.recv(65536) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: bytes) -> None: - with self._send_guard: - try: - await self._trio_socket.send(item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class UNIXDatagramSocket(_TrioSocketMixin[str], abc.UNIXDatagramSocket): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> UNIXDatagramPacketType: - with self._receive_guard: - try: - data, addr = await self._trio_socket.recvfrom(65536) - return data, addr - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: UNIXDatagramPacketType) -> None: - with self._send_guard: - try: - await self._trio_socket.sendto(*item) - except BaseException as exc: - self._convert_socket_error(exc) - - -class ConnectedUNIXDatagramSocket( - _TrioSocketMixin[str], abc.ConnectedUNIXDatagramSocket -): - def __init__(self, trio_socket: TrioSocketType) -> None: - super().__init__(trio_socket) - self._receive_guard = ResourceGuard("reading from") - self._send_guard = ResourceGuard("writing to") - - async def receive(self) -> bytes: - with self._receive_guard: - try: - return await self._trio_socket.recv(65536) - except BaseException as exc: - self._convert_socket_error(exc) - - async def send(self, item: bytes) -> None: - with self._send_guard: - try: - await self._trio_socket.send(item) - except BaseException as exc: - self._convert_socket_error(exc) - - -# -# Synchronization -# - - -class Event(BaseEvent): - def __new__(cls) -> Event: - return object.__new__(cls) - - def __init__(self) -> None: - self.__original = trio.Event() - - def is_set(self) -> bool: - return self.__original.is_set() - - async def wait(self) -> None: - return await self.__original.wait() - - def statistics(self) -> EventStatistics: - orig_statistics = self.__original.statistics() - return EventStatistics(tasks_waiting=orig_statistics.tasks_waiting) - - def set(self) -> None: - self.__original.set() - - -class Lock(BaseLock): - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False) -> None: - self._fast_acquire = fast_acquire - self.__original = trio.Lock() - - @staticmethod - def _convert_runtime_error_msg(exc: RuntimeError) -> None: - if exc.args == ("attempt to re-acquire an already held Lock",): - exc.args = ("Attempted to acquire an already held Lock",) - - async def acquire(self) -> None: - if not self._fast_acquire: - try: - await self.__original.acquire() - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - return - - # This is the "fast path" where we don't let other tasks run - await trio.lowlevel.checkpoint_if_cancelled() - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - await self.__original._lot.park() - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - def acquire_nowait(self) -> None: - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - raise WouldBlock from None - except RuntimeError as exc: - self._convert_runtime_error_msg(exc) - raise - - def locked(self) -> bool: - return self.__original.locked() - - def release(self) -> None: - self.__original.release() - - def statistics(self) -> LockStatistics: - orig_statistics = self.__original.statistics() - owner = TrioTaskInfo(orig_statistics.owner) if orig_statistics.owner else None - return LockStatistics( - orig_statistics.locked, owner, orig_statistics.tasks_waiting - ) - - -class Semaphore(BaseSemaphore): - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> None: - super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) - self.__original = trio.Semaphore(initial_value, max_value=max_value) - - async def acquire(self) -> None: - if not self._fast_acquire: - await self.__original.acquire() - return - - # This is the "fast path" where we don't let other tasks run - await trio.lowlevel.checkpoint_if_cancelled() - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - await self.__original._lot.park() - - def acquire_nowait(self) -> None: - try: - self.__original.acquire_nowait() - except trio.WouldBlock: - raise WouldBlock from None - - @property - def max_value(self) -> int | None: - return self.__original.max_value - - @property - def value(self) -> int: - return self.__original.value - - def release(self) -> None: - self.__original.release() - - def statistics(self) -> SemaphoreStatistics: - orig_statistics = self.__original.statistics() - return SemaphoreStatistics(orig_statistics.tasks_waiting) - - -class CapacityLimiter(BaseCapacityLimiter): - def __new__( - cls, - total_tokens: float | None = None, - *, - original: trio.CapacityLimiter | None = None, - ) -> CapacityLimiter: - return object.__new__(cls) - - def __init__( - self, - total_tokens: float | None = None, - *, - original: trio.CapacityLimiter | None = None, - ) -> None: - if original is not None: - self.__original = original - else: - assert total_tokens is not None - self.__original = trio.CapacityLimiter(total_tokens) - - async def __aenter__(self) -> None: - return await self.__original.__aenter__() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.__original.__aexit__(exc_type, exc_val, exc_tb) - - @property - def total_tokens(self) -> float: - return self.__original.total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - self.__original.total_tokens = value - - @property - def borrowed_tokens(self) -> int: - return self.__original.borrowed_tokens - - @property - def available_tokens(self) -> float: - return self.__original.available_tokens - - def acquire_nowait(self) -> None: - self.__original.acquire_nowait() - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - self.__original.acquire_on_behalf_of_nowait(borrower) - - async def acquire(self) -> None: - await self.__original.acquire() - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await self.__original.acquire_on_behalf_of(borrower) - - def release(self) -> None: - return self.__original.release() - - def release_on_behalf_of(self, borrower: object) -> None: - return self.__original.release_on_behalf_of(borrower) - - def statistics(self) -> CapacityLimiterStatistics: - orig = self.__original.statistics() - return CapacityLimiterStatistics( - borrowed_tokens=orig.borrowed_tokens, - total_tokens=orig.total_tokens, - borrowers=tuple(orig.borrowers), - tasks_waiting=orig.tasks_waiting, - ) - - -_capacity_limiter_wrapper: trio.lowlevel.RunVar = RunVar("_capacity_limiter_wrapper") - - -# -# Signal handling -# - - -class _SignalReceiver: - _iterator: AsyncIterator[int] - - def __init__(self, signals: tuple[Signals, ...]): - self._signals = signals - - def __enter__(self) -> _SignalReceiver: - self._cm = trio.open_signal_receiver(*self._signals) - self._iterator = self._cm.__enter__() - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - return self._cm.__exit__(exc_type, exc_val, exc_tb) - - def __aiter__(self) -> _SignalReceiver: - return self - - async def __anext__(self) -> Signals: - signum = await self._iterator.__anext__() - return Signals(signum) - - -# -# Testing and debugging -# - - -class TestRunner(abc.TestRunner): - def __init__(self, **options: Any) -> None: - from queue import Queue - - self._call_queue: Queue[Callable[[], object]] = Queue() - self._send_stream: MemoryObjectSendStream | None = None - self._options = options - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> None: - if self._send_stream: - self._send_stream.close() - while self._send_stream is not None: - self._call_queue.get()() - - async def _run_tests_and_fixtures(self) -> None: - self._send_stream, receive_stream = create_memory_object_stream(1) - with receive_stream: - async for coro, outcome_holder in receive_stream: - try: - retval = await coro - except BaseException as exc: - outcome_holder.append(Error(exc)) - else: - outcome_holder.append(Value(retval)) - - def _main_task_finished(self, outcome: object) -> None: - self._send_stream = None - - def _call_in_runner_task( - self, - func: Callable[P, Awaitable[T_Retval]], - *args: P.args, - **kwargs: P.kwargs, - ) -> T_Retval: - if self._send_stream is None: - trio.lowlevel.start_guest_run( - self._run_tests_and_fixtures, - run_sync_soon_threadsafe=self._call_queue.put, - done_callback=self._main_task_finished, - **self._options, - ) - while self._send_stream is None: - self._call_queue.get()() - - outcome_holder: list[Outcome] = [] - self._send_stream.send_nowait((func(*args, **kwargs), outcome_holder)) - while not outcome_holder: - self._call_queue.get()() - - return outcome_holder[0].unwrap() - - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[T_Retval, Any]], - kwargs: dict[str, Any], - ) -> Iterable[T_Retval]: - asyncgen = fixture_func(**kwargs) - fixturevalue: T_Retval = self._call_in_runner_task(asyncgen.asend, None) - - yield fixturevalue - - try: - self._call_in_runner_task(asyncgen.asend, None) - except StopAsyncIteration: - pass - else: - self._call_in_runner_task(asyncgen.aclose) - raise RuntimeError("Async generator fixture did not stop") - - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, T_Retval]], - kwargs: dict[str, Any], - ) -> T_Retval: - return self._call_in_runner_task(fixture_func, **kwargs) - - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - self._call_in_runner_task(test_func, **kwargs) - - -class TrioTaskInfo(TaskInfo): - def __init__(self, task: trio.lowlevel.Task): - parent_id = None - if task.parent_nursery and task.parent_nursery.parent_task: - parent_id = id(task.parent_nursery.parent_task) - - super().__init__(id(task), parent_id, task.name, task.coro) - self._task = weakref.proxy(task) - - def has_pending_cancellation(self) -> bool: - try: - return self._task._cancel_status.effectively_cancelled - except ReferenceError: - # If the task is no longer around, it surely doesn't have a cancellation - # pending - return False - - -class TrioBackend(AsyncBackend): - @classmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - return trio.run(func, *args) - - @classmethod - def current_token(cls) -> object: - return trio.lowlevel.current_trio_token() - - @classmethod - def current_time(cls) -> float: - return trio.current_time() - - @classmethod - def cancelled_exception_class(cls) -> type[BaseException]: - return trio.Cancelled - - @classmethod - async def checkpoint(cls) -> None: - await trio.lowlevel.checkpoint() - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - await trio.lowlevel.checkpoint_if_cancelled() - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - await trio.lowlevel.cancel_shielded_checkpoint() - - @classmethod - async def sleep(cls, delay: float) -> None: - await trio.sleep(delay) - - @classmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> abc.CancelScope: - return CancelScope(deadline=deadline, shield=shield) - - @classmethod - def current_effective_deadline(cls) -> float: - return trio.current_effective_deadline() - - @classmethod - def create_task_group(cls) -> abc.TaskGroup: - return TaskGroup() - - @classmethod - def create_event(cls) -> abc.Event: - return Event() - - @classmethod - def create_lock(cls, *, fast_acquire: bool) -> Lock: - return Lock(fast_acquire=fast_acquire) - - @classmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> abc.Semaphore: - return Semaphore(initial_value, max_value=max_value, fast_acquire=fast_acquire) - - @classmethod - def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: - return CapacityLimiter(total_tokens) - - @classmethod - async def run_sync_in_worker_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: abc.CapacityLimiter | None = None, - ) -> T_Retval: - def wrapper() -> T_Retval: - with claim_worker_thread(TrioBackend, token): - return func(*args) - - token = TrioBackend.current_token() - return await run_sync( - wrapper, - abandon_on_cancel=abandon_on_cancel, - limiter=cast(trio.CapacityLimiter, limiter), - ) - - @classmethod - def check_cancelled(cls) -> None: - trio.from_thread.check_cancelled() - - @classmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - return trio.from_thread.run(func, *args) - - @classmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - return trio.from_thread.run_sync(func, *args) - - @classmethod - def create_blocking_portal(cls) -> abc.BlockingPortal: - return BlockingPortal() - - @classmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - def convert_item(item: StrOrBytesPath) -> str: - str_or_bytes = os.fspath(item) - if isinstance(str_or_bytes, str): - return str_or_bytes - else: - return os.fsdecode(str_or_bytes) - - if isinstance(command, (str, bytes, PathLike)): - process = await trio.lowlevel.open_process( - convert_item(command), - stdin=stdin, - stdout=stdout, - stderr=stderr, - shell=True, - **kwargs, - ) - else: - process = await trio.lowlevel.open_process( - [convert_item(item) for item in command], - stdin=stdin, - stdout=stdout, - stderr=stderr, - shell=False, - **kwargs, - ) - - stdin_stream = SendStreamWrapper(process.stdin) if process.stdin else None - stdout_stream = ReceiveStreamWrapper(process.stdout) if process.stdout else None - stderr_stream = ReceiveStreamWrapper(process.stderr) if process.stderr else None - return Process(process, stdin_stream, stdout_stream, stderr_stream) - - @classmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[abc.Process]) -> None: - trio.lowlevel.spawn_system_task(_shutdown_process_pool, workers) - - @classmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> SocketStream: - family = socket.AF_INET6 if ":" in host else socket.AF_INET - trio_socket = trio.socket.socket(family) - trio_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - if local_address: - await trio_socket.bind(local_address) - - try: - await trio_socket.connect((host, port)) - except BaseException: - trio_socket.close() - raise - - return SocketStream(trio_socket) - - @classmethod - async def connect_unix(cls, path: str | bytes) -> abc.UNIXSocketStream: - trio_socket = trio.socket.socket(socket.AF_UNIX) - try: - await trio_socket.connect(path) - except BaseException: - trio_socket.close() - raise - - return UNIXSocketStream(trio_socket) - - @classmethod - def create_tcp_listener(cls, sock: socket.socket) -> abc.SocketListener: - return TCPSocketListener(sock) - - @classmethod - def create_unix_listener(cls, sock: socket.socket) -> abc.SocketListener: - return UNIXSocketListener(sock) - - @classmethod - async def create_udp_socket( - cls, - family: socket.AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - trio_socket = trio.socket.socket(family=family, type=socket.SOCK_DGRAM) - - if reuse_port: - trio_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - - if local_address: - await trio_socket.bind(local_address) - - if remote_address: - await trio_socket.connect(remote_address) - return ConnectedUDPSocket(trio_socket) - else: - return UDPSocket(trio_socket) - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: None - ) -> abc.UNIXDatagramSocket: ... - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: str | bytes - ) -> abc.ConnectedUNIXDatagramSocket: ... - - @classmethod - async def create_unix_datagram_socket( - cls, raw_socket: socket.socket, remote_path: str | bytes | None - ) -> abc.UNIXDatagramSocket | abc.ConnectedUNIXDatagramSocket: - trio_socket = trio.socket.from_stdlib_socket(raw_socket) - - if remote_path: - await trio_socket.connect(remote_path) - return ConnectedUNIXDatagramSocket(trio_socket) - else: - return UNIXDatagramSocket(trio_socket) - - @classmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> list[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int], - ] - ]: - return await trio.socket.getaddrinfo(host, port, family, type, proto, flags) - - @classmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - return await trio.socket.getnameinfo(sockaddr, flags) - - @classmethod - async def wait_readable(cls, obj: HasFileno | int) -> None: - try: - await wait_readable(obj) - except trio.ClosedResourceError as exc: - raise ClosedResourceError().with_traceback(exc.__traceback__) from None - except trio.BusyResourceError: - raise BusyResourceError("reading from") from None - - @classmethod - async def wait_writable(cls, obj: HasFileno | int) -> None: - try: - await wait_writable(obj) - except trio.ClosedResourceError as exc: - raise ClosedResourceError().with_traceback(exc.__traceback__) from None - except trio.BusyResourceError: - raise BusyResourceError("writing to") from None - - @classmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - try: - return _capacity_limiter_wrapper.get() - except LookupError: - limiter = CapacityLimiter( - original=trio.to_thread.current_default_thread_limiter() - ) - _capacity_limiter_wrapper.set(limiter) - return limiter - - @classmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - return _SignalReceiver(signals) - - @classmethod - def get_current_task(cls) -> TaskInfo: - task = current_task() - return TrioTaskInfo(task) - - @classmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - root_task = current_root_task() - assert root_task - task_infos = [TrioTaskInfo(root_task)] - nurseries = root_task.child_nurseries - while nurseries: - new_nurseries: list[trio.Nursery] = [] - for nursery in nurseries: - for task in nursery.child_tasks: - task_infos.append(TrioTaskInfo(task)) - new_nurseries.extend(task.child_nurseries) - - nurseries = new_nurseries - - return task_infos - - @classmethod - async def wait_all_tasks_blocked(cls) -> None: - from trio.testing import wait_all_tasks_blocked - - await wait_all_tasks_blocked() - - @classmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - return TestRunner(**options) - - -backend_class = TrioBackend diff --git a/.venv/Lib/site-packages/anyio/_core/__init__.py b/.venv/Lib/site-packages/anyio/_core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py b/.venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py deleted file mode 100644 index d98c304..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_asyncio_selector_thread.py +++ /dev/null @@ -1,150 +0,0 @@ -from __future__ import annotations - -import asyncio -import socket -import threading -from collections.abc import Callable -from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector -from typing import TYPE_CHECKING, Any - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike - -_selector_lock = threading.Lock() -_selector: Selector | None = None - - -class Selector: - def __init__(self) -> None: - self._thread = threading.Thread(target=self.run, name="AnyIO socket selector") - self._selector = DefaultSelector() - self._send, self._receive = socket.socketpair() - self._send.setblocking(False) - self._receive.setblocking(False) - self._selector.register(self._receive, EVENT_READ) - self._closed = False - - def start(self) -> None: - self._thread.start() - threading._register_atexit(self._stop) # type: ignore[attr-defined] - - def _stop(self) -> None: - global _selector - self._closed = True - self._notify_self() - self._send.close() - self._thread.join() - self._selector.unregister(self._receive) - self._receive.close() - self._selector.close() - _selector = None - assert ( - not self._selector.get_map() - ), "selector still has registered file descriptors after shutdown" - - def _notify_self(self) -> None: - try: - self._send.send(b"\x00") - except BlockingIOError: - pass - - def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: - loop = asyncio.get_running_loop() - try: - key = self._selector.get_key(fd) - except KeyError: - self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)}) - else: - if EVENT_READ in key.data: - raise ValueError( - "this file descriptor is already registered for reading" - ) - - key.data[EVENT_READ] = loop, callback - self._selector.modify(fd, key.events | EVENT_READ, key.data) - - self._notify_self() - - def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None: - loop = asyncio.get_running_loop() - try: - key = self._selector.get_key(fd) - except KeyError: - self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)}) - else: - if EVENT_WRITE in key.data: - raise ValueError( - "this file descriptor is already registered for writing" - ) - - key.data[EVENT_WRITE] = loop, callback - self._selector.modify(fd, key.events | EVENT_WRITE, key.data) - - self._notify_self() - - def remove_reader(self, fd: FileDescriptorLike) -> bool: - try: - key = self._selector.get_key(fd) - except KeyError: - return False - - if new_events := key.events ^ EVENT_READ: - del key.data[EVENT_READ] - self._selector.modify(fd, new_events, key.data) - else: - self._selector.unregister(fd) - - return True - - def remove_writer(self, fd: FileDescriptorLike) -> bool: - try: - key = self._selector.get_key(fd) - except KeyError: - return False - - if new_events := key.events ^ EVENT_WRITE: - del key.data[EVENT_WRITE] - self._selector.modify(fd, new_events, key.data) - else: - self._selector.unregister(fd) - - return True - - def run(self) -> None: - while not self._closed: - for key, events in self._selector.select(): - if key.fileobj is self._receive: - try: - while self._receive.recv(4096): - pass - except BlockingIOError: - pass - - continue - - if events & EVENT_READ: - loop, callback = key.data[EVENT_READ] - self.remove_reader(key.fd) - try: - loop.call_soon_threadsafe(callback) - except RuntimeError: - pass # the loop was already closed - - if events & EVENT_WRITE: - loop, callback = key.data[EVENT_WRITE] - self.remove_writer(key.fd) - try: - loop.call_soon_threadsafe(callback) - except RuntimeError: - pass # the loop was already closed - - -def get_selector() -> Selector: - global _selector - - with _selector_lock: - if _selector is None: - _selector = Selector() - _selector.start() - - return _selector diff --git a/.venv/Lib/site-packages/anyio/_core/_eventloop.py b/.venv/Lib/site-packages/anyio/_core/_eventloop.py deleted file mode 100644 index 6dcb458..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_eventloop.py +++ /dev/null @@ -1,166 +0,0 @@ -from __future__ import annotations - -import math -import sys -import threading -from collections.abc import Awaitable, Callable, Generator -from contextlib import contextmanager -from importlib import import_module -from typing import TYPE_CHECKING, Any, TypeVar - -import sniffio - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if TYPE_CHECKING: - from ..abc import AsyncBackend - -# This must be updated when new backends are introduced -BACKENDS = "asyncio", "trio" - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - -threadlocals = threading.local() -loaded_backends: dict[str, type[AsyncBackend]] = {} - - -def run( - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - backend: str = "asyncio", - backend_options: dict[str, Any] | None = None, -) -> T_Retval: - """ - Run the given coroutine function in an asynchronous event loop. - - The current thread must not be already running an event loop. - - :param func: a coroutine function - :param args: positional arguments to ``func`` - :param backend: name of the asynchronous event loop implementation – currently - either ``asyncio`` or ``trio`` - :param backend_options: keyword arguments to call the backend ``run()`` - implementation with (documented :ref:`here `) - :return: the return value of the coroutine function - :raises RuntimeError: if an asynchronous event loop is already running in this - thread - :raises LookupError: if the named backend is not found - - """ - try: - asynclib_name = sniffio.current_async_library() - except sniffio.AsyncLibraryNotFoundError: - pass - else: - raise RuntimeError(f"Already running {asynclib_name} in this thread") - - try: - async_backend = get_async_backend(backend) - except ImportError as exc: - raise LookupError(f"No such backend: {backend}") from exc - - token = None - if sniffio.current_async_library_cvar.get(None) is None: - # Since we're in control of the event loop, we can cache the name of the async - # library - token = sniffio.current_async_library_cvar.set(backend) - - try: - backend_options = backend_options or {} - return async_backend.run(func, args, {}, backend_options) - finally: - if token: - sniffio.current_async_library_cvar.reset(token) - - -async def sleep(delay: float) -> None: - """ - Pause the current task for the specified duration. - - :param delay: the duration, in seconds - - """ - return await get_async_backend().sleep(delay) - - -async def sleep_forever() -> None: - """ - Pause the current task until it's cancelled. - - This is a shortcut for ``sleep(math.inf)``. - - .. versionadded:: 3.1 - - """ - await sleep(math.inf) - - -async def sleep_until(deadline: float) -> None: - """ - Pause the current task until the given time. - - :param deadline: the absolute time to wake up at (according to the internal - monotonic clock of the event loop) - - .. versionadded:: 3.1 - - """ - now = current_time() - await sleep(max(deadline - now, 0)) - - -def current_time() -> float: - """ - Return the current value of the event loop's internal clock. - - :return: the clock value (seconds) - - """ - return get_async_backend().current_time() - - -def get_all_backends() -> tuple[str, ...]: - """Return a tuple of the names of all built-in backends.""" - return BACKENDS - - -def get_cancelled_exc_class() -> type[BaseException]: - """Return the current async library's cancellation exception class.""" - return get_async_backend().cancelled_exception_class() - - -# -# Private API -# - - -@contextmanager -def claim_worker_thread( - backend_class: type[AsyncBackend], token: object -) -> Generator[Any, None, None]: - threadlocals.current_async_backend = backend_class - threadlocals.current_token = token - try: - yield - finally: - del threadlocals.current_async_backend - del threadlocals.current_token - - -def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]: - if asynclib_name is None: - asynclib_name = sniffio.current_async_library() - - # We use our own dict instead of sys.modules to get the already imported back-end - # class because the appropriate modules in sys.modules could potentially be only - # partially initialized - try: - return loaded_backends[asynclib_name] - except KeyError: - module = import_module(f"anyio._backends._{asynclib_name}") - loaded_backends[asynclib_name] = module.backend_class - return module.backend_class diff --git a/.venv/Lib/site-packages/anyio/_core/_exceptions.py b/.venv/Lib/site-packages/anyio/_core/_exceptions.py deleted file mode 100644 index 97ea313..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_exceptions.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Generator - -if sys.version_info < (3, 11): - from exceptiongroup import BaseExceptionGroup - - -class BrokenResourceError(Exception): - """ - Raised when trying to use a resource that has been rendered unusable due to external - causes (e.g. a send stream whose peer has disconnected). - """ - - -class BrokenWorkerProcess(Exception): - """ - Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or - otherwise misbehaves. - """ - - -class BusyResourceError(Exception): - """ - Raised when two tasks are trying to read from or write to the same resource - concurrently. - """ - - def __init__(self, action: str): - super().__init__(f"Another task is already {action} this resource") - - -class ClosedResourceError(Exception): - """Raised when trying to use a resource that has been closed.""" - - -class DelimiterNotFound(Exception): - """ - Raised during - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the - maximum number of bytes has been read without the delimiter being found. - """ - - def __init__(self, max_bytes: int) -> None: - super().__init__( - f"The delimiter was not found among the first {max_bytes} bytes" - ) - - -class EndOfStream(Exception): - """ - Raised when trying to read from a stream that has been closed from the other end. - """ - - -class IncompleteRead(Exception): - """ - Raised during - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or - :meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the - connection is closed before the requested amount of bytes has been read. - """ - - def __init__(self) -> None: - super().__init__( - "The stream was closed before the read operation could be completed" - ) - - -class TypedAttributeLookupError(LookupError): - """ - Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute - is not found and no default value has been given. - """ - - -class WouldBlock(Exception): - """Raised by ``X_nowait`` functions if ``X()`` would block.""" - - -def iterate_exceptions( - exception: BaseException, -) -> Generator[BaseException, None, None]: - if isinstance(exception, BaseExceptionGroup): - for exc in exception.exceptions: - yield from iterate_exceptions(exc) - else: - yield exception diff --git a/.venv/Lib/site-packages/anyio/_core/_fileio.py b/.venv/Lib/site-packages/anyio/_core/_fileio.py deleted file mode 100644 index ef2930e..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_fileio.py +++ /dev/null @@ -1,674 +0,0 @@ -from __future__ import annotations - -import os -import pathlib -import sys -from collections.abc import AsyncIterator, Callable, Iterable, Iterator, Sequence -from dataclasses import dataclass -from functools import partial -from os import PathLike -from typing import ( - IO, - TYPE_CHECKING, - Any, - AnyStr, - Final, - Generic, - overload, -) - -from .. import to_thread -from ..abc import AsyncResource - -if TYPE_CHECKING: - from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer -else: - ReadableBuffer = OpenBinaryMode = OpenTextMode = WriteableBuffer = object - - -class AsyncFile(AsyncResource, Generic[AnyStr]): - """ - An asynchronous file object. - - This class wraps a standard file object and provides async friendly versions of the - following blocking methods (where available on the original file object): - - * read - * read1 - * readline - * readlines - * readinto - * readinto1 - * write - * writelines - * truncate - * seek - * tell - * flush - - All other methods are directly passed through. - - This class supports the asynchronous context manager protocol which closes the - underlying file at the end of the context block. - - This class also supports asynchronous iteration:: - - async with await open_file(...) as f: - async for line in f: - print(line) - """ - - def __init__(self, fp: IO[AnyStr]) -> None: - self._fp: Any = fp - - def __getattr__(self, name: str) -> object: - return getattr(self._fp, name) - - @property - def wrapped(self) -> IO[AnyStr]: - """The wrapped file object.""" - return self._fp - - async def __aiter__(self) -> AsyncIterator[AnyStr]: - while True: - line = await self.readline() - if line: - yield line - else: - break - - async def aclose(self) -> None: - return await to_thread.run_sync(self._fp.close) - - async def read(self, size: int = -1) -> AnyStr: - return await to_thread.run_sync(self._fp.read, size) - - async def read1(self: AsyncFile[bytes], size: int = -1) -> bytes: - return await to_thread.run_sync(self._fp.read1, size) - - async def readline(self) -> AnyStr: - return await to_thread.run_sync(self._fp.readline) - - async def readlines(self) -> list[AnyStr]: - return await to_thread.run_sync(self._fp.readlines) - - async def readinto(self: AsyncFile[bytes], b: WriteableBuffer) -> int: - return await to_thread.run_sync(self._fp.readinto, b) - - async def readinto1(self: AsyncFile[bytes], b: WriteableBuffer) -> int: - return await to_thread.run_sync(self._fp.readinto1, b) - - @overload - async def write(self: AsyncFile[bytes], b: ReadableBuffer) -> int: ... - - @overload - async def write(self: AsyncFile[str], b: str) -> int: ... - - async def write(self, b: ReadableBuffer | str) -> int: - return await to_thread.run_sync(self._fp.write, b) - - @overload - async def writelines( - self: AsyncFile[bytes], lines: Iterable[ReadableBuffer] - ) -> None: ... - - @overload - async def writelines(self: AsyncFile[str], lines: Iterable[str]) -> None: ... - - async def writelines(self, lines: Iterable[ReadableBuffer] | Iterable[str]) -> None: - return await to_thread.run_sync(self._fp.writelines, lines) - - async def truncate(self, size: int | None = None) -> int: - return await to_thread.run_sync(self._fp.truncate, size) - - async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int: - return await to_thread.run_sync(self._fp.seek, offset, whence) - - async def tell(self) -> int: - return await to_thread.run_sync(self._fp.tell) - - async def flush(self) -> None: - return await to_thread.run_sync(self._fp.flush) - - -@overload -async def open_file( - file: str | PathLike[str] | int, - mode: OpenBinaryMode, - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - closefd: bool = ..., - opener: Callable[[str, int], int] | None = ..., -) -> AsyncFile[bytes]: ... - - -@overload -async def open_file( - file: str | PathLike[str] | int, - mode: OpenTextMode = ..., - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - closefd: bool = ..., - opener: Callable[[str, int], int] | None = ..., -) -> AsyncFile[str]: ... - - -async def open_file( - file: str | PathLike[str] | int, - mode: str = "r", - buffering: int = -1, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - closefd: bool = True, - opener: Callable[[str, int], int] | None = None, -) -> AsyncFile[Any]: - """ - Open a file asynchronously. - - The arguments are exactly the same as for the builtin :func:`open`. - - :return: an asynchronous file object - - """ - fp = await to_thread.run_sync( - open, file, mode, buffering, encoding, errors, newline, closefd, opener - ) - return AsyncFile(fp) - - -def wrap_file(file: IO[AnyStr]) -> AsyncFile[AnyStr]: - """ - Wrap an existing file as an asynchronous file. - - :param file: an existing file-like object - :return: an asynchronous file object - - """ - return AsyncFile(file) - - -@dataclass(eq=False) -class _PathIterator(AsyncIterator["Path"]): - iterator: Iterator[PathLike[str]] - - async def __anext__(self) -> Path: - nextval = await to_thread.run_sync( - next, self.iterator, None, abandon_on_cancel=True - ) - if nextval is None: - raise StopAsyncIteration from None - - return Path(nextval) - - -class Path: - """ - An asynchronous version of :class:`pathlib.Path`. - - This class cannot be substituted for :class:`pathlib.Path` or - :class:`pathlib.PurePath`, but it is compatible with the :class:`os.PathLike` - interface. - - It implements the Python 3.10 version of :class:`pathlib.Path` interface, except for - the deprecated :meth:`~pathlib.Path.link_to` method. - - Some methods may be unavailable or have limited functionality, based on the Python - version: - - * :meth:`~pathlib.Path.from_uri` (available on Python 3.13 or later) - * :meth:`~pathlib.Path.full_match` (available on Python 3.13 or later) - * :meth:`~pathlib.Path.is_junction` (available on Python 3.12 or later) - * :meth:`~pathlib.Path.match` (the ``case_sensitive`` paramater is only available on - Python 3.13 or later) - * :meth:`~pathlib.Path.relative_to` (the ``walk_up`` parameter is only available on - Python 3.12 or later) - * :meth:`~pathlib.Path.walk` (available on Python 3.12 or later) - - Any methods that do disk I/O need to be awaited on. These methods are: - - * :meth:`~pathlib.Path.absolute` - * :meth:`~pathlib.Path.chmod` - * :meth:`~pathlib.Path.cwd` - * :meth:`~pathlib.Path.exists` - * :meth:`~pathlib.Path.expanduser` - * :meth:`~pathlib.Path.group` - * :meth:`~pathlib.Path.hardlink_to` - * :meth:`~pathlib.Path.home` - * :meth:`~pathlib.Path.is_block_device` - * :meth:`~pathlib.Path.is_char_device` - * :meth:`~pathlib.Path.is_dir` - * :meth:`~pathlib.Path.is_fifo` - * :meth:`~pathlib.Path.is_file` - * :meth:`~pathlib.Path.is_junction` - * :meth:`~pathlib.Path.is_mount` - * :meth:`~pathlib.Path.is_socket` - * :meth:`~pathlib.Path.is_symlink` - * :meth:`~pathlib.Path.lchmod` - * :meth:`~pathlib.Path.lstat` - * :meth:`~pathlib.Path.mkdir` - * :meth:`~pathlib.Path.open` - * :meth:`~pathlib.Path.owner` - * :meth:`~pathlib.Path.read_bytes` - * :meth:`~pathlib.Path.read_text` - * :meth:`~pathlib.Path.readlink` - * :meth:`~pathlib.Path.rename` - * :meth:`~pathlib.Path.replace` - * :meth:`~pathlib.Path.resolve` - * :meth:`~pathlib.Path.rmdir` - * :meth:`~pathlib.Path.samefile` - * :meth:`~pathlib.Path.stat` - * :meth:`~pathlib.Path.symlink_to` - * :meth:`~pathlib.Path.touch` - * :meth:`~pathlib.Path.unlink` - * :meth:`~pathlib.Path.walk` - * :meth:`~pathlib.Path.write_bytes` - * :meth:`~pathlib.Path.write_text` - - Additionally, the following methods return an async iterator yielding - :class:`~.Path` objects: - - * :meth:`~pathlib.Path.glob` - * :meth:`~pathlib.Path.iterdir` - * :meth:`~pathlib.Path.rglob` - """ - - __slots__ = "_path", "__weakref__" - - __weakref__: Any - - def __init__(self, *args: str | PathLike[str]) -> None: - self._path: Final[pathlib.Path] = pathlib.Path(*args) - - def __fspath__(self) -> str: - return self._path.__fspath__() - - def __str__(self) -> str: - return self._path.__str__() - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.as_posix()!r})" - - def __bytes__(self) -> bytes: - return self._path.__bytes__() - - def __hash__(self) -> int: - return self._path.__hash__() - - def __eq__(self, other: object) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__eq__(target) - - def __lt__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__lt__(target) - - def __le__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__le__(target) - - def __gt__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__gt__(target) - - def __ge__(self, other: pathlib.PurePath | Path) -> bool: - target = other._path if isinstance(other, Path) else other - return self._path.__ge__(target) - - def __truediv__(self, other: str | PathLike[str]) -> Path: - return Path(self._path / other) - - def __rtruediv__(self, other: str | PathLike[str]) -> Path: - return Path(other) / self - - @property - def parts(self) -> tuple[str, ...]: - return self._path.parts - - @property - def drive(self) -> str: - return self._path.drive - - @property - def root(self) -> str: - return self._path.root - - @property - def anchor(self) -> str: - return self._path.anchor - - @property - def parents(self) -> Sequence[Path]: - return tuple(Path(p) for p in self._path.parents) - - @property - def parent(self) -> Path: - return Path(self._path.parent) - - @property - def name(self) -> str: - return self._path.name - - @property - def suffix(self) -> str: - return self._path.suffix - - @property - def suffixes(self) -> list[str]: - return self._path.suffixes - - @property - def stem(self) -> str: - return self._path.stem - - async def absolute(self) -> Path: - path = await to_thread.run_sync(self._path.absolute) - return Path(path) - - def as_posix(self) -> str: - return self._path.as_posix() - - def as_uri(self) -> str: - return self._path.as_uri() - - if sys.version_info >= (3, 13): - parser = pathlib.Path.parser - - @classmethod - def from_uri(cls, uri: str) -> Path: - return Path(pathlib.Path.from_uri(uri)) - - def full_match( - self, path_pattern: str, *, case_sensitive: bool | None = None - ) -> bool: - return self._path.full_match(path_pattern, case_sensitive=case_sensitive) - - def match( - self, path_pattern: str, *, case_sensitive: bool | None = None - ) -> bool: - return self._path.match(path_pattern, case_sensitive=case_sensitive) - else: - - def match(self, path_pattern: str) -> bool: - return self._path.match(path_pattern) - - def is_relative_to(self, other: str | PathLike[str]) -> bool: - try: - self.relative_to(other) - return True - except ValueError: - return False - - async def chmod(self, mode: int, *, follow_symlinks: bool = True) -> None: - func = partial(os.chmod, follow_symlinks=follow_symlinks) - return await to_thread.run_sync(func, self._path, mode) - - @classmethod - async def cwd(cls) -> Path: - path = await to_thread.run_sync(pathlib.Path.cwd) - return cls(path) - - async def exists(self) -> bool: - return await to_thread.run_sync(self._path.exists, abandon_on_cancel=True) - - async def expanduser(self) -> Path: - return Path( - await to_thread.run_sync(self._path.expanduser, abandon_on_cancel=True) - ) - - def glob(self, pattern: str) -> AsyncIterator[Path]: - gen = self._path.glob(pattern) - return _PathIterator(gen) - - async def group(self) -> str: - return await to_thread.run_sync(self._path.group, abandon_on_cancel=True) - - async def hardlink_to( - self, target: str | bytes | PathLike[str] | PathLike[bytes] - ) -> None: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(os.link, target, self) - - @classmethod - async def home(cls) -> Path: - home_path = await to_thread.run_sync(pathlib.Path.home) - return cls(home_path) - - def is_absolute(self) -> bool: - return self._path.is_absolute() - - async def is_block_device(self) -> bool: - return await to_thread.run_sync( - self._path.is_block_device, abandon_on_cancel=True - ) - - async def is_char_device(self) -> bool: - return await to_thread.run_sync( - self._path.is_char_device, abandon_on_cancel=True - ) - - async def is_dir(self) -> bool: - return await to_thread.run_sync(self._path.is_dir, abandon_on_cancel=True) - - async def is_fifo(self) -> bool: - return await to_thread.run_sync(self._path.is_fifo, abandon_on_cancel=True) - - async def is_file(self) -> bool: - return await to_thread.run_sync(self._path.is_file, abandon_on_cancel=True) - - if sys.version_info >= (3, 12): - - async def is_junction(self) -> bool: - return await to_thread.run_sync(self._path.is_junction) - - async def is_mount(self) -> bool: - return await to_thread.run_sync( - os.path.ismount, self._path, abandon_on_cancel=True - ) - - def is_reserved(self) -> bool: - return self._path.is_reserved() - - async def is_socket(self) -> bool: - return await to_thread.run_sync(self._path.is_socket, abandon_on_cancel=True) - - async def is_symlink(self) -> bool: - return await to_thread.run_sync(self._path.is_symlink, abandon_on_cancel=True) - - def iterdir(self) -> AsyncIterator[Path]: - gen = self._path.iterdir() - return _PathIterator(gen) - - def joinpath(self, *args: str | PathLike[str]) -> Path: - return Path(self._path.joinpath(*args)) - - async def lchmod(self, mode: int) -> None: - await to_thread.run_sync(self._path.lchmod, mode) - - async def lstat(self) -> os.stat_result: - return await to_thread.run_sync(self._path.lstat, abandon_on_cancel=True) - - async def mkdir( - self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False - ) -> None: - await to_thread.run_sync(self._path.mkdir, mode, parents, exist_ok) - - @overload - async def open( - self, - mode: OpenBinaryMode, - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - ) -> AsyncFile[bytes]: ... - - @overload - async def open( - self, - mode: OpenTextMode = ..., - buffering: int = ..., - encoding: str | None = ..., - errors: str | None = ..., - newline: str | None = ..., - ) -> AsyncFile[str]: ... - - async def open( - self, - mode: str = "r", - buffering: int = -1, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - ) -> AsyncFile[Any]: - fp = await to_thread.run_sync( - self._path.open, mode, buffering, encoding, errors, newline - ) - return AsyncFile(fp) - - async def owner(self) -> str: - return await to_thread.run_sync(self._path.owner, abandon_on_cancel=True) - - async def read_bytes(self) -> bytes: - return await to_thread.run_sync(self._path.read_bytes) - - async def read_text( - self, encoding: str | None = None, errors: str | None = None - ) -> str: - return await to_thread.run_sync(self._path.read_text, encoding, errors) - - if sys.version_info >= (3, 12): - - def relative_to( - self, *other: str | PathLike[str], walk_up: bool = False - ) -> Path: - return Path(self._path.relative_to(*other, walk_up=walk_up)) - - else: - - def relative_to(self, *other: str | PathLike[str]) -> Path: - return Path(self._path.relative_to(*other)) - - async def readlink(self) -> Path: - target = await to_thread.run_sync(os.readlink, self._path) - return Path(target) - - async def rename(self, target: str | pathlib.PurePath | Path) -> Path: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(self._path.rename, target) - return Path(target) - - async def replace(self, target: str | pathlib.PurePath | Path) -> Path: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(self._path.replace, target) - return Path(target) - - async def resolve(self, strict: bool = False) -> Path: - func = partial(self._path.resolve, strict=strict) - return Path(await to_thread.run_sync(func, abandon_on_cancel=True)) - - def rglob(self, pattern: str) -> AsyncIterator[Path]: - gen = self._path.rglob(pattern) - return _PathIterator(gen) - - async def rmdir(self) -> None: - await to_thread.run_sync(self._path.rmdir) - - async def samefile(self, other_path: str | PathLike[str]) -> bool: - if isinstance(other_path, Path): - other_path = other_path._path - - return await to_thread.run_sync( - self._path.samefile, other_path, abandon_on_cancel=True - ) - - async def stat(self, *, follow_symlinks: bool = True) -> os.stat_result: - func = partial(os.stat, follow_symlinks=follow_symlinks) - return await to_thread.run_sync(func, self._path, abandon_on_cancel=True) - - async def symlink_to( - self, - target: str | bytes | PathLike[str] | PathLike[bytes], - target_is_directory: bool = False, - ) -> None: - if isinstance(target, Path): - target = target._path - - await to_thread.run_sync(self._path.symlink_to, target, target_is_directory) - - async def touch(self, mode: int = 0o666, exist_ok: bool = True) -> None: - await to_thread.run_sync(self._path.touch, mode, exist_ok) - - async def unlink(self, missing_ok: bool = False) -> None: - try: - await to_thread.run_sync(self._path.unlink) - except FileNotFoundError: - if not missing_ok: - raise - - if sys.version_info >= (3, 12): - - async def walk( - self, - top_down: bool = True, - on_error: Callable[[OSError], object] | None = None, - follow_symlinks: bool = False, - ) -> AsyncIterator[tuple[Path, list[str], list[str]]]: - def get_next_value() -> tuple[pathlib.Path, list[str], list[str]] | None: - try: - return next(gen) - except StopIteration: - return None - - gen = self._path.walk(top_down, on_error, follow_symlinks) - while True: - value = await to_thread.run_sync(get_next_value) - if value is None: - return - - root, dirs, paths = value - yield Path(root), dirs, paths - - def with_name(self, name: str) -> Path: - return Path(self._path.with_name(name)) - - def with_stem(self, stem: str) -> Path: - return Path(self._path.with_name(stem + self._path.suffix)) - - def with_suffix(self, suffix: str) -> Path: - return Path(self._path.with_suffix(suffix)) - - def with_segments(self, *pathsegments: str | PathLike[str]) -> Path: - return Path(*pathsegments) - - async def write_bytes(self, data: bytes) -> int: - return await to_thread.run_sync(self._path.write_bytes, data) - - async def write_text( - self, - data: str, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - ) -> int: - # Path.write_text() does not support the "newline" parameter before Python 3.10 - def sync_write_text() -> int: - with self._path.open( - "w", encoding=encoding, errors=errors, newline=newline - ) as fp: - return fp.write(data) - - return await to_thread.run_sync(sync_write_text) - - -PathLike.register(Path) diff --git a/.venv/Lib/site-packages/anyio/_core/_resources.py b/.venv/Lib/site-packages/anyio/_core/_resources.py deleted file mode 100644 index b9a5344..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_resources.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from ..abc import AsyncResource -from ._tasks import CancelScope - - -async def aclose_forcefully(resource: AsyncResource) -> None: - """ - Close an asynchronous resource in a cancelled scope. - - Doing this closes the resource without waiting on anything. - - :param resource: the resource to close - - """ - with CancelScope() as scope: - scope.cancel() - await resource.aclose() diff --git a/.venv/Lib/site-packages/anyio/_core/_signals.py b/.venv/Lib/site-packages/anyio/_core/_signals.py deleted file mode 100644 index f3451d3..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_signals.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from collections.abc import AsyncIterator -from contextlib import AbstractContextManager -from signal import Signals - -from ._eventloop import get_async_backend - - -def open_signal_receiver( - *signals: Signals, -) -> AbstractContextManager[AsyncIterator[Signals]]: - """ - Start receiving operating system signals. - - :param signals: signals to receive (e.g. ``signal.SIGINT``) - :return: an asynchronous context manager for an asynchronous iterator which yields - signal numbers - - .. warning:: Windows does not support signals natively so it is best to avoid - relying on this in cross-platform applications. - - .. warning:: On asyncio, this permanently replaces any previous signal handler for - the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`. - - """ - return get_async_backend().open_signal_receiver(*signals) diff --git a/.venv/Lib/site-packages/anyio/_core/_sockets.py b/.venv/Lib/site-packages/anyio/_core/_sockets.py deleted file mode 100644 index a822d06..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_sockets.py +++ /dev/null @@ -1,787 +0,0 @@ -from __future__ import annotations - -import errno -import os -import socket -import ssl -import stat -import sys -from collections.abc import Awaitable -from ipaddress import IPv6Address, ip_address -from os import PathLike, chmod -from socket import AddressFamily, SocketKind -from typing import TYPE_CHECKING, Any, Literal, cast, overload - -from .. import to_thread -from ..abc import ( - ConnectedUDPSocket, - ConnectedUNIXDatagramSocket, - IPAddressType, - IPSockAddrType, - SocketListener, - SocketStream, - UDPSocket, - UNIXDatagramSocket, - UNIXSocketStream, -) -from ..streams.stapled import MultiListener -from ..streams.tls import TLSStream -from ._eventloop import get_async_backend -from ._resources import aclose_forcefully -from ._synchronization import Event -from ._tasks import create_task_group, move_on_after - -if TYPE_CHECKING: - from _typeshed import FileDescriptorLike -else: - FileDescriptorLike = object - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - -if sys.version_info < (3, 13): - from typing_extensions import deprecated -else: - from warnings import deprecated - -IPPROTO_IPV6 = getattr(socket, "IPPROTO_IPV6", 41) # https://bugs.python.org/issue29515 - -AnyIPAddressFamily = Literal[ - AddressFamily.AF_UNSPEC, AddressFamily.AF_INET, AddressFamily.AF_INET6 -] -IPAddressFamily = Literal[AddressFamily.AF_INET, AddressFamily.AF_INET6] - - -# tls_hostname given -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str, - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# ssl_context given -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - ssl_context: ssl.SSLContext, - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# tls=True -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - tls: Literal[True], - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> TLSStream: ... - - -# tls=False -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - tls: Literal[False], - ssl_context: ssl.SSLContext | None = ..., - tls_standard_compatible: bool = ..., - tls_hostname: str | None = ..., - happy_eyeballs_delay: float = ..., -) -> SocketStream: ... - - -# No TLS arguments -@overload -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = ..., - happy_eyeballs_delay: float = ..., -) -> SocketStream: ... - - -async def connect_tcp( - remote_host: IPAddressType, - remote_port: int, - *, - local_host: IPAddressType | None = None, - tls: bool = False, - ssl_context: ssl.SSLContext | None = None, - tls_standard_compatible: bool = True, - tls_hostname: str | None = None, - happy_eyeballs_delay: float = 0.25, -) -> SocketStream | TLSStream: - """ - Connect to a host using the TCP protocol. - - This function implements the stateless version of the Happy Eyeballs algorithm (RFC - 6555). If ``remote_host`` is a host name that resolves to multiple IP addresses, - each one is tried until one connection attempt succeeds. If the first attempt does - not connected within 250 milliseconds, a second attempt is started using the next - address in the list, and so on. On IPv6 enabled systems, an IPv6 address (if - available) is tried first. - - When the connection has been established, a TLS handshake will be done if either - ``ssl_context`` or ``tls_hostname`` is not ``None``, or if ``tls`` is ``True``. - - :param remote_host: the IP address or host name to connect to - :param remote_port: port on the target host to connect to - :param local_host: the interface address or name to bind the socket to before - connecting - :param tls: ``True`` to do a TLS handshake with the connected stream and return a - :class:`~anyio.streams.tls.TLSStream` instead - :param ssl_context: the SSL context object to use (if omitted, a default context is - created) - :param tls_standard_compatible: If ``True``, performs the TLS shutdown handshake - before closing the stream and requires that the server does this as well. - Otherwise, :exc:`~ssl.SSLEOFError` may be raised during reads from the stream. - Some protocols, such as HTTP, require this option to be ``False``. - See :meth:`~ssl.SSLContext.wrap_socket` for details. - :param tls_hostname: host name to check the server certificate against (defaults to - the value of ``remote_host``) - :param happy_eyeballs_delay: delay (in seconds) before starting the next connection - attempt - :return: a socket stream object if no TLS handshake was done, otherwise a TLS stream - :raises OSError: if the connection attempt fails - - """ - # Placed here due to https://github.com/python/mypy/issues/7057 - connected_stream: SocketStream | None = None - - async def try_connect(remote_host: str, event: Event) -> None: - nonlocal connected_stream - try: - stream = await asynclib.connect_tcp(remote_host, remote_port, local_address) - except OSError as exc: - oserrors.append(exc) - return - else: - if connected_stream is None: - connected_stream = stream - tg.cancel_scope.cancel() - else: - await stream.aclose() - finally: - event.set() - - asynclib = get_async_backend() - local_address: IPSockAddrType | None = None - family = socket.AF_UNSPEC - if local_host: - gai_res = await getaddrinfo(str(local_host), None) - family, *_, local_address = gai_res[0] - - target_host = str(remote_host) - try: - addr_obj = ip_address(remote_host) - except ValueError: - addr_obj = None - - if addr_obj is not None: - if isinstance(addr_obj, IPv6Address): - target_addrs = [(socket.AF_INET6, addr_obj.compressed)] - else: - target_addrs = [(socket.AF_INET, addr_obj.compressed)] - else: - # getaddrinfo() will raise an exception if name resolution fails - gai_res = await getaddrinfo( - target_host, remote_port, family=family, type=socket.SOCK_STREAM - ) - - # Organize the list so that the first address is an IPv6 address (if available) - # and the second one is an IPv4 addresses. The rest can be in whatever order. - v6_found = v4_found = False - target_addrs = [] - for af, *rest, sa in gai_res: - if af == socket.AF_INET6 and not v6_found: - v6_found = True - target_addrs.insert(0, (af, sa[0])) - elif af == socket.AF_INET and not v4_found and v6_found: - v4_found = True - target_addrs.insert(1, (af, sa[0])) - else: - target_addrs.append((af, sa[0])) - - oserrors: list[OSError] = [] - async with create_task_group() as tg: - for i, (af, addr) in enumerate(target_addrs): - event = Event() - tg.start_soon(try_connect, addr, event) - with move_on_after(happy_eyeballs_delay): - await event.wait() - - if connected_stream is None: - cause = ( - oserrors[0] - if len(oserrors) == 1 - else ExceptionGroup("multiple connection attempts failed", oserrors) - ) - raise OSError("All connection attempts failed") from cause - - if tls or tls_hostname or ssl_context: - try: - return await TLSStream.wrap( - connected_stream, - server_side=False, - hostname=tls_hostname or str(remote_host), - ssl_context=ssl_context, - standard_compatible=tls_standard_compatible, - ) - except BaseException: - await aclose_forcefully(connected_stream) - raise - - return connected_stream - - -async def connect_unix(path: str | bytes | PathLike[Any]) -> UNIXSocketStream: - """ - Connect to the given UNIX socket. - - Not available on Windows. - - :param path: path to the socket - :return: a socket stream object - - """ - path = os.fspath(path) - return await get_async_backend().connect_unix(path) - - -async def create_tcp_listener( - *, - local_host: IPAddressType | None = None, - local_port: int = 0, - family: AnyIPAddressFamily = socket.AddressFamily.AF_UNSPEC, - backlog: int = 65536, - reuse_port: bool = False, -) -> MultiListener[SocketStream]: - """ - Create a TCP socket listener. - - :param local_port: port number to listen on - :param local_host: IP address of the interface to listen on. If omitted, listen on - all IPv4 and IPv6 interfaces. To listen on all interfaces on a specific address - family, use ``0.0.0.0`` for IPv4 or ``::`` for IPv6. - :param family: address family (used if ``local_host`` was omitted) - :param backlog: maximum number of queued incoming connections (up to a maximum of - 2**16, or 65536) - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a list of listener objects - - """ - asynclib = get_async_backend() - backlog = min(backlog, 65536) - local_host = str(local_host) if local_host is not None else None - gai_res = await getaddrinfo( - local_host, - local_port, - family=family, - type=socket.SocketKind.SOCK_STREAM if sys.platform == "win32" else 0, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - listeners: list[SocketListener] = [] - try: - # The set() is here to work around a glibc bug: - # https://sourceware.org/bugzilla/show_bug.cgi?id=14969 - sockaddr: tuple[str, int] | tuple[str, int, int, int] - for fam, kind, *_, sockaddr in sorted(set(gai_res)): - # Workaround for an uvloop bug where we don't get the correct scope ID for - # IPv6 link-local addresses when passing type=socket.SOCK_STREAM to - # getaddrinfo(): https://github.com/MagicStack/uvloop/issues/539 - if sys.platform != "win32" and kind is not SocketKind.SOCK_STREAM: - continue - - raw_socket = socket.socket(fam) - raw_socket.setblocking(False) - - # For Windows, enable exclusive address use. For others, enable address - # reuse. - if sys.platform == "win32": - raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - else: - raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - - if reuse_port: - raw_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - - # If only IPv6 was requested, disable dual stack operation - if fam == socket.AF_INET6: - raw_socket.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) - - # Workaround for #554 - if "%" in sockaddr[0]: - addr, scope_id = sockaddr[0].split("%", 1) - sockaddr = (addr, sockaddr[1], 0, int(scope_id)) - - raw_socket.bind(sockaddr) - raw_socket.listen(backlog) - listener = asynclib.create_tcp_listener(raw_socket) - listeners.append(listener) - except BaseException: - for listener in listeners: - await listener.aclose() - - raise - - return MultiListener(listeners) - - -async def create_unix_listener( - path: str | bytes | PathLike[Any], - *, - mode: int | None = None, - backlog: int = 65536, -) -> SocketListener: - """ - Create a UNIX socket listener. - - Not available on Windows. - - :param path: path of the socket - :param mode: permissions to set on the socket - :param backlog: maximum number of queued incoming connections (up to a maximum of - 2**16, or 65536) - :return: a listener object - - .. versionchanged:: 3.0 - If a socket already exists on the file system in the given path, it will be - removed first. - - """ - backlog = min(backlog, 65536) - raw_socket = await setup_unix_local_socket(path, mode, socket.SOCK_STREAM) - try: - raw_socket.listen(backlog) - return get_async_backend().create_unix_listener(raw_socket) - except BaseException: - raw_socket.close() - raise - - -async def create_udp_socket( - family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, - *, - local_host: IPAddressType | None = None, - local_port: int = 0, - reuse_port: bool = False, -) -> UDPSocket: - """ - Create a UDP socket. - - If ``port`` has been given, the socket will be bound to this port on the local - machine, making this socket suitable for providing UDP based services. - - :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically - determined from ``local_host`` if omitted - :param local_host: IP address or host name of the local interface to bind to - :param local_port: local port to bind to - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a UDP socket - - """ - if family is AddressFamily.AF_UNSPEC and not local_host: - raise ValueError('Either "family" or "local_host" must be given') - - if local_host: - gai_res = await getaddrinfo( - str(local_host), - local_port, - family=family, - type=socket.SOCK_DGRAM, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - local_address = gai_res[0][-1] - elif family is AddressFamily.AF_INET6: - local_address = ("::", 0) - else: - local_address = ("0.0.0.0", 0) - - sock = await get_async_backend().create_udp_socket( - family, local_address, None, reuse_port - ) - return cast(UDPSocket, sock) - - -async def create_connected_udp_socket( - remote_host: IPAddressType, - remote_port: int, - *, - family: AnyIPAddressFamily = AddressFamily.AF_UNSPEC, - local_host: IPAddressType | None = None, - local_port: int = 0, - reuse_port: bool = False, -) -> ConnectedUDPSocket: - """ - Create a connected UDP socket. - - Connected UDP sockets can only communicate with the specified remote host/port, an - any packets sent from other sources are dropped. - - :param remote_host: remote host to set as the default target - :param remote_port: port on the remote host to set as the default target - :param family: address family (``AF_INET`` or ``AF_INET6``) – automatically - determined from ``local_host`` or ``remote_host`` if omitted - :param local_host: IP address or host name of the local interface to bind to - :param local_port: local port to bind to - :param reuse_port: ``True`` to allow multiple sockets to bind to the same - address/port (not supported on Windows) - :return: a connected UDP socket - - """ - local_address = None - if local_host: - gai_res = await getaddrinfo( - str(local_host), - local_port, - family=family, - type=socket.SOCK_DGRAM, - flags=socket.AI_PASSIVE | socket.AI_ADDRCONFIG, - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - local_address = gai_res[0][-1] - - gai_res = await getaddrinfo( - str(remote_host), remote_port, family=family, type=socket.SOCK_DGRAM - ) - family = cast(AnyIPAddressFamily, gai_res[0][0]) - remote_address = gai_res[0][-1] - - sock = await get_async_backend().create_udp_socket( - family, local_address, remote_address, reuse_port - ) - return cast(ConnectedUDPSocket, sock) - - -async def create_unix_datagram_socket( - *, - local_path: None | str | bytes | PathLike[Any] = None, - local_mode: int | None = None, -) -> UNIXDatagramSocket: - """ - Create a UNIX datagram socket. - - Not available on Windows. - - If ``local_path`` has been given, the socket will be bound to this path, making this - socket suitable for receiving datagrams from other processes. Other processes can - send datagrams to this socket only if ``local_path`` is set. - - If a socket already exists on the file system in the ``local_path``, it will be - removed first. - - :param local_path: the path on which to bind to - :param local_mode: permissions to set on the local socket - :return: a UNIX datagram socket - - """ - raw_socket = await setup_unix_local_socket( - local_path, local_mode, socket.SOCK_DGRAM - ) - return await get_async_backend().create_unix_datagram_socket(raw_socket, None) - - -async def create_connected_unix_datagram_socket( - remote_path: str | bytes | PathLike[Any], - *, - local_path: None | str | bytes | PathLike[Any] = None, - local_mode: int | None = None, -) -> ConnectedUNIXDatagramSocket: - """ - Create a connected UNIX datagram socket. - - Connected datagram sockets can only communicate with the specified remote path. - - If ``local_path`` has been given, the socket will be bound to this path, making - this socket suitable for receiving datagrams from other processes. Other processes - can send datagrams to this socket only if ``local_path`` is set. - - If a socket already exists on the file system in the ``local_path``, it will be - removed first. - - :param remote_path: the path to set as the default target - :param local_path: the path on which to bind to - :param local_mode: permissions to set on the local socket - :return: a connected UNIX datagram socket - - """ - remote_path = os.fspath(remote_path) - raw_socket = await setup_unix_local_socket( - local_path, local_mode, socket.SOCK_DGRAM - ) - return await get_async_backend().create_unix_datagram_socket( - raw_socket, remote_path - ) - - -async def getaddrinfo( - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, -) -> list[tuple[AddressFamily, SocketKind, int, str, tuple[str, int]]]: - """ - Look up a numeric IP address given a host name. - - Internationalized domain names are translated according to the (non-transitional) - IDNA 2008 standard. - - .. note:: 4-tuple IPv6 socket addresses are automatically converted to 2-tuples of - (host, port), unlike what :func:`socket.getaddrinfo` does. - - :param host: host name - :param port: port number - :param family: socket family (`'AF_INET``, ...) - :param type: socket type (``SOCK_STREAM``, ...) - :param proto: protocol number - :param flags: flags to pass to upstream ``getaddrinfo()`` - :return: list of tuples containing (family, type, proto, canonname, sockaddr) - - .. seealso:: :func:`socket.getaddrinfo` - - """ - # Handle unicode hostnames - if isinstance(host, str): - try: - encoded_host: bytes | None = host.encode("ascii") - except UnicodeEncodeError: - import idna - - encoded_host = idna.encode(host, uts46=True) - else: - encoded_host = host - - gai_res = await get_async_backend().getaddrinfo( - encoded_host, port, family=family, type=type, proto=proto, flags=flags - ) - return [ - (family, type, proto, canonname, convert_ipv6_sockaddr(sockaddr)) - for family, type, proto, canonname, sockaddr in gai_res - ] - - -def getnameinfo(sockaddr: IPSockAddrType, flags: int = 0) -> Awaitable[tuple[str, str]]: - """ - Look up the host name of an IP address. - - :param sockaddr: socket address (e.g. (ipaddress, port) for IPv4) - :param flags: flags to pass to upstream ``getnameinfo()`` - :return: a tuple of (host name, service name) - - .. seealso:: :func:`socket.getnameinfo` - - """ - return get_async_backend().getnameinfo(sockaddr, flags) - - -@deprecated("This function is deprecated; use `wait_readable` instead") -def wait_socket_readable(sock: socket.socket) -> Awaitable[None]: - """ - .. deprecated:: 4.7.0 - Use :func:`wait_readable` instead. - - Wait until the given socket has data to be read. - - .. warning:: Only use this on raw sockets that have not been wrapped by any higher - level constructs like socket streams! - - :param sock: a socket object - :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the - socket to become readable - :raises ~anyio.BusyResourceError: if another task is already waiting for the socket - to become readable - - """ - return get_async_backend().wait_readable(sock.fileno()) - - -@deprecated("This function is deprecated; use `wait_writable` instead") -def wait_socket_writable(sock: socket.socket) -> Awaitable[None]: - """ - .. deprecated:: 4.7.0 - Use :func:`wait_writable` instead. - - Wait until the given socket can be written to. - - This does **NOT** work on Windows when using the asyncio backend with a proactor - event loop (default on py3.8+). - - .. warning:: Only use this on raw sockets that have not been wrapped by any higher - level constructs like socket streams! - - :param sock: a socket object - :raises ~anyio.ClosedResourceError: if the socket was closed while waiting for the - socket to become writable - :raises ~anyio.BusyResourceError: if another task is already waiting for the socket - to become writable - - """ - return get_async_backend().wait_writable(sock.fileno()) - - -def wait_readable(obj: FileDescriptorLike) -> Awaitable[None]: - """ - Wait until the given object has data to be read. - - On Unix systems, ``obj`` must either be an integer file descriptor, or else an - object with a ``.fileno()`` method which returns an integer file descriptor. Any - kind of file descriptor can be passed, though the exact semantics will depend on - your kernel. For example, this probably won't do anything useful for on-disk files. - - On Windows systems, ``obj`` must either be an integer ``SOCKET`` handle, or else an - object with a ``.fileno()`` method which returns an integer ``SOCKET`` handle. File - descriptors aren't supported, and neither are handles that refer to anything besides - a ``SOCKET``. - - On backends where this functionality is not natively provided (asyncio - ``ProactorEventLoop`` on Windows), it is provided using a separate selector thread - which is set to shut down when the interpreter shuts down. - - .. warning:: Don't use this on raw sockets that have been wrapped by any higher - level constructs like socket streams! - - :param obj: an object with a ``.fileno()`` method or an integer handle - :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the - object to become readable - :raises ~anyio.BusyResourceError: if another task is already waiting for the object - to become readable - - """ - return get_async_backend().wait_readable(obj) - - -def wait_writable(obj: FileDescriptorLike) -> Awaitable[None]: - """ - Wait until the given object can be written to. - - :param obj: an object with a ``.fileno()`` method or an integer handle - :raises ~anyio.ClosedResourceError: if the object was closed while waiting for the - object to become writable - :raises ~anyio.BusyResourceError: if another task is already waiting for the object - to become writable - - .. seealso:: See the documentation of :func:`wait_readable` for the definition of - ``obj`` and notes on backend compatibility. - - .. warning:: Don't use this on raw sockets that have been wrapped by any higher - level constructs like socket streams! - - """ - return get_async_backend().wait_writable(obj) - - -# -# Private API -# - - -def convert_ipv6_sockaddr( - sockaddr: tuple[str, int, int, int] | tuple[str, int], -) -> tuple[str, int]: - """ - Convert a 4-tuple IPv6 socket address to a 2-tuple (address, port) format. - - If the scope ID is nonzero, it is added to the address, separated with ``%``. - Otherwise the flow id and scope id are simply cut off from the tuple. - Any other kinds of socket addresses are returned as-is. - - :param sockaddr: the result of :meth:`~socket.socket.getsockname` - :return: the converted socket address - - """ - # This is more complicated than it should be because of MyPy - if isinstance(sockaddr, tuple) and len(sockaddr) == 4: - host, port, flowinfo, scope_id = sockaddr - if scope_id: - # PyPy (as of v7.3.11) leaves the interface name in the result, so - # we discard it and only get the scope ID from the end - # (https://foss.heptapod.net/pypy/pypy/-/issues/3938) - host = host.split("%")[0] - - # Add scope_id to the address - return f"{host}%{scope_id}", port - else: - return host, port - else: - return sockaddr - - -async def setup_unix_local_socket( - path: None | str | bytes | PathLike[Any], - mode: int | None, - socktype: int, -) -> socket.socket: - """ - Create a UNIX local socket object, deleting the socket at the given path if it - exists. - - Not available on Windows. - - :param path: path of the socket - :param mode: permissions to set on the socket - :param socktype: socket.SOCK_STREAM or socket.SOCK_DGRAM - - """ - path_str: str | None - if path is not None: - path_str = os.fsdecode(path) - - # Linux abstract namespace sockets aren't backed by a concrete file so skip stat call - if not path_str.startswith("\0"): - # Copied from pathlib... - try: - stat_result = os.stat(path) - except OSError as e: - if e.errno not in ( - errno.ENOENT, - errno.ENOTDIR, - errno.EBADF, - errno.ELOOP, - ): - raise - else: - if stat.S_ISSOCK(stat_result.st_mode): - os.unlink(path) - else: - path_str = None - - raw_socket = socket.socket(socket.AF_UNIX, socktype) - raw_socket.setblocking(False) - - if path_str is not None: - try: - await to_thread.run_sync(raw_socket.bind, path_str, abandon_on_cancel=True) - if mode is not None: - await to_thread.run_sync(chmod, path_str, mode, abandon_on_cancel=True) - except BaseException: - raw_socket.close() - raise - - return raw_socket diff --git a/.venv/Lib/site-packages/anyio/_core/_streams.py b/.venv/Lib/site-packages/anyio/_core/_streams.py deleted file mode 100644 index 6a9814e..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_streams.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -import math -from typing import TypeVar -from warnings import warn - -from ..streams.memory import ( - MemoryObjectReceiveStream, - MemoryObjectSendStream, - MemoryObjectStreamState, -) - -T_Item = TypeVar("T_Item") - - -class create_memory_object_stream( - tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]], -): - """ - Create a memory object stream. - - The stream's item type can be annotated like - :func:`create_memory_object_stream[T_Item]`. - - :param max_buffer_size: number of items held in the buffer until ``send()`` starts - blocking - :param item_type: old way of marking the streams with the right generic type for - static typing (does nothing on AnyIO 4) - - .. deprecated:: 4.0 - Use ``create_memory_object_stream[YourItemType](...)`` instead. - :return: a tuple of (send stream, receive stream) - - """ - - def __new__( # type: ignore[misc] - cls, max_buffer_size: float = 0, item_type: object = None - ) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]: - if max_buffer_size != math.inf and not isinstance(max_buffer_size, int): - raise ValueError("max_buffer_size must be either an integer or math.inf") - if max_buffer_size < 0: - raise ValueError("max_buffer_size cannot be negative") - if item_type is not None: - warn( - "The item_type argument has been deprecated in AnyIO 4.0. " - "Use create_memory_object_stream[YourItemType](...) instead.", - DeprecationWarning, - stacklevel=2, - ) - - state = MemoryObjectStreamState[T_Item](max_buffer_size) - return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state)) diff --git a/.venv/Lib/site-packages/anyio/_core/_subprocesses.py b/.venv/Lib/site-packages/anyio/_core/_subprocesses.py deleted file mode 100644 index 7ba41a5..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_subprocesses.py +++ /dev/null @@ -1,196 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import AsyncIterable, Iterable, Mapping, Sequence -from io import BytesIO -from os import PathLike -from subprocess import DEVNULL, PIPE, CalledProcessError, CompletedProcess -from typing import IO, Any, Union, cast - -from ..abc import Process -from ._eventloop import get_async_backend -from ._tasks import create_task_group - -if sys.version_info >= (3, 10): - from typing import TypeAlias -else: - from typing_extensions import TypeAlias - -StrOrBytesPath: TypeAlias = Union[str, bytes, "PathLike[str]", "PathLike[bytes]"] - - -async def run_process( - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - input: bytes | None = None, - stdout: int | IO[Any] | None = PIPE, - stderr: int | IO[Any] | None = PIPE, - check: bool = True, - cwd: StrOrBytesPath | None = None, - env: Mapping[str, str] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - start_new_session: bool = False, - pass_fds: Sequence[int] = (), - user: str | int | None = None, - group: str | int | None = None, - extra_groups: Iterable[str | int] | None = None, - umask: int = -1, -) -> CompletedProcess[bytes]: - """ - Run an external command in a subprocess and wait until it completes. - - .. seealso:: :func:`subprocess.run` - - :param command: either a string to pass to the shell, or an iterable of strings - containing the executable name or path and its arguments - :param input: bytes passed to the standard input of the subprocess - :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - a file-like object, or `None` - :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - :data:`subprocess.STDOUT`, a file-like object, or `None` - :param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the - process terminates with a return code other than 0 - :param cwd: If not ``None``, change the working directory to this before running the - command - :param env: if not ``None``, this mapping replaces the inherited environment - variables from the parent process - :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used - to specify process startup parameters (Windows only) - :param creationflags: flags that can be used to control the creation of the - subprocess (see :class:`subprocess.Popen` for the specifics) - :param start_new_session: if ``true`` the setsid() system call will be made in the - child process prior to the execution of the subprocess. (POSIX only) - :param pass_fds: sequence of file descriptors to keep open between the parent and - child processes. (POSIX only) - :param user: effective user to run the process as (Python >= 3.9, POSIX only) - :param group: effective group to run the process as (Python >= 3.9, POSIX only) - :param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9, - POSIX only) - :param umask: if not negative, this umask is applied in the child process before - running the given command (Python >= 3.9, POSIX only) - :return: an object representing the completed process - :raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process - exits with a nonzero return code - - """ - - async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None: - buffer = BytesIO() - async for chunk in stream: - buffer.write(chunk) - - stream_contents[index] = buffer.getvalue() - - async with await open_process( - command, - stdin=PIPE if input else DEVNULL, - stdout=stdout, - stderr=stderr, - cwd=cwd, - env=env, - startupinfo=startupinfo, - creationflags=creationflags, - start_new_session=start_new_session, - pass_fds=pass_fds, - user=user, - group=group, - extra_groups=extra_groups, - umask=umask, - ) as process: - stream_contents: list[bytes | None] = [None, None] - async with create_task_group() as tg: - if process.stdout: - tg.start_soon(drain_stream, process.stdout, 0) - - if process.stderr: - tg.start_soon(drain_stream, process.stderr, 1) - - if process.stdin and input: - await process.stdin.send(input) - await process.stdin.aclose() - - await process.wait() - - output, errors = stream_contents - if check and process.returncode != 0: - raise CalledProcessError(cast(int, process.returncode), command, output, errors) - - return CompletedProcess(command, cast(int, process.returncode), output, errors) - - -async def open_process( - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None = PIPE, - stdout: int | IO[Any] | None = PIPE, - stderr: int | IO[Any] | None = PIPE, - cwd: StrOrBytesPath | None = None, - env: Mapping[str, str] | None = None, - startupinfo: Any = None, - creationflags: int = 0, - start_new_session: bool = False, - pass_fds: Sequence[int] = (), - user: str | int | None = None, - group: str | int | None = None, - extra_groups: Iterable[str | int] | None = None, - umask: int = -1, -) -> Process: - """ - Start an external command in a subprocess. - - .. seealso:: :class:`subprocess.Popen` - - :param command: either a string to pass to the shell, or an iterable of strings - containing the executable name or path and its arguments - :param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a - file-like object, or ``None`` - :param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - a file-like object, or ``None`` - :param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, - :data:`subprocess.STDOUT`, a file-like object, or ``None`` - :param cwd: If not ``None``, the working directory is changed before executing - :param env: If env is not ``None``, it must be a mapping that defines the - environment variables for the new process - :param creationflags: flags that can be used to control the creation of the - subprocess (see :class:`subprocess.Popen` for the specifics) - :param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used - to specify process startup parameters (Windows only) - :param start_new_session: if ``true`` the setsid() system call will be made in the - child process prior to the execution of the subprocess. (POSIX only) - :param pass_fds: sequence of file descriptors to keep open between the parent and - child processes. (POSIX only) - :param user: effective user to run the process as (POSIX only) - :param group: effective group to run the process as (POSIX only) - :param extra_groups: supplementary groups to set in the subprocess (POSIX only) - :param umask: if not negative, this umask is applied in the child process before - running the given command (POSIX only) - :return: an asynchronous process object - - """ - kwargs: dict[str, Any] = {} - if user is not None: - kwargs["user"] = user - - if group is not None: - kwargs["group"] = group - - if extra_groups is not None: - kwargs["extra_groups"] = group - - if umask >= 0: - kwargs["umask"] = umask - - return await get_async_backend().open_process( - command, - stdin=stdin, - stdout=stdout, - stderr=stderr, - cwd=cwd, - env=env, - startupinfo=startupinfo, - creationflags=creationflags, - start_new_session=start_new_session, - pass_fds=pass_fds, - **kwargs, - ) diff --git a/.venv/Lib/site-packages/anyio/_core/_synchronization.py b/.venv/Lib/site-packages/anyio/_core/_synchronization.py deleted file mode 100644 index 7878ba6..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_synchronization.py +++ /dev/null @@ -1,733 +0,0 @@ -from __future__ import annotations - -import math -from collections import deque -from dataclasses import dataclass -from types import TracebackType - -from sniffio import AsyncLibraryNotFoundError - -from ..lowlevel import checkpoint -from ._eventloop import get_async_backend -from ._exceptions import BusyResourceError -from ._tasks import CancelScope -from ._testing import TaskInfo, get_current_task - - -@dataclass(frozen=True) -class EventStatistics: - """ - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait` - """ - - tasks_waiting: int - - -@dataclass(frozen=True) -class CapacityLimiterStatistics: - """ - :ivar int borrowed_tokens: number of tokens currently borrowed by tasks - :ivar float total_tokens: total number of available tokens - :ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from - this limiter - :ivar int tasks_waiting: number of tasks waiting on - :meth:`~.CapacityLimiter.acquire` or - :meth:`~.CapacityLimiter.acquire_on_behalf_of` - """ - - borrowed_tokens: int - total_tokens: float - borrowers: tuple[object, ...] - tasks_waiting: int - - -@dataclass(frozen=True) -class LockStatistics: - """ - :ivar bool locked: flag indicating if this lock is locked or not - :ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the - lock is not held by any task) - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire` - """ - - locked: bool - owner: TaskInfo | None - tasks_waiting: int - - -@dataclass(frozen=True) -class ConditionStatistics: - """ - :ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait` - :ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying - :class:`~.Lock` - """ - - tasks_waiting: int - lock_statistics: LockStatistics - - -@dataclass(frozen=True) -class SemaphoreStatistics: - """ - :ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire` - - """ - - tasks_waiting: int - - -class Event: - def __new__(cls) -> Event: - try: - return get_async_backend().create_event() - except AsyncLibraryNotFoundError: - return EventAdapter() - - def set(self) -> None: - """Set the flag, notifying all listeners.""" - raise NotImplementedError - - def is_set(self) -> bool: - """Return ``True`` if the flag is set, ``False`` if not.""" - raise NotImplementedError - - async def wait(self) -> None: - """ - Wait until the flag has been set. - - If the flag has already been set when this method is called, it returns - immediately. - - """ - raise NotImplementedError - - def statistics(self) -> EventStatistics: - """Return statistics about the current state of this event.""" - raise NotImplementedError - - -class EventAdapter(Event): - _internal_event: Event | None = None - _is_set: bool = False - - def __new__(cls) -> EventAdapter: - return object.__new__(cls) - - @property - def _event(self) -> Event: - if self._internal_event is None: - self._internal_event = get_async_backend().create_event() - if self._is_set: - self._internal_event.set() - - return self._internal_event - - def set(self) -> None: - if self._internal_event is None: - self._is_set = True - else: - self._event.set() - - def is_set(self) -> bool: - if self._internal_event is None: - return self._is_set - - return self._internal_event.is_set() - - async def wait(self) -> None: - await self._event.wait() - - def statistics(self) -> EventStatistics: - if self._internal_event is None: - return EventStatistics(tasks_waiting=0) - - return self._internal_event.statistics() - - -class Lock: - def __new__(cls, *, fast_acquire: bool = False) -> Lock: - try: - return get_async_backend().create_lock(fast_acquire=fast_acquire) - except AsyncLibraryNotFoundError: - return LockAdapter(fast_acquire=fast_acquire) - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - async def acquire(self) -> None: - """Acquire the lock.""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire the lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - raise NotImplementedError - - def release(self) -> None: - """Release the lock.""" - raise NotImplementedError - - def locked(self) -> bool: - """Return True if the lock is currently held.""" - raise NotImplementedError - - def statistics(self) -> LockStatistics: - """ - Return statistics about the current state of this lock. - - .. versionadded:: 3.0 - """ - raise NotImplementedError - - -class LockAdapter(Lock): - _internal_lock: Lock | None = None - - def __new__(cls, *, fast_acquire: bool = False) -> LockAdapter: - return object.__new__(cls) - - def __init__(self, *, fast_acquire: bool = False): - self._fast_acquire = fast_acquire - - @property - def _lock(self) -> Lock: - if self._internal_lock is None: - self._internal_lock = get_async_backend().create_lock( - fast_acquire=self._fast_acquire - ) - - return self._internal_lock - - async def __aenter__(self) -> None: - await self._lock.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - if self._internal_lock is not None: - self._internal_lock.release() - - async def acquire(self) -> None: - """Acquire the lock.""" - await self._lock.acquire() - - def acquire_nowait(self) -> None: - """ - Acquire the lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - self._lock.acquire_nowait() - - def release(self) -> None: - """Release the lock.""" - self._lock.release() - - def locked(self) -> bool: - """Return True if the lock is currently held.""" - return self._lock.locked() - - def statistics(self) -> LockStatistics: - """ - Return statistics about the current state of this lock. - - .. versionadded:: 3.0 - - """ - if self._internal_lock is None: - return LockStatistics(False, None, 0) - - return self._internal_lock.statistics() - - -class Condition: - _owner_task: TaskInfo | None = None - - def __init__(self, lock: Lock | None = None): - self._lock = lock or Lock() - self._waiters: deque[Event] = deque() - - async def __aenter__(self) -> None: - await self.acquire() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - def _check_acquired(self) -> None: - if self._owner_task != get_current_task(): - raise RuntimeError("The current task is not holding the underlying lock") - - async def acquire(self) -> None: - """Acquire the underlying lock.""" - await self._lock.acquire() - self._owner_task = get_current_task() - - def acquire_nowait(self) -> None: - """ - Acquire the underlying lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - self._lock.acquire_nowait() - self._owner_task = get_current_task() - - def release(self) -> None: - """Release the underlying lock.""" - self._lock.release() - - def locked(self) -> bool: - """Return True if the lock is set.""" - return self._lock.locked() - - def notify(self, n: int = 1) -> None: - """Notify exactly n listeners.""" - self._check_acquired() - for _ in range(n): - try: - event = self._waiters.popleft() - except IndexError: - break - - event.set() - - def notify_all(self) -> None: - """Notify all the listeners.""" - self._check_acquired() - for event in self._waiters: - event.set() - - self._waiters.clear() - - async def wait(self) -> None: - """Wait for a notification.""" - await checkpoint() - event = Event() - self._waiters.append(event) - self.release() - try: - await event.wait() - except BaseException: - if not event.is_set(): - self._waiters.remove(event) - - raise - finally: - with CancelScope(shield=True): - await self.acquire() - - def statistics(self) -> ConditionStatistics: - """ - Return statistics about the current state of this condition. - - .. versionadded:: 3.0 - """ - return ConditionStatistics(len(self._waiters), self._lock.statistics()) - - -class Semaphore: - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - try: - return get_async_backend().create_semaphore( - initial_value, max_value=max_value, fast_acquire=fast_acquire - ) - except AsyncLibraryNotFoundError: - return SemaphoreAdapter(initial_value, max_value=max_value) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ): - if not isinstance(initial_value, int): - raise TypeError("initial_value must be an integer") - if initial_value < 0: - raise ValueError("initial_value must be >= 0") - if max_value is not None: - if not isinstance(max_value, int): - raise TypeError("max_value must be an integer or None") - if max_value < initial_value: - raise ValueError( - "max_value must be equal to or higher than initial_value" - ) - - self._fast_acquire = fast_acquire - - async def __aenter__(self) -> Semaphore: - await self.acquire() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.release() - - async def acquire(self) -> None: - """Decrement the semaphore value, blocking if necessary.""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire the underlying lock, without blocking. - - :raises ~anyio.WouldBlock: if the operation would block - - """ - raise NotImplementedError - - def release(self) -> None: - """Increment the semaphore value.""" - raise NotImplementedError - - @property - def value(self) -> int: - """The current value of the semaphore.""" - raise NotImplementedError - - @property - def max_value(self) -> int | None: - """The maximum value of the semaphore.""" - raise NotImplementedError - - def statistics(self) -> SemaphoreStatistics: - """ - Return statistics about the current state of this semaphore. - - .. versionadded:: 3.0 - """ - raise NotImplementedError - - -class SemaphoreAdapter(Semaphore): - _internal_semaphore: Semaphore | None = None - - def __new__( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> SemaphoreAdapter: - return object.__new__(cls) - - def __init__( - self, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> None: - super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire) - self._initial_value = initial_value - self._max_value = max_value - - @property - def _semaphore(self) -> Semaphore: - if self._internal_semaphore is None: - self._internal_semaphore = get_async_backend().create_semaphore( - self._initial_value, max_value=self._max_value - ) - - return self._internal_semaphore - - async def acquire(self) -> None: - await self._semaphore.acquire() - - def acquire_nowait(self) -> None: - self._semaphore.acquire_nowait() - - def release(self) -> None: - self._semaphore.release() - - @property - def value(self) -> int: - if self._internal_semaphore is None: - return self._initial_value - - return self._semaphore.value - - @property - def max_value(self) -> int | None: - return self._max_value - - def statistics(self) -> SemaphoreStatistics: - if self._internal_semaphore is None: - return SemaphoreStatistics(tasks_waiting=0) - - return self._semaphore.statistics() - - -class CapacityLimiter: - def __new__(cls, total_tokens: float) -> CapacityLimiter: - try: - return get_async_backend().create_capacity_limiter(total_tokens) - except AsyncLibraryNotFoundError: - return CapacityLimiterAdapter(total_tokens) - - async def __aenter__(self) -> None: - raise NotImplementedError - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - raise NotImplementedError - - @property - def total_tokens(self) -> float: - """ - The total number of tokens available for borrowing. - - This is a read-write property. If the total number of tokens is increased, the - proportionate number of tasks waiting on this limiter will be granted their - tokens. - - .. versionchanged:: 3.0 - The property is now writable. - - """ - raise NotImplementedError - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - raise NotImplementedError - - @property - def borrowed_tokens(self) -> int: - """The number of tokens that have currently been borrowed.""" - raise NotImplementedError - - @property - def available_tokens(self) -> float: - """The number of tokens currently available to be borrowed""" - raise NotImplementedError - - def acquire_nowait(self) -> None: - """ - Acquire a token for the current task without waiting for one to become - available. - - :raises ~anyio.WouldBlock: if there are no tokens available for borrowing - - """ - raise NotImplementedError - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - """ - Acquire a token without waiting for one to become available. - - :param borrower: the entity borrowing a token - :raises ~anyio.WouldBlock: if there are no tokens available for borrowing - - """ - raise NotImplementedError - - async def acquire(self) -> None: - """ - Acquire a token for the current task, waiting if necessary for one to become - available. - - """ - raise NotImplementedError - - async def acquire_on_behalf_of(self, borrower: object) -> None: - """ - Acquire a token, waiting if necessary for one to become available. - - :param borrower: the entity borrowing a token - - """ - raise NotImplementedError - - def release(self) -> None: - """ - Release the token held by the current task. - - :raises RuntimeError: if the current task has not borrowed a token from this - limiter. - - """ - raise NotImplementedError - - def release_on_behalf_of(self, borrower: object) -> None: - """ - Release the token held by the given borrower. - - :raises RuntimeError: if the borrower has not borrowed a token from this - limiter. - - """ - raise NotImplementedError - - def statistics(self) -> CapacityLimiterStatistics: - """ - Return statistics about the current state of this limiter. - - .. versionadded:: 3.0 - - """ - raise NotImplementedError - - -class CapacityLimiterAdapter(CapacityLimiter): - _internal_limiter: CapacityLimiter | None = None - - def __new__(cls, total_tokens: float) -> CapacityLimiterAdapter: - return object.__new__(cls) - - def __init__(self, total_tokens: float) -> None: - self.total_tokens = total_tokens - - @property - def _limiter(self) -> CapacityLimiter: - if self._internal_limiter is None: - self._internal_limiter = get_async_backend().create_capacity_limiter( - self._total_tokens - ) - - return self._internal_limiter - - async def __aenter__(self) -> None: - await self._limiter.__aenter__() - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - return await self._limiter.__aexit__(exc_type, exc_val, exc_tb) - - @property - def total_tokens(self) -> float: - if self._internal_limiter is None: - return self._total_tokens - - return self._internal_limiter.total_tokens - - @total_tokens.setter - def total_tokens(self, value: float) -> None: - if not isinstance(value, int) and value is not math.inf: - raise TypeError("total_tokens must be an int or math.inf") - elif value < 1: - raise ValueError("total_tokens must be >= 1") - - if self._internal_limiter is None: - self._total_tokens = value - return - - self._limiter.total_tokens = value - - @property - def borrowed_tokens(self) -> int: - if self._internal_limiter is None: - return 0 - - return self._internal_limiter.borrowed_tokens - - @property - def available_tokens(self) -> float: - if self._internal_limiter is None: - return self._total_tokens - - return self._internal_limiter.available_tokens - - def acquire_nowait(self) -> None: - self._limiter.acquire_nowait() - - def acquire_on_behalf_of_nowait(self, borrower: object) -> None: - self._limiter.acquire_on_behalf_of_nowait(borrower) - - async def acquire(self) -> None: - await self._limiter.acquire() - - async def acquire_on_behalf_of(self, borrower: object) -> None: - await self._limiter.acquire_on_behalf_of(borrower) - - def release(self) -> None: - self._limiter.release() - - def release_on_behalf_of(self, borrower: object) -> None: - self._limiter.release_on_behalf_of(borrower) - - def statistics(self) -> CapacityLimiterStatistics: - if self._internal_limiter is None: - return CapacityLimiterStatistics( - borrowed_tokens=0, - total_tokens=self.total_tokens, - borrowers=(), - tasks_waiting=0, - ) - - return self._internal_limiter.statistics() - - -class ResourceGuard: - """ - A context manager for ensuring that a resource is only used by a single task at a - time. - - Entering this context manager while the previous has not exited it yet will trigger - :exc:`BusyResourceError`. - - :param action: the action to guard against (visible in the :exc:`BusyResourceError` - when triggered, e.g. "Another task is already {action} this resource") - - .. versionadded:: 4.1 - """ - - __slots__ = "action", "_guarded" - - def __init__(self, action: str = "using"): - self.action: str = action - self._guarded = False - - def __enter__(self) -> None: - if self._guarded: - raise BusyResourceError(self.action) - - self._guarded = True - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - self._guarded = False - return None diff --git a/.venv/Lib/site-packages/anyio/_core/_tasks.py b/.venv/Lib/site-packages/anyio/_core/_tasks.py deleted file mode 100644 index 2f21ea2..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_tasks.py +++ /dev/null @@ -1,158 +0,0 @@ -from __future__ import annotations - -import math -from collections.abc import Generator -from contextlib import contextmanager -from types import TracebackType - -from ..abc._tasks import TaskGroup, TaskStatus -from ._eventloop import get_async_backend - - -class _IgnoredTaskStatus(TaskStatus[object]): - def started(self, value: object = None) -> None: - pass - - -TASK_STATUS_IGNORED = _IgnoredTaskStatus() - - -class CancelScope: - """ - Wraps a unit of work that can be made separately cancellable. - - :param deadline: The time (clock value) when this scope is cancelled automatically - :param shield: ``True`` to shield the cancel scope from external cancellation - """ - - def __new__( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline) - - def cancel(self) -> None: - """Cancel this scope immediately.""" - raise NotImplementedError - - @property - def deadline(self) -> float: - """ - The time (clock value) when this scope is cancelled automatically. - - Will be ``float('inf')`` if no timeout has been set. - - """ - raise NotImplementedError - - @deadline.setter - def deadline(self, value: float) -> None: - raise NotImplementedError - - @property - def cancel_called(self) -> bool: - """``True`` if :meth:`cancel` has been called.""" - raise NotImplementedError - - @property - def cancelled_caught(self) -> bool: - """ - ``True`` if this scope suppressed a cancellation exception it itself raised. - - This is typically used to check if any work was interrupted, or to see if the - scope was cancelled due to its deadline being reached. The value will, however, - only be ``True`` if the cancellation was triggered by the scope itself (and not - an outer scope). - - """ - raise NotImplementedError - - @property - def shield(self) -> bool: - """ - ``True`` if this scope is shielded from external cancellation. - - While a scope is shielded, it will not receive cancellations from outside. - - """ - raise NotImplementedError - - @shield.setter - def shield(self, value: bool) -> None: - raise NotImplementedError - - def __enter__(self) -> CancelScope: - raise NotImplementedError - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - raise NotImplementedError - - -@contextmanager -def fail_after( - delay: float | None, shield: bool = False -) -> Generator[CancelScope, None, None]: - """ - Create a context manager which raises a :class:`TimeoutError` if does not finish in - time. - - :param delay: maximum allowed time (in seconds) before raising the exception, or - ``None`` to disable the timeout - :param shield: ``True`` to shield the cancel scope from external cancellation - :return: a context manager that yields a cancel scope - :rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\] - - """ - current_time = get_async_backend().current_time - deadline = (current_time() + delay) if delay is not None else math.inf - with get_async_backend().create_cancel_scope( - deadline=deadline, shield=shield - ) as cancel_scope: - yield cancel_scope - - if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline: - raise TimeoutError - - -def move_on_after(delay: float | None, shield: bool = False) -> CancelScope: - """ - Create a cancel scope with a deadline that expires after the given delay. - - :param delay: maximum allowed time (in seconds) before exiting the context block, or - ``None`` to disable the timeout - :param shield: ``True`` to shield the cancel scope from external cancellation - :return: a cancel scope - - """ - deadline = ( - (get_async_backend().current_time() + delay) if delay is not None else math.inf - ) - return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield) - - -def current_effective_deadline() -> float: - """ - Return the nearest deadline among all the cancel scopes effective for the current - task. - - :return: a clock value from the event loop's internal clock (or ``float('inf')`` if - there is no deadline in effect, or ``float('-inf')`` if the current scope has - been cancelled) - :rtype: float - - """ - return get_async_backend().current_effective_deadline() - - -def create_task_group() -> TaskGroup: - """ - Create a task group. - - :return: a task group - - """ - return get_async_backend().create_task_group() diff --git a/.venv/Lib/site-packages/anyio/_core/_testing.py b/.venv/Lib/site-packages/anyio/_core/_testing.py deleted file mode 100644 index 9e28b22..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_testing.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -from collections.abc import Awaitable, Generator -from typing import Any, cast - -from ._eventloop import get_async_backend - - -class TaskInfo: - """ - Represents an asynchronous task. - - :ivar int id: the unique identifier of the task - :ivar parent_id: the identifier of the parent task, if any - :vartype parent_id: Optional[int] - :ivar str name: the description of the task (if any) - :ivar ~collections.abc.Coroutine coro: the coroutine object of the task - """ - - __slots__ = "_name", "id", "parent_id", "name", "coro" - - def __init__( - self, - id: int, - parent_id: int | None, - name: str | None, - coro: Generator[Any, Any, Any] | Awaitable[Any], - ): - func = get_current_task - self._name = f"{func.__module__}.{func.__qualname__}" - self.id: int = id - self.parent_id: int | None = parent_id - self.name: str | None = name - self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro - - def __eq__(self, other: object) -> bool: - if isinstance(other, TaskInfo): - return self.id == other.id - - return NotImplemented - - def __hash__(self) -> int: - return hash(self.id) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})" - - def has_pending_cancellation(self) -> bool: - """ - Return ``True`` if the task has a cancellation pending, ``False`` otherwise. - - """ - return False - - -def get_current_task() -> TaskInfo: - """ - Return the current task. - - :return: a representation of the current task - - """ - return get_async_backend().get_current_task() - - -def get_running_tasks() -> list[TaskInfo]: - """ - Return a list of running tasks in the current event loop. - - :return: a list of task info objects - - """ - return cast("list[TaskInfo]", get_async_backend().get_running_tasks()) - - -async def wait_all_tasks_blocked() -> None: - """Wait until all other tasks are waiting for something.""" - await get_async_backend().wait_all_tasks_blocked() diff --git a/.venv/Lib/site-packages/anyio/_core/_typedattr.py b/.venv/Lib/site-packages/anyio/_core/_typedattr.py deleted file mode 100644 index f358a44..0000000 --- a/.venv/Lib/site-packages/anyio/_core/_typedattr.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping -from typing import Any, TypeVar, final, overload - -from ._exceptions import TypedAttributeLookupError - -T_Attr = TypeVar("T_Attr") -T_Default = TypeVar("T_Default") -undefined = object() - - -def typed_attribute() -> Any: - """Return a unique object, used to mark typed attributes.""" - return object() - - -class TypedAttributeSet: - """ - Superclass for typed attribute collections. - - Checks that every public attribute of every subclass has a type annotation. - """ - - def __init_subclass__(cls) -> None: - annotations: dict[str, Any] = getattr(cls, "__annotations__", {}) - for attrname in dir(cls): - if not attrname.startswith("_") and attrname not in annotations: - raise TypeError( - f"Attribute {attrname!r} is missing its type annotation" - ) - - super().__init_subclass__() - - -class TypedAttributeProvider: - """Base class for classes that wish to provide typed extra attributes.""" - - @property - def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]: - """ - A mapping of the extra attributes to callables that return the corresponding - values. - - If the provider wraps another provider, the attributes from that wrapper should - also be included in the returned mapping (but the wrapper may override the - callables from the wrapped instance). - - """ - return {} - - @overload - def extra(self, attribute: T_Attr) -> T_Attr: ... - - @overload - def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ... - - @final - def extra(self, attribute: Any, default: object = undefined) -> object: - """ - extra(attribute, default=undefined) - - Return the value of the given typed extra attribute. - - :param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to - look for - :param default: the value that should be returned if no value is found for the - attribute - :raises ~anyio.TypedAttributeLookupError: if the search failed and no default - value was given - - """ - try: - getter = self.extra_attributes[attribute] - except KeyError: - if default is undefined: - raise TypedAttributeLookupError("Attribute not found") from None - else: - return default - - return getter() diff --git a/.venv/Lib/site-packages/anyio/abc/__init__.py b/.venv/Lib/site-packages/anyio/abc/__init__.py deleted file mode 100644 index 3d3b61c..0000000 --- a/.venv/Lib/site-packages/anyio/abc/__init__.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations - -from ._eventloop import AsyncBackend as AsyncBackend -from ._resources import AsyncResource as AsyncResource -from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket -from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket -from ._sockets import IPAddressType as IPAddressType -from ._sockets import IPSockAddrType as IPSockAddrType -from ._sockets import SocketAttribute as SocketAttribute -from ._sockets import SocketListener as SocketListener -from ._sockets import SocketStream as SocketStream -from ._sockets import UDPPacketType as UDPPacketType -from ._sockets import UDPSocket as UDPSocket -from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType -from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket -from ._sockets import UNIXSocketStream as UNIXSocketStream -from ._streams import AnyByteReceiveStream as AnyByteReceiveStream -from ._streams import AnyByteSendStream as AnyByteSendStream -from ._streams import AnyByteStream as AnyByteStream -from ._streams import AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream -from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream -from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream -from ._streams import ByteReceiveStream as ByteReceiveStream -from ._streams import ByteSendStream as ByteSendStream -from ._streams import ByteStream as ByteStream -from ._streams import Listener as Listener -from ._streams import ObjectReceiveStream as ObjectReceiveStream -from ._streams import ObjectSendStream as ObjectSendStream -from ._streams import ObjectStream as ObjectStream -from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream -from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream -from ._streams import UnreliableObjectStream as UnreliableObjectStream -from ._subprocesses import Process as Process -from ._tasks import TaskGroup as TaskGroup -from ._tasks import TaskStatus as TaskStatus -from ._testing import TestRunner as TestRunner - -# Re-exported here, for backwards compatibility -# isort: off -from .._core._synchronization import ( - CapacityLimiter as CapacityLimiter, - Condition as Condition, - Event as Event, - Lock as Lock, - Semaphore as Semaphore, -) -from .._core._tasks import CancelScope as CancelScope -from ..from_thread import BlockingPortal as BlockingPortal - -# Re-export imports so they look like they live directly in this package -for __value in list(locals().values()): - if getattr(__value, "__module__", "").startswith("anyio.abc."): - __value.__module__ = __name__ - -del __value diff --git a/.venv/Lib/site-packages/anyio/abc/_eventloop.py b/.venv/Lib/site-packages/anyio/abc/_eventloop.py deleted file mode 100644 index 2bfdf28..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_eventloop.py +++ /dev/null @@ -1,376 +0,0 @@ -from __future__ import annotations - -import math -import sys -from abc import ABCMeta, abstractmethod -from collections.abc import AsyncIterator, Awaitable, Callable, Sequence -from contextlib import AbstractContextManager -from os import PathLike -from signal import Signals -from socket import AddressFamily, SocketKind, socket -from typing import ( - IO, - TYPE_CHECKING, - Any, - TypeVar, - Union, - overload, -) - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if sys.version_info >= (3, 10): - from typing import TypeAlias -else: - from typing_extensions import TypeAlias - -if TYPE_CHECKING: - from _typeshed import HasFileno - - from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore - from .._core._tasks import CancelScope - from .._core._testing import TaskInfo - from ..from_thread import BlockingPortal - from ._sockets import ( - ConnectedUDPSocket, - ConnectedUNIXDatagramSocket, - IPSockAddrType, - SocketListener, - SocketStream, - UDPSocket, - UNIXDatagramSocket, - UNIXSocketStream, - ) - from ._subprocesses import Process - from ._tasks import TaskGroup - from ._testing import TestRunner - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") -StrOrBytesPath: TypeAlias = Union[str, bytes, "PathLike[str]", "PathLike[bytes]"] - - -class AsyncBackend(metaclass=ABCMeta): - @classmethod - @abstractmethod - def run( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - options: dict[str, Any], - ) -> T_Retval: - """ - Run the given coroutine function in an asynchronous event loop. - - The current thread must not be already running an event loop. - - :param func: a coroutine function - :param args: positional arguments to ``func`` - :param kwargs: positional arguments to ``func`` - :param options: keyword arguments to call the backend ``run()`` implementation - with - :return: the return value of the coroutine function - """ - - @classmethod - @abstractmethod - def current_token(cls) -> object: - """ - - :return: - """ - - @classmethod - @abstractmethod - def current_time(cls) -> float: - """ - Return the current value of the event loop's internal clock. - - :return: the clock value (seconds) - """ - - @classmethod - @abstractmethod - def cancelled_exception_class(cls) -> type[BaseException]: - """Return the exception class that is raised in a task if it's cancelled.""" - - @classmethod - @abstractmethod - async def checkpoint(cls) -> None: - """ - Check if the task has been cancelled, and allow rescheduling of other tasks. - - This is effectively the same as running :meth:`checkpoint_if_cancelled` and then - :meth:`cancel_shielded_checkpoint`. - """ - - @classmethod - async def checkpoint_if_cancelled(cls) -> None: - """ - Check if the current task group has been cancelled. - - This will check if the task has been cancelled, but will not allow other tasks - to be scheduled if not. - - """ - if cls.current_effective_deadline() == -math.inf: - await cls.checkpoint() - - @classmethod - async def cancel_shielded_checkpoint(cls) -> None: - """ - Allow the rescheduling of other tasks. - - This will give other tasks the opportunity to run, but without checking if the - current task group has been cancelled, unlike with :meth:`checkpoint`. - - """ - with cls.create_cancel_scope(shield=True): - await cls.sleep(0) - - @classmethod - @abstractmethod - async def sleep(cls, delay: float) -> None: - """ - Pause the current task for the specified duration. - - :param delay: the duration, in seconds - """ - - @classmethod - @abstractmethod - def create_cancel_scope( - cls, *, deadline: float = math.inf, shield: bool = False - ) -> CancelScope: - pass - - @classmethod - @abstractmethod - def current_effective_deadline(cls) -> float: - """ - Return the nearest deadline among all the cancel scopes effective for the - current task. - - :return: - - a clock value from the event loop's internal clock - - ``inf`` if there is no deadline in effect - - ``-inf`` if the current scope has been cancelled - :rtype: float - """ - - @classmethod - @abstractmethod - def create_task_group(cls) -> TaskGroup: - pass - - @classmethod - @abstractmethod - def create_event(cls) -> Event: - pass - - @classmethod - @abstractmethod - def create_lock(cls, *, fast_acquire: bool) -> Lock: - pass - - @classmethod - @abstractmethod - def create_semaphore( - cls, - initial_value: int, - *, - max_value: int | None = None, - fast_acquire: bool = False, - ) -> Semaphore: - pass - - @classmethod - @abstractmethod - def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter: - pass - - @classmethod - @abstractmethod - async def run_sync_in_worker_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - abandon_on_cancel: bool = False, - limiter: CapacityLimiter | None = None, - ) -> T_Retval: - pass - - @classmethod - @abstractmethod - def check_cancelled(cls) -> None: - pass - - @classmethod - @abstractmethod - def run_async_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - pass - - @classmethod - @abstractmethod - def run_sync_from_thread( - cls, - func: Callable[[Unpack[PosArgsT]], T_Retval], - args: tuple[Unpack[PosArgsT]], - token: object, - ) -> T_Retval: - pass - - @classmethod - @abstractmethod - def create_blocking_portal(cls) -> BlockingPortal: - pass - - @classmethod - @abstractmethod - async def open_process( - cls, - command: StrOrBytesPath | Sequence[StrOrBytesPath], - *, - stdin: int | IO[Any] | None, - stdout: int | IO[Any] | None, - stderr: int | IO[Any] | None, - **kwargs: Any, - ) -> Process: - pass - - @classmethod - @abstractmethod - def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None: - pass - - @classmethod - @abstractmethod - async def connect_tcp( - cls, host: str, port: int, local_address: IPSockAddrType | None = None - ) -> SocketStream: - pass - - @classmethod - @abstractmethod - async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream: - pass - - @classmethod - @abstractmethod - def create_tcp_listener(cls, sock: socket) -> SocketListener: - pass - - @classmethod - @abstractmethod - def create_unix_listener(cls, sock: socket) -> SocketListener: - pass - - @classmethod - @abstractmethod - async def create_udp_socket( - cls, - family: AddressFamily, - local_address: IPSockAddrType | None, - remote_address: IPSockAddrType | None, - reuse_port: bool, - ) -> UDPSocket | ConnectedUDPSocket: - pass - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: None - ) -> UNIXDatagramSocket: ... - - @classmethod - @overload - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: str | bytes - ) -> ConnectedUNIXDatagramSocket: ... - - @classmethod - @abstractmethod - async def create_unix_datagram_socket( - cls, raw_socket: socket, remote_path: str | bytes | None - ) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket: - pass - - @classmethod - @abstractmethod - async def getaddrinfo( - cls, - host: bytes | str | None, - port: str | int | None, - *, - family: int | AddressFamily = 0, - type: int | SocketKind = 0, - proto: int = 0, - flags: int = 0, - ) -> list[ - tuple[ - AddressFamily, - SocketKind, - int, - str, - tuple[str, int] | tuple[str, int, int, int], - ] - ]: - pass - - @classmethod - @abstractmethod - async def getnameinfo( - cls, sockaddr: IPSockAddrType, flags: int = 0 - ) -> tuple[str, str]: - pass - - @classmethod - @abstractmethod - async def wait_readable(cls, obj: HasFileno | int) -> None: - pass - - @classmethod - @abstractmethod - async def wait_writable(cls, obj: HasFileno | int) -> None: - pass - - @classmethod - @abstractmethod - def current_default_thread_limiter(cls) -> CapacityLimiter: - pass - - @classmethod - @abstractmethod - def open_signal_receiver( - cls, *signals: Signals - ) -> AbstractContextManager[AsyncIterator[Signals]]: - pass - - @classmethod - @abstractmethod - def get_current_task(cls) -> TaskInfo: - pass - - @classmethod - @abstractmethod - def get_running_tasks(cls) -> Sequence[TaskInfo]: - pass - - @classmethod - @abstractmethod - async def wait_all_tasks_blocked(cls) -> None: - pass - - @classmethod - @abstractmethod - def create_test_runner(cls, options: dict[str, Any]) -> TestRunner: - pass diff --git a/.venv/Lib/site-packages/anyio/abc/_resources.py b/.venv/Lib/site-packages/anyio/abc/_resources.py deleted file mode 100644 index 10df115..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_resources.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from abc import ABCMeta, abstractmethod -from types import TracebackType -from typing import TypeVar - -T = TypeVar("T") - - -class AsyncResource(metaclass=ABCMeta): - """ - Abstract base class for all closeable asynchronous resources. - - Works as an asynchronous context manager which returns the instance itself on enter, - and calls :meth:`aclose` on exit. - """ - - __slots__ = () - - async def __aenter__(self: T) -> T: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - await self.aclose() - - @abstractmethod - async def aclose(self) -> None: - """Close the resource.""" diff --git a/.venv/Lib/site-packages/anyio/abc/_sockets.py b/.venv/Lib/site-packages/anyio/abc/_sockets.py deleted file mode 100644 index 1c6a450..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_sockets.py +++ /dev/null @@ -1,194 +0,0 @@ -from __future__ import annotations - -import socket -from abc import abstractmethod -from collections.abc import Callable, Collection, Mapping -from contextlib import AsyncExitStack -from io import IOBase -from ipaddress import IPv4Address, IPv6Address -from socket import AddressFamily -from types import TracebackType -from typing import Any, TypeVar, Union - -from .._core._typedattr import ( - TypedAttributeProvider, - TypedAttributeSet, - typed_attribute, -) -from ._streams import ByteStream, Listener, UnreliableObjectStream -from ._tasks import TaskGroup - -IPAddressType = Union[str, IPv4Address, IPv6Address] -IPSockAddrType = tuple[str, int] -SockAddrType = Union[IPSockAddrType, str] -UDPPacketType = tuple[bytes, IPSockAddrType] -UNIXDatagramPacketType = tuple[bytes, str] -T_Retval = TypeVar("T_Retval") - - -class _NullAsyncContextManager: - async def __aenter__(self) -> None: - pass - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - return None - - -class SocketAttribute(TypedAttributeSet): - #: the address family of the underlying socket - family: AddressFamily = typed_attribute() - #: the local socket address of the underlying socket - local_address: SockAddrType = typed_attribute() - #: for IP addresses, the local port the underlying socket is bound to - local_port: int = typed_attribute() - #: the underlying stdlib socket object - raw_socket: socket.socket = typed_attribute() - #: the remote address the underlying socket is connected to - remote_address: SockAddrType = typed_attribute() - #: for IP addresses, the remote port the underlying socket is connected to - remote_port: int = typed_attribute() - - -class _SocketProvider(TypedAttributeProvider): - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - from .._core._sockets import convert_ipv6_sockaddr as convert - - attributes: dict[Any, Callable[[], Any]] = { - SocketAttribute.family: lambda: self._raw_socket.family, - SocketAttribute.local_address: lambda: convert( - self._raw_socket.getsockname() - ), - SocketAttribute.raw_socket: lambda: self._raw_socket, - } - try: - peername: tuple[str, int] | None = convert(self._raw_socket.getpeername()) - except OSError: - peername = None - - # Provide the remote address for connected sockets - if peername is not None: - attributes[SocketAttribute.remote_address] = lambda: peername - - # Provide local and remote ports for IP based sockets - if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6): - attributes[SocketAttribute.local_port] = ( - lambda: self._raw_socket.getsockname()[1] - ) - if peername is not None: - remote_port = peername[1] - attributes[SocketAttribute.remote_port] = lambda: remote_port - - return attributes - - @property - @abstractmethod - def _raw_socket(self) -> socket.socket: - pass - - -class SocketStream(ByteStream, _SocketProvider): - """ - Transports bytes over a socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - -class UNIXSocketStream(SocketStream): - @abstractmethod - async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None: - """ - Send file descriptors along with a message to the peer. - - :param message: a non-empty bytestring - :param fds: a collection of files (either numeric file descriptors or open file - or socket objects) - """ - - @abstractmethod - async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]: - """ - Receive file descriptors along with a message from the peer. - - :param msglen: length of the message to expect from the peer - :param maxfds: maximum number of file descriptors to expect from the peer - :return: a tuple of (message, file descriptors) - """ - - -class SocketListener(Listener[SocketStream], _SocketProvider): - """ - Listens to incoming socket connections. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - @abstractmethod - async def accept(self) -> SocketStream: - """Accept an incoming connection.""" - - async def serve( - self, - handler: Callable[[SocketStream], Any], - task_group: TaskGroup | None = None, - ) -> None: - from .. import create_task_group - - async with AsyncExitStack() as stack: - if task_group is None: - task_group = await stack.enter_async_context(create_task_group()) - - while True: - stream = await self.accept() - task_group.start_soon(handler, stream) - - -class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider): - """ - Represents an unconnected UDP socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - async def sendto(self, data: bytes, host: str, port: int) -> None: - """ - Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))). - - """ - return await self.send((data, (host, port))) - - -class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider): - """ - Represents an connected UDP socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - -class UNIXDatagramSocket( - UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider -): - """ - Represents an unconnected Unix datagram socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ - - async def sendto(self, data: bytes, path: str) -> None: - """Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path)).""" - return await self.send((data, path)) - - -class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider): - """ - Represents a connected Unix datagram socket. - - Supports all relevant extra attributes from :class:`~SocketAttribute`. - """ diff --git a/.venv/Lib/site-packages/anyio/abc/_streams.py b/.venv/Lib/site-packages/anyio/abc/_streams.py deleted file mode 100644 index 8c63868..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_streams.py +++ /dev/null @@ -1,203 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from collections.abc import Callable -from typing import Any, Generic, TypeVar, Union - -from .._core._exceptions import EndOfStream -from .._core._typedattr import TypedAttributeProvider -from ._resources import AsyncResource -from ._tasks import TaskGroup - -T_Item = TypeVar("T_Item") -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) - - -class UnreliableObjectReceiveStream( - Generic[T_co], AsyncResource, TypedAttributeProvider -): - """ - An interface for receiving objects. - - This interface makes no guarantees that the received messages arrive in the order in - which they were sent, or that no messages are missed. - - Asynchronously iterating over objects of this type will yield objects matching the - given type parameter. - """ - - def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]: - return self - - async def __anext__(self) -> T_co: - try: - return await self.receive() - except EndOfStream: - raise StopAsyncIteration - - @abstractmethod - async def receive(self) -> T_co: - """ - Receive the next item. - - :raises ~anyio.ClosedResourceError: if the receive stream has been explicitly - closed - :raises ~anyio.EndOfStream: if this stream has been closed from the other end - :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable - due to external causes - """ - - -class UnreliableObjectSendStream( - Generic[T_contra], AsyncResource, TypedAttributeProvider -): - """ - An interface for sending objects. - - This interface makes no guarantees that the messages sent will reach the - recipient(s) in the same order in which they were sent, or at all. - """ - - @abstractmethod - async def send(self, item: T_contra) -> None: - """ - Send an item to the peer(s). - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if the send stream has been explicitly - closed - :raises ~anyio.BrokenResourceError: if this stream has been rendered unusable - due to external causes - """ - - -class UnreliableObjectStream( - UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item] -): - """ - A bidirectional message stream which does not guarantee the order or reliability of - message delivery. - """ - - -class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]): - """ - A receive message stream which guarantees that messages are received in the same - order in which they were sent, and that no messages are missed. - """ - - -class ObjectSendStream(UnreliableObjectSendStream[T_contra]): - """ - A send message stream which guarantees that messages are delivered in the same order - in which they were sent, without missing any messages in the middle. - """ - - -class ObjectStream( - ObjectReceiveStream[T_Item], - ObjectSendStream[T_Item], - UnreliableObjectStream[T_Item], -): - """ - A bidirectional message stream which guarantees the order and reliability of message - delivery. - """ - - @abstractmethod - async def send_eof(self) -> None: - """ - Send an end-of-file indication to the peer. - - You should not try to send any further data to this stream after calling this - method. This method is idempotent (does nothing on successive calls). - """ - - -class ByteReceiveStream(AsyncResource, TypedAttributeProvider): - """ - An interface for receiving bytes from a single peer. - - Iterating this byte stream will yield a byte string of arbitrary length, but no more - than 65536 bytes. - """ - - def __aiter__(self) -> ByteReceiveStream: - return self - - async def __anext__(self) -> bytes: - try: - return await self.receive() - except EndOfStream: - raise StopAsyncIteration - - @abstractmethod - async def receive(self, max_bytes: int = 65536) -> bytes: - """ - Receive at most ``max_bytes`` bytes from the peer. - - .. note:: Implementors of this interface should not return an empty - :class:`bytes` object, and users should ignore them. - - :param max_bytes: maximum number of bytes to receive - :return: the received bytes - :raises ~anyio.EndOfStream: if this stream has been closed from the other end - """ - - -class ByteSendStream(AsyncResource, TypedAttributeProvider): - """An interface for sending bytes to a single peer.""" - - @abstractmethod - async def send(self, item: bytes) -> None: - """ - Send the given bytes to the peer. - - :param item: the bytes to send - """ - - -class ByteStream(ByteReceiveStream, ByteSendStream): - """A bidirectional byte stream.""" - - @abstractmethod - async def send_eof(self) -> None: - """ - Send an end-of-file indication to the peer. - - You should not try to send any further data to this stream after calling this - method. This method is idempotent (does nothing on successive calls). - """ - - -#: Type alias for all unreliable bytes-oriented receive streams. -AnyUnreliableByteReceiveStream = Union[ - UnreliableObjectReceiveStream[bytes], ByteReceiveStream -] -#: Type alias for all unreliable bytes-oriented send streams. -AnyUnreliableByteSendStream = Union[UnreliableObjectSendStream[bytes], ByteSendStream] -#: Type alias for all unreliable bytes-oriented streams. -AnyUnreliableByteStream = Union[UnreliableObjectStream[bytes], ByteStream] -#: Type alias for all bytes-oriented receive streams. -AnyByteReceiveStream = Union[ObjectReceiveStream[bytes], ByteReceiveStream] -#: Type alias for all bytes-oriented send streams. -AnyByteSendStream = Union[ObjectSendStream[bytes], ByteSendStream] -#: Type alias for all bytes-oriented streams. -AnyByteStream = Union[ObjectStream[bytes], ByteStream] - - -class Listener(Generic[T_co], AsyncResource, TypedAttributeProvider): - """An interface for objects that let you accept incoming connections.""" - - @abstractmethod - async def serve( - self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None - ) -> None: - """ - Accept incoming connections as they come in and start tasks to handle them. - - :param handler: a callable that will be used to handle each accepted connection - :param task_group: the task group that will be used to start tasks for handling - each accepted connection (if omitted, an ad-hoc task group will be created) - """ diff --git a/.venv/Lib/site-packages/anyio/abc/_subprocesses.py b/.venv/Lib/site-packages/anyio/abc/_subprocesses.py deleted file mode 100644 index ce0564c..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_subprocesses.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from signal import Signals - -from ._resources import AsyncResource -from ._streams import ByteReceiveStream, ByteSendStream - - -class Process(AsyncResource): - """An asynchronous version of :class:`subprocess.Popen`.""" - - @abstractmethod - async def wait(self) -> int: - """ - Wait until the process exits. - - :return: the exit code of the process - """ - - @abstractmethod - def terminate(self) -> None: - """ - Terminates the process, gracefully if possible. - - On Windows, this calls ``TerminateProcess()``. - On POSIX systems, this sends ``SIGTERM`` to the process. - - .. seealso:: :meth:`subprocess.Popen.terminate` - """ - - @abstractmethod - def kill(self) -> None: - """ - Kills the process. - - On Windows, this calls ``TerminateProcess()``. - On POSIX systems, this sends ``SIGKILL`` to the process. - - .. seealso:: :meth:`subprocess.Popen.kill` - """ - - @abstractmethod - def send_signal(self, signal: Signals) -> None: - """ - Send a signal to the subprocess. - - .. seealso:: :meth:`subprocess.Popen.send_signal` - - :param signal: the signal number (e.g. :data:`signal.SIGHUP`) - """ - - @property - @abstractmethod - def pid(self) -> int: - """The process ID of the process.""" - - @property - @abstractmethod - def returncode(self) -> int | None: - """ - The return code of the process. If the process has not yet terminated, this will - be ``None``. - """ - - @property - @abstractmethod - def stdin(self) -> ByteSendStream | None: - """The stream for the standard input of the process.""" - - @property - @abstractmethod - def stdout(self) -> ByteReceiveStream | None: - """The stream for the standard output of the process.""" - - @property - @abstractmethod - def stderr(self) -> ByteReceiveStream | None: - """The stream for the standard error output of the process.""" diff --git a/.venv/Lib/site-packages/anyio/abc/_tasks.py b/.venv/Lib/site-packages/anyio/abc/_tasks.py deleted file mode 100644 index f6e5c40..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_tasks.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -import sys -from abc import ABCMeta, abstractmethod -from collections.abc import Awaitable, Callable -from types import TracebackType -from typing import TYPE_CHECKING, Any, Protocol, TypeVar, overload - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -if TYPE_CHECKING: - from .._core._tasks import CancelScope - -T_Retval = TypeVar("T_Retval") -T_contra = TypeVar("T_contra", contravariant=True) -PosArgsT = TypeVarTuple("PosArgsT") - - -class TaskStatus(Protocol[T_contra]): - @overload - def started(self: TaskStatus[None]) -> None: ... - - @overload - def started(self, value: T_contra) -> None: ... - - def started(self, value: T_contra | None = None) -> None: - """ - Signal that the task has started. - - :param value: object passed back to the starter of the task - """ - - -class TaskGroup(metaclass=ABCMeta): - """ - Groups several asynchronous tasks together. - - :ivar cancel_scope: the cancel scope inherited by all child tasks - :vartype cancel_scope: CancelScope - - .. note:: On asyncio, support for eager task factories is considered to be - **experimental**. In particular, they don't follow the usual semantics of new - tasks being scheduled on the next iteration of the event loop, and may thus - cause unexpected behavior in code that wasn't written with such semantics in - mind. - """ - - cancel_scope: CancelScope - - @abstractmethod - def start_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[Any]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> None: - """ - Start a new task in this task group. - - :param func: a coroutine function - :param args: positional arguments to call the function with - :param name: name of the task, for the purposes of introspection and debugging - - .. versionadded:: 3.0 - """ - - @abstractmethod - async def start( - self, - func: Callable[..., Awaitable[Any]], - *args: object, - name: object = None, - ) -> Any: - """ - Start a new task and wait until it signals for readiness. - - :param func: a coroutine function - :param args: positional arguments to call the function with - :param name: name of the task, for the purposes of introspection and debugging - :return: the value passed to ``task_status.started()`` - :raises RuntimeError: if the task finishes without calling - ``task_status.started()`` - - .. versionadded:: 3.0 - """ - - @abstractmethod - async def __aenter__(self) -> TaskGroup: - """Enter the task group context and allow starting new tasks.""" - - @abstractmethod - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - """Exit the task group context waiting for all tasks to finish.""" diff --git a/.venv/Lib/site-packages/anyio/abc/_testing.py b/.venv/Lib/site-packages/anyio/abc/_testing.py deleted file mode 100644 index 7c50ed7..0000000 --- a/.venv/Lib/site-packages/anyio/abc/_testing.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import types -from abc import ABCMeta, abstractmethod -from collections.abc import AsyncGenerator, Callable, Coroutine, Iterable -from typing import Any, TypeVar - -_T = TypeVar("_T") - - -class TestRunner(metaclass=ABCMeta): - """ - Encapsulates a running event loop. Every call made through this object will use the - same event loop. - """ - - def __enter__(self) -> TestRunner: - return self - - @abstractmethod - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> bool | None: ... - - @abstractmethod - def run_asyncgen_fixture( - self, - fixture_func: Callable[..., AsyncGenerator[_T, Any]], - kwargs: dict[str, Any], - ) -> Iterable[_T]: - """ - Run an async generator fixture. - - :param fixture_func: the fixture function - :param kwargs: keyword arguments to call the fixture function with - :return: an iterator yielding the value yielded from the async generator - """ - - @abstractmethod - def run_fixture( - self, - fixture_func: Callable[..., Coroutine[Any, Any, _T]], - kwargs: dict[str, Any], - ) -> _T: - """ - Run an async fixture. - - :param fixture_func: the fixture function - :param kwargs: keyword arguments to call the fixture function with - :return: the return value of the fixture function - """ - - @abstractmethod - def run_test( - self, test_func: Callable[..., Coroutine[Any, Any, Any]], kwargs: dict[str, Any] - ) -> None: - """ - Run an async test function. - - :param test_func: the test function - :param kwargs: keyword arguments to call the test function with - """ diff --git a/.venv/Lib/site-packages/anyio/from_thread.py b/.venv/Lib/site-packages/anyio/from_thread.py deleted file mode 100644 index 93a4cfe..0000000 --- a/.venv/Lib/site-packages/anyio/from_thread.py +++ /dev/null @@ -1,527 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Awaitable, Callable, Generator -from concurrent.futures import Future -from contextlib import ( - AbstractAsyncContextManager, - AbstractContextManager, - contextmanager, -) -from dataclasses import dataclass, field -from inspect import isawaitable -from threading import Lock, Thread, get_ident -from types import TracebackType -from typing import ( - Any, - Generic, - TypeVar, - cast, - overload, -) - -from ._core import _eventloop -from ._core._eventloop import get_async_backend, get_cancelled_exc_class, threadlocals -from ._core._synchronization import Event -from ._core._tasks import CancelScope, create_task_group -from .abc import AsyncBackend -from .abc._tasks import TaskStatus - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -T_Retval = TypeVar("T_Retval") -T_co = TypeVar("T_co", covariant=True) -PosArgsT = TypeVarTuple("PosArgsT") - - -def run( - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], *args: Unpack[PosArgsT] -) -> T_Retval: - """ - Call a coroutine function from a worker thread. - - :param func: a coroutine function - :param args: positional arguments for the callable - :return: the return value of the coroutine function - - """ - try: - async_backend = threadlocals.current_async_backend - token = threadlocals.current_token - except AttributeError: - raise RuntimeError( - "This function can only be run from an AnyIO worker thread" - ) from None - - return async_backend.run_async_from_thread(func, args, token=token) - - -def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] -) -> T_Retval: - """ - Call a function in the event loop thread from a worker thread. - - :param func: a callable - :param args: positional arguments for the callable - :return: the return value of the callable - - """ - try: - async_backend = threadlocals.current_async_backend - token = threadlocals.current_token - except AttributeError: - raise RuntimeError( - "This function can only be run from an AnyIO worker thread" - ) from None - - return async_backend.run_sync_from_thread(func, args, token=token) - - -class _BlockingAsyncContextManager(Generic[T_co], AbstractContextManager): - _enter_future: Future[T_co] - _exit_future: Future[bool | None] - _exit_event: Event - _exit_exc_info: tuple[ - type[BaseException] | None, BaseException | None, TracebackType | None - ] = (None, None, None) - - def __init__( - self, async_cm: AbstractAsyncContextManager[T_co], portal: BlockingPortal - ): - self._async_cm = async_cm - self._portal = portal - - async def run_async_cm(self) -> bool | None: - try: - self._exit_event = Event() - value = await self._async_cm.__aenter__() - except BaseException as exc: - self._enter_future.set_exception(exc) - raise - else: - self._enter_future.set_result(value) - - try: - # Wait for the sync context manager to exit. - # This next statement can raise `get_cancelled_exc_class()` if - # something went wrong in a task group in this async context - # manager. - await self._exit_event.wait() - finally: - # In case of cancellation, it could be that we end up here before - # `_BlockingAsyncContextManager.__exit__` is called, and an - # `_exit_exc_info` has been set. - result = await self._async_cm.__aexit__(*self._exit_exc_info) - return result - - def __enter__(self) -> T_co: - self._enter_future = Future() - self._exit_future = self._portal.start_task_soon(self.run_async_cm) - return self._enter_future.result() - - def __exit__( - self, - __exc_type: type[BaseException] | None, - __exc_value: BaseException | None, - __traceback: TracebackType | None, - ) -> bool | None: - self._exit_exc_info = __exc_type, __exc_value, __traceback - self._portal.call(self._exit_event.set) - return self._exit_future.result() - - -class _BlockingPortalTaskStatus(TaskStatus): - def __init__(self, future: Future): - self._future = future - - def started(self, value: object = None) -> None: - self._future.set_result(value) - - -class BlockingPortal: - """An object that lets external threads run code in an asynchronous event loop.""" - - def __new__(cls) -> BlockingPortal: - return get_async_backend().create_blocking_portal() - - def __init__(self) -> None: - self._event_loop_thread_id: int | None = get_ident() - self._stop_event = Event() - self._task_group = create_task_group() - self._cancelled_exc_class = get_cancelled_exc_class() - - async def __aenter__(self) -> BlockingPortal: - await self._task_group.__aenter__() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> bool | None: - await self.stop() - return await self._task_group.__aexit__(exc_type, exc_val, exc_tb) - - def _check_running(self) -> None: - if self._event_loop_thread_id is None: - raise RuntimeError("This portal is not running") - if self._event_loop_thread_id == get_ident(): - raise RuntimeError( - "This method cannot be called from the event loop thread" - ) - - async def sleep_until_stopped(self) -> None: - """Sleep until :meth:`stop` is called.""" - await self._stop_event.wait() - - async def stop(self, cancel_remaining: bool = False) -> None: - """ - Signal the portal to shut down. - - This marks the portal as no longer accepting new calls and exits from - :meth:`sleep_until_stopped`. - - :param cancel_remaining: ``True`` to cancel all the remaining tasks, ``False`` - to let them finish before returning - - """ - self._event_loop_thread_id = None - self._stop_event.set() - if cancel_remaining: - self._task_group.cancel_scope.cancel() - - async def _call_func( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - future: Future[T_Retval], - ) -> None: - def callback(f: Future[T_Retval]) -> None: - if f.cancelled() and self._event_loop_thread_id not in ( - None, - get_ident(), - ): - self.call(scope.cancel) - - try: - retval_or_awaitable = func(*args, **kwargs) - if isawaitable(retval_or_awaitable): - with CancelScope() as scope: - if future.cancelled(): - scope.cancel() - else: - future.add_done_callback(callback) - - retval = await retval_or_awaitable - else: - retval = retval_or_awaitable - except self._cancelled_exc_class: - future.cancel() - future.set_running_or_notify_cancel() - except BaseException as exc: - if not future.cancelled(): - future.set_exception(exc) - - # Let base exceptions fall through - if not isinstance(exc, Exception): - raise - else: - if not future.cancelled(): - future.set_result(retval) - finally: - scope = None # type: ignore[assignment] - - def _spawn_task_from_thread( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - args: tuple[Unpack[PosArgsT]], - kwargs: dict[str, Any], - name: object, - future: Future[T_Retval], - ) -> None: - """ - Spawn a new task using the given callable. - - Implementors must ensure that the future is resolved when the task finishes. - - :param func: a callable - :param args: positional arguments to be passed to the callable - :param kwargs: keyword arguments to be passed to the callable - :param name: name of the task (will be coerced to a string if not ``None``) - :param future: a future that will resolve to the return value of the callable, - or the exception raised during its execution - - """ - raise NotImplementedError - - @overload - def call( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - ) -> T_Retval: ... - - @overload - def call( - self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] - ) -> T_Retval: ... - - def call( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - *args: Unpack[PosArgsT], - ) -> T_Retval: - """ - Call the given function in the event loop thread. - - If the callable returns a coroutine object, it is awaited on. - - :param func: any callable - :raises RuntimeError: if the portal is not running or if this method is called - from within the event loop thread - - """ - return cast(T_Retval, self.start_task_soon(func, *args).result()) - - @overload - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: ... - - @overload - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: ... - - def start_task_soon( - self, - func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval] | T_Retval], - *args: Unpack[PosArgsT], - name: object = None, - ) -> Future[T_Retval]: - """ - Start a task in the portal's task group. - - The task will be run inside a cancel scope which can be cancelled by cancelling - the returned future. - - :param func: the target function - :param args: positional arguments passed to ``func`` - :param name: name of the task (will be coerced to a string if not ``None``) - :return: a future that resolves with the return value of the callable if the - task completes successfully, or with the exception raised in the task - :raises RuntimeError: if the portal is not running or if this method is called - from within the event loop thread - :rtype: concurrent.futures.Future[T_Retval] - - .. versionadded:: 3.0 - - """ - self._check_running() - f: Future[T_Retval] = Future() - self._spawn_task_from_thread(func, args, {}, name, f) - return f - - def start_task( - self, - func: Callable[..., Awaitable[T_Retval]], - *args: object, - name: object = None, - ) -> tuple[Future[T_Retval], Any]: - """ - Start a task in the portal's task group and wait until it signals for readiness. - - This method works the same way as :meth:`.abc.TaskGroup.start`. - - :param func: the target function - :param args: positional arguments passed to ``func`` - :param name: name of the task (will be coerced to a string if not ``None``) - :return: a tuple of (future, task_status_value) where the ``task_status_value`` - is the value passed to ``task_status.started()`` from within the target - function - :rtype: tuple[concurrent.futures.Future[T_Retval], Any] - - .. versionadded:: 3.0 - - """ - - def task_done(future: Future[T_Retval]) -> None: - if not task_status_future.done(): - if future.cancelled(): - task_status_future.cancel() - elif future.exception(): - task_status_future.set_exception(future.exception()) - else: - exc = RuntimeError( - "Task exited without calling task_status.started()" - ) - task_status_future.set_exception(exc) - - self._check_running() - task_status_future: Future = Future() - task_status = _BlockingPortalTaskStatus(task_status_future) - f: Future = Future() - f.add_done_callback(task_done) - self._spawn_task_from_thread(func, args, {"task_status": task_status}, name, f) - return f, task_status_future.result() - - def wrap_async_context_manager( - self, cm: AbstractAsyncContextManager[T_co] - ) -> AbstractContextManager[T_co]: - """ - Wrap an async context manager as a synchronous context manager via this portal. - - Spawns a task that will call both ``__aenter__()`` and ``__aexit__()``, stopping - in the middle until the synchronous context manager exits. - - :param cm: an asynchronous context manager - :return: a synchronous context manager - - .. versionadded:: 2.1 - - """ - return _BlockingAsyncContextManager(cm, self) - - -@dataclass -class BlockingPortalProvider: - """ - A manager for a blocking portal. Used as a context manager. The first thread to - enter this context manager causes a blocking portal to be started with the specific - parameters, and the last thread to exit causes the portal to be shut down. Thus, - there will be exactly one blocking portal running in this context as long as at - least one thread has entered this context manager. - - The parameters are the same as for :func:`~anyio.run`. - - :param backend: name of the backend - :param backend_options: backend options - - .. versionadded:: 4.4 - """ - - backend: str = "asyncio" - backend_options: dict[str, Any] | None = None - _lock: Lock = field(init=False, default_factory=Lock) - _leases: int = field(init=False, default=0) - _portal: BlockingPortal = field(init=False) - _portal_cm: AbstractContextManager[BlockingPortal] | None = field( - init=False, default=None - ) - - def __enter__(self) -> BlockingPortal: - with self._lock: - if self._portal_cm is None: - self._portal_cm = start_blocking_portal( - self.backend, self.backend_options - ) - self._portal = self._portal_cm.__enter__() - - self._leases += 1 - return self._portal - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - portal_cm: AbstractContextManager[BlockingPortal] | None = None - with self._lock: - assert self._portal_cm - assert self._leases > 0 - self._leases -= 1 - if not self._leases: - portal_cm = self._portal_cm - self._portal_cm = None - del self._portal - - if portal_cm: - portal_cm.__exit__(None, None, None) - - -@contextmanager -def start_blocking_portal( - backend: str = "asyncio", backend_options: dict[str, Any] | None = None -) -> Generator[BlockingPortal, Any, None]: - """ - Start a new event loop in a new thread and run a blocking portal in its main task. - - The parameters are the same as for :func:`~anyio.run`. - - :param backend: name of the backend - :param backend_options: backend options - :return: a context manager that yields a blocking portal - - .. versionchanged:: 3.0 - Usage as a context manager is now required. - - """ - - async def run_portal() -> None: - async with BlockingPortal() as portal_: - future.set_result(portal_) - await portal_.sleep_until_stopped() - - def run_blocking_portal() -> None: - if future.set_running_or_notify_cancel(): - try: - _eventloop.run( - run_portal, backend=backend, backend_options=backend_options - ) - except BaseException as exc: - if not future.done(): - future.set_exception(exc) - - future: Future[BlockingPortal] = Future() - thread = Thread(target=run_blocking_portal, daemon=True) - thread.start() - try: - cancel_remaining_tasks = False - portal = future.result() - try: - yield portal - except BaseException: - cancel_remaining_tasks = True - raise - finally: - try: - portal.call(portal.stop, cancel_remaining_tasks) - except RuntimeError: - pass - finally: - thread.join() - - -def check_cancelled() -> None: - """ - Check if the cancel scope of the host task's running the current worker thread has - been cancelled. - - If the host task's current cancel scope has indeed been cancelled, the - backend-specific cancellation exception will be raised. - - :raises RuntimeError: if the current thread was not spawned by - :func:`.to_thread.run_sync` - - """ - try: - async_backend: AsyncBackend = threadlocals.current_async_backend - except AttributeError: - raise RuntimeError( - "This function can only be run from an AnyIO worker thread" - ) from None - - async_backend.check_cancelled() diff --git a/.venv/Lib/site-packages/anyio/lowlevel.py b/.venv/Lib/site-packages/anyio/lowlevel.py deleted file mode 100644 index 14c7668..0000000 --- a/.venv/Lib/site-packages/anyio/lowlevel.py +++ /dev/null @@ -1,161 +0,0 @@ -from __future__ import annotations - -import enum -from dataclasses import dataclass -from typing import Any, Generic, Literal, TypeVar, overload -from weakref import WeakKeyDictionary - -from ._core._eventloop import get_async_backend - -T = TypeVar("T") -D = TypeVar("D") - - -async def checkpoint() -> None: - """ - Check for cancellation and allow the scheduler to switch to another task. - - Equivalent to (but more efficient than):: - - await checkpoint_if_cancelled() - await cancel_shielded_checkpoint() - - - .. versionadded:: 3.0 - - """ - await get_async_backend().checkpoint() - - -async def checkpoint_if_cancelled() -> None: - """ - Enter a checkpoint if the enclosing cancel scope has been cancelled. - - This does not allow the scheduler to switch to a different task. - - .. versionadded:: 3.0 - - """ - await get_async_backend().checkpoint_if_cancelled() - - -async def cancel_shielded_checkpoint() -> None: - """ - Allow the scheduler to switch to another task but without checking for cancellation. - - Equivalent to (but potentially more efficient than):: - - with CancelScope(shield=True): - await checkpoint() - - - .. versionadded:: 3.0 - - """ - await get_async_backend().cancel_shielded_checkpoint() - - -def current_token() -> object: - """ - Return a backend specific token object that can be used to get back to the event - loop. - - """ - return get_async_backend().current_token() - - -_run_vars: WeakKeyDictionary[Any, dict[str, Any]] = WeakKeyDictionary() -_token_wrappers: dict[Any, _TokenWrapper] = {} - - -@dataclass(frozen=True) -class _TokenWrapper: - __slots__ = "_token", "__weakref__" - _token: object - - -class _NoValueSet(enum.Enum): - NO_VALUE_SET = enum.auto() - - -class RunvarToken(Generic[T]): - __slots__ = "_var", "_value", "_redeemed" - - def __init__(self, var: RunVar[T], value: T | Literal[_NoValueSet.NO_VALUE_SET]): - self._var = var - self._value: T | Literal[_NoValueSet.NO_VALUE_SET] = value - self._redeemed = False - - -class RunVar(Generic[T]): - """ - Like a :class:`~contextvars.ContextVar`, except scoped to the running event loop. - """ - - __slots__ = "_name", "_default" - - NO_VALUE_SET: Literal[_NoValueSet.NO_VALUE_SET] = _NoValueSet.NO_VALUE_SET - - _token_wrappers: set[_TokenWrapper] = set() - - def __init__( - self, name: str, default: T | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET - ): - self._name = name - self._default = default - - @property - def _current_vars(self) -> dict[str, T]: - token = current_token() - try: - return _run_vars[token] - except KeyError: - run_vars = _run_vars[token] = {} - return run_vars - - @overload - def get(self, default: D) -> T | D: ... - - @overload - def get(self) -> T: ... - - def get( - self, default: D | Literal[_NoValueSet.NO_VALUE_SET] = NO_VALUE_SET - ) -> T | D: - try: - return self._current_vars[self._name] - except KeyError: - if default is not RunVar.NO_VALUE_SET: - return default - elif self._default is not RunVar.NO_VALUE_SET: - return self._default - - raise LookupError( - f'Run variable "{self._name}" has no value and no default set' - ) - - def set(self, value: T) -> RunvarToken[T]: - current_vars = self._current_vars - token = RunvarToken(self, current_vars.get(self._name, RunVar.NO_VALUE_SET)) - current_vars[self._name] = value - return token - - def reset(self, token: RunvarToken[T]) -> None: - if token._var is not self: - raise ValueError("This token does not belong to this RunVar") - - if token._redeemed: - raise ValueError("This token has already been used") - - if token._value is _NoValueSet.NO_VALUE_SET: - try: - del self._current_vars[self._name] - except KeyError: - pass - else: - self._current_vars[self._name] = token._value - - token._redeemed = True - - def __repr__(self) -> str: - return f"" diff --git a/.venv/Lib/site-packages/anyio/py.typed b/.venv/Lib/site-packages/anyio/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/anyio/pytest_plugin.py b/.venv/Lib/site-packages/anyio/pytest_plugin.py deleted file mode 100644 index 4a0d59d..0000000 --- a/.venv/Lib/site-packages/anyio/pytest_plugin.py +++ /dev/null @@ -1,191 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Generator, Iterator -from contextlib import ExitStack, contextmanager -from inspect import isasyncgenfunction, iscoroutinefunction, ismethod -from typing import Any, cast - -import pytest -import sniffio -from _pytest.fixtures import SubRequest -from _pytest.outcomes import Exit - -from ._core._eventloop import get_all_backends, get_async_backend -from ._core._exceptions import iterate_exceptions -from .abc import TestRunner - -if sys.version_info < (3, 11): - from exceptiongroup import ExceptionGroup - -_current_runner: TestRunner | None = None -_runner_stack: ExitStack | None = None -_runner_leases = 0 - - -def extract_backend_and_options(backend: object) -> tuple[str, dict[str, Any]]: - if isinstance(backend, str): - return backend, {} - elif isinstance(backend, tuple) and len(backend) == 2: - if isinstance(backend[0], str) and isinstance(backend[1], dict): - return cast(tuple[str, dict[str, Any]], backend) - - raise TypeError("anyio_backend must be either a string or tuple of (string, dict)") - - -@contextmanager -def get_runner( - backend_name: str, backend_options: dict[str, Any] -) -> Iterator[TestRunner]: - global _current_runner, _runner_leases, _runner_stack - if _current_runner is None: - asynclib = get_async_backend(backend_name) - _runner_stack = ExitStack() - if sniffio.current_async_library_cvar.get(None) is None: - # Since we're in control of the event loop, we can cache the name of the - # async library - token = sniffio.current_async_library_cvar.set(backend_name) - _runner_stack.callback(sniffio.current_async_library_cvar.reset, token) - - backend_options = backend_options or {} - _current_runner = _runner_stack.enter_context( - asynclib.create_test_runner(backend_options) - ) - - _runner_leases += 1 - try: - yield _current_runner - finally: - _runner_leases -= 1 - if not _runner_leases: - assert _runner_stack is not None - _runner_stack.close() - _runner_stack = _current_runner = None - - -def pytest_configure(config: Any) -> None: - config.addinivalue_line( - "markers", - "anyio: mark the (coroutine function) test to be run " - "asynchronously via anyio.", - ) - - -@pytest.hookimpl(hookwrapper=True) -def pytest_fixture_setup(fixturedef: Any, request: Any) -> Generator[Any]: - def wrapper( - *args: Any, anyio_backend: Any, request: SubRequest, **kwargs: Any - ) -> Any: - # Rebind any fixture methods to the request instance - if ( - request.instance - and ismethod(func) - and type(func.__self__) is type(request.instance) - ): - local_func = func.__func__.__get__(request.instance) - else: - local_func = func - - backend_name, backend_options = extract_backend_and_options(anyio_backend) - if has_backend_arg: - kwargs["anyio_backend"] = anyio_backend - - if has_request_arg: - kwargs["request"] = request - - with get_runner(backend_name, backend_options) as runner: - if isasyncgenfunction(local_func): - yield from runner.run_asyncgen_fixture(local_func, kwargs) - else: - yield runner.run_fixture(local_func, kwargs) - - # Only apply this to coroutine functions and async generator functions in requests - # that involve the anyio_backend fixture - func = fixturedef.func - if isasyncgenfunction(func) or iscoroutinefunction(func): - if "anyio_backend" in request.fixturenames: - fixturedef.func = wrapper - original_argname = fixturedef.argnames - - if not (has_backend_arg := "anyio_backend" in fixturedef.argnames): - fixturedef.argnames += ("anyio_backend",) - - if not (has_request_arg := "request" in fixturedef.argnames): - fixturedef.argnames += ("request",) - - try: - return (yield) - finally: - fixturedef.func = func - fixturedef.argnames = original_argname - - return (yield) - - -@pytest.hookimpl(tryfirst=True) -def pytest_pycollect_makeitem(collector: Any, name: Any, obj: Any) -> None: - if collector.istestfunction(obj, name): - inner_func = obj.hypothesis.inner_test if hasattr(obj, "hypothesis") else obj - if iscoroutinefunction(inner_func): - marker = collector.get_closest_marker("anyio") - own_markers = getattr(obj, "pytestmark", ()) - if marker or any(marker.name == "anyio" for marker in own_markers): - pytest.mark.usefixtures("anyio_backend")(obj) - - -@pytest.hookimpl(tryfirst=True) -def pytest_pyfunc_call(pyfuncitem: Any) -> bool | None: - def run_with_hypothesis(**kwargs: Any) -> None: - with get_runner(backend_name, backend_options) as runner: - runner.run_test(original_func, kwargs) - - backend = pyfuncitem.funcargs.get("anyio_backend") - if backend: - backend_name, backend_options = extract_backend_and_options(backend) - - if hasattr(pyfuncitem.obj, "hypothesis"): - # Wrap the inner test function unless it's already wrapped - original_func = pyfuncitem.obj.hypothesis.inner_test - if original_func.__qualname__ != run_with_hypothesis.__qualname__: - if iscoroutinefunction(original_func): - pyfuncitem.obj.hypothesis.inner_test = run_with_hypothesis - - return None - - if iscoroutinefunction(pyfuncitem.obj): - funcargs = pyfuncitem.funcargs - testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} - with get_runner(backend_name, backend_options) as runner: - try: - runner.run_test(pyfuncitem.obj, testargs) - except ExceptionGroup as excgrp: - for exc in iterate_exceptions(excgrp): - if isinstance(exc, (Exit, KeyboardInterrupt, SystemExit)): - raise exc from excgrp - - raise - - return True - - return None - - -@pytest.fixture(scope="module", params=get_all_backends()) -def anyio_backend(request: Any) -> Any: - return request.param - - -@pytest.fixture -def anyio_backend_name(anyio_backend: Any) -> str: - if isinstance(anyio_backend, str): - return anyio_backend - else: - return anyio_backend[0] - - -@pytest.fixture -def anyio_backend_options(anyio_backend: Any) -> dict[str, Any]: - if isinstance(anyio_backend, str): - return {} - else: - return anyio_backend[1] diff --git a/.venv/Lib/site-packages/anyio/streams/__init__.py b/.venv/Lib/site-packages/anyio/streams/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/anyio/streams/buffered.py b/.venv/Lib/site-packages/anyio/streams/buffered.py deleted file mode 100644 index f5d5e83..0000000 --- a/.venv/Lib/site-packages/anyio/streams/buffered.py +++ /dev/null @@ -1,119 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping -from dataclasses import dataclass, field -from typing import Any - -from .. import ClosedResourceError, DelimiterNotFound, EndOfStream, IncompleteRead -from ..abc import AnyByteReceiveStream, ByteReceiveStream - - -@dataclass(eq=False) -class BufferedByteReceiveStream(ByteReceiveStream): - """ - Wraps any bytes-based receive stream and uses a buffer to provide sophisticated - receiving capabilities in the form of a byte stream. - """ - - receive_stream: AnyByteReceiveStream - _buffer: bytearray = field(init=False, default_factory=bytearray) - _closed: bool = field(init=False, default=False) - - async def aclose(self) -> None: - await self.receive_stream.aclose() - self._closed = True - - @property - def buffer(self) -> bytes: - """The bytes currently in the buffer.""" - return bytes(self._buffer) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.receive_stream.extra_attributes - - async def receive(self, max_bytes: int = 65536) -> bytes: - if self._closed: - raise ClosedResourceError - - if self._buffer: - chunk = bytes(self._buffer[:max_bytes]) - del self._buffer[:max_bytes] - return chunk - elif isinstance(self.receive_stream, ByteReceiveStream): - return await self.receive_stream.receive(max_bytes) - else: - # With a bytes-oriented object stream, we need to handle any surplus bytes - # we get from the receive() call - chunk = await self.receive_stream.receive() - if len(chunk) > max_bytes: - # Save the surplus bytes in the buffer - self._buffer.extend(chunk[max_bytes:]) - return chunk[:max_bytes] - else: - return chunk - - async def receive_exactly(self, nbytes: int) -> bytes: - """ - Read exactly the given amount of bytes from the stream. - - :param nbytes: the number of bytes to read - :return: the bytes read - :raises ~anyio.IncompleteRead: if the stream was closed before the requested - amount of bytes could be read from the stream - - """ - while True: - remaining = nbytes - len(self._buffer) - if remaining <= 0: - retval = self._buffer[:nbytes] - del self._buffer[:nbytes] - return bytes(retval) - - try: - if isinstance(self.receive_stream, ByteReceiveStream): - chunk = await self.receive_stream.receive(remaining) - else: - chunk = await self.receive_stream.receive() - except EndOfStream as exc: - raise IncompleteRead from exc - - self._buffer.extend(chunk) - - async def receive_until(self, delimiter: bytes, max_bytes: int) -> bytes: - """ - Read from the stream until the delimiter is found or max_bytes have been read. - - :param delimiter: the marker to look for in the stream - :param max_bytes: maximum number of bytes that will be read before raising - :exc:`~anyio.DelimiterNotFound` - :return: the bytes read (not including the delimiter) - :raises ~anyio.IncompleteRead: if the stream was closed before the delimiter - was found - :raises ~anyio.DelimiterNotFound: if the delimiter is not found within the - bytes read up to the maximum allowed - - """ - delimiter_size = len(delimiter) - offset = 0 - while True: - # Check if the delimiter can be found in the current buffer - index = self._buffer.find(delimiter, offset) - if index >= 0: - found = self._buffer[:index] - del self._buffer[: index + len(delimiter) :] - return bytes(found) - - # Check if the buffer is already at or over the limit - if len(self._buffer) >= max_bytes: - raise DelimiterNotFound(max_bytes) - - # Read more data into the buffer from the socket - try: - data = await self.receive_stream.receive() - except EndOfStream as exc: - raise IncompleteRead from exc - - # Move the offset forward and add the new data to the buffer - offset = max(len(self._buffer) - delimiter_size + 1, 0) - self._buffer.extend(data) diff --git a/.venv/Lib/site-packages/anyio/streams/file.py b/.venv/Lib/site-packages/anyio/streams/file.py deleted file mode 100644 index f492464..0000000 --- a/.venv/Lib/site-packages/anyio/streams/file.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping -from io import SEEK_SET, UnsupportedOperation -from os import PathLike -from pathlib import Path -from typing import Any, BinaryIO, cast - -from .. import ( - BrokenResourceError, - ClosedResourceError, - EndOfStream, - TypedAttributeSet, - to_thread, - typed_attribute, -) -from ..abc import ByteReceiveStream, ByteSendStream - - -class FileStreamAttribute(TypedAttributeSet): - #: the open file descriptor - file: BinaryIO = typed_attribute() - #: the path of the file on the file system, if available (file must be a real file) - path: Path = typed_attribute() - #: the file number, if available (file must be a real file or a TTY) - fileno: int = typed_attribute() - - -class _BaseFileStream: - def __init__(self, file: BinaryIO): - self._file = file - - async def aclose(self) -> None: - await to_thread.run_sync(self._file.close) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - attributes: dict[Any, Callable[[], Any]] = { - FileStreamAttribute.file: lambda: self._file, - } - - if hasattr(self._file, "name"): - attributes[FileStreamAttribute.path] = lambda: Path(self._file.name) - - try: - self._file.fileno() - except UnsupportedOperation: - pass - else: - attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno() - - return attributes - - -class FileReadStream(_BaseFileStream, ByteReceiveStream): - """ - A byte stream that reads from a file in the file system. - - :param file: a file that has been opened for reading in binary mode - - .. versionadded:: 3.0 - """ - - @classmethod - async def from_path(cls, path: str | PathLike[str]) -> FileReadStream: - """ - Create a file read stream by opening the given file. - - :param path: path of the file to read from - - """ - file = await to_thread.run_sync(Path(path).open, "rb") - return cls(cast(BinaryIO, file)) - - async def receive(self, max_bytes: int = 65536) -> bytes: - try: - data = await to_thread.run_sync(self._file.read, max_bytes) - except ValueError: - raise ClosedResourceError from None - except OSError as exc: - raise BrokenResourceError from exc - - if data: - return data - else: - raise EndOfStream - - async def seek(self, position: int, whence: int = SEEK_SET) -> int: - """ - Seek the file to the given position. - - .. seealso:: :meth:`io.IOBase.seek` - - .. note:: Not all file descriptors are seekable. - - :param position: position to seek the file to - :param whence: controls how ``position`` is interpreted - :return: the new absolute position - :raises OSError: if the file is not seekable - - """ - return await to_thread.run_sync(self._file.seek, position, whence) - - async def tell(self) -> int: - """ - Return the current stream position. - - .. note:: Not all file descriptors are seekable. - - :return: the current absolute position - :raises OSError: if the file is not seekable - - """ - return await to_thread.run_sync(self._file.tell) - - -class FileWriteStream(_BaseFileStream, ByteSendStream): - """ - A byte stream that writes to a file in the file system. - - :param file: a file that has been opened for writing in binary mode - - .. versionadded:: 3.0 - """ - - @classmethod - async def from_path( - cls, path: str | PathLike[str], append: bool = False - ) -> FileWriteStream: - """ - Create a file write stream by opening the given file for writing. - - :param path: path of the file to write to - :param append: if ``True``, open the file for appending; if ``False``, any - existing file at the given path will be truncated - - """ - mode = "ab" if append else "wb" - file = await to_thread.run_sync(Path(path).open, mode) - return cls(cast(BinaryIO, file)) - - async def send(self, item: bytes) -> None: - try: - await to_thread.run_sync(self._file.write, item) - except ValueError: - raise ClosedResourceError from None - except OSError as exc: - raise BrokenResourceError from exc diff --git a/.venv/Lib/site-packages/anyio/streams/memory.py b/.venv/Lib/site-packages/anyio/streams/memory.py deleted file mode 100644 index b547aa6..0000000 --- a/.venv/Lib/site-packages/anyio/streams/memory.py +++ /dev/null @@ -1,317 +0,0 @@ -from __future__ import annotations - -import warnings -from collections import OrderedDict, deque -from dataclasses import dataclass, field -from types import TracebackType -from typing import Generic, NamedTuple, TypeVar - -from .. import ( - BrokenResourceError, - ClosedResourceError, - EndOfStream, - WouldBlock, -) -from .._core._testing import TaskInfo, get_current_task -from ..abc import Event, ObjectReceiveStream, ObjectSendStream -from ..lowlevel import checkpoint - -T_Item = TypeVar("T_Item") -T_co = TypeVar("T_co", covariant=True) -T_contra = TypeVar("T_contra", contravariant=True) - - -class MemoryObjectStreamStatistics(NamedTuple): - current_buffer_used: int #: number of items stored in the buffer - #: maximum number of items that can be stored on this stream (or :data:`math.inf`) - max_buffer_size: float - open_send_streams: int #: number of unclosed clones of the send stream - open_receive_streams: int #: number of unclosed clones of the receive stream - #: number of tasks blocked on :meth:`MemoryObjectSendStream.send` - tasks_waiting_send: int - #: number of tasks blocked on :meth:`MemoryObjectReceiveStream.receive` - tasks_waiting_receive: int - - -@dataclass(eq=False) -class MemoryObjectItemReceiver(Generic[T_Item]): - task_info: TaskInfo = field(init=False, default_factory=get_current_task) - item: T_Item = field(init=False) - - def __repr__(self) -> str: - # When item is not defined, we get following error with default __repr__: - # AttributeError: 'MemoryObjectItemReceiver' object has no attribute 'item' - item = getattr(self, "item", None) - return f"{self.__class__.__name__}(task_info={self.task_info}, item={item!r})" - - -@dataclass(eq=False) -class MemoryObjectStreamState(Generic[T_Item]): - max_buffer_size: float = field() - buffer: deque[T_Item] = field(init=False, default_factory=deque) - open_send_channels: int = field(init=False, default=0) - open_receive_channels: int = field(init=False, default=0) - waiting_receivers: OrderedDict[Event, MemoryObjectItemReceiver[T_Item]] = field( - init=False, default_factory=OrderedDict - ) - waiting_senders: OrderedDict[Event, T_Item] = field( - init=False, default_factory=OrderedDict - ) - - def statistics(self) -> MemoryObjectStreamStatistics: - return MemoryObjectStreamStatistics( - len(self.buffer), - self.max_buffer_size, - self.open_send_channels, - self.open_receive_channels, - len(self.waiting_senders), - len(self.waiting_receivers), - ) - - -@dataclass(eq=False) -class MemoryObjectReceiveStream(Generic[T_co], ObjectReceiveStream[T_co]): - _state: MemoryObjectStreamState[T_co] - _closed: bool = field(init=False, default=False) - - def __post_init__(self) -> None: - self._state.open_receive_channels += 1 - - def receive_nowait(self) -> T_co: - """ - Receive the next item if it can be done without waiting. - - :return: the received item - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.EndOfStream: if the buffer is empty and this stream has been - closed from the sending end - :raises ~anyio.WouldBlock: if there are no items in the buffer and no tasks - waiting to send - - """ - if self._closed: - raise ClosedResourceError - - if self._state.waiting_senders: - # Get the item from the next sender - send_event, item = self._state.waiting_senders.popitem(last=False) - self._state.buffer.append(item) - send_event.set() - - if self._state.buffer: - return self._state.buffer.popleft() - elif not self._state.open_send_channels: - raise EndOfStream - - raise WouldBlock - - async def receive(self) -> T_co: - await checkpoint() - try: - return self.receive_nowait() - except WouldBlock: - # Add ourselves in the queue - receive_event = Event() - receiver = MemoryObjectItemReceiver[T_co]() - self._state.waiting_receivers[receive_event] = receiver - - try: - await receive_event.wait() - finally: - self._state.waiting_receivers.pop(receive_event, None) - - try: - return receiver.item - except AttributeError: - raise EndOfStream - - def clone(self) -> MemoryObjectReceiveStream[T_co]: - """ - Create a clone of this receive stream. - - Each clone can be closed separately. Only when all clones have been closed will - the receiving end of the memory stream be considered closed by the sending ends. - - :return: the cloned stream - - """ - if self._closed: - raise ClosedResourceError - - return MemoryObjectReceiveStream(_state=self._state) - - def close(self) -> None: - """ - Close the stream. - - This works the exact same way as :meth:`aclose`, but is provided as a special - case for the benefit of synchronous callbacks. - - """ - if not self._closed: - self._closed = True - self._state.open_receive_channels -= 1 - if self._state.open_receive_channels == 0: - send_events = list(self._state.waiting_senders.keys()) - for event in send_events: - event.set() - - async def aclose(self) -> None: - self.close() - - def statistics(self) -> MemoryObjectStreamStatistics: - """ - Return statistics about the current state of this stream. - - .. versionadded:: 3.0 - """ - return self._state.statistics() - - def __enter__(self) -> MemoryObjectReceiveStream[T_co]: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def __del__(self) -> None: - if not self._closed: - warnings.warn( - f"Unclosed <{self.__class__.__name__} at {id(self):x}>", - ResourceWarning, - source=self, - ) - - -@dataclass(eq=False) -class MemoryObjectSendStream(Generic[T_contra], ObjectSendStream[T_contra]): - _state: MemoryObjectStreamState[T_contra] - _closed: bool = field(init=False, default=False) - - def __post_init__(self) -> None: - self._state.open_send_channels += 1 - - def send_nowait(self, item: T_contra) -> None: - """ - Send an item immediately if it can be done without waiting. - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.BrokenResourceError: if the stream has been closed from the - receiving end - :raises ~anyio.WouldBlock: if the buffer is full and there are no tasks waiting - to receive - - """ - if self._closed: - raise ClosedResourceError - if not self._state.open_receive_channels: - raise BrokenResourceError - - while self._state.waiting_receivers: - receive_event, receiver = self._state.waiting_receivers.popitem(last=False) - if not receiver.task_info.has_pending_cancellation(): - receiver.item = item - receive_event.set() - return - - if len(self._state.buffer) < self._state.max_buffer_size: - self._state.buffer.append(item) - else: - raise WouldBlock - - async def send(self, item: T_contra) -> None: - """ - Send an item to the stream. - - If the buffer is full, this method blocks until there is again room in the - buffer or the item can be sent directly to a receiver. - - :param item: the item to send - :raises ~anyio.ClosedResourceError: if this send stream has been closed - :raises ~anyio.BrokenResourceError: if the stream has been closed from the - receiving end - - """ - await checkpoint() - try: - self.send_nowait(item) - except WouldBlock: - # Wait until there's someone on the receiving end - send_event = Event() - self._state.waiting_senders[send_event] = item - try: - await send_event.wait() - except BaseException: - self._state.waiting_senders.pop(send_event, None) - raise - - if send_event in self._state.waiting_senders: - del self._state.waiting_senders[send_event] - raise BrokenResourceError from None - - def clone(self) -> MemoryObjectSendStream[T_contra]: - """ - Create a clone of this send stream. - - Each clone can be closed separately. Only when all clones have been closed will - the sending end of the memory stream be considered closed by the receiving ends. - - :return: the cloned stream - - """ - if self._closed: - raise ClosedResourceError - - return MemoryObjectSendStream(_state=self._state) - - def close(self) -> None: - """ - Close the stream. - - This works the exact same way as :meth:`aclose`, but is provided as a special - case for the benefit of synchronous callbacks. - - """ - if not self._closed: - self._closed = True - self._state.open_send_channels -= 1 - if self._state.open_send_channels == 0: - receive_events = list(self._state.waiting_receivers.keys()) - self._state.waiting_receivers.clear() - for event in receive_events: - event.set() - - async def aclose(self) -> None: - self.close() - - def statistics(self) -> MemoryObjectStreamStatistics: - """ - Return statistics about the current state of this stream. - - .. versionadded:: 3.0 - """ - return self._state.statistics() - - def __enter__(self) -> MemoryObjectSendStream[T_contra]: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: - self.close() - - def __del__(self) -> None: - if not self._closed: - warnings.warn( - f"Unclosed <{self.__class__.__name__} at {id(self):x}>", - ResourceWarning, - source=self, - ) diff --git a/.venv/Lib/site-packages/anyio/streams/stapled.py b/.venv/Lib/site-packages/anyio/streams/stapled.py deleted file mode 100644 index 80f64a2..0000000 --- a/.venv/Lib/site-packages/anyio/streams/stapled.py +++ /dev/null @@ -1,141 +0,0 @@ -from __future__ import annotations - -from collections.abc import Callable, Mapping, Sequence -from dataclasses import dataclass -from typing import Any, Generic, TypeVar - -from ..abc import ( - ByteReceiveStream, - ByteSendStream, - ByteStream, - Listener, - ObjectReceiveStream, - ObjectSendStream, - ObjectStream, - TaskGroup, -) - -T_Item = TypeVar("T_Item") -T_Stream = TypeVar("T_Stream") - - -@dataclass(eq=False) -class StapledByteStream(ByteStream): - """ - Combines two byte streams into a single, bidirectional byte stream. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param ByteSendStream send_stream: the sending byte stream - :param ByteReceiveStream receive_stream: the receiving byte stream - """ - - send_stream: ByteSendStream - receive_stream: ByteReceiveStream - - async def receive(self, max_bytes: int = 65536) -> bytes: - return await self.receive_stream.receive(max_bytes) - - async def send(self, item: bytes) -> None: - await self.send_stream.send(item) - - async def send_eof(self) -> None: - await self.send_stream.aclose() - - async def aclose(self) -> None: - await self.send_stream.aclose() - await self.receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.send_stream.extra_attributes, - **self.receive_stream.extra_attributes, - } - - -@dataclass(eq=False) -class StapledObjectStream(Generic[T_Item], ObjectStream[T_Item]): - """ - Combines two object streams into a single, bidirectional object stream. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param ObjectSendStream send_stream: the sending object stream - :param ObjectReceiveStream receive_stream: the receiving object stream - """ - - send_stream: ObjectSendStream[T_Item] - receive_stream: ObjectReceiveStream[T_Item] - - async def receive(self) -> T_Item: - return await self.receive_stream.receive() - - async def send(self, item: T_Item) -> None: - await self.send_stream.send(item) - - async def send_eof(self) -> None: - await self.send_stream.aclose() - - async def aclose(self) -> None: - await self.send_stream.aclose() - await self.receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.send_stream.extra_attributes, - **self.receive_stream.extra_attributes, - } - - -@dataclass(eq=False) -class MultiListener(Generic[T_Stream], Listener[T_Stream]): - """ - Combines multiple listeners into one, serving connections from all of them at once. - - Any MultiListeners in the given collection of listeners will have their listeners - moved into this one. - - Extra attributes are provided from each listener, with each successive listener - overriding any conflicting attributes from the previous one. - - :param listeners: listeners to serve - :type listeners: Sequence[Listener[T_Stream]] - """ - - listeners: Sequence[Listener[T_Stream]] - - def __post_init__(self) -> None: - listeners: list[Listener[T_Stream]] = [] - for listener in self.listeners: - if isinstance(listener, MultiListener): - listeners.extend(listener.listeners) - del listener.listeners[:] # type: ignore[attr-defined] - else: - listeners.append(listener) - - self.listeners = listeners - - async def serve( - self, handler: Callable[[T_Stream], Any], task_group: TaskGroup | None = None - ) -> None: - from .. import create_task_group - - async with create_task_group() as tg: - for listener in self.listeners: - tg.start_soon(listener.serve, handler, task_group) - - async def aclose(self) -> None: - for listener in self.listeners: - await listener.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - attributes: dict = {} - for listener in self.listeners: - attributes.update(listener.extra_attributes) - - return attributes diff --git a/.venv/Lib/site-packages/anyio/streams/text.py b/.venv/Lib/site-packages/anyio/streams/text.py deleted file mode 100644 index f1a1127..0000000 --- a/.venv/Lib/site-packages/anyio/streams/text.py +++ /dev/null @@ -1,147 +0,0 @@ -from __future__ import annotations - -import codecs -from collections.abc import Callable, Mapping -from dataclasses import InitVar, dataclass, field -from typing import Any - -from ..abc import ( - AnyByteReceiveStream, - AnyByteSendStream, - AnyByteStream, - ObjectReceiveStream, - ObjectSendStream, - ObjectStream, -) - - -@dataclass(eq=False) -class TextReceiveStream(ObjectReceiveStream[str]): - """ - Stream wrapper that decodes bytes to strings using the given encoding. - - Decoding is done using :class:`~codecs.IncrementalDecoder` which returns any - completely received unicode characters as soon as they come in. - - :param transport_stream: any bytes-based receive stream - :param encoding: character encoding to use for decoding bytes to strings (defaults - to ``utf-8``) - :param errors: handling scheme for decoding errors (defaults to ``strict``; see the - `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteReceiveStream - encoding: InitVar[str] = "utf-8" - errors: InitVar[str] = "strict" - _decoder: codecs.IncrementalDecoder = field(init=False) - - def __post_init__(self, encoding: str, errors: str) -> None: - decoder_class = codecs.getincrementaldecoder(encoding) - self._decoder = decoder_class(errors=errors) - - async def receive(self) -> str: - while True: - chunk = await self.transport_stream.receive() - decoded = self._decoder.decode(chunk) - if decoded: - return decoded - - async def aclose(self) -> None: - await self.transport_stream.aclose() - self._decoder.reset() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.transport_stream.extra_attributes - - -@dataclass(eq=False) -class TextSendStream(ObjectSendStream[str]): - """ - Sends strings to the wrapped stream as bytes using the given encoding. - - :param AnyByteSendStream transport_stream: any bytes-based send stream - :param str encoding: character encoding to use for encoding strings to bytes - (defaults to ``utf-8``) - :param str errors: handling scheme for encoding errors (defaults to ``strict``; see - the `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteSendStream - encoding: InitVar[str] = "utf-8" - errors: str = "strict" - _encoder: Callable[..., tuple[bytes, int]] = field(init=False) - - def __post_init__(self, encoding: str) -> None: - self._encoder = codecs.getencoder(encoding) - - async def send(self, item: str) -> None: - encoded = self._encoder(item, self.errors)[0] - await self.transport_stream.send(encoded) - - async def aclose(self) -> None: - await self.transport_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return self.transport_stream.extra_attributes - - -@dataclass(eq=False) -class TextStream(ObjectStream[str]): - """ - A bidirectional stream that decodes bytes to strings on receive and encodes strings - to bytes on send. - - Extra attributes will be provided from both streams, with the receive stream - providing the values in case of a conflict. - - :param AnyByteStream transport_stream: any bytes-based stream - :param str encoding: character encoding to use for encoding/decoding strings to/from - bytes (defaults to ``utf-8``) - :param str errors: handling scheme for encoding errors (defaults to ``strict``; see - the `codecs module documentation`_ for a comprehensive list of options) - - .. _codecs module documentation: - https://docs.python.org/3/library/codecs.html#codec-objects - """ - - transport_stream: AnyByteStream - encoding: InitVar[str] = "utf-8" - errors: InitVar[str] = "strict" - _receive_stream: TextReceiveStream = field(init=False) - _send_stream: TextSendStream = field(init=False) - - def __post_init__(self, encoding: str, errors: str) -> None: - self._receive_stream = TextReceiveStream( - self.transport_stream, encoding=encoding, errors=errors - ) - self._send_stream = TextSendStream( - self.transport_stream, encoding=encoding, errors=errors - ) - - async def receive(self) -> str: - return await self._receive_stream.receive() - - async def send(self, item: str) -> None: - await self._send_stream.send(item) - - async def send_eof(self) -> None: - await self.transport_stream.send_eof() - - async def aclose(self) -> None: - await self._send_stream.aclose() - await self._receive_stream.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self._send_stream.extra_attributes, - **self._receive_stream.extra_attributes, - } diff --git a/.venv/Lib/site-packages/anyio/streams/tls.py b/.venv/Lib/site-packages/anyio/streams/tls.py deleted file mode 100644 index b6961be..0000000 --- a/.venv/Lib/site-packages/anyio/streams/tls.py +++ /dev/null @@ -1,337 +0,0 @@ -from __future__ import annotations - -import logging -import re -import ssl -import sys -from collections.abc import Callable, Mapping -from dataclasses import dataclass -from functools import wraps -from typing import Any, TypeVar - -from .. import ( - BrokenResourceError, - EndOfStream, - aclose_forcefully, - get_cancelled_exc_class, -) -from .._core._typedattr import TypedAttributeSet, typed_attribute -from ..abc import AnyByteStream, ByteStream, Listener, TaskGroup - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") -_PCTRTT = tuple[tuple[str, str], ...] -_PCTRTTT = tuple[_PCTRTT, ...] - - -class TLSAttribute(TypedAttributeSet): - """Contains Transport Layer Security related attributes.""" - - #: the selected ALPN protocol - alpn_protocol: str | None = typed_attribute() - #: the channel binding for type ``tls-unique`` - channel_binding_tls_unique: bytes = typed_attribute() - #: the selected cipher - cipher: tuple[str, str, int] = typed_attribute() - #: the peer certificate in dictionary form (see :meth:`ssl.SSLSocket.getpeercert` - # for more information) - peer_certificate: None | (dict[str, str | _PCTRTTT | _PCTRTT]) = typed_attribute() - #: the peer certificate in binary form - peer_certificate_binary: bytes | None = typed_attribute() - #: ``True`` if this is the server side of the connection - server_side: bool = typed_attribute() - #: ciphers shared by the client during the TLS handshake (``None`` if this is the - #: client side) - shared_ciphers: list[tuple[str, str, int]] | None = typed_attribute() - #: the :class:`~ssl.SSLObject` used for encryption - ssl_object: ssl.SSLObject = typed_attribute() - #: ``True`` if this stream does (and expects) a closing TLS handshake when the - #: stream is being closed - standard_compatible: bool = typed_attribute() - #: the TLS protocol version (e.g. ``TLSv1.2``) - tls_version: str = typed_attribute() - - -@dataclass(eq=False) -class TLSStream(ByteStream): - """ - A stream wrapper that encrypts all sent data and decrypts received data. - - This class has no public initializer; use :meth:`wrap` instead. - All extra attributes from :class:`~TLSAttribute` are supported. - - :var AnyByteStream transport_stream: the wrapped stream - - """ - - transport_stream: AnyByteStream - standard_compatible: bool - _ssl_object: ssl.SSLObject - _read_bio: ssl.MemoryBIO - _write_bio: ssl.MemoryBIO - - @classmethod - async def wrap( - cls, - transport_stream: AnyByteStream, - *, - server_side: bool | None = None, - hostname: str | None = None, - ssl_context: ssl.SSLContext | None = None, - standard_compatible: bool = True, - ) -> TLSStream: - """ - Wrap an existing stream with Transport Layer Security. - - This performs a TLS handshake with the peer. - - :param transport_stream: a bytes-transporting stream to wrap - :param server_side: ``True`` if this is the server side of the connection, - ``False`` if this is the client side (if omitted, will be set to ``False`` - if ``hostname`` has been provided, ``False`` otherwise). Used only to create - a default context when an explicit context has not been provided. - :param hostname: host name of the peer (if host name checking is desired) - :param ssl_context: the SSLContext object to use (if not provided, a secure - default will be created) - :param standard_compatible: if ``False``, skip the closing handshake when - closing the connection, and don't raise an exception if the peer does the - same - :raises ~ssl.SSLError: if the TLS handshake fails - - """ - if server_side is None: - server_side = not hostname - - if not ssl_context: - purpose = ( - ssl.Purpose.CLIENT_AUTH if server_side else ssl.Purpose.SERVER_AUTH - ) - ssl_context = ssl.create_default_context(purpose) - - # Re-enable detection of unexpected EOFs if it was disabled by Python - if hasattr(ssl, "OP_IGNORE_UNEXPECTED_EOF"): - ssl_context.options &= ~ssl.OP_IGNORE_UNEXPECTED_EOF - - bio_in = ssl.MemoryBIO() - bio_out = ssl.MemoryBIO() - ssl_object = ssl_context.wrap_bio( - bio_in, bio_out, server_side=server_side, server_hostname=hostname - ) - wrapper = cls( - transport_stream=transport_stream, - standard_compatible=standard_compatible, - _ssl_object=ssl_object, - _read_bio=bio_in, - _write_bio=bio_out, - ) - await wrapper._call_sslobject_method(ssl_object.do_handshake) - return wrapper - - async def _call_sslobject_method( - self, func: Callable[[Unpack[PosArgsT]], T_Retval], *args: Unpack[PosArgsT] - ) -> T_Retval: - while True: - try: - result = func(*args) - except ssl.SSLWantReadError: - try: - # Flush any pending writes first - if self._write_bio.pending: - await self.transport_stream.send(self._write_bio.read()) - - data = await self.transport_stream.receive() - except EndOfStream: - self._read_bio.write_eof() - except OSError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - raise BrokenResourceError from exc - else: - self._read_bio.write(data) - except ssl.SSLWantWriteError: - await self.transport_stream.send(self._write_bio.read()) - except ssl.SSLSyscallError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - raise BrokenResourceError from exc - except ssl.SSLError as exc: - self._read_bio.write_eof() - self._write_bio.write_eof() - if isinstance(exc, ssl.SSLEOFError) or ( - exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror - ): - if self.standard_compatible: - raise BrokenResourceError from exc - else: - raise EndOfStream from None - - raise - else: - # Flush any pending writes first - if self._write_bio.pending: - await self.transport_stream.send(self._write_bio.read()) - - return result - - async def unwrap(self) -> tuple[AnyByteStream, bytes]: - """ - Does the TLS closing handshake. - - :return: a tuple of (wrapped byte stream, bytes left in the read buffer) - - """ - await self._call_sslobject_method(self._ssl_object.unwrap) - self._read_bio.write_eof() - self._write_bio.write_eof() - return self.transport_stream, self._read_bio.read() - - async def aclose(self) -> None: - if self.standard_compatible: - try: - await self.unwrap() - except BaseException: - await aclose_forcefully(self.transport_stream) - raise - - await self.transport_stream.aclose() - - async def receive(self, max_bytes: int = 65536) -> bytes: - data = await self._call_sslobject_method(self._ssl_object.read, max_bytes) - if not data: - raise EndOfStream - - return data - - async def send(self, item: bytes) -> None: - await self._call_sslobject_method(self._ssl_object.write, item) - - async def send_eof(self) -> None: - tls_version = self.extra(TLSAttribute.tls_version) - match = re.match(r"TLSv(\d+)(?:\.(\d+))?", tls_version) - if match: - major, minor = int(match.group(1)), int(match.group(2) or 0) - if (major, minor) < (1, 3): - raise NotImplementedError( - f"send_eof() requires at least TLSv1.3; current " - f"session uses {tls_version}" - ) - - raise NotImplementedError( - "send_eof() has not yet been implemented for TLS streams" - ) - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - **self.transport_stream.extra_attributes, - TLSAttribute.alpn_protocol: self._ssl_object.selected_alpn_protocol, - TLSAttribute.channel_binding_tls_unique: ( - self._ssl_object.get_channel_binding - ), - TLSAttribute.cipher: self._ssl_object.cipher, - TLSAttribute.peer_certificate: lambda: self._ssl_object.getpeercert(False), - TLSAttribute.peer_certificate_binary: lambda: self._ssl_object.getpeercert( - True - ), - TLSAttribute.server_side: lambda: self._ssl_object.server_side, - TLSAttribute.shared_ciphers: lambda: self._ssl_object.shared_ciphers() - if self._ssl_object.server_side - else None, - TLSAttribute.standard_compatible: lambda: self.standard_compatible, - TLSAttribute.ssl_object: lambda: self._ssl_object, - TLSAttribute.tls_version: self._ssl_object.version, - } - - -@dataclass(eq=False) -class TLSListener(Listener[TLSStream]): - """ - A convenience listener that wraps another listener and auto-negotiates a TLS session - on every accepted connection. - - If the TLS handshake times out or raises an exception, - :meth:`handle_handshake_error` is called to do whatever post-mortem processing is - deemed necessary. - - Supports only the :attr:`~TLSAttribute.standard_compatible` extra attribute. - - :param Listener listener: the listener to wrap - :param ssl_context: the SSL context object - :param standard_compatible: a flag passed through to :meth:`TLSStream.wrap` - :param handshake_timeout: time limit for the TLS handshake - (passed to :func:`~anyio.fail_after`) - """ - - listener: Listener[Any] - ssl_context: ssl.SSLContext - standard_compatible: bool = True - handshake_timeout: float = 30 - - @staticmethod - async def handle_handshake_error(exc: BaseException, stream: AnyByteStream) -> None: - """ - Handle an exception raised during the TLS handshake. - - This method does 3 things: - - #. Forcefully closes the original stream - #. Logs the exception (unless it was a cancellation exception) using the - ``anyio.streams.tls`` logger - #. Reraises the exception if it was a base exception or a cancellation exception - - :param exc: the exception - :param stream: the original stream - - """ - await aclose_forcefully(stream) - - # Log all except cancellation exceptions - if not isinstance(exc, get_cancelled_exc_class()): - # CPython (as of 3.11.5) returns incorrect `sys.exc_info()` here when using - # any asyncio implementation, so we explicitly pass the exception to log - # (https://github.com/python/cpython/issues/108668). Trio does not have this - # issue because it works around the CPython bug. - logging.getLogger(__name__).exception( - "Error during TLS handshake", exc_info=exc - ) - - # Only reraise base exceptions and cancellation exceptions - if not isinstance(exc, Exception) or isinstance(exc, get_cancelled_exc_class()): - raise - - async def serve( - self, - handler: Callable[[TLSStream], Any], - task_group: TaskGroup | None = None, - ) -> None: - @wraps(handler) - async def handler_wrapper(stream: AnyByteStream) -> None: - from .. import fail_after - - try: - with fail_after(self.handshake_timeout): - wrapped_stream = await TLSStream.wrap( - stream, - ssl_context=self.ssl_context, - standard_compatible=self.standard_compatible, - ) - except BaseException as exc: - await self.handle_handshake_error(exc, stream) - else: - await handler(wrapped_stream) - - await self.listener.serve(handler_wrapper, task_group) - - async def aclose(self) -> None: - await self.listener.aclose() - - @property - def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]: - return { - TLSAttribute.standard_compatible: lambda: self.standard_compatible, - } diff --git a/.venv/Lib/site-packages/anyio/to_process.py b/.venv/Lib/site-packages/anyio/to_process.py deleted file mode 100644 index 5050dee..0000000 --- a/.venv/Lib/site-packages/anyio/to_process.py +++ /dev/null @@ -1,258 +0,0 @@ -from __future__ import annotations - -import os -import pickle -import subprocess -import sys -from collections import deque -from collections.abc import Callable -from importlib.util import module_from_spec, spec_from_file_location -from typing import TypeVar, cast - -from ._core._eventloop import current_time, get_async_backend, get_cancelled_exc_class -from ._core._exceptions import BrokenWorkerProcess -from ._core._subprocesses import open_process -from ._core._synchronization import CapacityLimiter -from ._core._tasks import CancelScope, fail_after -from .abc import ByteReceiveStream, ByteSendStream, Process -from .lowlevel import RunVar, checkpoint_if_cancelled -from .streams.buffered import BufferedByteReceiveStream - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -WORKER_MAX_IDLE_TIME = 300 # 5 minutes - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - -_process_pool_workers: RunVar[set[Process]] = RunVar("_process_pool_workers") -_process_pool_idle_workers: RunVar[deque[tuple[Process, float]]] = RunVar( - "_process_pool_idle_workers" -) -_default_process_limiter: RunVar[CapacityLimiter] = RunVar("_default_process_limiter") - - -async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - cancellable: bool = False, - limiter: CapacityLimiter | None = None, -) -> T_Retval: - """ - Call the given function with the given arguments in a worker process. - - If the ``cancellable`` option is enabled and the task waiting for its completion is - cancelled, the worker process running it will be abruptly terminated using SIGKILL - (or ``terminateProcess()`` on Windows). - - :param func: a callable - :param args: positional arguments for the callable - :param cancellable: ``True`` to allow cancellation of the operation while it's - running - :param limiter: capacity limiter to use to limit the total amount of processes - running (if omitted, the default limiter is used) - :return: an awaitable that yields the return value of the function. - - """ - - async def send_raw_command(pickled_cmd: bytes) -> object: - try: - await stdin.send(pickled_cmd) - response = await buffered.receive_until(b"\n", 50) - status, length = response.split(b" ") - if status not in (b"RETURN", b"EXCEPTION"): - raise RuntimeError( - f"Worker process returned unexpected response: {response!r}" - ) - - pickled_response = await buffered.receive_exactly(int(length)) - except BaseException as exc: - workers.discard(process) - try: - process.kill() - with CancelScope(shield=True): - await process.aclose() - except ProcessLookupError: - pass - - if isinstance(exc, get_cancelled_exc_class()): - raise - else: - raise BrokenWorkerProcess from exc - - retval = pickle.loads(pickled_response) - if status == b"EXCEPTION": - assert isinstance(retval, BaseException) - raise retval - else: - return retval - - # First pickle the request before trying to reserve a worker process - await checkpoint_if_cancelled() - request = pickle.dumps(("run", func, args), protocol=pickle.HIGHEST_PROTOCOL) - - # If this is the first run in this event loop thread, set up the necessary variables - try: - workers = _process_pool_workers.get() - idle_workers = _process_pool_idle_workers.get() - except LookupError: - workers = set() - idle_workers = deque() - _process_pool_workers.set(workers) - _process_pool_idle_workers.set(idle_workers) - get_async_backend().setup_process_pool_exit_at_shutdown(workers) - - async with limiter or current_default_process_limiter(): - # Pop processes from the pool (starting from the most recently used) until we - # find one that hasn't exited yet - process: Process - while idle_workers: - process, idle_since = idle_workers.pop() - if process.returncode is None: - stdin = cast(ByteSendStream, process.stdin) - buffered = BufferedByteReceiveStream( - cast(ByteReceiveStream, process.stdout) - ) - - # Prune any other workers that have been idle for WORKER_MAX_IDLE_TIME - # seconds or longer - now = current_time() - killed_processes: list[Process] = [] - while idle_workers: - if now - idle_workers[0][1] < WORKER_MAX_IDLE_TIME: - break - - process_to_kill, idle_since = idle_workers.popleft() - process_to_kill.kill() - workers.remove(process_to_kill) - killed_processes.append(process_to_kill) - - with CancelScope(shield=True): - for killed_process in killed_processes: - await killed_process.aclose() - - break - - workers.remove(process) - else: - command = [sys.executable, "-u", "-m", __name__] - process = await open_process( - command, stdin=subprocess.PIPE, stdout=subprocess.PIPE - ) - try: - stdin = cast(ByteSendStream, process.stdin) - buffered = BufferedByteReceiveStream( - cast(ByteReceiveStream, process.stdout) - ) - with fail_after(20): - message = await buffered.receive(6) - - if message != b"READY\n": - raise BrokenWorkerProcess( - f"Worker process returned unexpected response: {message!r}" - ) - - main_module_path = getattr(sys.modules["__main__"], "__file__", None) - pickled = pickle.dumps( - ("init", sys.path, main_module_path), - protocol=pickle.HIGHEST_PROTOCOL, - ) - await send_raw_command(pickled) - except (BrokenWorkerProcess, get_cancelled_exc_class()): - raise - except BaseException as exc: - process.kill() - raise BrokenWorkerProcess( - "Error during worker process initialization" - ) from exc - - workers.add(process) - - with CancelScope(shield=not cancellable): - try: - return cast(T_Retval, await send_raw_command(request)) - finally: - if process in workers: - idle_workers.append((process, current_time())) - - -def current_default_process_limiter() -> CapacityLimiter: - """ - Return the capacity limiter that is used by default to limit the number of worker - processes. - - :return: a capacity limiter object - - """ - try: - return _default_process_limiter.get() - except LookupError: - limiter = CapacityLimiter(os.cpu_count() or 2) - _default_process_limiter.set(limiter) - return limiter - - -def process_worker() -> None: - # Redirect standard streams to os.devnull so that user code won't interfere with the - # parent-worker communication - stdin = sys.stdin - stdout = sys.stdout - sys.stdin = open(os.devnull) - sys.stdout = open(os.devnull, "w") - - stdout.buffer.write(b"READY\n") - while True: - retval = exception = None - try: - command, *args = pickle.load(stdin.buffer) - except EOFError: - return - except BaseException as exc: - exception = exc - else: - if command == "run": - func, args = args - try: - retval = func(*args) - except BaseException as exc: - exception = exc - elif command == "init": - main_module_path: str | None - sys.path, main_module_path = args - del sys.modules["__main__"] - if main_module_path and os.path.isfile(main_module_path): - # Load the parent's main module but as __mp_main__ instead of - # __main__ (like multiprocessing does) to avoid infinite recursion - try: - spec = spec_from_file_location("__mp_main__", main_module_path) - if spec and spec.loader: - main = module_from_spec(spec) - spec.loader.exec_module(main) - sys.modules["__main__"] = main - except BaseException as exc: - exception = exc - try: - if exception is not None: - status = b"EXCEPTION" - pickled = pickle.dumps(exception, pickle.HIGHEST_PROTOCOL) - else: - status = b"RETURN" - pickled = pickle.dumps(retval, pickle.HIGHEST_PROTOCOL) - except BaseException as exc: - exception = exc - status = b"EXCEPTION" - pickled = pickle.dumps(exc, pickle.HIGHEST_PROTOCOL) - - stdout.buffer.write(b"%s %d\n" % (status, len(pickled))) - stdout.buffer.write(pickled) - - # Respect SIGTERM - if isinstance(exception, SystemExit): - raise exception - - -if __name__ == "__main__": - process_worker() diff --git a/.venv/Lib/site-packages/anyio/to_thread.py b/.venv/Lib/site-packages/anyio/to_thread.py deleted file mode 100644 index 5070516..0000000 --- a/.venv/Lib/site-packages/anyio/to_thread.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import annotations - -import sys -from collections.abc import Callable -from typing import TypeVar -from warnings import warn - -from ._core._eventloop import get_async_backend -from .abc import CapacityLimiter - -if sys.version_info >= (3, 11): - from typing import TypeVarTuple, Unpack -else: - from typing_extensions import TypeVarTuple, Unpack - -T_Retval = TypeVar("T_Retval") -PosArgsT = TypeVarTuple("PosArgsT") - - -async def run_sync( - func: Callable[[Unpack[PosArgsT]], T_Retval], - *args: Unpack[PosArgsT], - abandon_on_cancel: bool = False, - cancellable: bool | None = None, - limiter: CapacityLimiter | None = None, -) -> T_Retval: - """ - Call the given function with the given arguments in a worker thread. - - If the ``cancellable`` option is enabled and the task waiting for its completion is - cancelled, the thread will still run its course but its return value (or any raised - exception) will be ignored. - - :param func: a callable - :param args: positional arguments for the callable - :param abandon_on_cancel: ``True`` to abandon the thread (leaving it to run - unchecked on own) if the host task is cancelled, ``False`` to ignore - cancellations in the host task until the operation has completed in the worker - thread - :param cancellable: deprecated alias of ``abandon_on_cancel``; will override - ``abandon_on_cancel`` if both parameters are passed - :param limiter: capacity limiter to use to limit the total amount of threads running - (if omitted, the default limiter is used) - :return: an awaitable that yields the return value of the function. - - """ - if cancellable is not None: - abandon_on_cancel = cancellable - warn( - "The `cancellable=` keyword argument to `anyio.to_thread.run_sync` is " - "deprecated since AnyIO 4.1.0; use `abandon_on_cancel=` instead", - DeprecationWarning, - stacklevel=2, - ) - - return await get_async_backend().run_sync_in_worker_thread( - func, args, abandon_on_cancel=abandon_on_cancel, limiter=limiter - ) - - -def current_default_thread_limiter() -> CapacityLimiter: - """ - Return the capacity limiter that is used by default to limit the number of - concurrent threads. - - :return: a capacity limiter object - - """ - return get_async_backend().current_default_thread_limiter() diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/INSTALLER b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/LICENSE b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/LICENSE deleted file mode 100644 index 11069ed..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/METADATA b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/METADATA deleted file mode 100644 index bbb8c3c..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/METADATA +++ /dev/null @@ -1,322 +0,0 @@ -Metadata-Version: 2.1 -Name: bcrypt -Version: 4.2.1 -Summary: Modern password hashing for your software and your servers -Author-email: The Python Cryptographic Authority developers -License: Apache-2.0 -Project-URL: homepage, https://github.com/pyca/bcrypt/ -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -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 :: 3.13 -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE -Provides-Extra: tests -Requires-Dist: pytest!=3.3.0,>=3.2.1; extra == "tests" -Provides-Extra: typecheck -Requires-Dist: mypy; extra == "typecheck" - -bcrypt -====== - -.. image:: https://img.shields.io/pypi/v/bcrypt.svg - :target: https://pypi.org/project/bcrypt/ - :alt: Latest Version - -.. image:: https://github.com/pyca/bcrypt/workflows/CI/badge.svg?branch=main - :target: https://github.com/pyca/bcrypt/actions?query=workflow%3ACI+branch%3Amain - -Acceptable password hashing for your software and your servers (but you should -really use argon2id or scrypt) - - -Installation -============ - -To install bcrypt, simply: - -.. code:: console - - $ pip install bcrypt - -Note that bcrypt should build very easily on Linux provided you have a C -compiler and a Rust compiler (the minimum supported Rust version is 1.56.0). - -For Debian and Ubuntu, the following command will ensure that the required dependencies are installed: - -.. code:: console - - $ sudo apt-get install build-essential cargo - -For Fedora and RHEL-derivatives, the following command will ensure that the required dependencies are installed: - -.. code:: console - - $ sudo yum install gcc cargo - -For Alpine, the following command will ensure that the required dependencies are installed: - -.. code:: console - - $ apk add --update musl-dev gcc cargo - - -Alternatives -============ - -While bcrypt remains an acceptable choice for password storage, depending on your specific use case you may also want to consider using scrypt (either via `standard library`_ or `cryptography`_) or argon2id via `argon2_cffi`_. - -Changelog -========= - -4.2.1 ------ - -* Bump Rust dependency versions - this should resolve crashes on Python 3.13 - free-threaded builds. -* We no longer build ``manylinux`` wheels for PyPy 3.9. - -4.2.0 ------ - -* Bump Rust dependency versions -* Removed the ``BCRYPT_ALLOW_RUST_163`` environment variable. - -4.1.3 ------ - -* Bump Rust dependency versions - -4.1.2 ------ - -* Publish both ``py37`` and ``py39`` wheels. This should resolve some errors - relating to initializing a module multiple times per process. - -4.1.1 ------ - -* Fixed the type signature on the ``kdf`` method. -* Fixed packaging bug on Windows. -* Fixed incompatibility with passlib package detection assumptions. - -4.1.0 ------ - -* Dropped support for Python 3.6. -* Bumped MSRV to 1.64. (Note: Rust 1.63 can be used by setting the ``BCRYPT_ALLOW_RUST_163`` environment variable) - -4.0.1 ------ - -* We now build PyPy ``manylinux`` wheels. -* Fixed a bug where passing an invalid ``salt`` to ``checkpw`` could result in - a ``pyo3_runtime.PanicException``. It now correctly raises a ``ValueError``. - -4.0.0 ------ - -* ``bcrypt`` is now implemented in Rust. Users building from source will need - to have a Rust compiler available. Nothing will change for users downloading - wheels. -* We no longer ship ``manylinux2010`` wheels. Users should upgrade to the latest - ``pip`` to ensure this doesn’t cause issues downloading wheels on their - platform. We now ship ``manylinux_2_28`` wheels for users on new enough platforms. -* ``NUL`` bytes are now allowed in inputs. - - -3.2.2 ------ - -* Fixed packaging of ``py.typed`` files in wheels so that ``mypy`` works. - -3.2.1 ------ - -* Added support for compilation on z/OS -* The next release of ``bcrypt`` with be 4.0 and it will require Rust at - compile time, for users building from source. There will be no additional - requirement for users who are installing from wheels. Users on most - platforms will be able to obtain a wheel by making sure they have an up to - date ``pip``. The minimum supported Rust version will be 1.56.0. -* This will be the final release for which we ship ``manylinux2010`` wheels. - Going forward the minimum supported manylinux ABI for our wheels will be - ``manylinux2014``. The vast majority of users will continue to receive - ``manylinux`` wheels provided they have an up to date ``pip``. - - -3.2.0 ------ - -* Added typehints for library functions. -* Dropped support for Python versions less than 3.6 (2.7, 3.4, 3.5). -* Shipped ``abi3`` Windows wheels (requires pip >= 20). - -3.1.7 ------ - -* Set a ``setuptools`` lower bound for PEP517 wheel building. -* We no longer distribute 32-bit ``manylinux1`` wheels. Continuing to produce - them was a maintenance burden. - -3.1.6 ------ - -* Added support for compilation on Haiku. - -3.1.5 ------ - -* Added support for compilation on AIX. -* Dropped Python 2.6 and 3.3 support. -* Switched to using ``abi3`` wheels for Python 3. If you are not getting a - wheel on a compatible platform please upgrade your ``pip`` version. - -3.1.4 ------ - -* Fixed compilation with mingw and on illumos. - -3.1.3 ------ -* Fixed a compilation issue on Solaris. -* Added a warning when using too few rounds with ``kdf``. - -3.1.2 ------ -* Fixed a compile issue affecting big endian platforms. -* Fixed invalid escape sequence warnings on Python 3.6. -* Fixed building in non-UTF8 environments on Python 2. - -3.1.1 ------ -* Resolved a ``UserWarning`` when used with ``cffi`` 1.8.3. - -3.1.0 ------ -* Added support for ``checkpw``, a convenience method for verifying a password. -* Ensure that you get a ``$2y$`` hash when you input a ``$2y$`` salt. -* Fixed a regression where ``$2a`` hashes were vulnerable to a wraparound bug. -* Fixed compilation under Alpine Linux. - -3.0.0 ------ -* Switched the C backend to code obtained from the OpenBSD project rather than - openwall. -* Added support for ``bcrypt_pbkdf`` via the ``kdf`` function. - -2.0.0 ------ -* Added support for an adjustible prefix when calling ``gensalt``. -* Switched to CFFI 1.0+ - -Usage ------ - -Password Hashing -~~~~~~~~~~~~~~~~ - -Hashing and then later checking that a password matches the previous hashed -password is very simple: - -.. code:: pycon - - >>> import bcrypt - >>> password = b"super secret password" - >>> # Hash a password for the first time, with a randomly-generated salt - >>> hashed = bcrypt.hashpw(password, bcrypt.gensalt()) - >>> # Check that an unhashed password matches one that has previously been - >>> # hashed - >>> if bcrypt.checkpw(password, hashed): - ... print("It Matches!") - ... else: - ... print("It Does not Match :(") - -KDF -~~~ - -As of 3.0.0 ``bcrypt`` now offers a ``kdf`` function which does ``bcrypt_pbkdf``. -This KDF is used in OpenSSH's newer encrypted private key format. - -.. code:: pycon - - >>> import bcrypt - >>> key = bcrypt.kdf( - ... password=b'password', - ... salt=b'salt', - ... desired_key_bytes=32, - ... rounds=100) - - -Adjustable Work Factor -~~~~~~~~~~~~~~~~~~~~~~ -One of bcrypt's features is an adjustable logarithmic work factor. To adjust -the work factor merely pass the desired number of rounds to -``bcrypt.gensalt(rounds=12)`` which defaults to 12): - -.. code:: pycon - - >>> import bcrypt - >>> password = b"super secret password" - >>> # Hash a password for the first time, with a certain number of rounds - >>> hashed = bcrypt.hashpw(password, bcrypt.gensalt(14)) - >>> # Check that a unhashed password matches one that has previously been - >>> # hashed - >>> if bcrypt.checkpw(password, hashed): - ... print("It Matches!") - ... else: - ... print("It Does not Match :(") - - -Adjustable Prefix -~~~~~~~~~~~~~~~~~ - -Another one of bcrypt's features is an adjustable prefix to let you define what -libraries you'll remain compatible with. To adjust this, pass either ``2a`` or -``2b`` (the default) to ``bcrypt.gensalt(prefix=b"2b")`` as a bytes object. - -As of 3.0.0 the ``$2y$`` prefix is still supported in ``hashpw`` but deprecated. - -Maximum Password Length -~~~~~~~~~~~~~~~~~~~~~~~ - -The bcrypt algorithm only handles passwords up to 72 characters, any characters -beyond that are ignored. To work around this, a common approach is to hash a -password with a cryptographic hash (such as ``sha256``) and then base64 -encode it to prevent NULL byte problems before hashing the result with -``bcrypt``: - -.. code:: pycon - - >>> password = b"an incredibly long password" * 10 - >>> hashed = bcrypt.hashpw( - ... base64.b64encode(hashlib.sha256(password).digest()), - ... bcrypt.gensalt() - ... ) - -Compatibility -------------- - -This library should be compatible with py-bcrypt and it will run on Python -3.6+, and PyPy 3. - -Security --------- - -``bcrypt`` follows the `same security policy as cryptography`_, if you -identify a vulnerability, we ask you to contact us privately. - -.. _`same security policy as cryptography`: https://cryptography.io/en/latest/security.html -.. _`standard library`: https://docs.python.org/3/library/hashlib.html#hashlib.scrypt -.. _`argon2_cffi`: https://argon2-cffi.readthedocs.io -.. _`cryptography`: https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/#cryptography.hazmat.primitives.kdf.scrypt.Scrypt diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/RECORD b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/RECORD deleted file mode 100644 index cef311c..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/RECORD +++ /dev/null @@ -1,11 +0,0 @@ -bcrypt-4.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -bcrypt-4.2.1.dist-info/LICENSE,sha256=gXPVwptPlW1TJ4HSuG5OMPg-a3h43OGMkZRR1rpwfJA,10850 -bcrypt-4.2.1.dist-info/METADATA,sha256=BT_plSpIKU0J1b9jOlF-QX9i8uENQDs5HuUNm8lmlBY,10129 -bcrypt-4.2.1.dist-info/RECORD,, -bcrypt-4.2.1.dist-info/WHEEL,sha256=fQZsXnP4cy6-BHSJpJ09ORcawTrqmonn3VTcssgJM_I,100 -bcrypt-4.2.1.dist-info/top_level.txt,sha256=BkR_qBzDbSuycMzHWE1vzXrfYecAzUVmQs6G2CukqNI,7 -bcrypt/__init__.py,sha256=zTtuqGGQxDgxcqm1f_0UbbPS6uCl-WxL98gSYDMSUbw,1000 -bcrypt/__init__.pyi,sha256=ITUCB9mPVU8sKUbJQMDUH5YfQXZb1O55F9qvKZR_o8I,333 -bcrypt/__pycache__/__init__.cpython-311.pyc,, -bcrypt/_bcrypt.pyd,sha256=9IEyYrFO7SI0T1KNYyF1ZXjiuaglMyp5zS0FcXnNLg4,308224 -bcrypt/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/WHEEL b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/WHEEL deleted file mode 100644 index e73f907..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.45.0) -Root-Is-Purelib: false -Tag: cp39-abi3-win_amd64 - diff --git a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/top_level.txt b/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/top_level.txt deleted file mode 100644 index 7f0b6e7..0000000 --- a/.venv/Lib/site-packages/bcrypt-4.2.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -bcrypt diff --git a/.venv/Lib/site-packages/bcrypt/__init__.py b/.venv/Lib/site-packages/bcrypt/__init__.py deleted file mode 100644 index c201934..0000000 --- a/.venv/Lib/site-packages/bcrypt/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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 ._bcrypt import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - checkpw, - gensalt, - hashpw, - kdf, -) -from ._bcrypt import ( - __version_ex__ as __version__, -) - -__all__ = [ - "gensalt", - "hashpw", - "checkpw", - "kdf", - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/.venv/Lib/site-packages/bcrypt/__init__.pyi b/.venv/Lib/site-packages/bcrypt/__init__.pyi deleted file mode 100644 index 12e4a2e..0000000 --- a/.venv/Lib/site-packages/bcrypt/__init__.pyi +++ /dev/null @@ -1,10 +0,0 @@ -def gensalt(rounds: int = 12, prefix: bytes = b"2b") -> bytes: ... -def hashpw(password: bytes, salt: bytes) -> bytes: ... -def checkpw(password: bytes, hashed_password: bytes) -> bool: ... -def kdf( - password: bytes, - salt: bytes, - desired_key_bytes: int, - rounds: int, - ignore_few_rounds: bool = False, -) -> bytes: ... diff --git a/.venv/Lib/site-packages/bcrypt/_bcrypt.pyd b/.venv/Lib/site-packages/bcrypt/_bcrypt.pyd deleted file mode 100644 index 76558858256eadb0243b97cabc97ff09f925ea10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308224 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%0AkCG*)`_W9$+5gs5|}ftnHNq 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

t<`M~mJ_wVT?%3xDcLU3>jtj_m*N7>gg(t_GcZ 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 Rk=xiD!OQh6a`0bo{|mG$T&e&7 diff --git a/.venv/Lib/site-packages/bcrypt/py.typed b/.venv/Lib/site-packages/bcrypt/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/INSTALLER b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/LICENSE b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/LICENSE deleted file mode 100644 index 29225ee..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ - -Except when otherwise stated (look for LICENSE files in directories or -information at the beginning of each file) all software and -documentation is licensed as follows: - - 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. - diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/METADATA b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/METADATA deleted file mode 100644 index 60b0779..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/METADATA +++ /dev/null @@ -1,40 +0,0 @@ -Metadata-Version: 2.1 -Name: cffi -Version: 1.17.1 -Summary: Foreign Function Interface for Python calling C code. -Home-page: http://cffi.readthedocs.org -Author: Armin Rigo, Maciej Fijalkowski -Author-email: python-cffi@googlegroups.com -License: MIT -Project-URL: Documentation, http://cffi.readthedocs.org/ -Project-URL: Source Code, https://github.com/python-cffi/cffi -Project-URL: Issue Tracker, https://github.com/python-cffi/cffi/issues -Project-URL: Changelog, https://cffi.readthedocs.io/en/latest/whatsnew.html -Project-URL: Downloads, https://github.com/python-cffi/cffi/releases -Project-URL: Contact, https://groups.google.com/forum/#!forum/python-cffi -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: License :: OSI Approved :: MIT License -Requires-Python: >=3.8 -License-File: LICENSE -Requires-Dist: pycparser - - -CFFI -==== - -Foreign Function Interface for Python calling C code. -Please see the `Documentation `_. - -Contact -------- - -`Mailing list `_ diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/RECORD b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/RECORD deleted file mode 100644 index 755f076..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/RECORD +++ /dev/null @@ -1,48 +0,0 @@ -_cffi_backend.cp311-win_amd64.pyd,sha256=mu6Qz3mAyP9pS7P_4Gxx-H62phMDP3PjF0pzJkjTmYA,178176 -cffi-1.17.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cffi-1.17.1.dist-info/LICENSE,sha256=BLgPWwd7vtaICM_rreteNSPyqMmpZJXFh72W3x6sKjM,1294 -cffi-1.17.1.dist-info/METADATA,sha256=avJrvo-kUNx6iXJEaZVjGXNy42QS-YfjNHdJdeiBlFc,1571 -cffi-1.17.1.dist-info/RECORD,, -cffi-1.17.1.dist-info/WHEEL,sha256=gP9oq1B6BRaUd7LM9qWlox_06QqMeQKU8gW0ScfyBso,101 -cffi-1.17.1.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 -cffi-1.17.1.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 -cffi/__init__.py,sha256=H6t_ebva6EeHpUuItFLW1gbRp94eZRNJODLaWKdbx1I,513 -cffi/__pycache__/__init__.cpython-311.pyc,, -cffi/__pycache__/_imp_emulation.cpython-311.pyc,, -cffi/__pycache__/_shimmed_dist_utils.cpython-311.pyc,, -cffi/__pycache__/api.cpython-311.pyc,, -cffi/__pycache__/backend_ctypes.cpython-311.pyc,, -cffi/__pycache__/cffi_opcode.cpython-311.pyc,, -cffi/__pycache__/commontypes.cpython-311.pyc,, -cffi/__pycache__/cparser.cpython-311.pyc,, -cffi/__pycache__/error.cpython-311.pyc,, -cffi/__pycache__/ffiplatform.cpython-311.pyc,, -cffi/__pycache__/lock.cpython-311.pyc,, -cffi/__pycache__/model.cpython-311.pyc,, -cffi/__pycache__/pkgconfig.cpython-311.pyc,, -cffi/__pycache__/recompiler.cpython-311.pyc,, -cffi/__pycache__/setuptools_ext.cpython-311.pyc,, -cffi/__pycache__/vengine_cpy.cpython-311.pyc,, -cffi/__pycache__/vengine_gen.cpython-311.pyc,, -cffi/__pycache__/verifier.cpython-311.pyc,, -cffi/_cffi_errors.h,sha256=zQXt7uR_m8gUW-fI2hJg0KoSkJFwXv8RGUkEDZ177dQ,3908 -cffi/_cffi_include.h,sha256=Exhmgm9qzHWzWivjfTe0D7Xp4rPUkVxdNuwGhMTMzbw,15055 -cffi/_embedding.h,sha256=EDKw5QrLvQoe3uosXB3H1xPVTYxsn33eV3A43zsA_Fw,18787 -cffi/_imp_emulation.py,sha256=RxREG8zAbI2RPGBww90u_5fi8sWdahpdipOoPzkp7C0,2960 -cffi/_shimmed_dist_utils.py,sha256=Bjj2wm8yZbvFvWEx5AEfmqaqZyZFhYfoyLLQHkXZuao,2230 -cffi/api.py,sha256=alBv6hZQkjpmZplBphdaRn2lPO9-CORs_M7ixabvZWI,42169 -cffi/backend_ctypes.py,sha256=h5ZIzLc6BFVXnGyc9xPqZWUS7qGy7yFSDqXe68Sa8z4,42454 -cffi/cffi_opcode.py,sha256=JDV5l0R0_OadBX_uE7xPPTYtMdmpp8I9UYd6av7aiDU,5731 -cffi/commontypes.py,sha256=7N6zPtCFlvxXMWhHV08psUjdYIK2XgsN3yo5dgua_v4,2805 -cffi/cparser.py,sha256=0qI3mEzZSNVcCangoyXOoAcL-RhpQL08eG8798T024s,44789 -cffi/error.py,sha256=v6xTiS4U0kvDcy4h_BDRo5v39ZQuj-IMRYLv5ETddZs,877 -cffi/ffiplatform.py,sha256=avxFjdikYGJoEtmJO7ewVmwG_VEVl6EZ_WaNhZYCqv4,3584 -cffi/lock.py,sha256=l9TTdwMIMpi6jDkJGnQgE9cvTIR7CAntIJr8EGHt3pY,747 -cffi/model.py,sha256=W30UFQZE73jL5Mx5N81YT77us2W2iJjTm0XYfnwz1cg,21797 -cffi/parse_c_type.h,sha256=OdwQfwM9ktq6vlCB43exFQmxDBtj2MBNdK8LYl15tjw,5976 -cffi/pkgconfig.py,sha256=LP1w7vmWvmKwyqLaU1Z243FOWGNQMrgMUZrvgFuOlco,4374 -cffi/recompiler.py,sha256=sim4Tm7lamt2Jn8uzKN0wMYp6ODByk3g7of47-h9LD4,65367 -cffi/setuptools_ext.py,sha256=-ebj79lO2_AUH-kRcaja2pKY1Z_5tloGwsJgzK8P3Cc,8871 -cffi/vengine_cpy.py,sha256=8UagT6ZEOZf6Dju7_CfNulue8CnsHLEzJYhnqUhoF04,43752 -cffi/vengine_gen.py,sha256=DUlEIrDiVin1Pnhn1sfoamnS5NLqfJcOdhRoeSNeJRg,26939 -cffi/verifier.py,sha256=oX8jpaohg2Qm3aHcznidAdvrVm5N4sQYG0a3Eo5mIl4,11182 diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/WHEEL b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/WHEEL deleted file mode 100644 index eac371e..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (74.1.1) -Root-Is-Purelib: false -Tag: cp311-cp311-win_amd64 - diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/entry_points.txt b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/entry_points.txt deleted file mode 100644 index 4b0274f..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[distutils.setup_keywords] -cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/top_level.txt b/.venv/Lib/site-packages/cffi-1.17.1.dist-info/top_level.txt deleted file mode 100644 index f645779..0000000 --- a/.venv/Lib/site-packages/cffi-1.17.1.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_cffi_backend -cffi diff --git a/.venv/Lib/site-packages/cffi/__init__.py b/.venv/Lib/site-packages/cffi/__init__.py deleted file mode 100644 index 2e35a38..0000000 --- a/.venv/Lib/site-packages/cffi/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', - 'FFIError'] - -from .api import FFI -from .error import CDefError, FFIError, VerificationError, VerificationMissing -from .error import PkgConfigError - -__version__ = "1.17.1" -__version_info__ = (1, 17, 1) - -# The verifier module file names are based on the CRC32 of a string that -# contains the following version number. It may be older than __version__ -# if nothing is clearly incompatible. -__version_verifier_modules__ = "0.8.6" diff --git a/.venv/Lib/site-packages/cffi/_cffi_errors.h b/.venv/Lib/site-packages/cffi/_cffi_errors.h deleted file mode 100644 index 158e059..0000000 --- a/.venv/Lib/site-packages/cffi/_cffi_errors.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef CFFI_MESSAGEBOX -# ifdef _MSC_VER -# define CFFI_MESSAGEBOX 1 -# else -# define CFFI_MESSAGEBOX 0 -# endif -#endif - - -#if CFFI_MESSAGEBOX -/* Windows only: logic to take the Python-CFFI embedding logic - initialization errors and display them in a background thread - with MessageBox. The idea is that if the whole program closes - as a result of this problem, then likely it is already a console - program and you can read the stderr output in the console too. - If it is not a console program, then it will likely show its own - dialog to complain, or generally not abruptly close, and for this - case the background thread should stay alive. -*/ -static void *volatile _cffi_bootstrap_text; - -static PyObject *_cffi_start_error_capture(void) -{ - PyObject *result = NULL; - PyObject *x, *m, *bi; - - if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, - (void *)1, NULL) != NULL) - return (PyObject *)1; - - m = PyImport_AddModule("_cffi_error_capture"); - if (m == NULL) - goto error; - - result = PyModule_GetDict(m); - if (result == NULL) - goto error; - -#if PY_MAJOR_VERSION >= 3 - bi = PyImport_ImportModule("builtins"); -#else - bi = PyImport_ImportModule("__builtin__"); -#endif - if (bi == NULL) - goto error; - PyDict_SetItemString(result, "__builtins__", bi); - Py_DECREF(bi); - - x = PyRun_String( - "import sys\n" - "class FileLike:\n" - " def write(self, x):\n" - " try:\n" - " of.write(x)\n" - " except: pass\n" - " self.buf += x\n" - " def flush(self):\n" - " pass\n" - "fl = FileLike()\n" - "fl.buf = ''\n" - "of = sys.stderr\n" - "sys.stderr = fl\n" - "def done():\n" - " sys.stderr = of\n" - " return fl.buf\n", /* make sure the returned value stays alive */ - Py_file_input, - result, result); - Py_XDECREF(x); - - error: - if (PyErr_Occurred()) - { - PyErr_WriteUnraisable(Py_None); - PyErr_Clear(); - } - return result; -} - -#pragma comment(lib, "user32.lib") - -static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) -{ - Sleep(666); /* may be interrupted if the whole process is closing */ -#if PY_MAJOR_VERSION >= 3 - MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, - L"Python-CFFI error", - MB_OK | MB_ICONERROR); -#else - MessageBoxA(NULL, (char *)_cffi_bootstrap_text, - "Python-CFFI error", - MB_OK | MB_ICONERROR); -#endif - _cffi_bootstrap_text = NULL; - return 0; -} - -static void _cffi_stop_error_capture(PyObject *ecap) -{ - PyObject *s; - void *text; - - if (ecap == (PyObject *)1) - return; - - if (ecap == NULL) - goto error; - - s = PyRun_String("done()", Py_eval_input, ecap, ecap); - if (s == NULL) - goto error; - - /* Show a dialog box, but in a background thread, and - never show multiple dialog boxes at once. */ -#if PY_MAJOR_VERSION >= 3 - text = PyUnicode_AsWideCharString(s, NULL); -#else - text = PyString_AsString(s); -#endif - - _cffi_bootstrap_text = text; - - if (text != NULL) - { - HANDLE h; - h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, - NULL, 0, NULL); - if (h != NULL) - CloseHandle(h); - } - /* decref the string, but it should stay alive as 'fl.buf' - in the small module above. It will really be freed only if - we later get another similar error. So it's a leak of at - most one copy of the small module. That's fine for this - situation which is usually a "fatal error" anyway. */ - Py_DECREF(s); - PyErr_Clear(); - return; - - error: - _cffi_bootstrap_text = NULL; - PyErr_Clear(); -} - -#else - -static PyObject *_cffi_start_error_capture(void) { return NULL; } -static void _cffi_stop_error_capture(PyObject *ecap) { } - -#endif diff --git a/.venv/Lib/site-packages/cffi/_cffi_include.h b/.venv/Lib/site-packages/cffi/_cffi_include.h deleted file mode 100644 index 908a1d7..0000000 --- a/.venv/Lib/site-packages/cffi/_cffi_include.h +++ /dev/null @@ -1,389 +0,0 @@ -#define _CFFI_ - -/* We try to define Py_LIMITED_API before including Python.h. - - Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and - Py_REF_DEBUG are not defined. This is a best-effort approximation: - we can learn about Py_DEBUG from pyconfig.h, but it is unclear if - the same works for the other two macros. Py_DEBUG implies them, - but not the other way around. - - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv - version >= 16.0.0. With older versions of either, you don't get a - copy of PYTHON3.DLL in the virtualenv. We can't check the version of - CPython *before* we even include pyconfig.h. ffi.set_source() puts - a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is - running on Windows < 3.5, as an attempt at fixing it, but that's - arguably wrong because it may not be the target version of Python. - Still better than nothing I guess. As another workaround, you can - remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. -*/ -#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# endif -#endif - -#include -#ifdef __cplusplus -extern "C" { -#endif -#include -#include "parse_c_type.h" - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif - -#ifdef __GNUC__ -# define _CFFI_UNUSED_FN __attribute__((unused)) -#else -# define _CFFI_UNUSED_FN /* nothing */ -#endif - -#ifdef __cplusplus -# ifndef _Bool - typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ -# endif -#endif - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - not used any more -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ - PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) -#define _CFFI_CPIDX 25 -#define _cffi_call_python \ - ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) -#define _cffi_to_c_wchar3216_t \ - ((int(*)(PyObject *))_cffi_exports[26]) -#define _cffi_from_c_wchar3216_t \ - ((PyObject *(*)(int))_cffi_exports[27]) -#define _CFFI_NUM_EXPORTS 28 - -struct _cffi_ctypedescr; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; - -#define _cffi_type(index) ( \ - assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ - (struct _cffi_ctypedescr *)_cffi_types[index]) - -static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, - const struct _cffi_type_context_s *ctx) -{ - PyObject *module, *o_arg, *new_module; - void *raw[] = { - (void *)module_name, - (void *)version, - (void *)_cffi_exports, - (void *)ctx, - }; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - o_arg = PyLong_FromVoidPtr((void *)raw); - if (o_arg == NULL) - goto failure; - - new_module = PyObject_CallMethod( - module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); - - Py_DECREF(o_arg); - Py_DECREF(module); - return new_module; - - failure: - Py_XDECREF(module); - return NULL; -} - - -#ifdef HAVE_WCHAR_H -typedef wchar_t _cffi_wchar_t; -#else -typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ -#endif - -_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 2) - return (uint16_t)_cffi_to_c_wchar_t(o); - else - return (uint16_t)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) -{ - if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 4) - return (int)_cffi_to_c_wchar_t(o); - else - return (int)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) -{ - if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -_CFFI_UNUSED_FN static int -_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -_CFFI_UNUSED_FN static void -_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -/********** end CPython-specific section **********/ -#else -_CFFI_UNUSED_FN -static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); -# define _cffi_call_python _cffi_call_python_org -#endif - - -#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) - -#define _cffi_prim_int(size, sign) \ - ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ - (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ - (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ - (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ - _CFFI__UNKNOWN_PRIM) - -#define _cffi_prim_float(size) \ - ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ - (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ - (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ - _CFFI__UNKNOWN_FLOAT_PRIM) - -#define _cffi_check_int(got, got_nonpos, expected) \ - ((got_nonpos) == (expected <= 0) && \ - (got) == (unsigned long long)expected) - -#ifdef MS_WIN32 -# define _cffi_stdcall __stdcall -#else -# define _cffi_stdcall /* nothing */ -#endif - -#ifdef __cplusplus -} -#endif diff --git a/.venv/Lib/site-packages/cffi/_embedding.h b/.venv/Lib/site-packages/cffi/_embedding.h deleted file mode 100644 index 94d8b30..0000000 --- a/.venv/Lib/site-packages/cffi/_embedding.h +++ /dev/null @@ -1,550 +0,0 @@ - -/***** Support code for embedding *****/ - -#ifdef __cplusplus -extern "C" { -#endif - - -#if defined(_WIN32) -# define CFFI_DLLEXPORT __declspec(dllexport) -#elif defined(__GNUC__) -# define CFFI_DLLEXPORT __attribute__((visibility("default"))) -#else -# define CFFI_DLLEXPORT /* nothing */ -#endif - - -/* There are two global variables of type _cffi_call_python_fnptr: - - * _cffi_call_python, which we declare just below, is the one called - by ``extern "Python"`` implementations. - - * _cffi_call_python_org, which on CPython is actually part of the - _cffi_exports[] array, is the function pointer copied from - _cffi_backend. If _cffi_start_python() fails, then this is set - to NULL; otherwise, it should never be NULL. - - After initialization is complete, both are equal. However, the - first one remains equal to &_cffi_start_and_call_python until the - very end of initialization, when we are (or should be) sure that - concurrent threads also see a completely initialized world, and - only then is it changed. -*/ -#undef _cffi_call_python -typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); -static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); -static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; - - -#ifndef _MSC_VER - /* --- Assuming a GCC not infinitely old --- */ -# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) -# define cffi_write_barrier() __sync_synchronize() -# if !defined(__amd64__) && !defined(__x86_64__) && \ - !defined(__i386__) && !defined(__i386) -# define cffi_read_barrier() __sync_synchronize() -# else -# define cffi_read_barrier() (void)0 -# endif -#else - /* --- Windows threads version --- */ -# include -# define cffi_compare_and_swap(l,o,n) \ - (InterlockedCompareExchangePointer(l,n,o) == (o)) -# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) -# define cffi_read_barrier() (void)0 -static volatile LONG _cffi_dummy; -#endif - -#ifdef WITH_THREAD -# ifndef _MSC_VER -# include - static pthread_mutex_t _cffi_embed_startup_lock; -# else - static CRITICAL_SECTION _cffi_embed_startup_lock; -# endif - static char _cffi_embed_startup_lock_ready = 0; -#endif - -static void _cffi_acquire_reentrant_mutex(void) -{ - static void *volatile lock = NULL; - - while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: pthread_mutex_init() should be very fast, and - this is only run at start-up anyway. */ - } - -#ifdef WITH_THREAD - if (!_cffi_embed_startup_lock_ready) { -# ifndef _MSC_VER - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_cffi_embed_startup_lock, &attr); -# else - InitializeCriticalSection(&_cffi_embed_startup_lock); -# endif - _cffi_embed_startup_lock_ready = 1; - } -#endif - - while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) - ; - -#ifndef _MSC_VER - pthread_mutex_lock(&_cffi_embed_startup_lock); -#else - EnterCriticalSection(&_cffi_embed_startup_lock); -#endif -} - -static void _cffi_release_reentrant_mutex(void) -{ -#ifndef _MSC_VER - pthread_mutex_unlock(&_cffi_embed_startup_lock); -#else - LeaveCriticalSection(&_cffi_embed_startup_lock); -#endif -} - - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - -#include "_cffi_errors.h" - - -#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ - -static void _cffi_py_initialize(void) -{ - /* XXX use initsigs=0, which "skips initialization registration of - signal handlers, which might be useful when Python is - embedded" according to the Python docs. But review and think - if it should be a user-controllable setting. - - XXX we should also give a way to write errors to a buffer - instead of to stderr. - - XXX if importing 'site' fails, CPython (any version) calls - exit(). Should we try to work around this behavior here? - */ - Py_InitializeEx(0); -} - -static int _cffi_initialize_python(void) -{ - /* This initializes Python, imports _cffi_backend, and then the - present .dll/.so is set up as a CPython C extension module. - */ - int result; - PyGILState_STATE state; - PyObject *pycode=NULL, *global_dict=NULL, *x; - PyObject *builtins; - - state = PyGILState_Ensure(); - - /* Call the initxxx() function from the present module. It will - create and initialize us as a CPython extension module, instead - of letting the startup Python code do it---it might reimport - the same .dll/.so and get maybe confused on some platforms. - It might also have troubles locating the .dll/.so again for all - I know. - */ - (void)_CFFI_PYTHON_STARTUP_FUNC(); - if (PyErr_Occurred()) - goto error; - - /* Now run the Python code provided to ffi.embedding_init_code(). - */ - pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, - "", - Py_file_input); - if (pycode == NULL) - goto error; - global_dict = PyDict_New(); - if (global_dict == NULL) - goto error; - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) - goto error; - x = PyEval_EvalCode( -#if PY_MAJOR_VERSION < 3 - (PyCodeObject *) -#endif - pycode, global_dict, global_dict); - if (x == NULL) - goto error; - Py_DECREF(x); - - /* Done! Now if we've been called from - _cffi_start_and_call_python() in an ``extern "Python"``, we can - only hope that the Python code did correctly set up the - corresponding @ffi.def_extern() function. Otherwise, the - general logic of ``extern "Python"`` functions (inside the - _cffi_backend module) will find that the reference is still - missing and print an error. - */ - result = 0; - done: - Py_XDECREF(pycode); - Py_XDECREF(global_dict); - PyGILState_Release(state); - return result; - - error:; - { - /* Print as much information as potentially useful. - Debugging load-time failures with embedding is not fun - */ - PyObject *ecap; - PyObject *exception, *v, *tb, *f, *modules, *mod; - PyErr_Fetch(&exception, &v, &tb); - ecap = _cffi_start_error_capture(); - f = PySys_GetObject((char *)"stderr"); - if (f != NULL && f != Py_None) { - PyFile_WriteString( - "Failed to initialize the Python-CFFI embedding logic:\n\n", f); - } - - if (exception != NULL) { - PyErr_NormalizeException(&exception, &v, &tb); - PyErr_Display(exception, v, tb); - } - Py_XDECREF(exception); - Py_XDECREF(v); - Py_XDECREF(tb); - - if (f != NULL && f != Py_None) { - PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.17.1" - "\n_cffi_backend module: ", f); - modules = PyImport_GetModuleDict(); - mod = PyDict_GetItemString(modules, "_cffi_backend"); - if (mod == NULL) { - PyFile_WriteString("not loaded", f); - } - else { - v = PyObject_GetAttrString(mod, "__file__"); - PyFile_WriteObject(v, f, 0); - Py_XDECREF(v); - } - PyFile_WriteString("\nsys.path: ", f); - PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); - PyFile_WriteString("\n\n", f); - } - _cffi_stop_error_capture(ecap); - } - result = -1; - goto done; -} - -#if PY_VERSION_HEX < 0x03080000 -PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ -#endif - -static int _cffi_carefully_make_gil(void) -{ - /* This does the basic initialization of Python. It can be called - completely concurrently from unrelated threads. It assumes - that we don't hold the GIL before (if it exists), and we don't - hold it afterwards. - - (What it really does used to be completely different in Python 2 - and Python 3, with the Python 2 solution avoiding the spin-lock - around the Py_InitializeEx() call. However, after recent changes - to CPython 2.7 (issue #358) it no longer works. So we use the - Python 3 solution everywhere.) - - This initializes Python by calling Py_InitializeEx(). - Important: this must not be called concurrently at all. - So we use a global variable as a simple spin lock. This global - variable must be from 'libpythonX.Y.so', not from this - cffi-based extension module, because it must be shared from - different cffi-based extension modules. - - In Python < 3.8, we choose - _PyParser_TokenNames[0] as a completely arbitrary pointer value - that is never written to. The default is to point to the - string "ENDMARKER". We change it temporarily to point to the - next character in that string. (Yes, I know it's REALLY - obscure.) - - In Python >= 3.8, this string array is no longer writable, so - instead we pick PyCapsuleType.tp_version_tag. We can't change - Python < 3.8 because someone might use a mixture of cffi - embedded modules, some of which were compiled before this file - changed. - - In Python >= 3.12, this stopped working because that particular - tp_version_tag gets modified during interpreter startup. It's - arguably a bad idea before 3.12 too, but again we can't change - that because someone might use a mixture of cffi embedded - modules, and no-one reported a bug so far. In Python >= 3.12 - we go instead for PyCapsuleType.tp_as_buffer, which is supposed - to always be NULL. We write to it temporarily a pointer to - a struct full of NULLs, which is semantically the same. - */ - -#ifdef WITH_THREAD -# if PY_VERSION_HEX < 0x03080000 - char *volatile *lock = (char *volatile *)_PyParser_TokenNames; - char *old_value, *locked_value; - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = old_value + 1; - if (old_value[0] == 'E') { - assert(old_value[1] == 'N'); - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value[0] == 'N'); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# else -# if PY_VERSION_HEX < 0x030C0000 - int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; - int old_value, locked_value = -42; - assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); -# else - static struct ebp_s { PyBufferProcs buf; int mark; } empty_buffer_procs; - empty_buffer_procs.mark = -42; - PyBufferProcs *volatile *lock = (PyBufferProcs *volatile *) - &PyCapsule_Type.tp_as_buffer; - PyBufferProcs *old_value, *locked_value = &empty_buffer_procs.buf; -# endif - - while (1) { /* spin loop */ - old_value = *lock; - if (old_value == 0) { - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { -# if PY_VERSION_HEX < 0x030C0000 - assert(old_value == locked_value); -# else - /* The pointer should point to a possibly different - empty_buffer_procs from another C extension module */ - assert(((struct ebp_s *)old_value)->mark == -42); -# endif - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# endif -#endif - - /* call Py_InitializeEx() */ - if (!Py_IsInitialized()) { - _cffi_py_initialize(); -#if PY_VERSION_HEX < 0x03070000 - PyEval_InitThreads(); -#endif - PyEval_SaveThread(); /* release the GIL */ - /* the returned tstate must be the one that has been stored into the - autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ - } - else { -#if PY_VERSION_HEX < 0x03070000 - /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ - PyGILState_STATE state = PyGILState_Ensure(); - PyEval_InitThreads(); - PyGILState_Release(state); -#endif - } - -#ifdef WITH_THREAD - /* release the lock */ - while (!cffi_compare_and_swap(lock, locked_value, old_value)) - ; -#endif - - return 0; -} - -/********** end CPython-specific section **********/ - - -#else - - -/********** PyPy-specific section **********/ - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ - -static struct _cffi_pypy_init_s { - const char *name; - void *func; /* function pointer */ - const char *code; -} _cffi_pypy_init = { - _CFFI_MODULE_NAME, - _CFFI_PYTHON_STARTUP_FUNC, - _CFFI_PYTHON_STARTUP_CODE, -}; - -extern int pypy_carefully_make_gil(const char *); -extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); - -static int _cffi_carefully_make_gil(void) -{ - return pypy_carefully_make_gil(_CFFI_MODULE_NAME); -} - -static int _cffi_initialize_python(void) -{ - return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); -} - -/********** end PyPy-specific section **********/ - - -#endif - - -#ifdef __GNUC__ -__attribute__((noinline)) -#endif -static _cffi_call_python_fnptr _cffi_start_python(void) -{ - /* Delicate logic to initialize Python. This function can be - called multiple times concurrently, e.g. when the process calls - its first ``extern "Python"`` functions in multiple threads at - once. It can also be called recursively, in which case we must - ignore it. We also have to consider what occurs if several - different cffi-based extensions reach this code in parallel - threads---it is a different copy of the code, then, and we - can't have any shared global variable unless it comes from - 'libpythonX.Y.so'. - - Idea: - - * _cffi_carefully_make_gil(): "carefully" call - PyEval_InitThreads() (possibly with Py_InitializeEx() first). - - * then we use a (local) custom lock to make sure that a call to this - cffi-based extension will wait if another call to the *same* - extension is running the initialization in another thread. - It is reentrant, so that a recursive call will not block, but - only one from a different thread. - - * then we grab the GIL and (Python 2) we call Py_InitializeEx(). - At this point, concurrent calls to Py_InitializeEx() are not - possible: we have the GIL. - - * do the rest of the specific initialization, which may - temporarily release the GIL but not the custom lock. - Only release the custom lock when we are done. - */ - static char called = 0; - - if (_cffi_carefully_make_gil() != 0) - return NULL; - - _cffi_acquire_reentrant_mutex(); - - /* Here the GIL exists, but we don't have it. We're only protected - from concurrency by the reentrant mutex. */ - - /* This file only initializes the embedded module once, the first - time this is called, even if there are subinterpreters. */ - if (!called) { - called = 1; /* invoke _cffi_initialize_python() only once, - but don't set '_cffi_call_python' right now, - otherwise concurrent threads won't call - this function at all (we need them to wait) */ - if (_cffi_initialize_python() == 0) { - /* now initialization is finished. Switch to the fast-path. */ - - /* We would like nobody to see the new value of - '_cffi_call_python' without also seeing the rest of the - data initialized. However, this is not possible. But - the new value of '_cffi_call_python' is the function - 'cffi_call_python()' from _cffi_backend. So: */ - cffi_write_barrier(); - /* ^^^ we put a write barrier here, and a corresponding - read barrier at the start of cffi_call_python(). This - ensures that after that read barrier, we see everything - done here before the write barrier. - */ - - assert(_cffi_call_python_org != NULL); - _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; - } - else { - /* initialization failed. Reset this to NULL, even if it was - already set to some other value. Future calls to - _cffi_start_python() are still forced to occur, and will - always return NULL from now on. */ - _cffi_call_python_org = NULL; - } - } - - _cffi_release_reentrant_mutex(); - - return (_cffi_call_python_fnptr)_cffi_call_python_org; -} - -static -void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) -{ - _cffi_call_python_fnptr fnptr; - int current_err = errno; -#ifdef _MSC_VER - int current_lasterr = GetLastError(); -#endif - fnptr = _cffi_start_python(); - if (fnptr == NULL) { - fprintf(stderr, "function %s() called, but initialization code " - "failed. Returning 0.\n", externpy->name); - memset(args, 0, externpy->size_of_result); - } -#ifdef _MSC_VER - SetLastError(current_lasterr); -#endif - errno = current_err; - - if (fnptr != NULL) - fnptr(externpy, args); -} - - -/* The cffi_start_python() function makes sure Python is initialized - and our cffi module is set up. It can be called manually from the - user C code. The same effect is obtained automatically from any - dll-exported ``extern "Python"`` function. This function returns - -1 if initialization failed, 0 if all is OK. */ -_CFFI_UNUSED_FN -static int cffi_start_python(void) -{ - if (_cffi_call_python == &_cffi_start_and_call_python) { - if (_cffi_start_python() == NULL) - return -1; - } - cffi_read_barrier(); - return 0; -} - -#undef cffi_compare_and_swap -#undef cffi_write_barrier -#undef cffi_read_barrier - -#ifdef __cplusplus -} -#endif diff --git a/.venv/Lib/site-packages/cffi/_imp_emulation.py b/.venv/Lib/site-packages/cffi/_imp_emulation.py deleted file mode 100644 index 136abdd..0000000 --- a/.venv/Lib/site-packages/cffi/_imp_emulation.py +++ /dev/null @@ -1,83 +0,0 @@ - -try: - # this works on Python < 3.12 - from imp import * - -except ImportError: - # this is a limited emulation for Python >= 3.12. - # Note that this is used only for tests or for the old ffi.verify(). - # This is copied from the source code of Python 3.11. - - from _imp import (acquire_lock, release_lock, - is_builtin, is_frozen) - - from importlib._bootstrap import _load - - from importlib import machinery - import os - import sys - import tokenize - - SEARCH_ERROR = 0 - PY_SOURCE = 1 - PY_COMPILED = 2 - C_EXTENSION = 3 - PY_RESOURCE = 4 - PKG_DIRECTORY = 5 - C_BUILTIN = 6 - PY_FROZEN = 7 - PY_CODERESOURCE = 8 - IMP_HOOK = 9 - - def get_suffixes(): - extensions = [(s, 'rb', C_EXTENSION) - for s in machinery.EXTENSION_SUFFIXES] - source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] - bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] - return extensions + source + bytecode - - def find_module(name, path=None): - if not isinstance(name, str): - raise TypeError("'name' must be a str, not {}".format(type(name))) - elif not isinstance(path, (type(None), list)): - # Backwards-compatibility - raise RuntimeError("'path' must be None or a list, " - "not {}".format(type(path))) - - if path is None: - if is_builtin(name): - return None, None, ('', '', C_BUILTIN) - elif is_frozen(name): - return None, None, ('', '', PY_FROZEN) - else: - path = sys.path - - for entry in path: - package_directory = os.path.join(entry, name) - for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: - package_file_name = '__init__' + suffix - file_path = os.path.join(package_directory, package_file_name) - if os.path.isfile(file_path): - return None, package_directory, ('', '', PKG_DIRECTORY) - for suffix, mode, type_ in get_suffixes(): - file_name = name + suffix - file_path = os.path.join(entry, file_name) - if os.path.isfile(file_path): - break - else: - continue - break # Break out of outer loop when breaking out of inner loop. - else: - raise ImportError(name, name=name) - - encoding = None - if 'b' not in mode: - with open(file_path, 'rb') as file: - encoding = tokenize.detect_encoding(file.readline)[0] - file = open(file_path, mode, encoding=encoding) - return file, file_path, (suffix, mode, type_) - - def load_dynamic(name, path, file=None): - loader = machinery.ExtensionFileLoader(name, path) - spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) - return _load(spec) diff --git a/.venv/Lib/site-packages/cffi/_shimmed_dist_utils.py b/.venv/Lib/site-packages/cffi/_shimmed_dist_utils.py deleted file mode 100644 index c3d2312..0000000 --- a/.venv/Lib/site-packages/cffi/_shimmed_dist_utils.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful -error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. - -This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. -""" -import sys - -try: - # import setuptools first; this is the most robust way to ensure its embedded distutils is available - # (the .pth shim should usually work, but this is even more robust) - import setuptools -except Exception as ex: - if sys.version_info >= (3, 12): - # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal - raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex - - # silently ignore on older Pythons (support fallback to stdlib distutils where available) -else: - del setuptools - -try: - # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils - from distutils import log, sysconfig - from distutils.ccompiler import CCompiler - from distutils.command.build_ext import build_ext - from distutils.core import Distribution, Extension - from distutils.dir_util import mkpath - from distutils.errors import DistutilsSetupError, CompileError, LinkError - from distutils.log import set_threshold, set_verbosity - - if sys.platform == 'win32': - try: - # FUTURE: msvc9compiler module was removed in setuptools 74; consider removing, as it's only used by an ancient patch in `recompiler` - from distutils.msvc9compiler import MSVCCompiler - except ImportError: - MSVCCompiler = None -except Exception as ex: - if sys.version_info >= (3, 12): - raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex - - # anything older, just let the underlying distutils import error fly - raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex - -del sys diff --git a/.venv/Lib/site-packages/cffi/api.py b/.venv/Lib/site-packages/cffi/api.py deleted file mode 100644 index 5a474f3..0000000 --- a/.venv/Lib/site-packages/cffi/api.py +++ /dev/null @@ -1,967 +0,0 @@ -import sys, types -from .lock import allocate_lock -from .error import CDefError -from . import model - -try: - callable -except NameError: - # Python 3.1 - from collections import Callable - callable = lambda x: isinstance(x, Callable) - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -_unspecified = object() - - - -class FFI(object): - r''' - The main top-level class that you instantiate once, or once per module. - - Example usage: - - ffi = FFI() - ffi.cdef(""" - int printf(const char *, ...); - """) - - C = ffi.dlopen(None) # standard library - -or- - C = ffi.verify() # use a C compiler: verify the decl above is right - - C.printf("hello, %s!\n", ffi.new("char[]", "world")) - ''' - - def __init__(self, backend=None): - """Create an FFI instance. The 'backend' argument is used to - select a non-default backend, mostly for tests. - """ - if backend is None: - # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with - # _cffi_backend.so compiled. - import _cffi_backend as backend - from . import __version__ - if backend.__version__ != __version__: - # bad version! Try to be as explicit as possible. - if hasattr(backend, '__file__'): - # CPython - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( - __version__, __file__, - backend.__version__, backend.__file__)) - else: - # PyPy - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( - __version__, __file__, backend.__version__)) - # (If you insist you can also try to pass the option - # 'backend=backend_ctypes.CTypesBackend()', but don't - # rely on it! It's probably not going to work well.) - - from . import cparser - self._backend = backend - self._lock = allocate_lock() - self._parser = cparser.Parser() - self._cached_btypes = {} - self._parsed_types = types.ModuleType('parsed_types').__dict__ - self._new_types = types.ModuleType('new_types').__dict__ - self._function_caches = [] - self._libraries = [] - self._cdefsources = [] - self._included_ffis = [] - self._windows_unicode = None - self._init_once_cache = {} - self._cdef_version = None - self._embedding = None - self._typecache = model.get_typecache(backend) - if hasattr(backend, 'set_ffi'): - backend.set_ffi(self) - for name in list(backend.__dict__): - if name.startswith('RTLD_'): - setattr(self, name, getattr(backend, name)) - # - with self._lock: - self.BVoidP = self._get_cached_btype(model.voidp_type) - self.BCharA = self._get_cached_btype(model.char_array_type) - if isinstance(backend, types.ModuleType): - # _cffi_backend: attach these constants to the class - if not hasattr(FFI, 'NULL'): - FFI.NULL = self.cast(self.BVoidP, 0) - FFI.CData, FFI.CType = backend._get_types() - else: - # ctypes backend: attach these constants to the instance - self.NULL = self.cast(self.BVoidP, 0) - self.CData, self.CType = backend._get_types() - self.buffer = backend.buffer - - def cdef(self, csource, override=False, packed=False, pack=None): - """Parse the given C source. This registers all declared functions, - types, and global variables. The functions and global variables can - then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. - The types can be used in 'ffi.new()' and other functions. - If 'packed' is specified as True, all structs declared inside this - cdef are packed, i.e. laid out without any field alignment at all. - Alternatively, 'pack' can be a small integer, and requests for - alignment greater than that are ignored (pack=1 is equivalent to - packed=True). - """ - self._cdef(csource, override=override, packed=packed, pack=pack) - - def embedding_api(self, csource, packed=False, pack=None): - self._cdef(csource, packed=packed, pack=pack, dllexport=True) - if self._embedding is None: - self._embedding = '' - - def _cdef(self, csource, override=False, **options): - if not isinstance(csource, str): # unicode, on Python 2 - if not isinstance(csource, basestring): - raise TypeError("cdef() argument must be a string") - csource = csource.encode('ascii') - with self._lock: - self._cdef_version = object() - self._parser.parse(csource, override=override, **options) - self._cdefsources.append(csource) - if override: - for cache in self._function_caches: - cache.clear() - finishlist = self._parser._recomplete - if finishlist: - self._parser._recomplete = [] - for tp in finishlist: - tp.finish_backend_type(self, finishlist) - - def dlopen(self, name, flags=0): - """Load and return a dynamic library identified by 'name'. - The standard C library can be loaded by passing None. - Note that functions and types declared by 'ffi.cdef()' are not - linked to a particular library, just like C headers; in the - library we only look for the actual (untyped) symbols. - """ - if not (isinstance(name, basestring) or - name is None or - isinstance(name, self.CData)): - raise TypeError("dlopen(name): name must be a file name, None, " - "or an already-opened 'void *' handle") - with self._lock: - lib, function_cache = _make_ffi_library(self, name, flags) - self._function_caches.append(function_cache) - self._libraries.append(lib) - return lib - - def dlclose(self, lib): - """Close a library obtained with ffi.dlopen(). After this call, - access to functions or variables from the library will fail - (possibly with a segmentation fault). - """ - type(lib).__cffi_close__(lib) - - def _typeof_locked(self, cdecl): - # call me with the lock! - key = cdecl - if key in self._parsed_types: - return self._parsed_types[key] - # - if not isinstance(cdecl, str): # unicode, on Python 2 - cdecl = cdecl.encode('ascii') - # - type = self._parser.parse_type(cdecl) - really_a_function_type = type.is_raw_function - if really_a_function_type: - type = type.as_function_pointer() - btype = self._get_cached_btype(type) - result = btype, really_a_function_type - self._parsed_types[key] = result - return result - - def _typeof(self, cdecl, consider_function_as_funcptr=False): - # string -> ctype object - try: - result = self._parsed_types[cdecl] - except KeyError: - with self._lock: - result = self._typeof_locked(cdecl) - # - btype, really_a_function_type = result - if really_a_function_type and not consider_function_as_funcptr: - raise CDefError("the type %r is a function type, not a " - "pointer-to-function type" % (cdecl,)) - return btype - - def typeof(self, cdecl): - """Parse the C type given as a string and return the - corresponding object. - It can also be used on 'cdata' instance to get its C type. - """ - if isinstance(cdecl, basestring): - return self._typeof(cdecl) - if isinstance(cdecl, self.CData): - return self._backend.typeof(cdecl) - if isinstance(cdecl, types.BuiltinFunctionType): - res = _builtin_function_type(cdecl) - if res is not None: - return res - if (isinstance(cdecl, types.FunctionType) - and hasattr(cdecl, '_cffi_base_type')): - with self._lock: - return self._get_cached_btype(cdecl._cffi_base_type) - raise TypeError(type(cdecl)) - - def sizeof(self, cdecl): - """Return the size in bytes of the argument. It can be a - string naming a C type, or a 'cdata' instance. - """ - if isinstance(cdecl, basestring): - BType = self._typeof(cdecl) - return self._backend.sizeof(BType) - else: - return self._backend.sizeof(cdecl) - - def alignof(self, cdecl): - """Return the natural alignment size in bytes of the C type - given as a string. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.alignof(cdecl) - - def offsetof(self, cdecl, *fields_or_indexes): - """Return the offset of the named field inside the given - structure or array, which must be given as a C type name. - You can give several field names in case of nested structures. - You can also give numeric values which correspond to array - items, in case of an array type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._typeoffsetof(cdecl, *fields_or_indexes)[1] - - def new(self, cdecl, init=None): - """Allocate an instance according to the specified C type and - return a pointer to it. The specified C type must be either a - pointer or an array: ``new('X *')`` allocates an X and returns - a pointer to it, whereas ``new('X[n]')`` allocates an array of - n X'es and returns an array referencing it (which works - mostly like a pointer, like in C). You can also use - ``new('X[]', n)`` to allocate an array of a non-constant - length n. - - The memory is initialized following the rules of declaring a - global variable in C: by default it is zero-initialized, but - an explicit initializer can be given which can be used to - fill all or part of the memory. - - When the returned object goes out of scope, the memory - is freed. In other words the returned object has - ownership of the value of type 'cdecl' that it points to. This - means that the raw data can be used as long as this object is - kept alive, but must not be used for a longer time. Be careful - about that when copying the pointer to the memory somewhere - else, e.g. into another structure. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.newp(cdecl, init) - - def new_allocator(self, alloc=None, free=None, - should_clear_after_alloc=True): - """Return a new allocator, i.e. a function that behaves like ffi.new() - but uses the provided low-level 'alloc' and 'free' functions. - - 'alloc' is called with the size as argument. If it returns NULL, a - MemoryError is raised. 'free' is called with the result of 'alloc' - as argument. Both can be either Python function or directly C - functions. If 'free' is None, then no free function is called. - If both 'alloc' and 'free' are None, the default is used. - - If 'should_clear_after_alloc' is set to False, then the memory - returned by 'alloc' is assumed to be already cleared (or you are - fine with garbage); otherwise CFFI will clear it. - """ - compiled_ffi = self._backend.FFI() - allocator = compiled_ffi.new_allocator(alloc, free, - should_clear_after_alloc) - def allocate(cdecl, init=None): - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return allocator(cdecl, init) - return allocate - - def cast(self, cdecl, source): - """Similar to a C cast: returns an instance of the named C - type initialized with the given 'source'. The source is - casted between integers or pointers of any type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.cast(cdecl, source) - - def string(self, cdata, maxlen=-1): - """Return a Python string (or unicode string) from the 'cdata'. - If 'cdata' is a pointer or array of characters or bytes, returns - the null-terminated string. The returned string extends until - the first null character, or at most 'maxlen' characters. If - 'cdata' is an array then 'maxlen' defaults to its length. - - If 'cdata' is a pointer or array of wchar_t, returns a unicode - string following the same rules. - - If 'cdata' is a single character or byte or a wchar_t, returns - it as a string or unicode string. - - If 'cdata' is an enum, returns the value of the enumerator as a - string, or 'NUMBER' if the value is out of range. - """ - return self._backend.string(cdata, maxlen) - - def unpack(self, cdata, length): - """Unpack an array of C data of the given length, - returning a Python string/unicode/list. - - If 'cdata' is a pointer to 'char', returns a byte string. - It does not stop at the first null. This is equivalent to: - ffi.buffer(cdata, length)[:] - - If 'cdata' is a pointer to 'wchar_t', returns a unicode string. - 'length' is measured in wchar_t's; it is not the size in bytes. - - If 'cdata' is a pointer to anything else, returns a list of - 'length' items. This is a faster equivalent to: - [cdata[i] for i in range(length)] - """ - return self._backend.unpack(cdata, length) - - #def buffer(self, cdata, size=-1): - # """Return a read-write buffer object that references the raw C data - # pointed to by the given 'cdata'. The 'cdata' must be a pointer or - # an array. Can be passed to functions expecting a buffer, or directly - # manipulated with: - # - # buf[:] get a copy of it in a regular string, or - # buf[idx] as a single character - # buf[:] = ... - # buf[idx] = ... change the content - # """ - # note that 'buffer' is a type, set on this instance by __init__ - - def from_buffer(self, cdecl, python_buffer=_unspecified, - require_writable=False): - """Return a cdata of the given type pointing to the data of the - given Python object, which must support the buffer interface. - Note that this is not meant to be used on the built-in types - str or unicode (you can build 'char[]' arrays explicitly) - but only on objects containing large quantities of raw data - in some other format, like 'array.array' or numpy arrays. - - The first argument is optional and default to 'char[]'. - """ - if python_buffer is _unspecified: - cdecl, python_buffer = self.BCharA, cdecl - elif isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.from_buffer(cdecl, python_buffer, - require_writable) - - def memmove(self, dest, src, n): - """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. - - Like the C function memmove(), the memory areas may overlap; - apart from that it behaves like the C function memcpy(). - - 'src' can be any cdata ptr or array, or any Python buffer object. - 'dest' can be any cdata ptr or array, or a writable Python buffer - object. The size to copy, 'n', is always measured in bytes. - - Unlike other methods, this one supports all Python buffer including - byte strings and bytearrays---but it still does not support - non-contiguous buffers. - """ - return self._backend.memmove(dest, src, n) - - def callback(self, cdecl, python_callable=None, error=None, onerror=None): - """Return a callback object or a decorator making such a - callback object. 'cdecl' must name a C function pointer type. - The callback invokes the specified 'python_callable' (which may - be provided either directly or via a decorator). Important: the - callback object must be manually kept alive for as long as the - callback may be invoked from the C level. - """ - def callback_decorator_wrap(python_callable): - if not callable(python_callable): - raise TypeError("the 'python_callable' argument " - "is not callable") - return self._backend.callback(cdecl, python_callable, - error, onerror) - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) - if python_callable is None: - return callback_decorator_wrap # decorator mode - else: - return callback_decorator_wrap(python_callable) # direct mode - - def getctype(self, cdecl, replace_with=''): - """Return a string giving the C type 'cdecl', which may be itself - a string or a object. If 'replace_with' is given, it gives - extra text to append (or insert for more complicated C types), like - a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - replace_with = replace_with.strip() - if (replace_with.startswith('*') - and '&[' in self._backend.getcname(cdecl, '&')): - replace_with = '(%s)' % replace_with - elif replace_with and not replace_with[0] in '[(': - replace_with = ' ' + replace_with - return self._backend.getcname(cdecl, replace_with) - - def gc(self, cdata, destructor, size=0): - """Return a new cdata object that points to the same - data. Later, when this new cdata object is garbage-collected, - 'destructor(old_cdata_object)' will be called. - - The optional 'size' gives an estimate of the size, used to - trigger the garbage collection more eagerly. So far only used - on PyPy. It tells the GC that the returned object keeps alive - roughly 'size' bytes of external memory. - """ - return self._backend.gcp(cdata, destructor, size) - - def _get_cached_btype(self, type): - assert self._lock.acquire(False) is False - # call me with the lock! - try: - BType = self._cached_btypes[type] - except KeyError: - finishlist = [] - BType = type.get_cached_btype(self, finishlist) - for type in finishlist: - type.finish_backend_type(self, finishlist) - return BType - - def verify(self, source='', tmpdir=None, **kwargs): - """Verify that the current ffi signatures compile on this - machine, and return a dynamic library object. The dynamic - library can be used to call functions and access global - variables declared in this 'ffi'. The library is compiled - by the C compiler: it gives you C-level API compatibility - (including calling macros). This is unlike 'ffi.dlopen()', - which requires binary compatibility in the signatures. - """ - from .verifier import Verifier, _caller_dir_pycache - # - # If set_unicode(True) was called, insert the UNICODE and - # _UNICODE macro declarations - if self._windows_unicode: - self._apply_windows_unicode(kwargs) - # - # Set the tmpdir here, and not in Verifier.__init__: it picks - # up the caller's directory, which we want to be the caller of - # ffi.verify(), as opposed to the caller of Veritier(). - tmpdir = tmpdir or _caller_dir_pycache() - # - # Make a Verifier() and use it to load the library. - self.verifier = Verifier(self, source, tmpdir, **kwargs) - lib = self.verifier.load_library() - # - # Save the loaded library for keep-alive purposes, even - # if the caller doesn't keep it alive itself (it should). - self._libraries.append(lib) - return lib - - def _get_errno(self): - return self._backend.get_errno() - def _set_errno(self, errno): - self._backend.set_errno(errno) - errno = property(_get_errno, _set_errno, None, - "the value of 'errno' from/to the C calls") - - def getwinerror(self, code=-1): - return self._backend.getwinerror(code) - - def _pointer_to(self, ctype): - with self._lock: - return model.pointer_cache(self, ctype) - - def addressof(self, cdata, *fields_or_indexes): - """Return the address of a . - If 'fields_or_indexes' are given, returns the address of that - field or array item in the structure or array, recursively in - case of nested structures. - """ - try: - ctype = self._backend.typeof(cdata) - except TypeError: - if '__addressof__' in type(cdata).__dict__: - return type(cdata).__addressof__(cdata, *fields_or_indexes) - raise - if fields_or_indexes: - ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) - else: - if ctype.kind == "pointer": - raise TypeError("addressof(pointer)") - offset = 0 - ctypeptr = self._pointer_to(ctype) - return self._backend.rawaddressof(ctypeptr, cdata, offset) - - def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): - ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) - for field1 in fields_or_indexes: - ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) - offset += offset1 - return ctype, offset - - def include(self, ffi_to_include): - """Includes the typedefs, structs, unions and enums defined - in another FFI instance. Usage is similar to a #include in C, - where a part of the program might include types defined in - another part for its own usage. Note that the include() - method has no effect on functions, constants and global - variables, which must anyway be accessed directly from the - lib object returned by the original FFI instance. - """ - if not isinstance(ffi_to_include, FFI): - raise TypeError("ffi.include() expects an argument that is also of" - " type cffi.FFI, not %r" % ( - type(ffi_to_include).__name__,)) - if ffi_to_include is self: - raise ValueError("self.include(self)") - with ffi_to_include._lock: - with self._lock: - self._parser.include(ffi_to_include._parser) - self._cdefsources.append('[') - self._cdefsources.extend(ffi_to_include._cdefsources) - self._cdefsources.append(']') - self._included_ffis.append(ffi_to_include) - - def new_handle(self, x): - return self._backend.newp_handle(self.BVoidP, x) - - def from_handle(self, x): - return self._backend.from_handle(x) - - def release(self, x): - self._backend.release(x) - - def set_unicode(self, enabled_flag): - """Windows: if 'enabled_flag' is True, enable the UNICODE and - _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR - to be (pointers to) wchar_t. If 'enabled_flag' is False, - declare these types to be (pointers to) plain 8-bit characters. - This is mostly for backward compatibility; you usually want True. - """ - if self._windows_unicode is not None: - raise ValueError("set_unicode() can only be called once") - enabled_flag = bool(enabled_flag) - if enabled_flag: - self.cdef("typedef wchar_t TBYTE;" - "typedef wchar_t TCHAR;" - "typedef const wchar_t *LPCTSTR;" - "typedef const wchar_t *PCTSTR;" - "typedef wchar_t *LPTSTR;" - "typedef wchar_t *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - else: - self.cdef("typedef char TBYTE;" - "typedef char TCHAR;" - "typedef const char *LPCTSTR;" - "typedef const char *PCTSTR;" - "typedef char *LPTSTR;" - "typedef char *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - self._windows_unicode = enabled_flag - - def _apply_windows_unicode(self, kwds): - defmacros = kwds.get('define_macros', ()) - if not isinstance(defmacros, (list, tuple)): - raise TypeError("'define_macros' must be a list or tuple") - defmacros = list(defmacros) + [('UNICODE', '1'), - ('_UNICODE', '1')] - kwds['define_macros'] = defmacros - - def _apply_embedding_fix(self, kwds): - # must include an argument like "-lpython2.7" for the compiler - def ensure(key, value): - lst = kwds.setdefault(key, []) - if value not in lst: - lst.append(value) - # - if '__pypy__' in sys.builtin_module_names: - import os - if sys.platform == "win32": - # we need 'libpypy-c.lib'. Current distributions of - # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'libs')) - else: - # we need 'libpypy-c.{so,dylib}', which should be by - # default located in 'sys.prefix/bin' for installed - # systems. - if sys.version_info < (3,): - pythonlib = "pypy-c" - else: - pythonlib = "pypy3-c" - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'bin')) - # On uninstalled pypy's, the libpypy-c is typically found in - # .../pypy/goal/. - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) - else: - if sys.platform == "win32": - template = "python%d%d" - if hasattr(sys, 'gettotalrefcount'): - template += '_d' - else: - try: - import sysconfig - except ImportError: # 2.6 - from cffi._shimmed_dist_utils import sysconfig - template = "python%d.%d" - if sysconfig.get_config_var('DEBUG_EXT'): - template += sysconfig.get_config_var('DEBUG_EXT') - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - if hasattr(sys, 'abiflags'): - pythonlib += sys.abiflags - ensure('libraries', pythonlib) - if sys.platform == "win32": - ensure('extra_link_args', '/MANIFEST') - - def set_source(self, module_name, source, source_extension='.c', **kwds): - import os - if hasattr(self, '_assigned_source'): - raise ValueError("set_source() cannot be called several times " - "per ffi object") - if not isinstance(module_name, basestring): - raise TypeError("'module_name' must be a string") - if os.sep in module_name or (os.altsep and os.altsep in module_name): - raise ValueError("'module_name' must not contain '/': use a dotted " - "name to make a 'package.module' location") - self._assigned_source = (str(module_name), source, - source_extension, kwds) - - def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, - source_extension='.c', **kwds): - from . import pkgconfig - if not isinstance(pkgconfig_libs, list): - raise TypeError("the pkgconfig_libs argument must be a list " - "of package names") - kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) - pkgconfig.merge_flags(kwds, kwds2) - self.set_source(module_name, source, source_extension, **kwds) - - def distutils_extension(self, tmpdir='build', verbose=True): - from cffi._shimmed_dist_utils import mkpath - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored - return self.verifier.get_extension() - raise ValueError("set_source() must be called before" - " distutils_extension()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("distutils_extension() is only for C extension " - "modules, not for dlopen()-style pure Python " - "modules") - mkpath(tmpdir) - ext, updated = recompile(self, module_name, - source, tmpdir=tmpdir, extradir=tmpdir, - source_extension=source_extension, - call_c_compiler=False, **kwds) - if verbose: - if updated: - sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) - else: - sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) - return ext - - def emit_c_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("emit_c_code() is only for C extension modules, " - "not for dlopen()-style pure Python modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, - uses_ffiplatform=False, **kwds) - - def emit_python_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is not None: - raise TypeError("emit_python_code() is only for dlopen()-style " - "pure Python modules, not for C extension modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, - uses_ffiplatform=False, **kwds) - - def compile(self, tmpdir='.', verbose=0, target=None, debug=None): - """The 'target' argument gives the final file name of the - compiled DLL. Use '*' to force distutils' choice, suitable for - regular CPython C API modules. Use a file name ending in '.*' - to ask for the system's default extension for dynamic libraries - (.so/.dll/.dylib). - - The default is '*' when building a non-embedded C API extension, - and (module_name + '.*') when building an embedded library. - """ - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before compile()") - module_name, source, source_extension, kwds = self._assigned_source - return recompile(self, module_name, source, tmpdir=tmpdir, - target=target, source_extension=source_extension, - compiler_verbose=verbose, debug=debug, **kwds) - - def init_once(self, func, tag): - # Read _init_once_cache[tag], which is either (False, lock) if - # we're calling the function now in some thread, or (True, result). - # Don't call setdefault() in most cases, to avoid allocating and - # immediately freeing a lock; but still use setdefaut() to avoid - # races. - try: - x = self._init_once_cache[tag] - except KeyError: - x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) - # Common case: we got (True, result), so we return the result. - if x[0]: - return x[1] - # Else, it's a lock. Acquire it to serialize the following tests. - with x[1]: - # Read again from _init_once_cache the current status. - x = self._init_once_cache[tag] - if x[0]: - return x[1] - # Call the function and store the result back. - result = func() - self._init_once_cache[tag] = (True, result) - return result - - def embedding_init_code(self, pysource): - if self._embedding: - raise ValueError("embedding_init_code() can only be called once") - # fix 'pysource' before it gets dumped into the C file: - # - remove empty lines at the beginning, so it starts at "line 1" - # - dedent, if all non-empty lines are indented - # - check for SyntaxErrors - import re - match = re.match(r'\s*\n', pysource) - if match: - pysource = pysource[match.end():] - lines = pysource.splitlines() or [''] - prefix = re.match(r'\s*', lines[0]).group() - for i in range(1, len(lines)): - line = lines[i] - if line.rstrip(): - while not line.startswith(prefix): - prefix = prefix[:-1] - i = len(prefix) - lines = [line[i:]+'\n' for line in lines] - pysource = ''.join(lines) - # - compile(pysource, "cffi_init", "exec") - # - self._embedding = pysource - - def def_extern(self, *args, **kwds): - raise ValueError("ffi.def_extern() is only available on API-mode FFI " - "objects") - - def list_types(self): - """Returns the user type names known to this FFI instance. - This returns a tuple containing three lists of names: - (typedef_names, names_of_structs, names_of_unions) - """ - typedefs = [] - structs = [] - unions = [] - for key in self._parser._declarations: - if key.startswith('typedef '): - typedefs.append(key[8:]) - elif key.startswith('struct '): - structs.append(key[7:]) - elif key.startswith('union '): - unions.append(key[6:]) - typedefs.sort() - structs.sort() - unions.sort() - return (typedefs, structs, unions) - - -def _load_backend_lib(backend, name, flags): - import os - if not isinstance(name, basestring): - if sys.platform != "win32" or name is not None: - return backend.load_library(name, flags) - name = "c" # Windows: load_library(None) fails, but this works - # on Python 2 (backward compatibility hack only) - first_error = None - if '.' in name or '/' in name or os.sep in name: - try: - return backend.load_library(name, flags) - except OSError as e: - first_error = e - import ctypes.util - path = ctypes.util.find_library(name) - if path is None: - if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): - raise OSError("dlopen(None) cannot work on Windows for Python 3 " - "(see http://bugs.python.org/issue23606)") - msg = ("ctypes.util.find_library() did not manage " - "to locate a library called %r" % (name,)) - if first_error is not None: - msg = "%s. Additionally, %s" % (first_error, msg) - raise OSError(msg) - return backend.load_library(path, flags) - -def _make_ffi_library(ffi, libname, flags): - backend = ffi._backend - backendlib = _load_backend_lib(backend, libname, flags) - # - def accessor_function(name): - key = 'function ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - value = backendlib.load_function(BType, name) - library.__dict__[name] = value - # - def accessor_variable(name): - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - read_variable = backendlib.read_variable - write_variable = backendlib.write_variable - setattr(FFILibrary, name, property( - lambda self: read_variable(BType, name), - lambda self, value: write_variable(BType, name, value))) - # - def addressof_var(name): - try: - return addr_variables[name] - except KeyError: - with ffi._lock: - if name not in addr_variables: - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - if BType.kind != 'array': - BType = model.pointer_cache(ffi, BType) - p = backendlib.load_function(BType, name) - addr_variables[name] = p - return addr_variables[name] - # - def accessor_constant(name): - raise NotImplementedError("non-integer constant '%s' cannot be " - "accessed from a dlopen() library" % (name,)) - # - def accessor_int_constant(name): - library.__dict__[name] = ffi._parser._int_constants[name] - # - accessors = {} - accessors_version = [False] - addr_variables = {} - # - def update_accessors(): - if accessors_version[0] is ffi._cdef_version: - return - # - for key, (tp, _) in ffi._parser._declarations.items(): - if not isinstance(tp, model.EnumType): - tag, name = key.split(' ', 1) - if tag == 'function': - accessors[name] = accessor_function - elif tag == 'variable': - accessors[name] = accessor_variable - elif tag == 'constant': - accessors[name] = accessor_constant - else: - for i, enumname in enumerate(tp.enumerators): - def accessor_enum(name, tp=tp, i=i): - tp.check_not_partial() - library.__dict__[name] = tp.enumvalues[i] - accessors[enumname] = accessor_enum - for name in ffi._parser._int_constants: - accessors.setdefault(name, accessor_int_constant) - accessors_version[0] = ffi._cdef_version - # - def make_accessor(name): - with ffi._lock: - if name in library.__dict__ or name in FFILibrary.__dict__: - return # added by another thread while waiting for the lock - if name not in accessors: - update_accessors() - if name not in accessors: - raise AttributeError(name) - accessors[name](name) - # - class FFILibrary(object): - def __getattr__(self, name): - make_accessor(name) - return getattr(self, name) - def __setattr__(self, name, value): - try: - property = getattr(self.__class__, name) - except AttributeError: - make_accessor(name) - setattr(self, name, value) - else: - property.__set__(self, value) - def __dir__(self): - with ffi._lock: - update_accessors() - return accessors.keys() - def __addressof__(self, name): - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - make_accessor(name) - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - raise AttributeError("cffi library has no function or " - "global variable named '%s'" % (name,)) - def __cffi_close__(self): - backendlib.close_lib() - self.__dict__.clear() - # - if isinstance(libname, basestring): - try: - if not isinstance(libname, str): # unicode, on Python 2 - libname = libname.encode('utf-8') - FFILibrary.__name__ = 'FFILibrary_%s' % libname - except UnicodeError: - pass - library = FFILibrary() - return library, library.__dict__ - -def _builtin_function_type(func): - # a hack to make at least ffi.typeof(builtin_function) work, - # if the builtin function was obtained by 'vengine_cpy'. - import sys - try: - module = sys.modules[func.__module__] - ffi = module._cffi_original_ffi - types_of_builtin_funcs = module._cffi_types_of_builtin_funcs - tp = types_of_builtin_funcs[func] - except (KeyError, AttributeError, TypeError): - return None - else: - with ffi._lock: - return ffi._get_cached_btype(tp) diff --git a/.venv/Lib/site-packages/cffi/backend_ctypes.py b/.venv/Lib/site-packages/cffi/backend_ctypes.py deleted file mode 100644 index e7956a7..0000000 --- a/.venv/Lib/site-packages/cffi/backend_ctypes.py +++ /dev/null @@ -1,1121 +0,0 @@ -import ctypes, ctypes.util, operator, sys -from . import model - -if sys.version_info < (3,): - bytechr = chr -else: - unicode = str - long = int - xrange = range - bytechr = lambda num: bytes([num]) - -class CTypesType(type): - pass - -class CTypesData(object): - __metaclass__ = CTypesType - __slots__ = ['__weakref__'] - __name__ = '' - - def __init__(self, *args): - raise TypeError("cannot instantiate %r" % (self.__class__,)) - - @classmethod - def _newp(cls, init): - raise TypeError("expected a pointer or array ctype, got '%s'" - % (cls._get_c_name(),)) - - @staticmethod - def _to_ctypes(value): - raise TypeError - - @classmethod - def _arg_to_ctypes(cls, *value): - try: - ctype = cls._ctype - except AttributeError: - raise TypeError("cannot create an instance of %r" % (cls,)) - if value: - res = cls._to_ctypes(*value) - if not isinstance(res, ctype): - res = cls._ctype(res) - else: - res = cls._ctype() - return res - - @classmethod - def _create_ctype_obj(cls, init): - if init is None: - return cls._arg_to_ctypes() - else: - return cls._arg_to_ctypes(init) - - @staticmethod - def _from_ctypes(ctypes_value): - raise TypeError - - @classmethod - def _get_c_name(cls, replace_with=''): - return cls._reftypename.replace(' &', replace_with) - - @classmethod - def _fix_class(cls): - cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__module__ = 'ffi' - - def _get_own_repr(self): - raise NotImplementedError - - def _addr_repr(self, address): - if address == 0: - return 'NULL' - else: - if address < 0: - address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) - return '0x%x' % address - - def __repr__(self, c_name=None): - own = self._get_own_repr() - return '' % (c_name or self._get_c_name(), own) - - def _convert_to_address(self, BClass): - if BClass is None: - raise TypeError("cannot convert %r to an address" % ( - self._get_c_name(),)) - else: - raise TypeError("cannot convert %r to %r" % ( - self._get_c_name(), BClass._get_c_name())) - - @classmethod - def _get_size(cls): - return ctypes.sizeof(cls._ctype) - - def _get_size_of_instance(self): - return ctypes.sizeof(self._ctype) - - @classmethod - def _cast_from(cls, source): - raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) - - def _cast_to_integer(self): - return self._convert_to_address(None) - - @classmethod - def _alignment(cls): - return ctypes.alignment(cls._ctype) - - def __iter__(self): - raise TypeError("cdata %r does not support iteration" % ( - self._get_c_name()),) - - def _make_cmp(name): - cmpfunc = getattr(operator, name) - def cmp(self, other): - v_is_ptr = not isinstance(self, CTypesGenericPrimitive) - w_is_ptr = (isinstance(other, CTypesData) and - not isinstance(other, CTypesGenericPrimitive)) - if v_is_ptr and w_is_ptr: - return cmpfunc(self._convert_to_address(None), - other._convert_to_address(None)) - elif v_is_ptr or w_is_ptr: - return NotImplemented - else: - if isinstance(self, CTypesGenericPrimitive): - self = self._value - if isinstance(other, CTypesGenericPrimitive): - other = other._value - return cmpfunc(self, other) - cmp.func_name = name - return cmp - - __eq__ = _make_cmp('__eq__') - __ne__ = _make_cmp('__ne__') - __lt__ = _make_cmp('__lt__') - __le__ = _make_cmp('__le__') - __gt__ = _make_cmp('__gt__') - __ge__ = _make_cmp('__ge__') - - def __hash__(self): - return hash(self._convert_to_address(None)) - - def _to_string(self, maxlen): - raise TypeError("string(): %r" % (self,)) - - -class CTypesGenericPrimitive(CTypesData): - __slots__ = [] - - def __hash__(self): - return hash(self._value) - - def _get_own_repr(self): - return repr(self._from_ctypes(self._value)) - - -class CTypesGenericArray(CTypesData): - __slots__ = [] - - @classmethod - def _newp(cls, init): - return cls(init) - - def __iter__(self): - for i in xrange(len(self)): - yield self[i] - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - -class CTypesGenericPtr(CTypesData): - __slots__ = ['_address', '_as_ctype_ptr'] - _automatic_casts = False - kind = "pointer" - - @classmethod - def _newp(cls, init): - return cls(init) - - @classmethod - def _cast_from(cls, source): - if source is None: - address = 0 - elif isinstance(source, CTypesData): - address = source._cast_to_integer() - elif isinstance(source, (int, long)): - address = source - else: - raise TypeError("bad type for cast to %r: %r" % - (cls, type(source).__name__)) - return cls._new_pointer_at(address) - - @classmethod - def _new_pointer_at(cls, address): - self = cls.__new__(cls) - self._address = address - self._as_ctype_ptr = ctypes.cast(address, cls._ctype) - return self - - def _get_own_repr(self): - try: - return self._addr_repr(self._address) - except AttributeError: - return '???' - - def _cast_to_integer(self): - return self._address - - def __nonzero__(self): - return bool(self._address) - __bool__ = __nonzero__ - - @classmethod - def _to_ctypes(cls, value): - if not isinstance(value, CTypesData): - raise TypeError("unexpected %s object" % type(value).__name__) - address = value._convert_to_address(cls) - return ctypes.cast(address, cls._ctype) - - @classmethod - def _from_ctypes(cls, ctypes_ptr): - address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 - return cls._new_pointer_at(address) - - @classmethod - def _initialize(cls, ctypes_ptr, value): - if value: - ctypes_ptr.contents = cls._to_ctypes(value).contents - - def _convert_to_address(self, BClass): - if (BClass in (self.__class__, None) or BClass._automatic_casts - or self._automatic_casts): - return self._address - else: - return CTypesData._convert_to_address(self, BClass) - - -class CTypesBaseStructOrUnion(CTypesData): - __slots__ = ['_blob'] - - @classmethod - def _create_ctype_obj(cls, init): - # may be overridden - raise TypeError("cannot instantiate opaque type %s" % (cls,)) - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - @classmethod - def _offsetof(cls, fieldname): - return getattr(cls._ctype, fieldname).offset - - def _convert_to_address(self, BClass): - if getattr(BClass, '_BItem', None) is self.__class__: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @classmethod - def _from_ctypes(cls, ctypes_struct_or_union): - self = cls.__new__(cls) - self._blob = ctypes_struct_or_union - return self - - @classmethod - def _to_ctypes(cls, value): - return value._blob - - def __repr__(self, c_name=None): - return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) - - -class CTypesBackend(object): - - PRIMITIVE_TYPES = { - 'char': ctypes.c_char, - 'short': ctypes.c_short, - 'int': ctypes.c_int, - 'long': ctypes.c_long, - 'long long': ctypes.c_longlong, - 'signed char': ctypes.c_byte, - 'unsigned char': ctypes.c_ubyte, - 'unsigned short': ctypes.c_ushort, - 'unsigned int': ctypes.c_uint, - 'unsigned long': ctypes.c_ulong, - 'unsigned long long': ctypes.c_ulonglong, - 'float': ctypes.c_float, - 'double': ctypes.c_double, - '_Bool': ctypes.c_bool, - } - - for _name in ['unsigned long long', 'unsigned long', - 'unsigned int', 'unsigned short', 'unsigned char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] - - for _name in ['long long', 'long', 'int', 'short', 'signed char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] - PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] - - - def __init__(self): - self.RTLD_LAZY = 0 # not supported anyway by ctypes - self.RTLD_NOW = 0 - self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL - self.RTLD_LOCAL = ctypes.RTLD_LOCAL - - def set_ffi(self, ffi): - self.ffi = ffi - - def _get_types(self): - return CTypesData, CTypesType - - def load_library(self, path, flags=0): - cdll = ctypes.CDLL(path, flags) - return CTypesLibrary(self, cdll) - - def new_void_type(self): - class CTypesVoid(CTypesData): - __slots__ = [] - _reftypename = 'void &' - @staticmethod - def _from_ctypes(novalue): - return None - @staticmethod - def _to_ctypes(novalue): - if novalue is not None: - raise TypeError("None expected, got %s object" % - (type(novalue).__name__,)) - return None - CTypesVoid._fix_class() - return CTypesVoid - - def new_primitive_type(self, name): - if name == 'wchar_t': - raise NotImplementedError(name) - ctype = self.PRIMITIVE_TYPES[name] - if name == 'char': - kind = 'char' - elif name in ('float', 'double'): - kind = 'float' - else: - if name in ('signed char', 'unsigned char'): - kind = 'byte' - elif name == '_Bool': - kind = 'bool' - else: - kind = 'int' - is_signed = (ctype(-1).value == -1) - # - def _cast_source_to_int(source): - if isinstance(source, (int, long, float)): - source = int(source) - elif isinstance(source, CTypesData): - source = source._cast_to_integer() - elif isinstance(source, bytes): - source = ord(source) - elif source is None: - source = 0 - else: - raise TypeError("bad type for cast to %r: %r" % - (CTypesPrimitive, type(source).__name__)) - return source - # - kind1 = kind - class CTypesPrimitive(CTypesGenericPrimitive): - __slots__ = ['_value'] - _ctype = ctype - _reftypename = '%s &' % name - kind = kind1 - - def __init__(self, value): - self._value = value - - @staticmethod - def _create_ctype_obj(init): - if init is None: - return ctype() - return ctype(CTypesPrimitive._to_ctypes(init)) - - if kind == 'int' or kind == 'byte': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = ctype(source).value # cast within range - return cls(source) - def __int__(self): - return self._value - - if kind == 'bool': - @classmethod - def _cast_from(cls, source): - if not isinstance(source, (int, long, float)): - source = _cast_source_to_int(source) - return cls(bool(source)) - def __int__(self): - return int(self._value) - - if kind == 'char': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = bytechr(source & 0xFF) - return cls(source) - def __int__(self): - return ord(self._value) - - if kind == 'float': - @classmethod - def _cast_from(cls, source): - if isinstance(source, float): - pass - elif isinstance(source, CTypesGenericPrimitive): - if hasattr(source, '__float__'): - source = float(source) - else: - source = int(source) - else: - source = _cast_source_to_int(source) - source = ctype(source).value # fix precision - return cls(source) - def __int__(self): - return int(self._value) - def __float__(self): - return self._value - - _cast_to_integer = __int__ - - if kind == 'int' or kind == 'byte' or kind == 'bool': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long)): - if isinstance(x, CTypesData): - x = int(x) - else: - raise TypeError("integer expected, got %s" % - type(x).__name__) - if ctype(x).value != x: - if not is_signed and x < 0: - raise OverflowError("%s: negative integer" % name) - else: - raise OverflowError("%s: integer out of bounds" - % name) - return x - - if kind == 'char': - @staticmethod - def _to_ctypes(x): - if isinstance(x, bytes) and len(x) == 1: - return x - if isinstance(x, CTypesPrimitive): # > - return x._value - raise TypeError("character expected, got %s" % - type(x).__name__) - def __nonzero__(self): - return ord(self._value) != 0 - else: - def __nonzero__(self): - return self._value != 0 - __bool__ = __nonzero__ - - if kind == 'float': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long, float, CTypesData)): - raise TypeError("float expected, got %s" % - type(x).__name__) - return ctype(x).value - - @staticmethod - def _from_ctypes(value): - return getattr(value, 'value', value) - - @staticmethod - def _initialize(blob, init): - blob.value = CTypesPrimitive._to_ctypes(init) - - if kind == 'char': - def _to_string(self, maxlen): - return self._value - if kind == 'byte': - def _to_string(self, maxlen): - return chr(self._value & 0xff) - # - CTypesPrimitive._fix_class() - return CTypesPrimitive - - def new_pointer_type(self, BItem): - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'charp' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'bytep' - elif BItem is getbtype(model.void_type): - kind = 'voidp' - else: - kind = 'generic' - # - class CTypesPtr(CTypesGenericPtr): - __slots__ = ['_own'] - if kind == 'charp': - __slots__ += ['__as_strbuf'] - _BItem = BItem - if hasattr(BItem, '_ctype'): - _ctype = ctypes.POINTER(BItem._ctype) - _bitem_size = ctypes.sizeof(BItem._ctype) - else: - _ctype = ctypes.c_void_p - if issubclass(BItem, CTypesGenericArray): - _reftypename = BItem._get_c_name('(* &)') - else: - _reftypename = BItem._get_c_name(' * &') - - def __init__(self, init): - ctypeobj = BItem._create_ctype_obj(init) - if kind == 'charp': - self.__as_strbuf = ctypes.create_string_buffer( - ctypeobj.value + b'\x00') - self._as_ctype_ptr = ctypes.cast( - self.__as_strbuf, self._ctype) - else: - self._as_ctype_ptr = ctypes.pointer(ctypeobj) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own = True - - def __add__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address + - other * self._bitem_size) - else: - return NotImplemented - - def __sub__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address - - other * self._bitem_size) - elif type(self) is type(other): - return (self._address - other._address) // self._bitem_size - else: - return NotImplemented - - def __getitem__(self, index): - if getattr(self, '_own', False) and index != 0: - raise IndexError - return BItem._from_ctypes(self._as_ctype_ptr[index]) - - def __setitem__(self, index, value): - self._as_ctype_ptr[index] = BItem._to_ctypes(value) - - if kind == 'charp' or kind == 'voidp': - @classmethod - def _arg_to_ctypes(cls, *value): - if value and isinstance(value[0], bytes): - return ctypes.c_char_p(value[0]) - else: - return super(CTypesPtr, cls)._arg_to_ctypes(*value) - - if kind == 'charp' or kind == 'bytep': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = sys.maxsize - p = ctypes.cast(self._as_ctype_ptr, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % ( - ctypes.sizeof(self._as_ctype_ptr.contents),) - return super(CTypesPtr, self)._get_own_repr() - # - if (BItem is self.ffi._get_cached_btype(model.void_type) or - BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): - CTypesPtr._automatic_casts = True - # - CTypesPtr._fix_class() - return CTypesPtr - - def new_array_type(self, CTypesPtr, length): - if length is None: - brackets = ' &[]' - else: - brackets = ' &[%d]' % length - BItem = CTypesPtr._BItem - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'char' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'byte' - else: - kind = 'generic' - # - class CTypesArray(CTypesGenericArray): - __slots__ = ['_blob', '_own'] - if length is not None: - _ctype = BItem._ctype * length - else: - __slots__.append('_ctype') - _reftypename = BItem._get_c_name(brackets) - _declared_length = length - _CTPtr = CTypesPtr - - def __init__(self, init): - if length is None: - if isinstance(init, (int, long)): - len1 = init - init = None - elif kind == 'char' and isinstance(init, bytes): - len1 = len(init) + 1 # extra null - else: - init = tuple(init) - len1 = len(init) - self._ctype = BItem._ctype * len1 - self._blob = self._ctype() - self._own = True - if init is not None: - self._initialize(self._blob, init) - - @staticmethod - def _initialize(blob, init): - if isinstance(init, bytes): - init = [init[i:i+1] for i in range(len(init))] - else: - if isinstance(init, CTypesGenericArray): - if (len(init) != len(blob) or - not isinstance(init, CTypesArray)): - raise TypeError("length/type mismatch: %s" % (init,)) - init = tuple(init) - if len(init) > len(blob): - raise IndexError("too many initializers") - addr = ctypes.cast(blob, ctypes.c_void_p).value - PTR = ctypes.POINTER(BItem._ctype) - itemsize = ctypes.sizeof(BItem._ctype) - for i, value in enumerate(init): - p = ctypes.cast(addr + i * itemsize, PTR) - BItem._initialize(p.contents, value) - - def __len__(self): - return len(self._blob) - - def __getitem__(self, index): - if not (0 <= index < len(self._blob)): - raise IndexError - return BItem._from_ctypes(self._blob[index]) - - def __setitem__(self, index, value): - if not (0 <= index < len(self._blob)): - raise IndexError - self._blob[index] = BItem._to_ctypes(value) - - if kind == 'char' or kind == 'byte': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = len(self._blob) - p = ctypes.cast(self._blob, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % (ctypes.sizeof(self._blob),) - return super(CTypesArray, self)._get_own_repr() - - def _convert_to_address(self, BClass): - if BClass in (CTypesPtr, None) or BClass._automatic_casts: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @staticmethod - def _from_ctypes(ctypes_array): - self = CTypesArray.__new__(CTypesArray) - self._blob = ctypes_array - return self - - @staticmethod - def _arg_to_ctypes(value): - return CTypesPtr._arg_to_ctypes(value) - - def __add__(self, other): - if isinstance(other, (int, long)): - return CTypesPtr._new_pointer_at( - ctypes.addressof(self._blob) + - other * ctypes.sizeof(BItem._ctype)) - else: - return NotImplemented - - @classmethod - def _cast_from(cls, source): - raise NotImplementedError("casting to %r" % ( - cls._get_c_name(),)) - # - CTypesArray._fix_class() - return CTypesArray - - def _new_struct_or_union(self, kind, name, base_ctypes_class): - # - class struct_or_union(base_ctypes_class): - pass - struct_or_union.__name__ = '%s_%s' % (kind, name) - kind1 = kind - # - class CTypesStructOrUnion(CTypesBaseStructOrUnion): - __slots__ = ['_blob'] - _ctype = struct_or_union - _reftypename = '%s &' % (name,) - _kind = kind = kind1 - # - CTypesStructOrUnion._fix_class() - return CTypesStructOrUnion - - def new_struct_type(self, name): - return self._new_struct_or_union('struct', name, ctypes.Structure) - - def new_union_type(self, name): - return self._new_struct_or_union('union', name, ctypes.Union) - - def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1, sflags=0, - pack=0): - if totalsize >= 0 or totalalignment >= 0: - raise NotImplementedError("the ctypes backend of CFFI does not support " - "structures completed by verify(); please " - "compile and install the _cffi_backend module.") - struct_or_union = CTypesStructOrUnion._ctype - fnames = [fname for (fname, BField, bitsize) in fields] - btypes = [BField for (fname, BField, bitsize) in fields] - bitfields = [bitsize for (fname, BField, bitsize) in fields] - # - bfield_types = {} - cfields = [] - for (fname, BField, bitsize) in fields: - if bitsize < 0: - cfields.append((fname, BField._ctype)) - bfield_types[fname] = BField - else: - cfields.append((fname, BField._ctype, bitsize)) - bfield_types[fname] = Ellipsis - if sflags & 8: - struct_or_union._pack_ = 1 - elif pack: - struct_or_union._pack_ = pack - struct_or_union._fields_ = cfields - CTypesStructOrUnion._bfield_types = bfield_types - # - @staticmethod - def _create_ctype_obj(init): - result = struct_or_union() - if init is not None: - initialize(result, init) - return result - CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj - # - def initialize(blob, init): - if is_union: - if len(init) > 1: - raise ValueError("union initializer: %d items given, but " - "only one supported (use a dict if needed)" - % (len(init),)) - if not isinstance(init, dict): - if isinstance(init, (bytes, unicode)): - raise TypeError("union initializer: got a str") - init = tuple(init) - if len(init) > len(fnames): - raise ValueError("too many values for %s initializer" % - CTypesStructOrUnion._get_c_name()) - init = dict(zip(fnames, init)) - addr = ctypes.addressof(blob) - for fname, value in init.items(): - BField, bitsize = name2fieldtype[fname] - assert bitsize < 0, \ - "not implemented: initializer with bit fields" - offset = CTypesStructOrUnion._offsetof(fname) - PTR = ctypes.POINTER(BField._ctype) - p = ctypes.cast(addr + offset, PTR) - BField._initialize(p.contents, value) - is_union = CTypesStructOrUnion._kind == 'union' - name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) - # - for fname, BField, bitsize in fields: - if fname == '': - raise NotImplementedError("nested anonymous structs/unions") - if hasattr(CTypesStructOrUnion, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - if bitsize < 0: - def getter(self, fname=fname, BField=BField, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BField._from_ctypes(p.contents) - def setter(self, value, fname=fname, BField=BField): - setattr(self._blob, fname, BField._to_ctypes(value)) - # - if issubclass(BField, CTypesGenericArray): - setter = None - if BField._declared_length == 0: - def getter(self, fname=fname, BFieldPtr=BField._CTPtr, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BFieldPtr._from_ctypes(p) - # - else: - def getter(self, fname=fname, BField=BField): - return BField._from_ctypes(getattr(self._blob, fname)) - def setter(self, value, fname=fname, BField=BField): - # xxx obscure workaround - value = BField._to_ctypes(value) - oldvalue = getattr(self._blob, fname) - setattr(self._blob, fname, value) - if value != getattr(self._blob, fname): - setattr(self._blob, fname, oldvalue) - raise OverflowError("value too large for bitfield") - setattr(CTypesStructOrUnion, fname, property(getter, setter)) - # - CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) - for fname in fnames: - if hasattr(CTypesPtr, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - def getter(self, fname=fname): - return getattr(self[0], fname) - def setter(self, value, fname=fname): - setattr(self[0], fname, value) - setattr(CTypesPtr, fname, property(getter, setter)) - - def new_function_type(self, BArgs, BResult, has_varargs): - nameargs = [BArg._get_c_name() for BArg in BArgs] - if has_varargs: - nameargs.append('...') - nameargs = ', '.join(nameargs) - # - class CTypesFunctionPtr(CTypesGenericPtr): - __slots__ = ['_own_callback', '_name'] - _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), - *[BArg._ctype for BArg in BArgs], - use_errno=True) - _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) - - def __init__(self, init, error=None): - # create a callback to the Python callable init() - import traceback - assert not has_varargs, "varargs not supported for callbacks" - if getattr(BResult, '_ctype', None) is not None: - error = BResult._from_ctypes( - BResult._create_ctype_obj(error)) - else: - error = None - def callback(*args): - args2 = [] - for arg, BArg in zip(args, BArgs): - args2.append(BArg._from_ctypes(arg)) - try: - res2 = init(*args2) - res2 = BResult._to_ctypes(res2) - except: - traceback.print_exc() - res2 = error - if issubclass(BResult, CTypesGenericPtr): - if res2: - res2 = ctypes.cast(res2, ctypes.c_void_p).value - # .value: http://bugs.python.org/issue1574593 - else: - res2 = None - #print repr(res2) - return res2 - if issubclass(BResult, CTypesGenericPtr): - # The only pointers callbacks can return are void*s: - # http://bugs.python.org/issue5710 - callback_ctype = ctypes.CFUNCTYPE( - ctypes.c_void_p, - *[BArg._ctype for BArg in BArgs], - use_errno=True) - else: - callback_ctype = CTypesFunctionPtr._ctype - self._as_ctype_ptr = callback_ctype(callback) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own_callback = init - - @staticmethod - def _initialize(ctypes_ptr, value): - if value: - raise NotImplementedError("ctypes backend: not supported: " - "initializers for function pointers") - - def __repr__(self): - c_name = getattr(self, '_name', None) - if c_name: - i = self._reftypename.index('(* &)') - if self._reftypename[i-1] not in ' )*': - c_name = ' ' + c_name - c_name = self._reftypename.replace('(* &)', c_name) - return CTypesData.__repr__(self, c_name) - - def _get_own_repr(self): - if getattr(self, '_own_callback', None) is not None: - return 'calling %r' % (self._own_callback,) - return super(CTypesFunctionPtr, self)._get_own_repr() - - def __call__(self, *args): - if has_varargs: - assert len(args) >= len(BArgs) - extraargs = args[len(BArgs):] - args = args[:len(BArgs)] - else: - assert len(args) == len(BArgs) - ctypes_args = [] - for arg, BArg in zip(args, BArgs): - ctypes_args.append(BArg._arg_to_ctypes(arg)) - if has_varargs: - for i, arg in enumerate(extraargs): - if arg is None: - ctypes_args.append(ctypes.c_void_p(0)) # NULL - continue - if not isinstance(arg, CTypesData): - raise TypeError( - "argument %d passed in the variadic part " - "needs to be a cdata object (got %s)" % - (1 + len(BArgs) + i, type(arg).__name__)) - ctypes_args.append(arg._arg_to_ctypes(arg)) - result = self._as_ctype_ptr(*ctypes_args) - return BResult._from_ctypes(result) - # - CTypesFunctionPtr._fix_class() - return CTypesFunctionPtr - - def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): - assert isinstance(name, str) - reverse_mapping = dict(zip(reversed(enumvalues), - reversed(enumerators))) - # - class CTypesEnum(CTypesInt): - __slots__ = [] - _reftypename = '%s &' % name - - def _get_own_repr(self): - value = self._value - try: - return '%d: %s' % (value, reverse_mapping[value]) - except KeyError: - return str(value) - - def _to_string(self, maxlen): - value = self._value - try: - return reverse_mapping[value] - except KeyError: - return str(value) - # - CTypesEnum._fix_class() - return CTypesEnum - - def get_errno(self): - return ctypes.get_errno() - - def set_errno(self, value): - ctypes.set_errno(value) - - def string(self, b, maxlen=-1): - return b._to_string(maxlen) - - def buffer(self, bptr, size=-1): - raise NotImplementedError("buffer() with ctypes backend") - - def sizeof(self, cdata_or_BType): - if isinstance(cdata_or_BType, CTypesData): - return cdata_or_BType._get_size_of_instance() - else: - assert issubclass(cdata_or_BType, CTypesData) - return cdata_or_BType._get_size() - - def alignof(self, BType): - assert issubclass(BType, CTypesData) - return BType._alignment() - - def newp(self, BType, source): - if not issubclass(BType, CTypesData): - raise TypeError - return BType._newp(source) - - def cast(self, BType, source): - return BType._cast_from(source) - - def callback(self, BType, source, error, onerror): - assert onerror is None # XXX not implemented - return BType(source, error) - - _weakref_cache_ref = None - - def gcp(self, cdata, destructor, size=0): - if self._weakref_cache_ref is None: - import weakref - class MyRef(weakref.ref): - def __eq__(self, other): - myref = self() - return self is other or ( - myref is not None and myref is other()) - def __ne__(self, other): - return not (self == other) - def __hash__(self): - try: - return self._hash - except AttributeError: - self._hash = hash(self()) - return self._hash - self._weakref_cache_ref = {}, MyRef - weak_cache, MyRef = self._weakref_cache_ref - - if destructor is None: - try: - del weak_cache[MyRef(cdata)] - except KeyError: - raise TypeError("Can remove destructor only on a object " - "previously returned by ffi.gc()") - return None - - def remove(k): - cdata, destructor = weak_cache.pop(k, (None, None)) - if destructor is not None: - destructor(cdata) - - new_cdata = self.cast(self.typeof(cdata), cdata) - assert new_cdata is not cdata - weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) - return new_cdata - - typeof = type - - def getcname(self, BType, replace_with): - return BType._get_c_name(replace_with) - - def typeoffsetof(self, BType, fieldname, num=0): - if isinstance(fieldname, str): - if num == 0 and issubclass(BType, CTypesGenericPtr): - BType = BType._BItem - if not issubclass(BType, CTypesBaseStructOrUnion): - raise TypeError("expected a struct or union ctype") - BField = BType._bfield_types[fieldname] - if BField is Ellipsis: - raise TypeError("not supported for bitfields") - return (BField, BType._offsetof(fieldname)) - elif isinstance(fieldname, (int, long)): - if issubclass(BType, CTypesGenericArray): - BType = BType._CTPtr - if not issubclass(BType, CTypesGenericPtr): - raise TypeError("expected an array or ptr ctype") - BItem = BType._BItem - offset = BItem._get_size() * fieldname - if offset > sys.maxsize: - raise OverflowError - return (BItem, offset) - else: - raise TypeError(type(fieldname)) - - def rawaddressof(self, BTypePtr, cdata, offset=None): - if isinstance(cdata, CTypesBaseStructOrUnion): - ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) - elif isinstance(cdata, CTypesGenericPtr): - if offset is None or not issubclass(type(cdata)._BItem, - CTypesBaseStructOrUnion): - raise TypeError("unexpected cdata type") - ptr = type(cdata)._to_ctypes(cdata) - elif isinstance(cdata, CTypesGenericArray): - ptr = type(cdata)._to_ctypes(cdata) - else: - raise TypeError("expected a ") - if offset: - ptr = ctypes.cast( - ctypes.c_void_p( - ctypes.cast(ptr, ctypes.c_void_p).value + offset), - type(ptr)) - return BTypePtr._from_ctypes(ptr) - - -class CTypesLibrary(object): - - def __init__(self, backend, cdll): - self.backend = backend - self.cdll = cdll - - def load_function(self, BType, name): - c_func = getattr(self.cdll, name) - funcobj = BType._from_ctypes(c_func) - funcobj._name = name - return funcobj - - def read_variable(self, BType, name): - try: - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - except AttributeError as e: - raise NotImplementedError(e) - return BType._from_ctypes(ctypes_obj) - - def write_variable(self, BType, name, value): - new_ctypes_obj = BType._to_ctypes(value) - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - ctypes.memmove(ctypes.addressof(ctypes_obj), - ctypes.addressof(new_ctypes_obj), - ctypes.sizeof(BType._ctype)) diff --git a/.venv/Lib/site-packages/cffi/cffi_opcode.py b/.venv/Lib/site-packages/cffi/cffi_opcode.py deleted file mode 100644 index 6421df6..0000000 --- a/.venv/Lib/site-packages/cffi/cffi_opcode.py +++ /dev/null @@ -1,187 +0,0 @@ -from .error import VerificationError - -class CffiOp(object): - def __init__(self, op, arg): - self.op = op - self.arg = arg - - def as_c_expr(self): - if self.op is None: - assert isinstance(self.arg, str) - return '(_cffi_opcode_t)(%s)' % (self.arg,) - classname = CLASS_NAME[self.op] - return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) - - def as_python_bytes(self): - if self.op is None and self.arg.isdigit(): - value = int(self.arg) # non-negative: '-' not in self.arg - if value >= 2**31: - raise OverflowError("cannot emit %r: limited to 2**31-1" - % (self.arg,)) - return format_four_bytes(value) - if isinstance(self.arg, str): - raise VerificationError("cannot emit to Python: %r" % (self.arg,)) - return format_four_bytes((self.arg << 8) | self.op) - - def __str__(self): - classname = CLASS_NAME.get(self.op, self.op) - return '(%s %s)' % (classname, self.arg) - -def format_four_bytes(num): - return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( - (num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - (num ) & 0xFF) - -OP_PRIMITIVE = 1 -OP_POINTER = 3 -OP_ARRAY = 5 -OP_OPEN_ARRAY = 7 -OP_STRUCT_UNION = 9 -OP_ENUM = 11 -OP_FUNCTION = 13 -OP_FUNCTION_END = 15 -OP_NOOP = 17 -OP_BITFIELD = 19 -OP_TYPENAME = 21 -OP_CPYTHON_BLTN_V = 23 # varargs -OP_CPYTHON_BLTN_N = 25 # noargs -OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) -OP_CONSTANT = 29 -OP_CONSTANT_INT = 31 -OP_GLOBAL_VAR = 33 -OP_DLOPEN_FUNC = 35 -OP_DLOPEN_CONST = 37 -OP_GLOBAL_VAR_F = 39 -OP_EXTERN_PYTHON = 41 - -PRIM_VOID = 0 -PRIM_BOOL = 1 -PRIM_CHAR = 2 -PRIM_SCHAR = 3 -PRIM_UCHAR = 4 -PRIM_SHORT = 5 -PRIM_USHORT = 6 -PRIM_INT = 7 -PRIM_UINT = 8 -PRIM_LONG = 9 -PRIM_ULONG = 10 -PRIM_LONGLONG = 11 -PRIM_ULONGLONG = 12 -PRIM_FLOAT = 13 -PRIM_DOUBLE = 14 -PRIM_LONGDOUBLE = 15 - -PRIM_WCHAR = 16 -PRIM_INT8 = 17 -PRIM_UINT8 = 18 -PRIM_INT16 = 19 -PRIM_UINT16 = 20 -PRIM_INT32 = 21 -PRIM_UINT32 = 22 -PRIM_INT64 = 23 -PRIM_UINT64 = 24 -PRIM_INTPTR = 25 -PRIM_UINTPTR = 26 -PRIM_PTRDIFF = 27 -PRIM_SIZE = 28 -PRIM_SSIZE = 29 -PRIM_INT_LEAST8 = 30 -PRIM_UINT_LEAST8 = 31 -PRIM_INT_LEAST16 = 32 -PRIM_UINT_LEAST16 = 33 -PRIM_INT_LEAST32 = 34 -PRIM_UINT_LEAST32 = 35 -PRIM_INT_LEAST64 = 36 -PRIM_UINT_LEAST64 = 37 -PRIM_INT_FAST8 = 38 -PRIM_UINT_FAST8 = 39 -PRIM_INT_FAST16 = 40 -PRIM_UINT_FAST16 = 41 -PRIM_INT_FAST32 = 42 -PRIM_UINT_FAST32 = 43 -PRIM_INT_FAST64 = 44 -PRIM_UINT_FAST64 = 45 -PRIM_INTMAX = 46 -PRIM_UINTMAX = 47 -PRIM_FLOATCOMPLEX = 48 -PRIM_DOUBLECOMPLEX = 49 -PRIM_CHAR16 = 50 -PRIM_CHAR32 = 51 - -_NUM_PRIM = 52 -_UNKNOWN_PRIM = -1 -_UNKNOWN_FLOAT_PRIM = -2 -_UNKNOWN_LONG_DOUBLE = -3 - -_IO_FILE_STRUCT = -1 - -PRIMITIVE_TO_INDEX = { - 'char': PRIM_CHAR, - 'short': PRIM_SHORT, - 'int': PRIM_INT, - 'long': PRIM_LONG, - 'long long': PRIM_LONGLONG, - 'signed char': PRIM_SCHAR, - 'unsigned char': PRIM_UCHAR, - 'unsigned short': PRIM_USHORT, - 'unsigned int': PRIM_UINT, - 'unsigned long': PRIM_ULONG, - 'unsigned long long': PRIM_ULONGLONG, - 'float': PRIM_FLOAT, - 'double': PRIM_DOUBLE, - 'long double': PRIM_LONGDOUBLE, - '_cffi_float_complex_t': PRIM_FLOATCOMPLEX, - '_cffi_double_complex_t': PRIM_DOUBLECOMPLEX, - '_Bool': PRIM_BOOL, - 'wchar_t': PRIM_WCHAR, - 'char16_t': PRIM_CHAR16, - 'char32_t': PRIM_CHAR32, - 'int8_t': PRIM_INT8, - 'uint8_t': PRIM_UINT8, - 'int16_t': PRIM_INT16, - 'uint16_t': PRIM_UINT16, - 'int32_t': PRIM_INT32, - 'uint32_t': PRIM_UINT32, - 'int64_t': PRIM_INT64, - 'uint64_t': PRIM_UINT64, - 'intptr_t': PRIM_INTPTR, - 'uintptr_t': PRIM_UINTPTR, - 'ptrdiff_t': PRIM_PTRDIFF, - 'size_t': PRIM_SIZE, - 'ssize_t': PRIM_SSIZE, - 'int_least8_t': PRIM_INT_LEAST8, - 'uint_least8_t': PRIM_UINT_LEAST8, - 'int_least16_t': PRIM_INT_LEAST16, - 'uint_least16_t': PRIM_UINT_LEAST16, - 'int_least32_t': PRIM_INT_LEAST32, - 'uint_least32_t': PRIM_UINT_LEAST32, - 'int_least64_t': PRIM_INT_LEAST64, - 'uint_least64_t': PRIM_UINT_LEAST64, - 'int_fast8_t': PRIM_INT_FAST8, - 'uint_fast8_t': PRIM_UINT_FAST8, - 'int_fast16_t': PRIM_INT_FAST16, - 'uint_fast16_t': PRIM_UINT_FAST16, - 'int_fast32_t': PRIM_INT_FAST32, - 'uint_fast32_t': PRIM_UINT_FAST32, - 'int_fast64_t': PRIM_INT_FAST64, - 'uint_fast64_t': PRIM_UINT_FAST64, - 'intmax_t': PRIM_INTMAX, - 'uintmax_t': PRIM_UINTMAX, - } - -F_UNION = 0x01 -F_CHECK_FIELDS = 0x02 -F_PACKED = 0x04 -F_EXTERNAL = 0x08 -F_OPAQUE = 0x10 - -G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) - for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', - 'F_EXTERNAL', 'F_OPAQUE']]) - -CLASS_NAME = {} -for _name, _value in list(globals().items()): - if _name.startswith('OP_') and isinstance(_value, int): - CLASS_NAME[_value] = _name[3:] diff --git a/.venv/Lib/site-packages/cffi/commontypes.py b/.venv/Lib/site-packages/cffi/commontypes.py deleted file mode 100644 index d4dae35..0000000 --- a/.venv/Lib/site-packages/cffi/commontypes.py +++ /dev/null @@ -1,82 +0,0 @@ -import sys -from . import model -from .error import FFIError - - -COMMON_TYPES = {} - -try: - # fetch "bool" and all simple Windows types - from _cffi_backend import _get_common_types - _get_common_types(COMMON_TYPES) -except ImportError: - pass - -COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') -COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above -COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t' -COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t' - -for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - if _type.endswith('_t'): - COMMON_TYPES[_type] = _type -del _type - -_CACHE = {} - -def resolve_common_type(parser, commontype): - try: - return _CACHE[commontype] - except KeyError: - cdecl = COMMON_TYPES.get(commontype, commontype) - if not isinstance(cdecl, str): - result, quals = cdecl, 0 # cdecl is already a BaseType - elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - result, quals = model.PrimitiveType(cdecl), 0 - elif cdecl == 'set-unicode-needed': - raise FFIError("The Windows type %r is only available after " - "you call ffi.set_unicode()" % (commontype,)) - else: - if commontype == cdecl: - raise FFIError( - "Unsupported type: %r. Please look at " - "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " - "and file an issue if you think this type should really " - "be supported." % (commontype,)) - result, quals = parser.parse_type_and_quals(cdecl) # recursive - - assert isinstance(result, model.BaseTypeByIdentity) - _CACHE[commontype] = result, quals - return result, quals - - -# ____________________________________________________________ -# extra types for Windows (most of them are in commontypes.c) - - -def win_common_types(): - return { - "UNICODE_STRING": model.StructType( - "_UNICODE_STRING", - ["Length", - "MaximumLength", - "Buffer"], - [model.PrimitiveType("unsigned short"), - model.PrimitiveType("unsigned short"), - model.PointerType(model.PrimitiveType("wchar_t"))], - [-1, -1, -1]), - "PUNICODE_STRING": "UNICODE_STRING *", - "PCUNICODE_STRING": "const UNICODE_STRING *", - - "TBYTE": "set-unicode-needed", - "TCHAR": "set-unicode-needed", - "LPCTSTR": "set-unicode-needed", - "PCTSTR": "set-unicode-needed", - "LPTSTR": "set-unicode-needed", - "PTSTR": "set-unicode-needed", - "PTBYTE": "set-unicode-needed", - "PTCHAR": "set-unicode-needed", - } - -if sys.platform == 'win32': - COMMON_TYPES.update(win_common_types()) diff --git a/.venv/Lib/site-packages/cffi/cparser.py b/.venv/Lib/site-packages/cffi/cparser.py deleted file mode 100644 index eee83ca..0000000 --- a/.venv/Lib/site-packages/cffi/cparser.py +++ /dev/null @@ -1,1015 +0,0 @@ -from . import model -from .commontypes import COMMON_TYPES, resolve_common_type -from .error import FFIError, CDefError -try: - from . import _pycparser as pycparser -except ImportError: - import pycparser -import weakref, re, sys - -try: - if sys.version_info < (3,): - import thread as _thread - else: - import _thread - lock = _thread.allocate_lock() -except ImportError: - lock = None - -def _workaround_for_static_import_finders(): - # Issue #392: packaging tools like cx_Freeze can not find these - # because pycparser uses exec dynamic import. This is an obscure - # workaround. This function is never called. - import pycparser.yacctab - import pycparser.lextab - -CDEF_SOURCE_STRING = "" -_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", - re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) -_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) -_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") -_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") -_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") -_r_words = re.compile(r"\w+|\S") -_parser_cache = None -_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) -_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") -_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") -_r_cdecl = re.compile(r"\b__cdecl\b") -_r_extern_python = re.compile(r'\bextern\s*"' - r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') -_r_star_const_space = re.compile( # matches "* const " - r"[*]\s*((const|volatile|restrict)\b\s*)+") -_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" - r"\.\.\.") -_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") - -def _get_parser(): - global _parser_cache - if _parser_cache is None: - _parser_cache = pycparser.CParser() - return _parser_cache - -def _workaround_for_old_pycparser(csource): - # Workaround for a pycparser issue (fixed between pycparser 2.10 and - # 2.14): "char*const***" gives us a wrong syntax tree, the same as - # for "char***(*const)". This means we can't tell the difference - # afterwards. But "char(*const(***))" gives us the right syntax - # tree. The issue only occurs if there are several stars in - # sequence with no parenthesis inbetween, just possibly qualifiers. - # Attempt to fix it by adding some parentheses in the source: each - # time we see "* const" or "* const *", we add an opening - # parenthesis before each star---the hard part is figuring out where - # to close them. - parts = [] - while True: - match = _r_star_const_space.search(csource) - if not match: - break - #print repr(''.join(parts)+csource), '=>', - parts.append(csource[:match.start()]) - parts.append('('); closing = ')' - parts.append(match.group()) # e.g. "* const " - endpos = match.end() - if csource.startswith('*', endpos): - parts.append('('); closing += ')' - level = 0 - i = endpos - while i < len(csource): - c = csource[i] - if c == '(': - level += 1 - elif c == ')': - if level == 0: - break - level -= 1 - elif c in ',;=': - if level == 0: - break - i += 1 - csource = csource[endpos:i] + closing + csource[i:] - #print repr(''.join(parts)+csource) - parts.append(csource) - return ''.join(parts) - -def _preprocess_extern_python(csource): - # input: `extern "Python" int foo(int);` or - # `extern "Python" { int foo(int); }` - # output: - # void __cffi_extern_python_start; - # int foo(int); - # void __cffi_extern_python_stop; - # - # input: `extern "Python+C" int foo(int);` - # output: - # void __cffi_extern_python_plus_c_start; - # int foo(int); - # void __cffi_extern_python_stop; - parts = [] - while True: - match = _r_extern_python.search(csource) - if not match: - break - endpos = match.end() - 1 - #print - #print ''.join(parts)+csource - #print '=>' - parts.append(csource[:match.start()]) - if 'C' in match.group(1): - parts.append('void __cffi_extern_python_plus_c_start; ') - else: - parts.append('void __cffi_extern_python_start; ') - if csource[endpos] == '{': - # grouping variant - closing = csource.find('}', endpos) - if closing < 0: - raise CDefError("'extern \"Python\" {': no '}' found") - if csource.find('{', endpos + 1, closing) >= 0: - raise NotImplementedError("cannot use { } inside a block " - "'extern \"Python\" { ... }'") - parts.append(csource[endpos+1:closing]) - csource = csource[closing+1:] - else: - # non-grouping variant - semicolon = csource.find(';', endpos) - if semicolon < 0: - raise CDefError("'extern \"Python\": no ';' found") - parts.append(csource[endpos:semicolon+1]) - csource = csource[semicolon+1:] - parts.append(' void __cffi_extern_python_stop;') - #print ''.join(parts)+csource - #print - parts.append(csource) - return ''.join(parts) - -def _warn_for_string_literal(csource): - if '"' not in csource: - return - for line in csource.splitlines(): - if '"' in line and not line.lstrip().startswith('#'): - import warnings - warnings.warn("String literal found in cdef() or type source. " - "String literals are ignored here, but you should " - "remove them anyway because some character sequences " - "confuse pre-parsing.") - break - -def _warn_for_non_extern_non_static_global_variable(decl): - if not decl.storage: - import warnings - warnings.warn("Global variable '%s' in cdef(): for consistency " - "with C it should have a storage class specifier " - "(usually 'extern')" % (decl.name,)) - -def _remove_line_directives(csource): - # _r_line_directive matches whole lines, without the final \n, if they - # start with '#line' with some spacing allowed, or '#NUMBER'. This - # function stores them away and replaces them with exactly the string - # '#line@N', where N is the index in the list 'line_directives'. - line_directives = [] - def replace(m): - i = len(line_directives) - line_directives.append(m.group()) - return '#line@%d' % i - csource = _r_line_directive.sub(replace, csource) - return csource, line_directives - -def _put_back_line_directives(csource, line_directives): - def replace(m): - s = m.group() - if not s.startswith('#line@'): - raise AssertionError("unexpected #line directive " - "(should have been processed and removed") - return line_directives[int(s[6:])] - return _r_line_directive.sub(replace, csource) - -def _preprocess(csource): - # First, remove the lines of the form '#line N "filename"' because - # the "filename" part could confuse the rest - csource, line_directives = _remove_line_directives(csource) - # Remove comments. NOTE: this only work because the cdef() section - # should not contain any string literals (except in line directives)! - def replace_keeping_newlines(m): - return ' ' + m.group().count('\n') * '\n' - csource = _r_comment.sub(replace_keeping_newlines, csource) - # Remove the "#define FOO x" lines - macros = {} - for match in _r_define.finditer(csource): - macroname, macrovalue = match.groups() - macrovalue = macrovalue.replace('\\\n', '').strip() - macros[macroname] = macrovalue - csource = _r_define.sub('', csource) - # - if pycparser.__version__ < '2.14': - csource = _workaround_for_old_pycparser(csource) - # - # BIG HACK: replace WINAPI or __stdcall with "volatile const". - # It doesn't make sense for the return type of a function to be - # "volatile volatile const", so we abuse it to detect __stdcall... - # Hack number 2 is that "int(volatile *fptr)();" is not valid C - # syntax, so we place the "volatile" before the opening parenthesis. - csource = _r_stdcall2.sub(' volatile volatile const(', csource) - csource = _r_stdcall1.sub(' volatile volatile const ', csource) - csource = _r_cdecl.sub(' ', csource) - # - # Replace `extern "Python"` with start/end markers - csource = _preprocess_extern_python(csource) - # - # Now there should not be any string literal left; warn if we get one - _warn_for_string_literal(csource) - # - # Replace "[...]" with "[__dotdotdotarray__]" - csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) - # - # Replace "...}" with "__dotdotdotNUM__}". This construction should - # occur only at the end of enums; at the end of structs we have "...;}" - # and at the end of vararg functions "...);". Also replace "=...[,}]" - # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when - # giving an unknown value. - matches = list(_r_partial_enum.finditer(csource)) - for number, match in enumerate(reversed(matches)): - p = match.start() - if csource[p] == '=': - p2 = csource.find('...', p, match.end()) - assert p2 > p - csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, - csource[p2+3:]) - else: - assert csource[p:p+3] == '...' - csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, - csource[p+3:]) - # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" - csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) - # Replace "float ..." or "double..." with "__dotdotdotfloat__" - csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) - # Replace all remaining "..." with the same name, "__dotdotdot__", - # which is declared with a typedef for the purpose of C parsing. - csource = csource.replace('...', ' __dotdotdot__ ') - # Finally, put back the line directives - csource = _put_back_line_directives(csource, line_directives) - return csource, macros - -def _common_type_names(csource): - # Look in the source for what looks like usages of types from the - # list of common types. A "usage" is approximated here as the - # appearance of the word, minus a "definition" of the type, which - # is the last word in a "typedef" statement. Approximative only - # but should be fine for all the common types. - look_for_words = set(COMMON_TYPES) - look_for_words.add(';') - look_for_words.add(',') - look_for_words.add('(') - look_for_words.add(')') - look_for_words.add('typedef') - words_used = set() - is_typedef = False - paren = 0 - previous_word = '' - for word in _r_words.findall(csource): - if word in look_for_words: - if word == ';': - if is_typedef: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - is_typedef = False - elif word == 'typedef': - is_typedef = True - paren = 0 - elif word == '(': - paren += 1 - elif word == ')': - paren -= 1 - elif word == ',': - if is_typedef and paren == 0: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - else: # word in COMMON_TYPES - words_used.add(word) - previous_word = word - return words_used - - -class Parser(object): - - def __init__(self): - self._declarations = {} - self._included_declarations = set() - self._anonymous_counter = 0 - self._structnode2type = weakref.WeakKeyDictionary() - self._options = {} - self._int_constants = {} - self._recomplete = [] - self._uses_new_feature = None - - def _parse(self, csource): - csource, macros = _preprocess(csource) - # XXX: for more efficiency we would need to poke into the - # internals of CParser... the following registers the - # typedefs, because their presence or absence influences the - # parsing itself (but what they are typedef'ed to plays no role) - ctn = _common_type_names(csource) - typenames = [] - for name in sorted(self._declarations): - if name.startswith('typedef '): - name = name[8:] - typenames.append(name) - ctn.discard(name) - typenames += sorted(ctn) - # - csourcelines = [] - csourcelines.append('# 1 ""') - for typename in typenames: - csourcelines.append('typedef int %s;' % typename) - csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' - ' __dotdotdot__;') - # this forces pycparser to consider the following in the file - # called from line 1 - csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) - csourcelines.append(csource) - csourcelines.append('') # see test_missing_newline_bug - fullcsource = '\n'.join(csourcelines) - if lock is not None: - lock.acquire() # pycparser is not thread-safe... - try: - ast = _get_parser().parse(fullcsource) - except pycparser.c_parser.ParseError as e: - self.convert_pycparser_error(e, csource) - finally: - if lock is not None: - lock.release() - # csource will be used to find buggy source text - return ast, macros, csource - - def _convert_pycparser_error(self, e, csource): - # xxx look for ":NUM:" at the start of str(e) - # and interpret that as a line number. This will not work if - # the user gives explicit ``# NUM "FILE"`` directives. - line = None - msg = str(e) - match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) - if match: - linenum = int(match.group(1), 10) - csourcelines = csource.splitlines() - if 1 <= linenum <= len(csourcelines): - line = csourcelines[linenum-1] - return line - - def convert_pycparser_error(self, e, csource): - line = self._convert_pycparser_error(e, csource) - - msg = str(e) - if line: - msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) - else: - msg = 'parse error\n%s' % (msg,) - raise CDefError(msg) - - def parse(self, csource, override=False, packed=False, pack=None, - dllexport=False): - if packed: - if packed != True: - raise ValueError("'packed' should be False or True; use " - "'pack' to give another value") - if pack: - raise ValueError("cannot give both 'pack' and 'packed'") - pack = 1 - elif pack: - if pack & (pack - 1): - raise ValueError("'pack' must be a power of two, not %r" % - (pack,)) - else: - pack = 0 - prev_options = self._options - try: - self._options = {'override': override, - 'packed': pack, - 'dllexport': dllexport} - self._internal_parse(csource) - finally: - self._options = prev_options - - def _internal_parse(self, csource): - ast, macros, csource = self._parse(csource) - # add the macros - self._process_macros(macros) - # find the first "__dotdotdot__" and use that as a separator - # between the repeated typedefs and the real csource - iterator = iter(ast.ext) - for decl in iterator: - if decl.name == '__dotdotdot__': - break - else: - assert 0 - current_decl = None - # - try: - self._inside_extern_python = '__cffi_extern_python_stop' - for decl in iterator: - current_decl = decl - if isinstance(decl, pycparser.c_ast.Decl): - self._parse_decl(decl) - elif isinstance(decl, pycparser.c_ast.Typedef): - if not decl.name: - raise CDefError("typedef does not declare any name", - decl) - quals = 0 - if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and - decl.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_type(decl) - elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and - isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and - isinstance(decl.type.type.type, - pycparser.c_ast.IdentifierType) and - decl.type.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_ptr_type(decl) - else: - realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True, - typedef_example="*(%s *)0" % (decl.name,)) - self._declare('typedef ' + decl.name, realtype, quals=quals) - elif decl.__class__.__name__ == 'Pragma': - # skip pragma, only in pycparser 2.15 - import warnings - warnings.warn( - "#pragma in cdef() are entirely ignored. " - "They should be removed for now, otherwise your " - "code might behave differently in a future version " - "of CFFI if #pragma support gets added. Note that " - "'#pragma pack' needs to be replaced with the " - "'packed' keyword argument to cdef().") - else: - raise CDefError("unexpected <%s>: this construct is valid " - "C but not valid in cdef()" % - decl.__class__.__name__, decl) - except CDefError as e: - if len(e.args) == 1: - e.args = e.args + (current_decl,) - raise - except FFIError as e: - msg = self._convert_pycparser_error(e, csource) - if msg: - e.args = (e.args[0] + "\n *** Err: %s" % msg,) - raise - - def _add_constants(self, key, val): - if key in self._int_constants: - if self._int_constants[key] == val: - return # ignore identical double declarations - raise FFIError( - "multiple declarations of constant: %s" % (key,)) - self._int_constants[key] = val - - def _add_integer_constant(self, name, int_str): - int_str = int_str.lower().rstrip("ul") - neg = int_str.startswith('-') - if neg: - int_str = int_str[1:] - # "010" is not valid oct in py3 - if (int_str.startswith("0") and int_str != '0' - and not int_str.startswith("0x")): - int_str = "0o" + int_str[1:] - pyvalue = int(int_str, 0) - if neg: - pyvalue = -pyvalue - self._add_constants(name, pyvalue) - self._declare('macro ' + name, pyvalue) - - def _process_macros(self, macros): - for key, value in macros.items(): - value = value.strip() - if _r_int_literal.match(value): - self._add_integer_constant(key, value) - elif value == '...': - self._declare('macro ' + key, value) - else: - raise CDefError( - 'only supports one of the following syntax:\n' - ' #define %s ... (literally dot-dot-dot)\n' - ' #define %s NUMBER (with NUMBER an integer' - ' constant, decimal/hex/octal)\n' - 'got:\n' - ' #define %s %s' - % (key, key, key, value)) - - def _declare_function(self, tp, quals, decl): - tp = self._get_type_pointer(tp, quals) - if self._options.get('dllexport'): - tag = 'dllexport_python ' - elif self._inside_extern_python == '__cffi_extern_python_start': - tag = 'extern_python ' - elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': - tag = 'extern_python_plus_c ' - else: - tag = 'function ' - self._declare(tag + decl.name, tp) - - def _parse_decl(self, decl): - node = decl.type - if isinstance(node, pycparser.c_ast.FuncDecl): - tp, quals = self._get_type_and_quals(node, name=decl.name) - assert isinstance(tp, model.RawFunctionType) - self._declare_function(tp, quals, decl) - else: - if isinstance(node, pycparser.c_ast.Struct): - self._get_struct_union_enum_type('struct', node) - elif isinstance(node, pycparser.c_ast.Union): - self._get_struct_union_enum_type('union', node) - elif isinstance(node, pycparser.c_ast.Enum): - self._get_struct_union_enum_type('enum', node) - elif not decl.name: - raise CDefError("construct does not declare any variable", - decl) - # - if decl.name: - tp, quals = self._get_type_and_quals(node, - partial_length_ok=True) - if tp.is_raw_function: - self._declare_function(tp, quals, decl) - elif (tp.is_integer_type() and - hasattr(decl, 'init') and - hasattr(decl.init, 'value') and - _r_int_literal.match(decl.init.value)): - self._add_integer_constant(decl.name, decl.init.value) - elif (tp.is_integer_type() and - isinstance(decl.init, pycparser.c_ast.UnaryOp) and - decl.init.op == '-' and - hasattr(decl.init.expr, 'value') and - _r_int_literal.match(decl.init.expr.value)): - self._add_integer_constant(decl.name, - '-' + decl.init.expr.value) - elif (tp is model.void_type and - decl.name.startswith('__cffi_extern_python_')): - # hack: `extern "Python"` in the C source is replaced - # with "void __cffi_extern_python_start;" and - # "void __cffi_extern_python_stop;" - self._inside_extern_python = decl.name - else: - if self._inside_extern_python !='__cffi_extern_python_stop': - raise CDefError( - "cannot declare constants or " - "variables with 'extern \"Python\"'") - if (quals & model.Q_CONST) and not tp.is_array_type: - self._declare('constant ' + decl.name, tp, quals=quals) - else: - _warn_for_non_extern_non_static_global_variable(decl) - self._declare('variable ' + decl.name, tp, quals=quals) - - def parse_type(self, cdecl): - return self.parse_type_and_quals(cdecl)[0] - - def parse_type_and_quals(self, cdecl): - ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] - assert not macros - exprnode = ast.ext[-1].type.args.params[0] - if isinstance(exprnode, pycparser.c_ast.ID): - raise CDefError("unknown identifier '%s'" % (exprnode.name,)) - return self._get_type_and_quals(exprnode.type) - - def _declare(self, name, obj, included=False, quals=0): - if name in self._declarations: - prevobj, prevquals = self._declarations[name] - if prevobj is obj and prevquals == quals: - return - if not self._options.get('override'): - raise FFIError( - "multiple declarations of %s (for interactive usage, " - "try cdef(xx, override=True))" % (name,)) - assert '__dotdotdot__' not in name.split() - self._declarations[name] = (obj, quals) - if included: - self._included_declarations.add(obj) - - def _extract_quals(self, type): - quals = 0 - if isinstance(type, (pycparser.c_ast.TypeDecl, - pycparser.c_ast.PtrDecl)): - if 'const' in type.quals: - quals |= model.Q_CONST - if 'volatile' in type.quals: - quals |= model.Q_VOLATILE - if 'restrict' in type.quals: - quals |= model.Q_RESTRICT - return quals - - def _get_type_pointer(self, type, quals, declname=None): - if isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - if (isinstance(type, model.StructOrUnionOrEnum) and - type.name.startswith('$') and type.name[1:].isdigit() and - type.forcename is None and declname is not None): - return model.NamedPointerType(type, declname, quals) - return model.PointerType(type, quals) - - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, - typedef_example=None): - # first, dereference typedefs, if we have it already parsed, we're good - if (isinstance(typenode, pycparser.c_ast.TypeDecl) and - isinstance(typenode.type, pycparser.c_ast.IdentifierType) and - len(typenode.type.names) == 1 and - ('typedef ' + typenode.type.names[0]) in self._declarations): - tp, quals = self._declarations['typedef ' + typenode.type.names[0]] - quals |= self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.ArrayDecl): - # array type - if typenode.dim is None: - length = None - else: - length = self._parse_constant( - typenode.dim, partial_length_ok=partial_length_ok) - # a hack: in 'typedef int foo_t[...][...];', don't use '...' as - # the length but use directly the C expression that would be - # generated by recompiler.py. This lets the typedef be used in - # many more places within recompiler.py - if typedef_example is not None: - if length == '...': - length = '_cffi_array_len(%s)' % (typedef_example,) - typedef_example = "*" + typedef_example - # - tp, quals = self._get_type_and_quals(typenode.type, - partial_length_ok=partial_length_ok, - typedef_example=typedef_example) - return model.ArrayType(tp, length), quals - # - if isinstance(typenode, pycparser.c_ast.PtrDecl): - # pointer type - itemtype, itemquals = self._get_type_and_quals(typenode.type) - tp = self._get_type_pointer(itemtype, itemquals, declname=name) - quals = self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.TypeDecl): - quals = self._extract_quals(typenode) - type = typenode.type - if isinstance(type, pycparser.c_ast.IdentifierType): - # assume a primitive type. get it from .names, but reduce - # synonyms to a single chosen combination - names = list(type.names) - if names != ['signed', 'char']: # keep this unmodified - prefixes = {} - while names: - name = names[0] - if name in ('short', 'long', 'signed', 'unsigned'): - prefixes[name] = prefixes.get(name, 0) + 1 - del names[0] - else: - break - # ignore the 'signed' prefix below, and reorder the others - newnames = [] - for prefix in ('unsigned', 'short', 'long'): - for i in range(prefixes.get(prefix, 0)): - newnames.append(prefix) - if not names: - names = ['int'] # implicitly - if names == ['int']: # but kill it if 'short' or 'long' - if 'short' in prefixes or 'long' in prefixes: - names = [] - names = newnames + names - ident = ' '.join(names) - if ident == 'void': - return model.void_type, quals - if ident == '__dotdotdot__': - raise FFIError(':%d: bad usage of "..."' % - typenode.coord.line) - tp0, quals0 = resolve_common_type(self, ident) - return tp0, (quals | quals0) - # - if isinstance(type, pycparser.c_ast.Struct): - # 'struct foobar' - tp = self._get_struct_union_enum_type('struct', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Union): - # 'union foobar' - tp = self._get_struct_union_enum_type('union', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Enum): - # 'enum foobar' - tp = self._get_struct_union_enum_type('enum', type, name) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.FuncDecl): - # a function type - return self._parse_function_type(typenode, name), 0 - # - # nested anonymous structs or unions end up here - if isinstance(typenode, pycparser.c_ast.Struct): - return self._get_struct_union_enum_type('struct', typenode, name, - nested=True), 0 - if isinstance(typenode, pycparser.c_ast.Union): - return self._get_struct_union_enum_type('union', typenode, name, - nested=True), 0 - # - raise FFIError(":%d: bad or unsupported type declaration" % - typenode.coord.line) - - def _parse_function_type(self, typenode, funcname=None): - params = list(getattr(typenode.args, 'params', [])) - for i, arg in enumerate(params): - if not hasattr(arg, 'type'): - raise CDefError("%s arg %d: unknown type '%s'" - " (if you meant to use the old C syntax of giving" - " untyped arguments, it is not supported)" - % (funcname or 'in expression', i + 1, - getattr(arg, 'name', '?'))) - ellipsis = ( - len(params) > 0 and - isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and - isinstance(params[-1].type.type, - pycparser.c_ast.IdentifierType) and - params[-1].type.type.names == ['__dotdotdot__']) - if ellipsis: - params.pop() - if not params: - raise CDefError( - "%s: a function with only '(...)' as argument" - " is not correct C" % (funcname or 'in expression')) - args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) - for argdeclnode in params] - if not ellipsis and args == [model.void_type]: - args = [] - result, quals = self._get_type_and_quals(typenode.type) - # the 'quals' on the result type are ignored. HACK: we absure them - # to detect __stdcall functions: we textually replace "__stdcall" - # with "volatile volatile const" above. - abi = None - if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway - if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: - abi = '__stdcall' - return model.RawFunctionType(tuple(args), result, ellipsis, abi) - - def _as_func_arg(self, type, quals): - if isinstance(type, model.ArrayType): - return model.PointerType(type.item, quals) - elif isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - else: - return type - - def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): - # First, a level of caching on the exact 'type' node of the AST. - # This is obscure, but needed because pycparser "unrolls" declarations - # such as "typedef struct { } foo_t, *foo_p" and we end up with - # an AST that is not a tree, but a DAG, with the "type" node of the - # two branches foo_t and foo_p of the trees being the same node. - # It's a bit silly but detecting "DAG-ness" in the AST tree seems - # to be the only way to distinguish this case from two independent - # structs. See test_struct_with_two_usages. - try: - return self._structnode2type[type] - except KeyError: - pass - # - # Note that this must handle parsing "struct foo" any number of - # times and always return the same StructType object. Additionally, - # one of these times (not necessarily the first), the fields of - # the struct can be specified with "struct foo { ...fields... }". - # If no name is given, then we have to create a new anonymous struct - # with no caching; in this case, the fields are either specified - # right now or never. - # - force_name = name - name = type.name - # - # get the type or create it if needed - if name is None: - # 'force_name' is used to guess a more readable name for - # anonymous structs, for the common case "typedef struct { } foo". - if force_name is not None: - explicit_name = '$%s' % force_name - else: - self._anonymous_counter += 1 - explicit_name = '$%d' % self._anonymous_counter - tp = None - else: - explicit_name = name - key = '%s %s' % (kind, name) - tp, _ = self._declarations.get(key, (None, None)) - # - if tp is None: - if kind == 'struct': - tp = model.StructType(explicit_name, None, None, None) - elif kind == 'union': - tp = model.UnionType(explicit_name, None, None, None) - elif kind == 'enum': - if explicit_name == '__dotdotdot__': - raise CDefError("Enums cannot be declared with ...") - tp = self._build_enum_type(explicit_name, type.values) - else: - raise AssertionError("kind = %r" % (kind,)) - if name is not None: - self._declare(key, tp) - else: - if kind == 'enum' and type.values is not None: - raise NotImplementedError( - "enum %s: the '{}' declaration should appear on the first " - "time the enum is mentioned, not later" % explicit_name) - if not tp.forcename: - tp.force_the_name(force_name) - if tp.forcename and '$' in tp.name: - self._declare('anonymous %s' % tp.forcename, tp) - # - self._structnode2type[type] = tp - # - # enums: done here - if kind == 'enum': - return tp - # - # is there a 'type.decls'? If yes, then this is the place in the - # C sources that declare the fields. If no, then just return the - # existing type, possibly still incomplete. - if type.decls is None: - return tp - # - if tp.fldnames is not None: - raise CDefError("duplicate declaration of struct %s" % name) - fldnames = [] - fldtypes = [] - fldbitsize = [] - fldquals = [] - for decl in type.decls: - if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and - ''.join(decl.type.names) == '__dotdotdot__'): - # XXX pycparser is inconsistent: 'names' should be a list - # of strings, but is sometimes just one string. Use - # str.join() as a way to cope with both. - self._make_partial(tp, nested) - continue - if decl.bitsize is None: - bitsize = -1 - else: - bitsize = self._parse_constant(decl.bitsize) - self._partial_length = False - type, fqual = self._get_type_and_quals(decl.type, - partial_length_ok=True) - if self._partial_length: - self._make_partial(tp, nested) - if isinstance(type, model.StructType) and type.partial: - self._make_partial(tp, nested) - fldnames.append(decl.name or '') - fldtypes.append(type) - fldbitsize.append(bitsize) - fldquals.append(fqual) - tp.fldnames = tuple(fldnames) - tp.fldtypes = tuple(fldtypes) - tp.fldbitsize = tuple(fldbitsize) - tp.fldquals = tuple(fldquals) - if fldbitsize != [-1] * len(fldbitsize): - if isinstance(tp, model.StructType) and tp.partial: - raise NotImplementedError("%s: using both bitfields and '...;'" - % (tp,)) - tp.packed = self._options.get('packed') - if tp.completed: # must be re-completed: it is not opaque any more - tp.completed = 0 - self._recomplete.append(tp) - return tp - - def _make_partial(self, tp, nested): - if not isinstance(tp, model.StructOrUnion): - raise CDefError("%s cannot be partial" % (tp,)) - if not tp.has_c_name() and not nested: - raise NotImplementedError("%s is partial but has no C name" %(tp,)) - tp.partial = True - - def _parse_constant(self, exprnode, partial_length_ok=False): - # for now, limited to expressions that are an immediate number - # or positive/negative number - if isinstance(exprnode, pycparser.c_ast.Constant): - s = exprnode.value - if '0' <= s[0] <= '9': - s = s.rstrip('uUlL') - try: - if s.startswith('0'): - return int(s, 8) - else: - return int(s, 10) - except ValueError: - if len(s) > 1: - if s.lower()[0:2] == '0x': - return int(s, 16) - elif s.lower()[0:2] == '0b': - return int(s, 2) - raise CDefError("invalid constant %r" % (s,)) - elif s[0] == "'" and s[-1] == "'" and ( - len(s) == 3 or (len(s) == 4 and s[1] == "\\")): - return ord(s[-2]) - else: - raise CDefError("invalid constant %r" % (s,)) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '+'): - return self._parse_constant(exprnode.expr) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '-'): - return -self._parse_constant(exprnode.expr) - # load previously defined int constant - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name in self._int_constants): - return self._int_constants[exprnode.name] - # - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name == '__dotdotdotarray__'): - if partial_length_ok: - self._partial_length = True - return '...' - raise FFIError(":%d: unsupported '[...]' here, cannot derive " - "the actual array length in this context" - % exprnode.coord.line) - # - if isinstance(exprnode, pycparser.c_ast.BinaryOp): - left = self._parse_constant(exprnode.left) - right = self._parse_constant(exprnode.right) - if exprnode.op == '+': - return left + right - elif exprnode.op == '-': - return left - right - elif exprnode.op == '*': - return left * right - elif exprnode.op == '/': - return self._c_div(left, right) - elif exprnode.op == '%': - return left - self._c_div(left, right) * right - elif exprnode.op == '<<': - return left << right - elif exprnode.op == '>>': - return left >> right - elif exprnode.op == '&': - return left & right - elif exprnode.op == '|': - return left | right - elif exprnode.op == '^': - return left ^ right - # - raise FFIError(":%d: unsupported expression: expected a " - "simple numeric constant" % exprnode.coord.line) - - def _c_div(self, a, b): - result = a // b - if ((a < 0) ^ (b < 0)) and (a % b) != 0: - result += 1 - return result - - def _build_enum_type(self, explicit_name, decls): - if decls is not None: - partial = False - enumerators = [] - enumvalues = [] - nextenumvalue = 0 - for enum in decls.enumerators: - if _r_enum_dotdotdot.match(enum.name): - partial = True - continue - if enum.value is not None: - nextenumvalue = self._parse_constant(enum.value) - enumerators.append(enum.name) - enumvalues.append(nextenumvalue) - self._add_constants(enum.name, nextenumvalue) - nextenumvalue += 1 - enumerators = tuple(enumerators) - enumvalues = tuple(enumvalues) - tp = model.EnumType(explicit_name, enumerators, enumvalues) - tp.partial = partial - else: # opaque enum - tp = model.EnumType(explicit_name, (), ()) - return tp - - def include(self, other): - for name, (tp, quals) in other._declarations.items(): - if name.startswith('anonymous $enum_$'): - continue # fix for test_anonymous_enum_include - kind = name.split(' ', 1)[0] - if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): - self._declare(name, tp, included=True, quals=quals) - for k, v in other._int_constants.items(): - self._add_constants(k, v) - - def _get_unknown_type(self, decl): - typenames = decl.type.type.names - if typenames == ['__dotdotdot__']: - return model.unknown_type(decl.name) - - if typenames == ['__dotdotdotint__']: - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef int... %s'" % decl.name - return model.UnknownIntegerType(decl.name) - - if typenames == ['__dotdotdotfloat__']: - # note: not for 'long double' so far - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef float... %s'" % decl.name - return model.UnknownFloatType(decl.name) - - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) - - def _get_unknown_ptr_type(self, decl): - if decl.type.type.type.names == ['__dotdotdot__']: - return model.unknown_ptr_type(decl.name) - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) diff --git a/.venv/Lib/site-packages/cffi/error.py b/.venv/Lib/site-packages/cffi/error.py deleted file mode 100644 index 0a27247..0000000 --- a/.venv/Lib/site-packages/cffi/error.py +++ /dev/null @@ -1,31 +0,0 @@ - -class FFIError(Exception): - __module__ = 'cffi' - -class CDefError(Exception): - __module__ = 'cffi' - def __str__(self): - try: - current_decl = self.args[1] - filename = current_decl.coord.file - linenum = current_decl.coord.line - prefix = '%s:%d: ' % (filename, linenum) - except (AttributeError, TypeError, IndexError): - prefix = '' - return '%s%s' % (prefix, self.args[0]) - -class VerificationError(Exception): - """ An error raised when verification fails - """ - __module__ = 'cffi' - -class VerificationMissing(Exception): - """ An error raised when incomplete structures are passed into - cdef, but no verification has been done - """ - __module__ = 'cffi' - -class PkgConfigError(Exception): - """ An error raised for missing modules in pkg-config - """ - __module__ = 'cffi' diff --git a/.venv/Lib/site-packages/cffi/ffiplatform.py b/.venv/Lib/site-packages/cffi/ffiplatform.py deleted file mode 100644 index adca28f..0000000 --- a/.venv/Lib/site-packages/cffi/ffiplatform.py +++ /dev/null @@ -1,113 +0,0 @@ -import sys, os -from .error import VerificationError - - -LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', - 'extra_objects', 'depends'] - -def get_extension(srcfilename, modname, sources=(), **kwds): - from cffi._shimmed_dist_utils import Extension - allsources = [srcfilename] - for src in sources: - allsources.append(os.path.normpath(src)) - return Extension(name=modname, sources=allsources, **kwds) - -def compile(tmpdir, ext, compiler_verbose=0, debug=None): - """Compile a C extension module using distutils.""" - - saved_environ = os.environ.copy() - try: - outputfilename = _build(tmpdir, ext, compiler_verbose, debug) - outputfilename = os.path.abspath(outputfilename) - finally: - # workaround for a distutils bugs where some env vars can - # become longer and longer every time it is used - for key, value in saved_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - return outputfilename - -def _build(tmpdir, ext, compiler_verbose=0, debug=None): - # XXX compact but horrible :-( - from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity - - dist = Distribution({'ext_modules': [ext]}) - dist.parse_config_files() - options = dist.get_option_dict('build_ext') - if debug is None: - debug = sys.flags.debug - options['debug'] = ('ffiplatform', debug) - options['force'] = ('ffiplatform', True) - options['build_lib'] = ('ffiplatform', tmpdir) - options['build_temp'] = ('ffiplatform', tmpdir) - # - try: - old_level = set_threshold(0) or 0 - try: - set_verbosity(compiler_verbose) - dist.run_command('build_ext') - cmd_obj = dist.get_command_obj('build_ext') - [soname] = cmd_obj.get_outputs() - finally: - set_threshold(old_level) - except (CompileError, LinkError) as e: - raise VerificationError('%s: %s' % (e.__class__.__name__, e)) - # - return soname - -try: - from os.path import samefile -except ImportError: - def samefile(f1, f2): - return os.path.abspath(f1) == os.path.abspath(f2) - -def maybe_relative_path(path): - if not os.path.isabs(path): - return path # already relative - dir = path - names = [] - while True: - prevdir = dir - dir, name = os.path.split(prevdir) - if dir == prevdir or not dir: - return path # failed to make it relative - names.append(name) - try: - if samefile(dir, os.curdir): - names.reverse() - return os.path.join(*names) - except OSError: - pass - -# ____________________________________________________________ - -try: - int_or_long = (int, long) - import cStringIO -except NameError: - int_or_long = int # Python 3 - import io as cStringIO - -def _flatten(x, f): - if isinstance(x, str): - f.write('%ds%s' % (len(x), x)) - elif isinstance(x, dict): - keys = sorted(x.keys()) - f.write('%dd' % len(keys)) - for key in keys: - _flatten(key, f) - _flatten(x[key], f) - elif isinstance(x, (list, tuple)): - f.write('%dl' % len(x)) - for value in x: - _flatten(value, f) - elif isinstance(x, int_or_long): - f.write('%di' % (x,)) - else: - raise TypeError( - "the keywords to verify() contains unsupported object %r" % (x,)) - -def flatten(x): - f = cStringIO.StringIO() - _flatten(x, f) - return f.getvalue() diff --git a/.venv/Lib/site-packages/cffi/lock.py b/.venv/Lib/site-packages/cffi/lock.py deleted file mode 100644 index db91b71..0000000 --- a/.venv/Lib/site-packages/cffi/lock.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys - -if sys.version_info < (3,): - try: - from thread import allocate_lock - except ImportError: - from dummy_thread import allocate_lock -else: - try: - from _thread import allocate_lock - except ImportError: - from _dummy_thread import allocate_lock - - -##import sys -##l1 = allocate_lock - -##class allocate_lock(object): -## def __init__(self): -## self._real = l1() -## def __enter__(self): -## for i in range(4, 0, -1): -## print sys._getframe(i).f_code -## print -## return self._real.__enter__() -## def __exit__(self, *args): -## return self._real.__exit__(*args) -## def acquire(self, f): -## assert f is False -## return self._real.acquire(f) diff --git a/.venv/Lib/site-packages/cffi/model.py b/.venv/Lib/site-packages/cffi/model.py deleted file mode 100644 index e5f4cae..0000000 --- a/.venv/Lib/site-packages/cffi/model.py +++ /dev/null @@ -1,618 +0,0 @@ -import types -import weakref - -from .lock import allocate_lock -from .error import CDefError, VerificationError, VerificationMissing - -# type qualifiers -Q_CONST = 0x01 -Q_RESTRICT = 0x02 -Q_VOLATILE = 0x04 - -def qualify(quals, replace_with): - if quals & Q_CONST: - replace_with = ' const ' + replace_with.lstrip() - if quals & Q_VOLATILE: - replace_with = ' volatile ' + replace_with.lstrip() - if quals & Q_RESTRICT: - # It seems that __restrict is supported by gcc and msvc. - # If you hit some different compiler, add a #define in - # _cffi_include.h for it (and in its copies, documented there) - replace_with = ' __restrict ' + replace_with.lstrip() - return replace_with - - -class BaseTypeByIdentity(object): - is_array_type = False - is_raw_function = False - - def get_c_name(self, replace_with='', context='a C file', quals=0): - result = self.c_name_with_marker - assert result.count('&') == 1 - # some logic duplication with ffi.getctype()... :-( - replace_with = replace_with.strip() - if replace_with: - if replace_with.startswith('*') and '&[' in result: - replace_with = '(%s)' % replace_with - elif not replace_with[0] in '[(': - replace_with = ' ' + replace_with - replace_with = qualify(quals, replace_with) - result = result.replace('&', replace_with) - if '$' in result: - raise VerificationError( - "cannot generate '%s' in %s: unknown type name" - % (self._get_c_name(), context)) - return result - - def _get_c_name(self): - return self.c_name_with_marker.replace('&', '') - - def has_c_name(self): - return '$' not in self._get_c_name() - - def is_integer_type(self): - return False - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - try: - BType = ffi._cached_btypes[self] - except KeyError: - BType = self.build_backend_type(ffi, finishlist) - BType2 = ffi._cached_btypes.setdefault(self, BType) - assert BType2 is BType - return BType - - def __repr__(self): - return '<%s>' % (self._get_c_name(),) - - def _get_items(self): - return [(name, getattr(self, name)) for name in self._attrs_] - - -class BaseType(BaseTypeByIdentity): - - def __eq__(self, other): - return (self.__class__ == other.__class__ and - self._get_items() == other._get_items()) - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.__class__, tuple(self._get_items()))) - - -class VoidType(BaseType): - _attrs_ = () - - def __init__(self): - self.c_name_with_marker = 'void&' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_void_type') - -void_type = VoidType() - - -class BasePrimitiveType(BaseType): - def is_complex_type(self): - return False - - -class PrimitiveType(BasePrimitiveType): - _attrs_ = ('name',) - - ALL_PRIMITIVE_TYPES = { - 'char': 'c', - 'short': 'i', - 'int': 'i', - 'long': 'i', - 'long long': 'i', - 'signed char': 'i', - 'unsigned char': 'i', - 'unsigned short': 'i', - 'unsigned int': 'i', - 'unsigned long': 'i', - 'unsigned long long': 'i', - 'float': 'f', - 'double': 'f', - 'long double': 'f', - '_cffi_float_complex_t': 'j', - '_cffi_double_complex_t': 'j', - '_Bool': 'i', - # the following types are not primitive in the C sense - 'wchar_t': 'c', - 'char16_t': 'c', - 'char32_t': 'c', - 'int8_t': 'i', - 'uint8_t': 'i', - 'int16_t': 'i', - 'uint16_t': 'i', - 'int32_t': 'i', - 'uint32_t': 'i', - 'int64_t': 'i', - 'uint64_t': 'i', - 'int_least8_t': 'i', - 'uint_least8_t': 'i', - 'int_least16_t': 'i', - 'uint_least16_t': 'i', - 'int_least32_t': 'i', - 'uint_least32_t': 'i', - 'int_least64_t': 'i', - 'uint_least64_t': 'i', - 'int_fast8_t': 'i', - 'uint_fast8_t': 'i', - 'int_fast16_t': 'i', - 'uint_fast16_t': 'i', - 'int_fast32_t': 'i', - 'uint_fast32_t': 'i', - 'int_fast64_t': 'i', - 'uint_fast64_t': 'i', - 'intptr_t': 'i', - 'uintptr_t': 'i', - 'intmax_t': 'i', - 'uintmax_t': 'i', - 'ptrdiff_t': 'i', - 'size_t': 'i', - 'ssize_t': 'i', - } - - def __init__(self, name): - assert name in self.ALL_PRIMITIVE_TYPES - self.name = name - self.c_name_with_marker = name + '&' - - def is_char_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' - def is_integer_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' - def is_float_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' - def is_complex_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_primitive_type', self.name) - - -class UnknownIntegerType(BasePrimitiveType): - _attrs_ = ('name',) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def is_integer_type(self): - return True - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("integer type '%s' can only be used after " - "compilation" % self.name) - -class UnknownFloatType(BasePrimitiveType): - _attrs_ = ('name', ) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("float type '%s' can only be used after " - "compilation" % self.name) - - -class BaseFunctionType(BaseType): - _attrs_ = ('args', 'result', 'ellipsis', 'abi') - - def __init__(self, args, result, ellipsis, abi=None): - self.args = args - self.result = result - self.ellipsis = ellipsis - self.abi = abi - # - reprargs = [arg._get_c_name() for arg in self.args] - if self.ellipsis: - reprargs.append('...') - reprargs = reprargs or ['void'] - replace_with = self._base_pattern % (', '.join(reprargs),) - if abi is not None: - replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] - self.c_name_with_marker = ( - self.result.c_name_with_marker.replace('&', replace_with)) - - -class RawFunctionType(BaseFunctionType): - # Corresponds to a C type like 'int(int)', which is the C type of - # a function, but not a pointer-to-function. The backend has no - # notion of such a type; it's used temporarily by parsing. - _base_pattern = '(&)(%s)' - is_raw_function = True - - def build_backend_type(self, ffi, finishlist): - raise CDefError("cannot render the type %r: it is a function " - "type, not a pointer-to-function type" % (self,)) - - def as_function_pointer(self): - return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) - - -class FunctionPtrType(BaseFunctionType): - _base_pattern = '(*&)(%s)' - - def build_backend_type(self, ffi, finishlist): - result = self.result.get_cached_btype(ffi, finishlist) - args = [] - for tp in self.args: - args.append(tp.get_cached_btype(ffi, finishlist)) - abi_args = () - if self.abi == "__stdcall": - if not self.ellipsis: # __stdcall ignored for variadic funcs - try: - abi_args = (ffi._backend.FFI_STDCALL,) - except AttributeError: - pass - return global_cache(self, ffi, 'new_function_type', - tuple(args), result, self.ellipsis, *abi_args) - - def as_raw_function(self): - return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) - - -class PointerType(BaseType): - _attrs_ = ('totype', 'quals') - - def __init__(self, totype, quals=0): - self.totype = totype - self.quals = quals - extra = " *&" - if totype.is_array_type: - extra = "(%s)" % (extra.lstrip(),) - extra = qualify(quals, extra) - self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) - - def build_backend_type(self, ffi, finishlist): - BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) - return global_cache(self, ffi, 'new_pointer_type', BItem) - -voidp_type = PointerType(void_type) - -def ConstPointerType(totype): - return PointerType(totype, Q_CONST) - -const_voidp_type = ConstPointerType(void_type) - - -class NamedPointerType(PointerType): - _attrs_ = ('totype', 'name') - - def __init__(self, totype, name, quals=0): - PointerType.__init__(self, totype, quals) - self.name = name - self.c_name_with_marker = name + '&' - - -class ArrayType(BaseType): - _attrs_ = ('item', 'length') - is_array_type = True - - def __init__(self, item, length): - self.item = item - self.length = length - # - if length is None: - brackets = '&[]' - elif length == '...': - brackets = '&[/*...*/]' - else: - brackets = '&[%s]' % length - self.c_name_with_marker = ( - self.item.c_name_with_marker.replace('&', brackets)) - - def length_is_unknown(self): - return isinstance(self.length, str) - - def resolve_length(self, newlength): - return ArrayType(self.item, newlength) - - def build_backend_type(self, ffi, finishlist): - if self.length_is_unknown(): - raise CDefError("cannot render the type %r: unknown length" % - (self,)) - self.item.get_cached_btype(ffi, finishlist) # force the item BType - BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) - return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) - -char_array_type = ArrayType(PrimitiveType('char'), None) - - -class StructOrUnionOrEnum(BaseTypeByIdentity): - _attrs_ = ('name',) - forcename = None - - def build_c_name_with_marker(self): - name = self.forcename or '%s %s' % (self.kind, self.name) - self.c_name_with_marker = name + '&' - - def force_the_name(self, forcename): - self.forcename = forcename - self.build_c_name_with_marker() - - def get_official_name(self): - assert self.c_name_with_marker.endswith('&') - return self.c_name_with_marker[:-1] - - -class StructOrUnion(StructOrUnionOrEnum): - fixedlayout = None - completed = 0 - partial = False - packed = 0 - - def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): - self.name = name - self.fldnames = fldnames - self.fldtypes = fldtypes - self.fldbitsize = fldbitsize - self.fldquals = fldquals - self.build_c_name_with_marker() - - def anonymous_struct_fields(self): - if self.fldtypes is not None: - for name, type in zip(self.fldnames, self.fldtypes): - if name == '' and isinstance(type, StructOrUnion): - yield type - - def enumfields(self, expand_anonymous_struct_union=True): - fldquals = self.fldquals - if fldquals is None: - fldquals = (0,) * len(self.fldnames) - for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, - self.fldbitsize, fldquals): - if (name == '' and isinstance(type, StructOrUnion) - and expand_anonymous_struct_union): - # nested anonymous struct/union - for result in type.enumfields(): - yield result - else: - yield (name, type, bitsize, quals) - - def force_flatten(self): - # force the struct or union to have a declaration that lists - # directly all fields returned by enumfields(), flattening - # nested anonymous structs/unions. - names = [] - types = [] - bitsizes = [] - fldquals = [] - for name, type, bitsize, quals in self.enumfields(): - names.append(name) - types.append(type) - bitsizes.append(bitsize) - fldquals.append(quals) - self.fldnames = tuple(names) - self.fldtypes = tuple(types) - self.fldbitsize = tuple(bitsizes) - self.fldquals = tuple(fldquals) - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, - can_delay) - if not can_delay: - self.finish_backend_type(ffi, finishlist) - return BType - - def finish_backend_type(self, ffi, finishlist): - if self.completed: - if self.completed != 2: - raise NotImplementedError("recursive structure declaration " - "for '%s'" % (self.name,)) - return - BType = ffi._cached_btypes[self] - # - self.completed = 1 - # - if self.fldtypes is None: - pass # not completing it: it's an opaque struct - # - elif self.fixedlayout is None: - fldtypes = [tp.get_cached_btype(ffi, finishlist) - for tp in self.fldtypes] - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - extra_flags = () - if self.packed: - if self.packed == 1: - extra_flags = (8,) # SF_PACKED - else: - extra_flags = (0, self.packed) - ffi._backend.complete_struct_or_union(BType, lst, self, - -1, -1, *extra_flags) - # - else: - fldtypes = [] - fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout - for i in range(len(self.fldnames)): - fsize = fieldsize[i] - ftype = self.fldtypes[i] - # - if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): - # fix the length to match the total size - BItemType = ftype.item.get_cached_btype(ffi, finishlist) - nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) - if nrest != 0: - self._verification_error( - "field '%s.%s' has a bogus size?" % ( - self.name, self.fldnames[i] or '{}')) - ftype = ftype.resolve_length(nlen) - self.fldtypes = (self.fldtypes[:i] + (ftype,) + - self.fldtypes[i+1:]) - # - BFieldType = ftype.get_cached_btype(ffi, finishlist) - if isinstance(ftype, ArrayType) and ftype.length is None: - assert fsize == 0 - else: - bitemsize = ffi.sizeof(BFieldType) - if bitemsize != fsize: - self._verification_error( - "field '%s.%s' is declared as %d bytes, but is " - "really %d bytes" % (self.name, - self.fldnames[i] or '{}', - bitemsize, fsize)) - fldtypes.append(BFieldType) - # - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) - ffi._backend.complete_struct_or_union(BType, lst, self, - totalsize, totalalignment) - self.completed = 2 - - def _verification_error(self, msg): - raise VerificationError(msg) - - def check_not_partial(self): - if self.partial and self.fixedlayout is None: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - finishlist.append(self) - # - return global_cache(self, ffi, 'new_%s_type' % self.kind, - self.get_official_name(), key=self) - - -class StructType(StructOrUnion): - kind = 'struct' - - -class UnionType(StructOrUnion): - kind = 'union' - - -class EnumType(StructOrUnionOrEnum): - kind = 'enum' - partial = False - partial_resolved = False - - def __init__(self, name, enumerators, enumvalues, baseinttype=None): - self.name = name - self.enumerators = enumerators - self.enumvalues = enumvalues - self.baseinttype = baseinttype - self.build_c_name_with_marker() - - def force_the_name(self, forcename): - StructOrUnionOrEnum.force_the_name(self, forcename) - if self.forcename is None: - name = self.get_official_name() - self.forcename = '$' + name.replace(' ', '_') - - def check_not_partial(self): - if self.partial and not self.partial_resolved: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - base_btype = self.build_baseinttype(ffi, finishlist) - return global_cache(self, ffi, 'new_enum_type', - self.get_official_name(), - self.enumerators, self.enumvalues, - base_btype, key=self) - - def build_baseinttype(self, ffi, finishlist): - if self.baseinttype is not None: - return self.baseinttype.get_cached_btype(ffi, finishlist) - # - if self.enumvalues: - smallest_value = min(self.enumvalues) - largest_value = max(self.enumvalues) - else: - import warnings - try: - # XXX! The goal is to ensure that the warnings.warn() - # will not suppress the warning. We want to get it - # several times if we reach this point several times. - __warningregistry__.clear() - except NameError: - pass - warnings.warn("%r has no values explicitly defined; " - "guessing that it is equivalent to 'unsigned int'" - % self._get_c_name()) - smallest_value = largest_value = 0 - if smallest_value < 0: # needs a signed type - sign = 1 - candidate1 = PrimitiveType("int") - candidate2 = PrimitiveType("long") - else: - sign = 0 - candidate1 = PrimitiveType("unsigned int") - candidate2 = PrimitiveType("unsigned long") - btype1 = candidate1.get_cached_btype(ffi, finishlist) - btype2 = candidate2.get_cached_btype(ffi, finishlist) - size1 = ffi.sizeof(btype1) - size2 = ffi.sizeof(btype2) - if (smallest_value >= ((-1) << (8*size1-1)) and - largest_value < (1 << (8*size1-sign))): - return btype1 - if (smallest_value >= ((-1) << (8*size2-1)) and - largest_value < (1 << (8*size2-sign))): - return btype2 - raise CDefError("%s values don't all fit into either 'long' " - "or 'unsigned long'" % self._get_c_name()) - -def unknown_type(name, structname=None): - if structname is None: - structname = '$%s' % name - tp = StructType(structname, None, None, None) - tp.force_the_name(name) - tp.origin = "unknown_type" - return tp - -def unknown_ptr_type(name, structname=None): - if structname is None: - structname = '$$%s' % name - tp = StructType(structname, None, None, None) - return NamedPointerType(tp, name) - - -global_lock = allocate_lock() -_typecache_cffi_backend = weakref.WeakValueDictionary() - -def get_typecache(backend): - # returns _typecache_cffi_backend if backend is the _cffi_backend - # module, or type(backend).__typecache if backend is an instance of - # CTypesBackend (or some FakeBackend class during tests) - if isinstance(backend, types.ModuleType): - return _typecache_cffi_backend - with global_lock: - if not hasattr(type(backend), '__typecache'): - type(backend).__typecache = weakref.WeakValueDictionary() - return type(backend).__typecache - -def global_cache(srctype, ffi, funcname, *args, **kwds): - key = kwds.pop('key', (funcname, args)) - assert not kwds - try: - return ffi._typecache[key] - except KeyError: - pass - try: - res = getattr(ffi._backend, funcname)(*args) - except NotImplementedError as e: - raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) - # note that setdefault() on WeakValueDictionary is not atomic - # and contains a rare bug (http://bugs.python.org/issue19542); - # we have to use a lock and do it ourselves - cache = ffi._typecache - with global_lock: - res1 = cache.get(key) - if res1 is None: - cache[key] = res - return res - else: - return res1 - -def pointer_cache(ffi, BType): - return global_cache('?', ffi, 'new_pointer_type', BType) - -def attach_exception_info(e, name): - if e.args and type(e.args[0]) is str: - e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/.venv/Lib/site-packages/cffi/parse_c_type.h b/.venv/Lib/site-packages/cffi/parse_c_type.h deleted file mode 100644 index 84e4ef8..0000000 --- a/.venv/Lib/site-packages/cffi/parse_c_type.h +++ /dev/null @@ -1,181 +0,0 @@ - -/* This part is from file 'cffi/parse_c_type.h'. It is copied at the - beginning of C sources generated by CFFI's ffi.set_source(). */ - -typedef void *_cffi_opcode_t; - -#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) -#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) - -#define _CFFI_OP_PRIMITIVE 1 -#define _CFFI_OP_POINTER 3 -#define _CFFI_OP_ARRAY 5 -#define _CFFI_OP_OPEN_ARRAY 7 -#define _CFFI_OP_STRUCT_UNION 9 -#define _CFFI_OP_ENUM 11 -#define _CFFI_OP_FUNCTION 13 -#define _CFFI_OP_FUNCTION_END 15 -#define _CFFI_OP_NOOP 17 -#define _CFFI_OP_BITFIELD 19 -#define _CFFI_OP_TYPENAME 21 -#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs -#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs -#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) -#define _CFFI_OP_CONSTANT 29 -#define _CFFI_OP_CONSTANT_INT 31 -#define _CFFI_OP_GLOBAL_VAR 33 -#define _CFFI_OP_DLOPEN_FUNC 35 -#define _CFFI_OP_DLOPEN_CONST 37 -#define _CFFI_OP_GLOBAL_VAR_F 39 -#define _CFFI_OP_EXTERN_PYTHON 41 - -#define _CFFI_PRIM_VOID 0 -#define _CFFI_PRIM_BOOL 1 -#define _CFFI_PRIM_CHAR 2 -#define _CFFI_PRIM_SCHAR 3 -#define _CFFI_PRIM_UCHAR 4 -#define _CFFI_PRIM_SHORT 5 -#define _CFFI_PRIM_USHORT 6 -#define _CFFI_PRIM_INT 7 -#define _CFFI_PRIM_UINT 8 -#define _CFFI_PRIM_LONG 9 -#define _CFFI_PRIM_ULONG 10 -#define _CFFI_PRIM_LONGLONG 11 -#define _CFFI_PRIM_ULONGLONG 12 -#define _CFFI_PRIM_FLOAT 13 -#define _CFFI_PRIM_DOUBLE 14 -#define _CFFI_PRIM_LONGDOUBLE 15 - -#define _CFFI_PRIM_WCHAR 16 -#define _CFFI_PRIM_INT8 17 -#define _CFFI_PRIM_UINT8 18 -#define _CFFI_PRIM_INT16 19 -#define _CFFI_PRIM_UINT16 20 -#define _CFFI_PRIM_INT32 21 -#define _CFFI_PRIM_UINT32 22 -#define _CFFI_PRIM_INT64 23 -#define _CFFI_PRIM_UINT64 24 -#define _CFFI_PRIM_INTPTR 25 -#define _CFFI_PRIM_UINTPTR 26 -#define _CFFI_PRIM_PTRDIFF 27 -#define _CFFI_PRIM_SIZE 28 -#define _CFFI_PRIM_SSIZE 29 -#define _CFFI_PRIM_INT_LEAST8 30 -#define _CFFI_PRIM_UINT_LEAST8 31 -#define _CFFI_PRIM_INT_LEAST16 32 -#define _CFFI_PRIM_UINT_LEAST16 33 -#define _CFFI_PRIM_INT_LEAST32 34 -#define _CFFI_PRIM_UINT_LEAST32 35 -#define _CFFI_PRIM_INT_LEAST64 36 -#define _CFFI_PRIM_UINT_LEAST64 37 -#define _CFFI_PRIM_INT_FAST8 38 -#define _CFFI_PRIM_UINT_FAST8 39 -#define _CFFI_PRIM_INT_FAST16 40 -#define _CFFI_PRIM_UINT_FAST16 41 -#define _CFFI_PRIM_INT_FAST32 42 -#define _CFFI_PRIM_UINT_FAST32 43 -#define _CFFI_PRIM_INT_FAST64 44 -#define _CFFI_PRIM_UINT_FAST64 45 -#define _CFFI_PRIM_INTMAX 46 -#define _CFFI_PRIM_UINTMAX 47 -#define _CFFI_PRIM_FLOATCOMPLEX 48 -#define _CFFI_PRIM_DOUBLECOMPLEX 49 -#define _CFFI_PRIM_CHAR16 50 -#define _CFFI_PRIM_CHAR32 51 - -#define _CFFI__NUM_PRIM 52 -#define _CFFI__UNKNOWN_PRIM (-1) -#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) -#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) - -#define _CFFI__IO_FILE_STRUCT (-1) - - -struct _cffi_global_s { - const char *name; - void *address; - _cffi_opcode_t type_op; - void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown - // OP_CPYTHON_BLTN_*: addr of direct function -}; - -struct _cffi_getconst_s { - unsigned long long value; - const struct _cffi_type_context_s *ctx; - int gindex; -}; - -struct _cffi_struct_union_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_STRUCT_UNION - int flags; // _CFFI_F_* flags below - size_t size; - int alignment; - int first_field_index; // -> _cffi_fields array - int num_fields; -}; -#define _CFFI_F_UNION 0x01 // is a union, not a struct -#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the - // "standard layout" or if some are missing -#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct -#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() -#define _CFFI_F_OPAQUE 0x10 // opaque - -struct _cffi_field_s { - const char *name; - size_t field_offset; - size_t field_size; - _cffi_opcode_t field_type_op; -}; - -struct _cffi_enum_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_ENUM - int type_prim; // _CFFI_PRIM_xxx - const char *enumerators; // comma-delimited string -}; - -struct _cffi_typename_s { - const char *name; - int type_index; /* if opaque, points to a possibly artificial - OP_STRUCT which is itself opaque */ -}; - -struct _cffi_type_context_s { - _cffi_opcode_t *types; - const struct _cffi_global_s *globals; - const struct _cffi_field_s *fields; - const struct _cffi_struct_union_s *struct_unions; - const struct _cffi_enum_s *enums; - const struct _cffi_typename_s *typenames; - int num_globals; - int num_struct_unions; - int num_enums; - int num_typenames; - const char *const *includes; - int num_types; - int flags; /* future extension */ -}; - -struct _cffi_parse_info_s { - const struct _cffi_type_context_s *ctx; - _cffi_opcode_t *output; - unsigned int output_size; - size_t error_location; - const char *error_message; -}; - -struct _cffi_externpy_s { - const char *name; - size_t size_of_result; - void *reserved1, *reserved2; -}; - -#ifdef _CFFI_INTERNAL -static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); -static int search_in_globals(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -#endif diff --git a/.venv/Lib/site-packages/cffi/pkgconfig.py b/.venv/Lib/site-packages/cffi/pkgconfig.py deleted file mode 100644 index 5c93f15..0000000 --- a/.venv/Lib/site-packages/cffi/pkgconfig.py +++ /dev/null @@ -1,121 +0,0 @@ -# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi -import sys, os, subprocess - -from .error import PkgConfigError - - -def merge_flags(cfg1, cfg2): - """Merge values from cffi config flags cfg2 to cf1 - - Example: - merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) - {"libraries": ["one", "two"]} - """ - for key, value in cfg2.items(): - if key not in cfg1: - cfg1[key] = value - else: - if not isinstance(cfg1[key], list): - raise TypeError("cfg1[%r] should be a list of strings" % (key,)) - if not isinstance(value, list): - raise TypeError("cfg2[%r] should be a list of strings" % (key,)) - cfg1[key].extend(value) - return cfg1 - - -def call(libname, flag, encoding=sys.getfilesystemencoding()): - """Calls pkg-config and returns the output if found - """ - a = ["pkg-config", "--print-errors"] - a.append(flag) - a.append(libname) - try: - pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except EnvironmentError as e: - raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) - - bout, berr = pc.communicate() - if pc.returncode != 0: - try: - berr = berr.decode(encoding) - except Exception: - pass - raise PkgConfigError(berr.strip()) - - if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x - try: - bout = bout.decode(encoding) - except UnicodeDecodeError: - raise PkgConfigError("pkg-config %s %s returned bytes that cannot " - "be decoded with encoding %r:\n%r" % - (flag, libname, encoding, bout)) - - if os.altsep != '\\' and '\\' in bout: - raise PkgConfigError("pkg-config %s %s returned an unsupported " - "backslash-escaped output:\n%r" % - (flag, libname, bout)) - return bout - - -def flags_from_pkgconfig(libs): - r"""Return compiler line flags for FFI.set_source based on pkg-config output - - Usage - ... - ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) - - If pkg-config is installed on build machine, then arguments include_dirs, - library_dirs, libraries, define_macros, extra_compile_args and - extra_link_args are extended with an output of pkg-config for libfoo and - libbar. - - Raises PkgConfigError in case the pkg-config call fails. - """ - - def get_include_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-I")] - - def get_library_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-L")] - - def get_libraries(string): - return [x[2:] for x in string.split() if x.startswith("-l")] - - # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils - def get_macros(string): - def _macro(x): - x = x[2:] # drop "-D" - if '=' in x: - return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") - else: - return (x, None) # "-Dfoo" => ("foo", None) - return [_macro(x) for x in string.split() if x.startswith("-D")] - - def get_other_cflags(string): - return [x for x in string.split() if not x.startswith("-I") and - not x.startswith("-D")] - - def get_other_libs(string): - return [x for x in string.split() if not x.startswith("-L") and - not x.startswith("-l")] - - # return kwargs for given libname - def kwargs(libname): - fse = sys.getfilesystemencoding() - all_cflags = call(libname, "--cflags") - all_libs = call(libname, "--libs") - return { - "include_dirs": get_include_dirs(all_cflags), - "library_dirs": get_library_dirs(all_libs), - "libraries": get_libraries(all_libs), - "define_macros": get_macros(all_cflags), - "extra_compile_args": get_other_cflags(all_cflags), - "extra_link_args": get_other_libs(all_libs), - } - - # merge all arguments together - ret = {} - for libname in libs: - lib_flags = kwargs(libname) - merge_flags(ret, lib_flags) - return ret diff --git a/.venv/Lib/site-packages/cffi/recompiler.py b/.venv/Lib/site-packages/cffi/recompiler.py deleted file mode 100644 index 57781a3..0000000 --- a/.venv/Lib/site-packages/cffi/recompiler.py +++ /dev/null @@ -1,1598 +0,0 @@ -import os, sys, io -from . import ffiplatform, model -from .error import VerificationError -from .cffi_opcode import * - -VERSION_BASE = 0x2601 -VERSION_EMBEDDED = 0x2701 -VERSION_CHAR16CHAR32 = 0x2801 - -USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or - sys.version_info >= (3, 5)) - - -class GlobalExpr: - def __init__(self, name, address, type_op, size=0, check_value=0): - self.name = name - self.address = address - self.type_op = type_op - self.size = size - self.check_value = check_value - - def as_c_expr(self): - return ' { "%s", (void *)%s, %s, (void *)%s },' % ( - self.name, self.address, self.type_op.as_c_expr(), self.size) - - def as_python_expr(self): - return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, - self.check_value) - -class FieldExpr: - def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): - self.name = name - self.field_offset = field_offset - self.field_size = field_size - self.fbitsize = fbitsize - self.field_type_op = field_type_op - - def as_c_expr(self): - spaces = " " * len(self.name) - return (' { "%s", %s,\n' % (self.name, self.field_offset) + - ' %s %s,\n' % (spaces, self.field_size) + - ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) - - def as_python_expr(self): - raise NotImplementedError - - def as_field_python_expr(self): - if self.field_type_op.op == OP_NOOP: - size_expr = '' - elif self.field_type_op.op == OP_BITFIELD: - size_expr = format_four_bytes(self.fbitsize) - else: - raise NotImplementedError - return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), - size_expr, - self.name) - -class StructUnionExpr: - def __init__(self, name, type_index, flags, size, alignment, comment, - first_field_index, c_fields): - self.name = name - self.type_index = type_index - self.flags = flags - self.size = size - self.alignment = alignment - self.comment = comment - self.first_field_index = first_field_index - self.c_fields = c_fields - - def as_c_expr(self): - return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) - + '\n %s, %s, ' % (self.size, self.alignment) - + '%d, %d ' % (self.first_field_index, len(self.c_fields)) - + ('/* %s */ ' % self.comment if self.comment else '') - + '},') - - def as_python_expr(self): - flags = eval(self.flags, G_FLAGS) - fields_expr = [c_field.as_field_python_expr() - for c_field in self.c_fields] - return "(b'%s%s%s',%s)" % ( - format_four_bytes(self.type_index), - format_four_bytes(flags), - self.name, - ','.join(fields_expr)) - -class EnumExpr: - def __init__(self, name, type_index, size, signed, allenums): - self.name = name - self.type_index = type_index - self.size = size - self.signed = signed - self.allenums = allenums - - def as_c_expr(self): - return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' - ' "%s" },' % (self.name, self.type_index, - self.size, self.signed, self.allenums)) - - def as_python_expr(self): - prim_index = { - (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, - (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, - (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, - (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, - }[self.size, self.signed] - return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), - format_four_bytes(prim_index), - self.name, self.allenums) - -class TypenameExpr: - def __init__(self, name, type_index): - self.name = name - self.type_index = type_index - - def as_c_expr(self): - return ' { "%s", %d },' % (self.name, self.type_index) - - def as_python_expr(self): - return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) - - -# ____________________________________________________________ - - -class Recompiler: - _num_externpy = 0 - - def __init__(self, ffi, module_name, target_is_python=False): - self.ffi = ffi - self.module_name = module_name - self.target_is_python = target_is_python - self._version = VERSION_BASE - - def needs_version(self, ver): - self._version = max(self._version, ver) - - def collect_type_table(self): - self._typesdict = {} - self._generate("collecttype") - # - all_decls = sorted(self._typesdict, key=str) - # - # prepare all FUNCTION bytecode sequences first - self.cffi_types = [] - for tp in all_decls: - if tp.is_raw_function: - assert self._typesdict[tp] is None - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - for tp1 in tp.args: - assert isinstance(tp1, (model.VoidType, - model.BasePrimitiveType, - model.PointerType, - model.StructOrUnionOrEnum, - model.FunctionPtrType)) - if self._typesdict[tp1] is None: - self._typesdict[tp1] = len(self.cffi_types) - self.cffi_types.append(tp1) # placeholder - self.cffi_types.append('END') # placeholder - # - # prepare all OTHER bytecode sequences - for tp in all_decls: - if not tp.is_raw_function and self._typesdict[tp] is None: - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - if tp.is_array_type and tp.length is not None: - self.cffi_types.append('LEN') # placeholder - assert None not in self._typesdict.values() - # - # collect all structs and unions and enums - self._struct_unions = {} - self._enums = {} - for tp in all_decls: - if isinstance(tp, model.StructOrUnion): - self._struct_unions[tp] = None - elif isinstance(tp, model.EnumType): - self._enums[tp] = None - for i, tp in enumerate(sorted(self._struct_unions, - key=lambda tp: tp.name)): - self._struct_unions[tp] = i - for i, tp in enumerate(sorted(self._enums, - key=lambda tp: tp.name)): - self._enums[tp] = i - # - # emit all bytecode sequences now - for tp in all_decls: - method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) - method(tp, self._typesdict[tp]) - # - # consistency check - for op in self.cffi_types: - assert isinstance(op, CffiOp) - self.cffi_types = tuple(self.cffi_types) # don't change any more - - def _enum_fields(self, tp): - # When producing C, expand all anonymous struct/union fields. - # That's necessary to have C code checking the offsets of the - # individual fields contained in them. When producing Python, - # don't do it and instead write it like it is, with the - # corresponding fields having an empty name. Empty names are - # recognized at runtime when we import the generated Python - # file. - expand_anonymous_struct_union = not self.target_is_python - return tp.enumfields(expand_anonymous_struct_union) - - def _do_collect_type(self, tp): - if not isinstance(tp, model.BaseTypeByIdentity): - if isinstance(tp, tuple): - for x in tp: - self._do_collect_type(x) - return - if tp not in self._typesdict: - self._typesdict[tp] = None - if isinstance(tp, model.FunctionPtrType): - self._do_collect_type(tp.as_raw_function()) - elif isinstance(tp, model.StructOrUnion): - if tp.fldtypes is not None and ( - tp not in self.ffi._parser._included_declarations): - for name1, tp1, _, _ in self._enum_fields(tp): - self._do_collect_type(self._field_type(tp, name1, tp1)) - else: - for _, x in tp._get_items(): - self._do_collect_type(x) - - def _generate(self, step_name): - lst = self.ffi._parser._declarations.items() - for name, (tp, quals) in sorted(lst): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in recompile(): %r" % name) - try: - self._current_quals = quals - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - # ---------- - - ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] - - def collect_step_tables(self): - # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. - self._lsts = {} - for step_name in self.ALL_STEPS: - self._lsts[step_name] = [] - self._seen_struct_unions = set() - self._generate("ctx") - self._add_missing_struct_unions() - # - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if step_name != "field": - lst.sort(key=lambda entry: entry.name) - self._lsts[step_name] = tuple(lst) # don't change any more - # - # check for a possible internal inconsistency: _cffi_struct_unions - # should have been generated with exactly self._struct_unions - lst = self._lsts["struct_union"] - for tp, i in self._struct_unions.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._struct_unions) - # same with enums - lst = self._lsts["enum"] - for tp, i in self._enums.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._enums) - - # ---------- - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self, f, preamble): - if self.target_is_python: - assert preamble is None - self.write_py_source_to_f(f) - else: - assert preamble is not None - self.write_c_source_to_f(f, preamble) - - def _rel_readlines(self, filename): - g = open(os.path.join(os.path.dirname(__file__), filename), 'r') - lines = g.readlines() - g.close() - return lines - - def write_c_source_to_f(self, f, preamble): - self._f = f - prnt = self._prnt - if self.ffi._embedding is not None: - prnt('#define _CFFI_USE_EMBEDDING') - if not USE_LIMITED_API: - prnt('#define _CFFI_NO_LIMITED_API') - # - # first the '#include' (actually done by inlining the file's content) - lines = self._rel_readlines('_cffi_include.h') - i = lines.index('#include "parse_c_type.h"\n') - lines[i:i+1] = self._rel_readlines('parse_c_type.h') - prnt(''.join(lines)) - # - # if we have ffi._embedding != None, we give it here as a macro - # and include an extra file - base_module_name = self.module_name.split('.')[-1] - if self.ffi._embedding is not None: - prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) - prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') - self._print_string_literal_in_array(self.ffi._embedding) - prnt('0 };') - prnt('#ifdef PYPY_VERSION') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( - base_module_name,)) - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( - base_module_name,)) - prnt('#else') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( - base_module_name,)) - prnt('#endif') - lines = self._rel_readlines('_embedding.h') - i = lines.index('#include "_cffi_errors.h"\n') - lines[i:i+1] = self._rel_readlines('_cffi_errors.h') - prnt(''.join(lines)) - self.needs_version(VERSION_EMBEDDED) - # - # then paste the C source given by the user, verbatim. - prnt('/************************************************************/') - prnt() - prnt(preamble) - prnt() - prnt('/************************************************************/') - prnt() - # - # the declaration of '_cffi_types' - prnt('static void *_cffi_types[] = {') - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - for i, op in enumerate(self.cffi_types): - comment = '' - if i in typeindex2type: - comment = ' // ' + typeindex2type[i]._get_c_name() - prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) - if not self.cffi_types: - prnt(' 0') - prnt('};') - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._seen_constants = set() - self._generate("decl") - # - # the declaration of '_cffi_globals' and '_cffi_typenames' - nums = {} - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - nums[step_name] = len(lst) - if nums[step_name] > 0: - prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( - step_name, step_name)) - for entry in lst: - prnt(entry.as_c_expr()) - prnt('};') - prnt() - # - # the declaration of '_cffi_includes' - if self.ffi._included_ffis: - prnt('static const char * const _cffi_includes[] = {') - for ffi_to_include in self.ffi._included_ffis: - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is None: - raise VerificationError( - "not implemented yet: ffi.include() of a Python-based " - "ffi inside a C-based ffi") - prnt(' "%s",' % (included_module_name,)) - prnt(' NULL') - prnt('};') - prnt() - # - # the declaration of '_cffi_type_context' - prnt('static const struct _cffi_type_context_s _cffi_type_context = {') - prnt(' _cffi_types,') - for step_name in self.ALL_STEPS: - if nums[step_name] > 0: - prnt(' _cffi_%ss,' % step_name) - else: - prnt(' NULL, /* no %ss */' % step_name) - for step_name in self.ALL_STEPS: - if step_name != "field": - prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) - if self.ffi._included_ffis: - prnt(' _cffi_includes,') - else: - prnt(' NULL, /* no includes */') - prnt(' %d, /* num_types */' % (len(self.cffi_types),)) - flags = 0 - if self._num_externpy > 0 or self.ffi._embedding is not None: - flags |= 1 # set to mean that we use extern "Python" - prnt(' %d, /* flags */' % flags) - prnt('};') - prnt() - # - # the init function - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') - prnt('#endif') - prnt() - prnt('#ifdef PYPY_VERSION') - prnt('PyMODINIT_FUNC') - prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) - prnt('{') - if flags & 1: - prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') - prnt(' _cffi_call_python_org = ' - '(void(*)(struct _cffi_externpy_s *, char *))p[1];') - prnt(' }') - prnt(' p[0] = (const void *)0x%x;' % self._version) - prnt(' p[1] = &_cffi_type_context;') - prnt('#if PY_MAJOR_VERSION >= 3') - prnt(' return NULL;') - prnt('#endif') - prnt('}') - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - prnt('# ifdef _MSC_VER') - prnt(' PyMODINIT_FUNC') - prnt('# if PY_MAJOR_VERSION >= 3') - prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) - prnt('# else') - prnt(' init%s(void) { }' % (base_module_name,)) - prnt('# endif') - prnt('# endif') - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % (base_module_name,)) - prnt('{') - prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#else') - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % (base_module_name,)) - prnt('{') - prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#endif') - prnt() - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility pop') - prnt('#endif') - self._version = None - - def _to_py(self, x): - if isinstance(x, str): - return "b'%s'" % (x,) - if isinstance(x, (list, tuple)): - rep = [self._to_py(item) for item in x] - if len(rep) == 1: - rep.append('') - return "(%s)" % (','.join(rep),) - return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. - - def write_py_source_to_f(self, f): - self._f = f - prnt = self._prnt - # - # header - prnt("# auto-generated file") - prnt("import _cffi_backend") - # - # the 'import' of the included ffis - num_includes = len(self.ffi._included_ffis or ()) - for i in range(num_includes): - ffi_to_include = self.ffi._included_ffis[i] - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is not None: - raise VerificationError( - "not implemented yet: ffi.include() of a C-based " - "ffi inside a Python-based ffi") - prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) - prnt() - prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) - prnt(" _version = 0x%x," % (self._version,)) - self._version = None - # - # the '_types' keyword argument - self.cffi_types = tuple(self.cffi_types) # don't change any more - types_lst = [op.as_python_bytes() for op in self.cffi_types] - prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - # - # the keyword arguments from ALL_STEPS - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if len(lst) > 0 and step_name != "field": - prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) - # - # the '_includes' keyword argument - if num_includes > 0: - prnt(' _includes = (%s,),' % ( - ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) - # - # the footer - prnt(')') - - # ---------- - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif isinstance(tp, model.UnknownFloatType): - # don't check with is_float_type(): it may be a 'long - # double' here, and _cffi_to_c_double would loose precision - converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) - else: - cname = tp.get_c_name('') - converter = '(%s)_cffi_to_c_%s' % (cname, - tp.name.replace(' ', '_')) - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif (isinstance(tp, model.StructOrUnionOrEnum) or - isinstance(tp, model.BasePrimitiveType)): - # a struct (not a struct pointer) as a function argument; - # or, a complex (the same code works) - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - '(%s)alloca((size_t)datasize) : NULL;' % ( - tovar, tp.get_c_name(''))) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.BasePrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif isinstance(tp, model.UnknownFloatType): - return '_cffi_from_c_double(%s)' % (var,) - elif tp.name != 'long double' and not tp.is_complex_type(): - cname = tp.name.replace(' ', '_') - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - return '_cffi_from_c_%s(%s)' % (cname, var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs - - def _typedef_type(self, tp, name): - return self._global_type(tp, "(*(%s *)0)" % (name,)) - - def _generate_cpy_typedef_collecttype(self, tp, name): - self._do_collect_type(self._typedef_type(tp, name)) - - def _generate_cpy_typedef_decl(self, tp, name): - pass - - def _typedef_ctx(self, tp, name): - type_index = self._typesdict[tp] - self._lsts["typename"].append(TypenameExpr(name, type_index)) - - def _generate_cpy_typedef_ctx(self, tp, name): - tp = self._typedef_type(tp, name) - self._typedef_ctx(tp, name) - if getattr(tp, "origin", None) == "unknown_type": - self._struct_ctx(tp, tp.name, approxname=None) - elif isinstance(tp, model.NamedPointerType): - self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, - named_ptr=tp) - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - self._do_collect_type(tp.as_raw_function()) - if tp.ellipsis and not self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_function_decl(self, tp, name): - assert not self.target_is_python - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_constant_decl(tp, name) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - # - # ------------------------------ - # the 'd' version of the function, only for addressof(lib, 'func') - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arguments.append(type.get_c_name(' x%d' % i, context)) - call_arguments.append('x%d' % i) - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) - prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) - prnt('{') - call_arguments = ', '.join(call_arguments) - result_code = 'return ' - if isinstance(tp.result, model.VoidType): - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, call_arguments)) - prnt('}') - # - prnt('#ifndef PYPY_VERSION') # ------------------------------ - # - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' x%d' % i, context) - prnt(' %s;' % arg) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - result_decl = ' %s;' % tp.result.get_c_name(' result', context) - prnt(result_decl) - prnt(' PyObject *pyresult;') - else: - result_decl = None - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( - name, len(rng), len(rng), - ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - call_arguments = ['x%d' % i for i in range(len(tp.args))] - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - # - prnt('#else') # ------------------------------ - # - # the PyPy version: need to replace struct/union arguments with - # pointers, and if the result is a struct/union, insert a first - # arg that is a pointer to the result. We also do that for - # complex args and return type. - def need_indirection(type): - return (isinstance(type, model.StructOrUnion) or - (isinstance(type, model.PrimitiveType) and - type.is_complex_type())) - difference = False - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - indirection = '' - if need_indirection(type): - indirection = '*' - difference = True - arg = type.get_c_name(' %sx%d' % (indirection, i), context) - arguments.append(arg) - call_arguments.append('%sx%d' % (indirection, i)) - tp_result = tp.result - if need_indirection(tp_result): - context = 'result of %s' % name - arg = tp_result.get_c_name(' *result', context) - arguments.insert(0, arg) - tp_result = model.void_type - result_decl = None - result_code = '*result = ' - difference = True - if difference: - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, - repr_arguments) - prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) - prnt('{') - if result_decl: - prnt(result_decl) - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - if result_decl: - prnt(' return result;') - prnt('}') - else: - prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) - # - prnt('#endif') # ------------------------------ - prnt() - - def _generate_cpy_function_ctx(self, tp, name): - if tp.ellipsis and not self.target_is_python: - self._generate_cpy_constant_ctx(tp, name) - return - type_index = self._typesdict[tp.as_raw_function()] - numargs = len(tp.args) - if self.target_is_python: - meth_kind = OP_DLOPEN_FUNC - elif numargs == 0: - meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' - elif numargs == 1: - meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' - else: - meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' - self._lsts["global"].append( - GlobalExpr(name, '_cffi_f_%s' % name, - CffiOp(meth_kind, type_index), - size='_cffi_d_%s' % name)) - - # ---------- - # named structs or unions - - def _field_type(self, tp_struct, field_name, tp_field): - if isinstance(tp_field, model.ArrayType): - actual_length = tp_field.length - if actual_length == '...': - ptr_struct_name = tp_struct.get_c_name('*') - actual_length = '_cffi_array_len(((%s)0)->%s)' % ( - ptr_struct_name, field_name) - tp_item = self._field_type(tp_struct, '%s[0]' % field_name, - tp_field.item) - tp_field = model.ArrayType(tp_item, actual_length) - return tp_field - - def _struct_collecttype(self, tp): - self._do_collect_type(tp) - if self.target_is_python: - # also requires nested anon struct/unions in ABI mode, recursively - for fldtype in tp.anonymous_struct_fields(): - self._struct_collecttype(fldtype) - - def _struct_decl(self, tp, cname, approxname): - if tp.fldtypes is None: - return - prnt = self._prnt - checkfuncname = '_cffi_checkfld_%s' % (approxname,) - prnt('_CFFI_UNUSED_FN') - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in self._enum_fields(tp): - try: - if ftype.is_integer_type() or fbitsize >= 0: - # accept all integers, but complain on float or double - if fname != '': - prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " - "an integer */" % (fname, cname, fname)) - continue - # only accept exactly the type declared, except that '[]' - # is interpreted as a '*' and so will match any array length. - # (It would also match '*', but that's harder to detect...) - while (isinstance(ftype, model.ArrayType) - and (ftype.length is None or ftype.length == '...')): - ftype = ftype.item - fname = fname + '[0]' - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) - prnt() - - def _struct_ctx(self, tp, cname, approxname, named_ptr=None): - type_index = self._typesdict[tp] - reason_for_not_expanding = None - flags = [] - if isinstance(tp, model.UnionType): - flags.append("_CFFI_F_UNION") - if tp.fldtypes is None: - flags.append("_CFFI_F_OPAQUE") - reason_for_not_expanding = "opaque" - if (tp not in self.ffi._parser._included_declarations and - (named_ptr is None or - named_ptr not in self.ffi._parser._included_declarations)): - if tp.fldtypes is None: - pass # opaque - elif tp.partial or any(tp.anonymous_struct_fields()): - pass # field layout obtained silently from the C compiler - else: - flags.append("_CFFI_F_CHECK_FIELDS") - if tp.packed: - if tp.packed > 1: - raise NotImplementedError( - "%r is declared with 'pack=%r'; only 0 or 1 are " - "supported in API mode (try to use \"...;\", which " - "does not require a 'pack' declaration)" % - (tp, tp.packed)) - flags.append("_CFFI_F_PACKED") - else: - flags.append("_CFFI_F_EXTERNAL") - reason_for_not_expanding = "external" - flags = '|'.join(flags) or '0' - c_fields = [] - if reason_for_not_expanding is None: - enumfields = list(self._enum_fields(tp)) - for fldname, fldtype, fbitsize, fqual in enumfields: - fldtype = self._field_type(tp, fldname, fldtype) - self._check_not_opaque(fldtype, - "field '%s.%s'" % (tp.name, fldname)) - # cname is None for _add_missing_struct_unions() only - op = OP_NOOP - if fbitsize >= 0: - op = OP_BITFIELD - size = '%d /* bits */' % fbitsize - elif cname is None or ( - isinstance(fldtype, model.ArrayType) and - fldtype.length is None): - size = '(size_t)-1' - else: - size = 'sizeof(((%s)0)->%s)' % ( - tp.get_c_name('*') if named_ptr is None - else named_ptr.name, - fldname) - if cname is None or fbitsize >= 0: - offset = '(size_t)-1' - elif named_ptr is not None: - offset = '((char *)&((%s)4096)->%s) - (char *)4096' % ( - named_ptr.name, fldname) - else: - offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) - c_fields.append( - FieldExpr(fldname, offset, size, fbitsize, - CffiOp(op, self._typesdict[fldtype]))) - first_field_index = len(self._lsts["field"]) - self._lsts["field"].extend(c_fields) - # - if cname is None: # unknown name, for _add_missing_struct_unions - size = '(size_t)-2' - align = -2 - comment = "unnamed" - else: - if named_ptr is not None: - size = 'sizeof(*(%s)0)' % (named_ptr.name,) - align = '-1 /* unknown alignment */' - else: - size = 'sizeof(%s)' % (cname,) - align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) - comment = None - else: - size = '(size_t)-1' - align = -1 - first_field_index = -1 - comment = reason_for_not_expanding - self._lsts["struct_union"].append( - StructUnionExpr(tp.name, type_index, flags, size, align, comment, - first_field_index, c_fields)) - self._seen_struct_unions.add(tp) - - def _check_not_opaque(self, tp, location): - while isinstance(tp, model.ArrayType): - tp = tp.item - if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: - raise TypeError( - "%s is of an opaque type (not declared in cdef())" % location) - - def _add_missing_struct_unions(self): - # not very nice, but some struct declarations might be missing - # because they don't have any known C name. Check that they are - # not partial (we can't complete or verify them!) and emit them - # anonymously. - lst = list(self._struct_unions.items()) - lst.sort(key=lambda tp_order: tp_order[1]) - for tp, order in lst: - if tp not in self._seen_struct_unions: - if tp.partial: - raise NotImplementedError("internal inconsistency: %r is " - "partial but was not seen at " - "this point" % (tp,)) - if tp.name.startswith('$') and tp.name[1:].isdigit(): - approxname = tp.name[1:] - elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': - approxname = 'FILE' - self._typedef_ctx(tp, 'FILE') - else: - raise NotImplementedError("internal inconsistency: %r" % - (tp,)) - self._struct_ctx(tp, None, approxname) - - def _generate_cpy_struct_collecttype(self, tp, name): - self._struct_collecttype(tp) - _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype - - def _struct_names(self, tp): - cname = tp.get_c_name('') - if ' ' in cname: - return cname, cname.replace(' ', '_') - else: - return cname, '_' + cname - - def _generate_cpy_struct_decl(self, tp, name): - self._struct_decl(tp, *self._struct_names(tp)) - _generate_cpy_union_decl = _generate_cpy_struct_decl - - def _generate_cpy_struct_ctx(self, tp, name): - self._struct_ctx(tp, *self._struct_names(tp)) - _generate_cpy_union_ctx = _generate_cpy_struct_ctx - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_cpy_anonymous_collecttype(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_collecttype(tp, name) - else: - self._struct_collecttype(tp) - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp) - else: - self._struct_decl(tp, name, 'typedef_' + name) - - def _generate_cpy_anonymous_ctx(self, tp, name): - if isinstance(tp, model.EnumType): - self._enum_ctx(tp, name) - else: - self._struct_ctx(tp, name, 'typedef_' + name) - - # ---------- - # constants, declared with "static const ..." - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - check_value=None): - if (category, name) in self._seen_constants: - raise VerificationError( - "duplicate declaration of %s '%s'" % (category, name)) - self._seen_constants.add((category, name)) - # - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - if is_int: - prnt('static int %s(unsigned long long *o)' % funcname) - prnt('{') - prnt(' int n = (%s) <= 0;' % (name,)) - prnt(' *o = (unsigned long long)((%s) | 0);' - ' /* check that %s is an integer */' % (name, name)) - if check_value is not None: - if check_value > 0: - check_value = '%dU' % (check_value,) - prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) - prnt(' n |= 2;') - prnt(' return n;') - prnt('}') - else: - assert check_value is None - prnt('static void %s(char *o)' % funcname) - prnt('{') - prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = tp.is_integer_type() - if not is_int or self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - def _generate_cpy_constant_ctx(self, tp, name): - if not self.target_is_python and tp.is_integer_type(): - type_op = CffiOp(OP_CONSTANT_INT, -1) - else: - if self.target_is_python: - const_kind = OP_DLOPEN_CONST - else: - const_kind = OP_CONSTANT - type_index = self._typesdict[tp] - type_op = CffiOp(const_kind, type_index) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op)) - - # ---------- - # enums - - def _generate_cpy_enum_collecttype(self, tp, name): - self._do_collect_type(tp) - - def _generate_cpy_enum_decl(self, tp, name=None): - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator) - - def _enum_ctx(self, tp, cname): - type_index = self._typesdict[tp] - type_op = CffiOp(OP_ENUM, -1) - if self.target_is_python: - tp.check_not_partial() - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._lsts["global"].append( - GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, - check_value=enumvalue)) - # - if cname is not None and '$' not in cname and not self.target_is_python: - size = "sizeof(%s)" % cname - signed = "((%s)-1) <= 0" % cname - else: - basetp = tp.build_baseinttype(self.ffi, []) - size = self.ffi.sizeof(basetp) - signed = int(int(self.ffi.cast(basetp, -1)) < 0) - allenums = ",".join(tp.enumerators) - self._lsts["enum"].append( - EnumExpr(tp.name, type_index, size, signed, allenums)) - - def _generate_cpy_enum_ctx(self, tp, name): - self._enum_ctx(tp, tp._get_c_name()) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_collecttype(self, tp, name): - pass - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - def _generate_cpy_macro_ctx(self, tp, name): - if tp == '...': - if self.target_is_python: - raise VerificationError( - "cannot use the syntax '...' in '#define %s ...' when " - "using the ABI mode" % (name,)) - check_value = None - else: - check_value = tp # an integer - type_op = CffiOp(OP_CONSTANT_INT, -1) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op, - check_value=check_value)) - - # ---------- - # global variables - - def _global_type(self, tp, global_name): - if isinstance(tp, model.ArrayType): - actual_length = tp.length - if actual_length == '...': - actual_length = '_cffi_array_len(%s)' % (global_name,) - tp_item = self._global_type(tp.item, '%s[0]' % global_name) - tp = model.ArrayType(tp_item, actual_length) - return tp - - def _generate_cpy_variable_collecttype(self, tp, name): - self._do_collect_type(self._global_type(tp, name)) - - def _generate_cpy_variable_decl(self, tp, name): - prnt = self._prnt - tp = self._global_type(tp, name) - if isinstance(tp, model.ArrayType) and tp.length is None: - tp = tp.item - ampersand = '' - else: - ampersand = '&' - # This code assumes that casts from "tp *" to "void *" is a - # no-op, i.e. a function that returns a "tp *" can be called - # as if it returned a "void *". This should be generally true - # on any modern machine. The only exception to that rule (on - # uncommon architectures, and as far as I can tell) might be - # if 'tp' were a function type, but that is not possible here. - # (If 'tp' is a function _pointer_ type, then casts from "fn_t - # **" to "void *" are again no-ops, as far as I can tell.) - decl = '*_cffi_var_%s(void)' % (name,) - prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) - prnt('{') - prnt(' return %s(%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_cpy_variable_ctx(self, tp, name): - tp = self._global_type(tp, name) - type_index = self._typesdict[tp] - if self.target_is_python: - op = OP_GLOBAL_VAR - else: - op = OP_GLOBAL_VAR_F - self._lsts["global"].append( - GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) - - # ---------- - # extern "Python" - - def _generate_cpy_extern_python_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - self._do_collect_type(tp) - _generate_cpy_dllexport_python_collecttype = \ - _generate_cpy_extern_python_plus_c_collecttype = \ - _generate_cpy_extern_python_collecttype - - def _extern_python_decl(self, tp, name, tag_and_space): - prnt = self._prnt - if isinstance(tp.result, model.VoidType): - size_of_result = '0' - else: - context = 'result of %s' % name - size_of_result = '(int)sizeof(%s)' % ( - tp.result.get_c_name('', context),) - prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) - prnt(' { "%s.%s", %s, 0, 0 };' % ( - self.module_name, name, size_of_result)) - prnt() - # - arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' a%d' % i, context) - arguments.append(arg) - # - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s(%s)' % (name, repr_arguments) - if tp.abi == "__stdcall": - name_and_arguments = '_cffi_stdcall ' + name_and_arguments - # - def may_need_128_bits(tp): - return (isinstance(tp, model.PrimitiveType) and - tp.name == 'long double') - # - size_of_a = max(len(tp.args)*8, 8) - if may_need_128_bits(tp.result): - size_of_a = max(size_of_a, 16) - if isinstance(tp.result, model.StructOrUnion): - size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( - tp.result.get_c_name(''), size_of_a, - tp.result.get_c_name(''), size_of_a) - prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) - prnt('{') - prnt(' char a[%s];' % size_of_a) - prnt(' char *p = a;') - for i, type in enumerate(tp.args): - arg = 'a%d' % i - if (isinstance(type, model.StructOrUnion) or - may_need_128_bits(type)): - arg = '&' + arg - type = model.PointerType(type) - prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) - prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) - if not isinstance(tp.result, model.VoidType): - prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) - prnt('}') - prnt() - self._num_externpy += 1 - - def _generate_cpy_extern_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'static ') - - def _generate_cpy_dllexport_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') - - def _generate_cpy_extern_python_plus_c_decl(self, tp, name): - self._extern_python_decl(tp, name, '') - - def _generate_cpy_extern_python_ctx(self, tp, name): - if self.target_is_python: - raise VerificationError( - "cannot use 'extern \"Python\"' in the ABI mode") - if tp.ellipsis: - raise NotImplementedError("a vararg function is extern \"Python\"") - type_index = self._typesdict[tp] - type_op = CffiOp(OP_EXTERN_PYTHON, type_index) - self._lsts["global"].append( - GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) - - _generate_cpy_dllexport_python_ctx = \ - _generate_cpy_extern_python_plus_c_ctx = \ - _generate_cpy_extern_python_ctx - - def _print_string_literal_in_array(self, s): - prnt = self._prnt - prnt('// # NB. this is not a string because of a size limit in MSVC') - if not isinstance(s, bytes): # unicode - s = s.encode('utf-8') # -> bytes - else: - s.decode('utf-8') # got bytes, check for valid utf-8 - try: - s.decode('ascii') - except UnicodeDecodeError: - s = b'# -*- encoding: utf8 -*-\n' + s - for line in s.splitlines(True): - comment = line - if type('//') is bytes: # python2 - line = map(ord, line) # make a list of integers - else: # python3 - # type(line) is bytes, which enumerates like a list of integers - comment = ascii(comment)[1:-1] - prnt(('// ' + comment).rstrip()) - printed_line = '' - for c in line: - if len(printed_line) >= 76: - prnt(printed_line) - printed_line = '' - printed_line += '%d,' % (c,) - prnt(printed_line) - - # ---------- - # emitting the opcodes for individual types - - def _emit_bytecode_VoidType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) - - def _emit_bytecode_PrimitiveType(self, tp, index): - prim_index = PRIMITIVE_TO_INDEX[tp.name] - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) - - def _emit_bytecode_UnknownIntegerType(self, tp, index): - s = ('_cffi_prim_int(sizeof(%s), (\n' - ' ((%s)-1) | 0 /* check that %s is an integer type */\n' - ' ) <= 0)' % (tp.name, tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_UnknownFloatType(self, tp, index): - s = ('_cffi_prim_float(sizeof(%s) *\n' - ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' - ' )' % (tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_RawFunctionType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) - index += 1 - for tp1 in tp.args: - realindex = self._typesdict[tp1] - if index != realindex: - if isinstance(tp1, model.PrimitiveType): - self._emit_bytecode_PrimitiveType(tp1, index) - else: - self.cffi_types[index] = CffiOp(OP_NOOP, realindex) - index += 1 - flags = int(tp.ellipsis) - if tp.abi is not None: - if tp.abi == '__stdcall': - flags |= 2 - else: - raise NotImplementedError("abi=%r" % (tp.abi,)) - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) - - def _emit_bytecode_PointerType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) - - _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType - _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType - - def _emit_bytecode_FunctionPtrType(self, tp, index): - raw = tp.as_raw_function() - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) - - def _emit_bytecode_ArrayType(self, tp, index): - item_index = self._typesdict[tp.item] - if tp.length is None: - self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) - elif tp.length == '...': - raise VerificationError( - "type %s badly placed: the '...' array length can only be " - "used on global arrays or on fields of structures" % ( - str(tp).replace('/*...*/', '...'),)) - else: - assert self.cffi_types[index + 1] == 'LEN' - self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) - self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) - - def _emit_bytecode_StructType(self, tp, index): - struct_index = self._struct_unions[tp] - self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) - _emit_bytecode_UnionType = _emit_bytecode_StructType - - def _emit_bytecode_EnumType(self, tp, index): - enum_index = self._enums[tp] - self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - -def _is_file_like(maybefile): - # compare to xml.etree.ElementTree._get_writer - return hasattr(maybefile, 'write') - -def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): - if verbose: - print("generating %s" % (target_file,)) - recompiler = Recompiler(ffi, module_name, - target_is_python=(preamble is None)) - recompiler.collect_type_table() - recompiler.collect_step_tables() - if _is_file_like(target_file): - recompiler.write_source_to_f(target_file, preamble) - return True - f = NativeIO() - recompiler.write_source_to_f(f, preamble) - output = f.getvalue() - try: - with open(target_file, 'r') as f1: - if f1.read(len(output) + 1) != output: - raise IOError - if verbose: - print("(already up-to-date)") - return False # already up-to-date - except IOError: - tmp_file = '%s.~%d' % (target_file, os.getpid()) - with open(tmp_file, 'w') as f1: - f1.write(output) - try: - os.rename(tmp_file, target_file) - except OSError: - os.unlink(target_file) - os.rename(tmp_file, target_file) - return True - -def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): - assert preamble is not None - return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, - verbose) - -def make_py_source(ffi, module_name, target_py_file, verbose=False): - return _make_c_or_py_source(ffi, module_name, None, target_py_file, - verbose) - -def _modname_to_file(outputdir, modname, extension): - parts = modname.split('.') - try: - os.makedirs(os.path.join(outputdir, *parts[:-1])) - except OSError: - pass - parts[-1] += extension - return os.path.join(outputdir, *parts), parts - - -# Aaargh. Distutils is not tested at all for the purpose of compiling -# DLLs that are not extension modules. Here are some hacks to work -# around that, in the _patch_for_*() functions... - -def _patch_meth(patchlist, cls, name, new_meth): - old = getattr(cls, name) - patchlist.append((cls, name, old)) - setattr(cls, name, new_meth) - return old - -def _unpatch_meths(patchlist): - for cls, name, old_meth in reversed(patchlist): - setattr(cls, name, old_meth) - -def _patch_for_embedding(patchlist): - if sys.platform == 'win32': - # we must not remove the manifest when building for embedding! - # FUTURE: this module was removed in setuptools 74; this is likely dead code and should be removed, - # since the toolchain it supports (VS2005-2008) is also long dead. - from cffi._shimmed_dist_utils import MSVCCompiler - if MSVCCompiler is not None: - _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', - lambda self, manifest_file: manifest_file) - - if sys.platform == 'darwin': - # we must not make a '-bundle', but a '-dynamiclib' instead - from cffi._shimmed_dist_utils import CCompiler - def my_link_shared_object(self, *args, **kwds): - if '-bundle' in self.linker_so: - self.linker_so = list(self.linker_so) - i = self.linker_so.index('-bundle') - self.linker_so[i] = '-dynamiclib' - return old_link_shared_object(self, *args, **kwds) - old_link_shared_object = _patch_meth(patchlist, CCompiler, - 'link_shared_object', - my_link_shared_object) - -def _patch_for_target(patchlist, target): - from cffi._shimmed_dist_utils import build_ext - # if 'target' is different from '*', we need to patch some internal - # method to just return this 'target' value, instead of having it - # built from module_name - if target.endswith('.*'): - target = target[:-2] - if sys.platform == 'win32': - target += '.dll' - elif sys.platform == 'darwin': - target += '.dylib' - else: - target += '.so' - _patch_meth(patchlist, build_ext, 'get_ext_filename', - lambda self, ext_name: target) - - -def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, - c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, debug=None, - uses_ffiplatform=True, **kwds): - if not isinstance(module_name, str): - module_name = module_name.encode('ascii') - if ffi._windows_unicode: - ffi._apply_windows_unicode(kwds) - if preamble is not None: - if call_c_compiler and _is_file_like(c_file): - raise TypeError("Writing to file-like objects is not supported " - "with call_c_compiler=True") - embedding = (ffi._embedding is not None) - if embedding: - ffi._apply_embedding_fix(kwds) - if c_file is None: - c_file, parts = _modname_to_file(tmpdir, module_name, - source_extension) - if extradir: - parts = [extradir] + parts - ext_c_file = os.path.join(*parts) - else: - ext_c_file = c_file - # - if target is None: - if embedding: - target = '%s.*' % module_name - else: - target = '*' - # - if uses_ffiplatform: - ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) - else: - ext = None - updated = make_c_source(ffi, module_name, preamble, c_file, - verbose=compiler_verbose) - if call_c_compiler: - patchlist = [] - cwd = os.getcwd() - try: - if embedding: - _patch_for_embedding(patchlist) - if target != '*': - _patch_for_target(patchlist, target) - if compiler_verbose: - if tmpdir == '.': - msg = 'the current directory is' - else: - msg = 'setting the current directory to' - print('%s %r' % (msg, os.path.abspath(tmpdir))) - os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, - compiler_verbose, debug) - finally: - os.chdir(cwd) - _unpatch_meths(patchlist) - return outputfilename - else: - return ext, updated - else: - if c_file is None: - c_file, _ = _modname_to_file(tmpdir, module_name, '.py') - updated = make_py_source(ffi, module_name, c_file, - verbose=compiler_verbose) - if call_c_compiler: - return c_file - else: - return None, updated - diff --git a/.venv/Lib/site-packages/cffi/setuptools_ext.py b/.venv/Lib/site-packages/cffi/setuptools_ext.py deleted file mode 100644 index 681b49d..0000000 --- a/.venv/Lib/site-packages/cffi/setuptools_ext.py +++ /dev/null @@ -1,216 +0,0 @@ -import os -import sys - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -def error(msg): - from cffi._shimmed_dist_utils import DistutilsSetupError - raise DistutilsSetupError(msg) - - -def execfile(filename, glob): - # We use execfile() (here rewritten for Python 3) instead of - # __import__() to load the build script. The problem with - # a normal import is that in some packages, the intermediate - # __init__.py files may already try to import the file that - # we are generating. - with open(filename) as f: - src = f.read() - src += '\n' # Python 2.6 compatibility - code = compile(src, filename, 'exec') - exec(code, glob, glob) - - -def add_cffi_module(dist, mod_spec): - from cffi.api import FFI - - if not isinstance(mod_spec, basestring): - error("argument to 'cffi_modules=...' must be a str or a list of str," - " not %r" % (type(mod_spec).__name__,)) - mod_spec = str(mod_spec) - try: - build_file_name, ffi_var_name = mod_spec.split(':') - except ValueError: - error("%r must be of the form 'path/build.py:ffi_variable'" % - (mod_spec,)) - if not os.path.exists(build_file_name): - ext = '' - rewritten = build_file_name.replace('.', '/') + '.py' - if os.path.exists(rewritten): - ext = ' (rewrite cffi_modules to [%r])' % ( - rewritten + ':' + ffi_var_name,) - error("%r does not name an existing file%s" % (build_file_name, ext)) - - mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} - execfile(build_file_name, mod_vars) - - try: - ffi = mod_vars[ffi_var_name] - except KeyError: - error("%r: object %r not found in module" % (mod_spec, - ffi_var_name)) - if not isinstance(ffi, FFI): - ffi = ffi() # maybe it's a function instead of directly an ffi - if not isinstance(ffi, FFI): - error("%r is not an FFI instance (got %r)" % (mod_spec, - type(ffi).__name__)) - if not hasattr(ffi, '_assigned_source'): - error("%r: the set_source() method was not called" % (mod_spec,)) - module_name, source, source_extension, kwds = ffi._assigned_source - if ffi._windows_unicode: - kwds = kwds.copy() - ffi._apply_windows_unicode(kwds) - - if source is None: - _add_py_module(dist, ffi, module_name) - else: - _add_c_module(dist, ffi, module_name, source, source_extension, kwds) - -def _set_py_limited_api(Extension, kwds): - """ - Add py_limited_api to kwds if setuptools >= 26 is in use. - Do not alter the setting if it already exists. - Setuptools takes care of ignoring the flag on Python 2 and PyPy. - - CPython itself should ignore the flag in a debugging version - (by not listing .abi3.so in the extensions it supports), but - it doesn't so far, creating troubles. That's why we check - for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent - of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) - - On Windows, with CPython <= 3.4, it's better not to use py_limited_api - because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. - Recently (2020) we started shipping only >= 3.5 wheels, though. So - we'll give it another try and set py_limited_api on Windows >= 3.5. - """ - from cffi import recompiler - - if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') - and recompiler.USE_LIMITED_API): - import setuptools - try: - setuptools_major_version = int(setuptools.__version__.partition('.')[0]) - if setuptools_major_version >= 26: - kwds['py_limited_api'] = True - except ValueError: # certain development versions of setuptools - # If we don't know the version number of setuptools, we - # try to set 'py_limited_api' anyway. At worst, we get a - # warning. - kwds['py_limited_api'] = True - return kwds - -def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): - # We are a setuptools extension. Need this build_ext for py_limited_api. - from setuptools.command.build_ext import build_ext - from cffi._shimmed_dist_utils import Extension, log, mkpath - from cffi import recompiler - - allsources = ['$PLACEHOLDER'] - allsources.extend(kwds.pop('sources', [])) - kwds = _set_py_limited_api(Extension, kwds) - ext = Extension(name=module_name, sources=allsources, **kwds) - - def make_mod(tmpdir, pre_run=None): - c_file = os.path.join(tmpdir, module_name + source_extension) - log.info("generating cffi module %r" % c_file) - mkpath(tmpdir) - # a setuptools-only, API-only hook: called with the "ext" and "ffi" - # arguments just before we turn the ffi into C code. To use it, - # subclass the 'distutils.command.build_ext.build_ext' class and - # add a method 'def pre_run(self, ext, ffi)'. - if pre_run is not None: - pre_run(ext, ffi) - updated = recompiler.make_c_source(ffi, module_name, source, c_file) - if not updated: - log.info("already up-to-date") - return c_file - - if dist.ext_modules is None: - dist.ext_modules = [] - dist.ext_modules.append(ext) - - base_class = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class): - def run(self): - if ext.sources[0] == '$PLACEHOLDER': - pre_run = getattr(self, 'pre_run', None) - ext.sources[0] = make_mod(self.build_temp, pre_run) - base_class.run(self) - dist.cmdclass['build_ext'] = build_ext_make_mod - # NB. multiple runs here will create multiple 'build_ext_make_mod' - # classes. Even in this case the 'build_ext' command should be - # run once; but just in case, the logic above does nothing if - # called again. - - -def _add_py_module(dist, ffi, module_name): - from setuptools.command.build_py import build_py - from setuptools.command.build_ext import build_ext - from cffi._shimmed_dist_utils import log, mkpath - from cffi import recompiler - - def generate_mod(py_file): - log.info("generating cffi module %r" % py_file) - mkpath(os.path.dirname(py_file)) - updated = recompiler.make_py_source(ffi, module_name, py_file) - if not updated: - log.info("already up-to-date") - - base_class = dist.cmdclass.get('build_py', build_py) - class build_py_make_mod(base_class): - def run(self): - base_class.run(self) - module_path = module_name.split('.') - module_path[-1] += '.py' - generate_mod(os.path.join(self.build_lib, *module_path)) - def get_source_files(self): - # This is called from 'setup.py sdist' only. Exclude - # the generate .py module in this case. - saved_py_modules = self.py_modules - try: - if saved_py_modules: - self.py_modules = [m for m in saved_py_modules - if m != module_name] - return base_class.get_source_files(self) - finally: - self.py_modules = saved_py_modules - dist.cmdclass['build_py'] = build_py_make_mod - - # distutils and setuptools have no notion I could find of a - # generated python module. If we don't add module_name to - # dist.py_modules, then things mostly work but there are some - # combination of options (--root and --record) that will miss - # the module. So we add it here, which gives a few apparently - # harmless warnings about not finding the file outside the - # build directory. - # Then we need to hack more in get_source_files(); see above. - if dist.py_modules is None: - dist.py_modules = [] - dist.py_modules.append(module_name) - - # the following is only for "build_ext -i" - base_class_2 = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class_2): - def run(self): - base_class_2.run(self) - if self.inplace: - # from get_ext_fullpath() in distutils/command/build_ext.py - module_path = module_name.split('.') - package = '.'.join(module_path[:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - file_name = module_path[-1] + '.py' - generate_mod(os.path.join(package_dir, file_name)) - dist.cmdclass['build_ext'] = build_ext_make_mod - -def cffi_modules(dist, attr, value): - assert attr == 'cffi_modules' - if isinstance(value, basestring): - value = [value] - - for cffi_module in value: - add_cffi_module(dist, cffi_module) diff --git a/.venv/Lib/site-packages/cffi/vengine_cpy.py b/.venv/Lib/site-packages/cffi/vengine_cpy.py deleted file mode 100644 index eb0b6f7..0000000 --- a/.venv/Lib/site-packages/cffi/vengine_cpy.py +++ /dev/null @@ -1,1084 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys -from . import model -from .error import VerificationError -from . import _imp_emulation as imp - - -class VCPythonEngine(object): - _class_key = 'x' - _gen_python_module = True - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self._struct_pending_verification = {} - self._types_of_builtin_functions = {} - - def patch_extension_kwds(self, kwds): - pass - - def find_module(self, module_name, path, so_suffixes): - try: - f, filename, descr = imp.find_module(module_name, path) - except ImportError: - return None - if f is not None: - f.close() - # Note that after a setuptools installation, there are both .py - # and .so files with the same basename. The code here relies on - # imp.find_module() locating the .so in priority. - if descr[0] not in so_suffixes: - return None - return filename - - def collect_types(self): - self._typesdict = {} - self._generate("collecttype") - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _do_collect_type(self, tp): - if ((not isinstance(tp, model.PrimitiveType) - or tp.name == 'long double') - and tp not in self._typesdict): - num = len(self._typesdict) - self._typesdict[tp] = num - - def write_source_to_f(self): - self.collect_types() - # - # The new module will have a _cffi_setup() function that receives - # objects from the ffi world, and that calls some setup code in - # the module. This setup code is split in several independent - # functions, e.g. one per constant. The functions are "chained" - # by ending in a tail call to each other. - # - # This is further split in two chained lists, depending on if we - # can do it at import-time or if we must wait for _cffi_setup() to - # provide us with the objects. This is needed because we - # need the values of the enum constants in order to build the - # that we may have to pass to _cffi_setup(). - # - # The following two 'chained_list_constants' items contains - # the head of these two chained lists, as a string that gives the - # call to do, if any. - self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] - # - prnt = self._prnt - # first paste some standard set of lines that are mostly '#define' - prnt(cffimod_header) - prnt() - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate("decl") - # - # implement the function _cffi_setup_custom() as calling the - # head of the chained list. - self._generate_setup_custom() - prnt() - # - # produce the method table, including the entries for the - # generated Python->C function wrappers, which are done - # by generate_cpy_function_method(). - prnt('static PyMethodDef _cffi_methods[] = {') - self._generate("method") - prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') - prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') - prnt('};') - prnt() - # - # standard init. - modname = self.verifier.get_module_name() - constants = self._chained_list_constants[False] - prnt('#if PY_MAJOR_VERSION >= 3') - prnt() - prnt('static struct PyModuleDef _cffi_module_def = {') - prnt(' PyModuleDef_HEAD_INIT,') - prnt(' "%s",' % modname) - prnt(' NULL,') - prnt(' -1,') - prnt(' _cffi_methods,') - prnt(' NULL, NULL, NULL, NULL') - prnt('};') - prnt() - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = PyModule_Create(&_cffi_module_def);') - prnt(' if (lib == NULL)') - prnt(' return NULL;') - prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) - prnt(' Py_DECREF(lib);') - prnt(' return NULL;') - prnt(' }') - prnt(' return lib;') - prnt('}') - prnt() - prnt('#else') - prnt() - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) - prnt(' if (lib == NULL)') - prnt(' return;') - prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) - prnt(' return;') - prnt(' return;') - prnt('}') - prnt() - prnt('#endif') - - def load_library(self, flags=None): - # XXX review all usages of 'self' here! - # import it as a new extension module - imp.acquire_lock() - try: - if hasattr(sys, "getdlopenflags"): - previous_flags = sys.getdlopenflags() - try: - if hasattr(sys, "setdlopenflags") and flags is not None: - sys.setdlopenflags(flags) - module = imp.load_dynamic(self.verifier.get_module_name(), - self.verifier.modulefilename) - except ImportError as e: - error = "importing %r: %s" % (self.verifier.modulefilename, e) - raise VerificationError(error) - finally: - if hasattr(sys, "setdlopenflags"): - sys.setdlopenflags(previous_flags) - finally: - imp.release_lock() - # - # call loading_cpy_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - # - # the C code will need the objects. Collect them in - # order in a list. - revmapping = dict([(value, key) - for (key, value) in self._typesdict.items()]) - lst = [revmapping[i] for i in range(len(revmapping))] - lst = list(map(self.ffi._get_cached_btype, lst)) - # - # build the FFILibrary class and instance and call _cffi_setup(). - # this will set up some fields like '_cffi_types', and only then - # it will invoke the chained list of functions that will really - # build (notably) the constant objects, as if they are - # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): - _cffi_python_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir + list(self.__dict__) - library = FFILibrary() - if module._cffi_setup(lst, VerificationError, library): - import warnings - warnings.warn("reimporting %r might overwrite older definitions" - % (self.verifier.get_module_name())) - # - # finally, call the loaded_cpy_xxx() functions. This will perform - # the final adjustments, like copying the Python->C wrapper - # functions from the module to the 'library' object, and setting - # up the FFILibrary class with properties for the global C variables. - self._load(module, 'loaded', library=library) - module._cffi_original_ffi = self.ffi - module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif tp.is_complex_type(): - raise VerificationError( - "not implemented in verify(): complex types") - else: - converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), - tp.name.replace(' ', '_')) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): - # a struct (not a struct pointer) as a function argument - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - 'alloca((size_t)datasize) : NULL;' % (tovar,)) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif tp.name != 'long double': - return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs: generates no code so far - - _generate_cpy_typedef_collecttype = _generate_nothing - _generate_cpy_typedef_decl = _generate_nothing - _generate_cpy_typedef_method = _generate_nothing - _loading_cpy_typedef = _loaded_noop - _loaded_cpy_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - self._do_collect_type(tp) - else: - # don't call _do_collect_type(tp) in this common case, - # otherwise test_autofilled_struct_as_argument fails - for type in tp.args: - self._do_collect_type(type) - self._do_collect_type(tp.result) - - def _generate_cpy_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - prnt(' %s;' % type.get_c_name(' x%d' % i, context)) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - prnt(' %s;' % tp.result.get_c_name(' result', context)) - prnt(' PyObject *pyresult;') - else: - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( - 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - prnt(' { %s%s(%s); }' % ( - result_code, name, - ', '.join(['x%d' % i for i in range(len(tp.args))]))) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - prnt() - - def _generate_cpy_function_method(self, tp, name): - if tp.ellipsis: - return - numargs = len(tp.args) - if numargs == 0: - meth = 'METH_NOARGS' - elif numargs == 1: - meth = 'METH_O' - else: - meth = 'METH_VARARGS' - self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) - - _loading_cpy_function = _loaded_noop - - def _loaded_cpy_function(self, tp, name, module, library): - if tp.ellipsis: - return - func = getattr(module, name) - setattr(library, name, func) - self._types_of_builtin_functions[func] = tp - - # ---------- - # named structs - - _generate_cpy_struct_collecttype = _generate_nothing - def _generate_cpy_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - def _generate_cpy_struct_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'struct', name) - def _loading_cpy_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - def _loaded_cpy_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - _generate_cpy_union_collecttype = _generate_nothing - def _generate_cpy_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - def _generate_cpy_union_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'union', name) - def _loading_cpy_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - def _loaded_cpy_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('static PyObject *') - prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static Py_ssize_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' (void)self; /* unused */') - prnt(' (void)noarg; /* unused */') - prnt(' return _cffi_get_struct_layout(nums);') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _generate_struct_or_union_method(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, - layoutfuncname)) - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - function = getattr(module, layoutfuncname) - layout = function() - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - _generate_cpy_anonymous_collecttype = _generate_nothing - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _generate_cpy_anonymous_method(self, tp, name): - if not isinstance(tp, model.EnumType): - self._generate_struct_or_union_method(tp, '', name) - - def _loading_cpy_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_cpy_enum(tp, name, module) - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_cpy_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_cpy_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - vartp=None, delayed=True, size_too=False, - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - prnt(' PyObject *o;') - prnt(' int res;') - if not is_int: - prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) - else: - assert category == 'const' - # - if check_value is not None: - self._check_int_constant_value(name, check_value) - # - if not is_int: - if category == 'var': - realexpr = '&' + name - else: - realexpr = name - prnt(' i = (%s);' % (realexpr,)) - prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', - 'variable type'),)) - assert delayed - else: - prnt(' o = _cffi_from_c_int_const(%s);' % name) - prnt(' if (o == NULL)') - prnt(' return -1;') - if size_too: - prnt(' {') - prnt(' PyObject *o1 = o;') - prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' - % (name,)) - prnt(' Py_DECREF(o1);') - prnt(' if (o == NULL)') - prnt(' return -1;') - prnt(' }') - prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) - prnt(' Py_DECREF(o);') - prnt(' if (res < 0)') - prnt(' return -1;') - prnt(' return %s;' % self._chained_list_constants[delayed]) - self._chained_list_constants[delayed] = funcname + '(lib)' - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - if not is_int: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - _generate_cpy_constant_method = _generate_nothing - _loading_cpy_constant = _loaded_noop - _loaded_cpy_constant = _loaded_noop - - # ---------- - # enums - - def _check_int_constant_value(self, name, value, err_prefix=''): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % - name) - prnt(' PyErr_Format(_cffi_VerificationError,') - prnt(' "%s%s has the real value %s, not %s",') - prnt(' "%s", "%s", buf, "%d");' % ( - err_prefix, name, value)) - prnt(' return -1;') - prnt(' }') - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator, delayed=False) - return - # - funcname = self._enum_funcname(prefix, name) - prnt = self._prnt - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue, - "enum %s: " % name) - prnt(' return %s;' % self._chained_list_constants[True]) - self._chained_list_constants[True] = funcname + '(lib)' - prnt('}') - prnt() - - _generate_cpy_enum_collecttype = _generate_nothing - _generate_cpy_enum_method = _generate_nothing - - def _loading_cpy_enum(self, tp, name, module): - if tp.partial: - enumvalues = [getattr(module, enumerator) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - - def _loaded_cpy_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - _generate_cpy_macro_collecttype = _generate_nothing - _generate_cpy_macro_method = _generate_nothing - _loading_cpy_macro = _loaded_noop - _loaded_cpy_macro = _loaded_noop - - # ---------- - # global variables - - def _generate_cpy_variable_collecttype(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - else: - tp_ptr = model.PointerType(tp) - self._do_collect_type(tp_ptr) - - def _generate_cpy_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - self._generate_cpy_const(False, name, tp, vartp=tp_ptr, - size_too = tp.length_is_unknown()) - else: - tp_ptr = model.PointerType(tp) - self._generate_cpy_const(False, name, tp_ptr, category='var') - - _generate_cpy_variable_method = _generate_nothing - _loading_cpy_variable = _loaded_noop - - def _loaded_cpy_variable(self, tp, name, module, library): - value = getattr(library, name) - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - assert isinstance(value, tuple) - (value, size) = value - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - ptr = value - delattr(library, name) - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - - # ---------- - - def _generate_setup_custom(self): - prnt = self._prnt - prnt('static int _cffi_setup_custom(PyObject *lib)') - prnt('{') - prnt(' return %s;' % self._chained_list_constants[True]) - prnt('}') - -cffimod_header = r''' -#include -#include - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif - -#if PY_MAJOR_VERSION < 3 -# undef PyCapsule_CheckExact -# undef PyCapsule_GetPointer -# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) -# define PyCapsule_GetPointer(capsule, name) \ - (PyCObject_AsVoidPtr(capsule)) -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int_const(x) \ - (((x) > 0) ? \ - ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ - ((long long)(x) >= (long long)LONG_MIN) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromLongLong((long long)(x))) - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) -#define _CFFI_NUM_EXPORTS 25 - -typedef struct _ctypedescr CTypeDescrObject; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; -static PyObject *_cffi_types, *_cffi_VerificationError; - -static int _cffi_setup_custom(PyObject *lib); /* forward */ - -static PyObject *_cffi_setup(PyObject *self, PyObject *args) -{ - PyObject *library; - int was_alive = (_cffi_types != NULL); - (void)self; /* unused */ - if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, - &library)) - return NULL; - Py_INCREF(_cffi_types); - Py_INCREF(_cffi_VerificationError); - if (_cffi_setup_custom(library) < 0) - return NULL; - return PyBool_FromLong(was_alive); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -static int _cffi_init(void) -{ - PyObject *module, *c_api_object = NULL; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) - goto failure; - if (!PyCapsule_CheckExact(c_api_object)) { - PyErr_SetNone(PyExc_ImportError); - goto failure; - } - memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), - _CFFI_NUM_EXPORTS * sizeof(void *)); - - Py_DECREF(module); - Py_DECREF(c_api_object); - return 0; - - failure: - Py_XDECREF(module); - Py_XDECREF(c_api_object); - return -1; -} - -#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) - -/**********/ -''' diff --git a/.venv/Lib/site-packages/cffi/vengine_gen.py b/.venv/Lib/site-packages/cffi/vengine_gen.py deleted file mode 100644 index bffc821..0000000 --- a/.venv/Lib/site-packages/cffi/vengine_gen.py +++ /dev/null @@ -1,679 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os -import types - -from . import model -from .error import VerificationError - - -class VGenericEngine(object): - _class_key = 'g' - _gen_python_module = False - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self.export_symbols = [] - self._struct_pending_verification = {} - - def patch_extension_kwds(self, kwds): - # add 'export_symbols' to the dictionary. Note that we add the - # list before filling it. When we fill it, it will thus also show - # up in kwds['export_symbols']. - kwds.setdefault('export_symbols', self.export_symbols) - - def find_module(self, module_name, path, so_suffixes): - for so_suffix in so_suffixes: - basename = module_name + so_suffix - if path is None: - path = sys.path - for dirname in path: - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - return filename - - def collect_types(self): - pass # not needed in the generic engine - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self): - prnt = self._prnt - # first paste some standard set of lines that are mostly '#include' - prnt(cffimod_header) - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - # - # call generate_gen_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate('decl') - # - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - if sys.platform == 'win32': - if sys.version_info >= (3,): - prefix = 'PyInit_' - else: - prefix = 'init' - modname = self.verifier.get_module_name() - prnt("void %s%s(void) { }\n" % (prefix, modname)) - - def load_library(self, flags=0): - # import it with the CFFI backend - backend = self.ffi._backend - # needs to make a path that contains '/', on Posix - filename = os.path.join(os.curdir, self.verifier.modulefilename) - module = backend.load_library(filename, flags) - # - # call loading_gen_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - - # build the FFILibrary class and instance, this is a module subclass - # because modules are expected to have usually-constant-attributes and - # in PyPy this means the JIT is able to treat attributes as constant, - # which we want. - class FFILibrary(types.ModuleType): - _cffi_generic_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir - library = FFILibrary("") - # - # finally, call the loaded_gen_xxx() functions. This will set - # up the 'library' object. - self._load(module, 'loaded', library=library) - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_gen_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_gen_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - # typedefs: generates no code so far - - _generate_gen_typedef_decl = _generate_nothing - _loading_gen_typedef = _loaded_noop - _loaded_gen_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_gen_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no _cffi_f_%s wrapper) - self._generate_gen_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - argnames = [] - for i, type in enumerate(tp.args): - indirection = '' - if isinstance(type, model.StructOrUnion): - indirection = '*' - argnames.append('%sx%d' % (indirection, i)) - context = 'argument of %s' % name - arglist = [type.get_c_name(' %s' % arg, context) - for type, arg in zip(tp.args, argnames)] - tpresult = tp.result - if isinstance(tpresult, model.StructOrUnion): - arglist.insert(0, tpresult.get_c_name(' *r', context)) - tpresult = model.void_type - arglist = ', '.join(arglist) or 'void' - wrappername = '_cffi_f_%s' % name - self.export_symbols.append(wrappername) - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) - context = 'result of %s' % name - prnt(tpresult.get_c_name(funcdecl, context)) - prnt('{') - # - if isinstance(tp.result, model.StructOrUnion): - result_code = '*r = ' - elif not isinstance(tp.result, model.VoidType): - result_code = 'return ' - else: - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) - prnt('}') - prnt() - - _loading_gen_function = _loaded_noop - - def _loaded_gen_function(self, tp, name, module, library): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - newfunction = self._load_constant(False, tp, name, module) - else: - indirections = [] - base_tp = tp - if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) - or isinstance(tp.result, model.StructOrUnion)): - indirect_args = [] - for i, typ in enumerate(tp.args): - if isinstance(typ, model.StructOrUnion): - typ = model.PointerType(typ) - indirections.append((i, typ)) - indirect_args.append(typ) - indirect_result = tp.result - if isinstance(indirect_result, model.StructOrUnion): - if indirect_result.fldtypes is None: - raise TypeError("'%s' is used as result type, " - "but is opaque" % ( - indirect_result._get_c_name(),)) - indirect_result = model.PointerType(indirect_result) - indirect_args.insert(0, indirect_result) - indirections.insert(0, ("result", indirect_result)) - indirect_result = model.void_type - tp = model.FunctionPtrType(tuple(indirect_args), - indirect_result, tp.ellipsis) - BFunc = self.ffi._get_cached_btype(tp) - wrappername = '_cffi_f_%s' % name - newfunction = module.load_function(BFunc, wrappername) - for i, typ in indirections: - newfunction = self._make_struct_wrapper(newfunction, i, typ, - base_tp) - setattr(library, name, newfunction) - type(library)._cffi_dir.append(name) - - def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): - backend = self.ffi._backend - BType = self.ffi._get_cached_btype(tp) - if i == "result": - ffi = self.ffi - def newfunc(*args): - res = ffi.new(BType) - oldfunc(res, *args) - return res[0] - else: - def newfunc(*args): - args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] - return oldfunc(*args) - newfunc._cffi_base_type = base_tp - return newfunc - - # ---------- - # named structs - - def _generate_gen_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - - def _loading_gen_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - - def _loaded_gen_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_gen_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - - def _loading_gen_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - - def _loaded_gen_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - self.export_symbols.append(layoutfuncname) - prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static intptr_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' return nums[i];') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] - function = module.load_function(BFunc, layoutfuncname) - layout = [] - num = 0 - while True: - x = function(num) - if x < 0: break - layout.append(x) - num += 1 - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_gen_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_gen_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _loading_gen_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_gen_enum(tp, name, module, '') - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_gen_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_gen_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_gen_const(self, is_int, name, tp=None, category='const', - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - self.export_symbols.append(funcname) - if check_value is not None: - assert is_int - assert category == 'const' - prnt('int %s(char *out_error)' % funcname) - prnt('{') - self._check_int_constant_value(name, check_value) - prnt(' return 0;') - prnt('}') - elif is_int: - assert category == 'const' - prnt('int %s(long long *out_value)' % funcname) - prnt('{') - prnt(' *out_value = (long long)(%s);' % (name,)) - prnt(' return (%s) <= 0;' % (name,)) - prnt('}') - else: - assert tp is not None - assert check_value is None - if category == 'var': - ampersand = '&' - else: - ampersand = '' - extra = '' - if category == 'const' and isinstance(tp, model.StructOrUnion): - extra = 'const *' - ampersand = '&' - prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) - prnt('{') - prnt(' return (%s%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_gen_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_gen_const(is_int, name, tp) - - _loading_gen_constant = _loaded_noop - - def _load_constant(self, is_int, tp, name, module, check_value=None): - funcname = '_cffi_const_%s' % name - if check_value is not None: - assert is_int - self._load_known_int_constant(module, funcname) - value = check_value - elif is_int: - BType = self.ffi._typeof_locked("long long*")[0] - BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType) - negative = function(p) - value = int(p[0]) - if value < 0 and not negative: - BLongLong = self.ffi._typeof_locked("long long")[0] - value += (1 << (8*self.ffi.sizeof(BLongLong))) - else: - assert check_value is None - fntypeextra = '(*)(void)' - if isinstance(tp, model.StructOrUnion): - fntypeextra = '*' + fntypeextra - BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] - function = module.load_function(BFunc, funcname) - value = function() - if isinstance(tp, model.StructOrUnion): - value = value[0] - return value - - def _loaded_gen_constant(self, tp, name, module, library): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - value = self._load_constant(is_int, tp, name, module) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # enums - - def _check_int_constant_value(self, name, value): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % - name) - prnt(' sprintf(out_error, "%s has the real value %s, not %s",') - prnt(' "%s", buf, "%d");' % (name[:100], value)) - prnt(' return -1;') - prnt(' }') - - def _load_known_int_constant(self, module, funcname): - BType = self.ffi._typeof_locked("char[]")[0] - BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType, 256) - if function(p) < 0: - error = self.ffi.string(p) - if sys.version_info >= (3,): - error = str(error, 'utf-8') - raise VerificationError(error) - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_gen_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_gen_const(True, enumerator) - return - # - funcname = self._enum_funcname(prefix, name) - self.export_symbols.append(funcname) - prnt = self._prnt - prnt('int %s(char *out_error)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue) - prnt(' return 0;') - prnt('}') - prnt() - - def _loading_gen_enum(self, tp, name, module, prefix='enum'): - if tp.partial: - enumvalues = [self._load_constant(True, tp, enumerator, module) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - else: - funcname = self._enum_funcname(prefix, name) - self._load_known_int_constant(module, funcname) - - def _loaded_gen_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - type(library)._cffi_dir.append(enumerator) - - # ---------- - # macros: for now only for integers - - def _generate_gen_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_gen_const(True, name, check_value=check_value) - - _loading_gen_macro = _loaded_noop - - def _loaded_gen_macro(self, tp, name, module, library): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - value = self._load_constant(True, tp, name, module, - check_value=check_value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # global variables - - def _generate_gen_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - if tp.length_is_unknown(): - prnt = self._prnt - funcname = '_cffi_sizeof_%s' % (name,) - self.export_symbols.append(funcname) - prnt("size_t %s(void)" % funcname) - prnt("{") - prnt(" return sizeof(%s);" % (name,)) - prnt("}") - tp_ptr = model.PointerType(tp.item) - self._generate_gen_const(False, name, tp_ptr) - else: - tp_ptr = model.PointerType(tp) - self._generate_gen_const(False, name, tp_ptr, category='var') - - _loading_gen_variable = _loaded_noop - - def _loaded_gen_variable(self, tp, name, module, library): - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - funcname = '_cffi_sizeof_%s' % (name,) - BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] - function = module.load_function(BFunc, funcname) - size = function() - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - tp_ptr = model.PointerType(tp.item) - value = self._load_constant(False, tp_ptr, name, module) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - funcname = '_cffi_var_%s' % name - BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] - function = module.load_function(BFunc, funcname) - ptr = function() - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - -cffimod_header = r''' -#include -#include -#include -#include -#include /* XXX for ssize_t on some platforms */ - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -# define _cffi_float_complex_t _Fcomplex /* include for it */ -# define _cffi_double_complex_t _Dcomplex /* include for it */ -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -# define _cffi_float_complex_t float _Complex -# define _cffi_double_complex_t double _Complex -#endif -''' diff --git a/.venv/Lib/site-packages/cffi/verifier.py b/.venv/Lib/site-packages/cffi/verifier.py deleted file mode 100644 index e392a2b..0000000 --- a/.venv/Lib/site-packages/cffi/verifier.py +++ /dev/null @@ -1,306 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os, binascii, shutil, io -from . import __version_verifier_modules__ -from . import ffiplatform -from .error import VerificationError - -if sys.version_info >= (3, 3): - import importlib.machinery - def _extension_suffixes(): - return importlib.machinery.EXTENSION_SUFFIXES[:] -else: - import imp - def _extension_suffixes(): - return [suffix for suffix, _, type in imp.get_suffixes() - if type == imp.C_EXTENSION] - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - - -class Verifier(object): - - def __init__(self, ffi, preamble, tmpdir=None, modulename=None, - ext_package=None, tag='', force_generic_engine=False, - source_extension='.c', flags=None, relative_to=None, **kwds): - if ffi._parser._uses_new_feature: - raise VerificationError( - "feature not supported with ffi.verify(), but only " - "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) - self.ffi = ffi - self.preamble = preamble - if not modulename: - flattened_kwds = ffiplatform.flatten(kwds) - vengine_class = _locate_engine_class(ffi, force_generic_engine) - self._vengine = vengine_class(self) - self._vengine.patch_extension_kwds(kwds) - self.flags = flags - self.kwds = self.make_relative_to(kwds, relative_to) - # - if modulename: - if tag: - raise TypeError("can't specify both 'modulename' and 'tag'") - else: - key = '\x00'.join(['%d.%d' % sys.version_info[:2], - __version_verifier_modules__, - preamble, flattened_kwds] + - ffi._cdefsources) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) - k1 = k1.lstrip('0x').rstrip('L') - k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) - k2 = k2.lstrip('0').rstrip('L') - modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, - k1, k2) - suffix = _get_so_suffixes()[0] - self.tmpdir = tmpdir or _caller_dir_pycache() - self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) - self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) - self.ext_package = ext_package - self._has_source = False - self._has_module = False - - def write_source(self, file=None): - """Write the C source code. It is produced in 'self.sourcefilename', - which can be tweaked beforehand.""" - with self.ffi._lock: - if self._has_source and file is None: - raise VerificationError( - "source code already written") - self._write_source(file) - - def compile_module(self): - """Write the C source code (if not done already) and compile it. - This produces a dynamic link library in 'self.modulefilename'.""" - with self.ffi._lock: - if self._has_module: - raise VerificationError("module already compiled") - if not self._has_source: - self._write_source() - self._compile_module() - - def load_library(self): - """Get a C module from this Verifier instance. - Returns an instance of a FFILibrary class that behaves like the - objects returned by ffi.dlopen(), but that delegates all - operations to the C module. If necessary, the C code is written - and compiled first. - """ - with self.ffi._lock: - if not self._has_module: - self._locate_module() - if not self._has_module: - if not self._has_source: - self._write_source() - self._compile_module() - return self._load_library() - - def get_module_name(self): - basename = os.path.basename(self.modulefilename) - # kill both the .so extension and the other .'s, as introduced - # by Python 3: 'basename.cpython-33m.so' - basename = basename.split('.', 1)[0] - # and the _d added in Python 2 debug builds --- but try to be - # conservative and not kill a legitimate _d - if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): - basename = basename[:-2] - return basename - - def get_extension(self): - if not self._has_source: - with self.ffi._lock: - if not self._has_source: - self._write_source() - sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) - modname = self.get_module_name() - return ffiplatform.get_extension(sourcename, modname, **self.kwds) - - def generates_python_module(self): - return self._vengine._gen_python_module - - def make_relative_to(self, kwds, relative_to): - if relative_to and os.path.dirname(relative_to): - dirname = os.path.dirname(relative_to) - kwds = kwds.copy() - for key in ffiplatform.LIST_OF_FILE_NAMES: - if key in kwds: - lst = kwds[key] - if not isinstance(lst, (list, tuple)): - raise TypeError("keyword '%s' should be a list or tuple" - % (key,)) - lst = [os.path.join(dirname, fn) for fn in lst] - kwds[key] = lst - return kwds - - # ---------- - - def _locate_module(self): - if not os.path.isfile(self.modulefilename): - if self.ext_package: - try: - pkg = __import__(self.ext_package, None, None, ['__doc__']) - except ImportError: - return # cannot import the package itself, give up - # (e.g. it might be called differently before installation) - path = pkg.__path__ - else: - path = None - filename = self._vengine.find_module(self.get_module_name(), path, - _get_so_suffixes()) - if filename is None: - return - self.modulefilename = filename - self._vengine.collect_types() - self._has_module = True - - def _write_source_to(self, file): - self._vengine._f = file - try: - self._vengine.write_source_to_f() - finally: - del self._vengine._f - - def _write_source(self, file=None): - if file is not None: - self._write_source_to(file) - else: - # Write our source file to an in memory file. - f = NativeIO() - self._write_source_to(f) - source_data = f.getvalue() - - # Determine if this matches the current file - if os.path.exists(self.sourcefilename): - with open(self.sourcefilename, "r") as fp: - needs_written = not (fp.read() == source_data) - else: - needs_written = True - - # Actually write the file out if it doesn't match - if needs_written: - _ensure_dir(self.sourcefilename) - with open(self.sourcefilename, "w") as fp: - fp.write(source_data) - - # Set this flag - self._has_source = True - - def _compile_module(self): - # compile this C source - tmpdir = os.path.dirname(self.sourcefilename) - outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) - try: - same = ffiplatform.samefile(outputfilename, self.modulefilename) - except OSError: - same = False - if not same: - _ensure_dir(self.modulefilename) - shutil.move(outputfilename, self.modulefilename) - self._has_module = True - - def _load_library(self): - assert self._has_module - if self.flags is not None: - return self._vengine.load_library(self.flags) - else: - return self._vengine.load_library() - -# ____________________________________________________________ - -_FORCE_GENERIC_ENGINE = False # for tests - -def _locate_engine_class(ffi, force_generic_engine): - if _FORCE_GENERIC_ENGINE: - force_generic_engine = True - if not force_generic_engine: - if '__pypy__' in sys.builtin_module_names: - force_generic_engine = True - else: - try: - import _cffi_backend - except ImportError: - _cffi_backend = '?' - if ffi._backend is not _cffi_backend: - force_generic_engine = True - if force_generic_engine: - from . import vengine_gen - return vengine_gen.VGenericEngine - else: - from . import vengine_cpy - return vengine_cpy.VCPythonEngine - -# ____________________________________________________________ - -_TMPDIR = None - -def _caller_dir_pycache(): - if _TMPDIR: - return _TMPDIR - result = os.environ.get('CFFI_TMPDIR') - if result: - return result - filename = sys._getframe(2).f_code.co_filename - return os.path.abspath(os.path.join(os.path.dirname(filename), - '__pycache__')) - -def set_tmpdir(dirname): - """Set the temporary directory to use instead of __pycache__.""" - global _TMPDIR - _TMPDIR = dirname - -def cleanup_tmpdir(tmpdir=None, keep_so=False): - """Clean up the temporary directory by removing all files in it - called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" - tmpdir = tmpdir or _caller_dir_pycache() - try: - filelist = os.listdir(tmpdir) - except OSError: - return - if keep_so: - suffix = '.c' # only remove .c files - else: - suffix = _get_so_suffixes()[0].lower() - for fn in filelist: - if fn.lower().startswith('_cffi_') and ( - fn.lower().endswith(suffix) or fn.lower().endswith('.c')): - try: - os.unlink(os.path.join(tmpdir, fn)) - except OSError: - pass - clean_dir = [os.path.join(tmpdir, 'build')] - for dir in clean_dir: - try: - for fn in os.listdir(dir): - fn = os.path.join(dir, fn) - if os.path.isdir(fn): - clean_dir.append(fn) - else: - os.unlink(fn) - except OSError: - pass - -def _get_so_suffixes(): - suffixes = _extension_suffixes() - if not suffixes: - # bah, no C_EXTENSION available. Occurs on pypy without cpyext - if sys.platform == 'win32': - suffixes = [".pyd"] - else: - suffixes = [".so"] - - return suffixes - -def _ensure_dir(filename): - dirname = os.path.dirname(filename) - if dirname and not os.path.isdir(dirname): - os.makedirs(dirname) diff --git a/.venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER b/.venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/click-8.1.8.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt b/.venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt deleted file mode 100644 index d12a849..0000000 --- a/.venv/Lib/site-packages/click-8.1.8.dist-info/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2014 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/Lib/site-packages/click-8.1.8.dist-info/METADATA b/.venv/Lib/site-packages/click-8.1.8.dist-info/METADATA deleted file mode 100644 index 366d1a7..0000000 --- a/.venv/Lib/site-packages/click-8.1.8.dist-info/METADATA +++ /dev/null @@ -1,74 +0,0 @@ -Metadata-Version: 2.3 -Name: click -Version: 8.1.8 -Summary: Composable command line interface toolkit -Maintainer-email: Pallets -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Typing :: Typed -Requires-Dist: colorama; platform_system == 'Windows' -Requires-Dist: importlib-metadata; python_version < '3.8' -Project-URL: Changes, https://click.palletsprojects.com/changes/ -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://click.palletsprojects.com/ -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Source, https://github.com/pallets/click/ - -# $ click_ - -Click is a Python package for creating beautiful command line interfaces -in a composable way with as little code as necessary. It's the "Command -Line Interface Creation Kit". It's highly configurable but comes with -sensible defaults out of the box. - -It aims to make the process of writing command line tools quick and fun -while also preventing any frustration caused by the inability to -implement an intended CLI API. - -Click in three points: - -- Arbitrary nesting of commands -- Automatic help page generation -- Supports lazy loading of subcommands at runtime - - -## A Simple Example - -```python -import click - -@click.command() -@click.option("--count", default=1, help="Number of greetings.") -@click.option("--name", prompt="Your name", help="The person to greet.") -def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - -if __name__ == '__main__': - hello() -``` - -``` -$ python hello.py --count=3 -Your name: Click -Hello, Click! -Hello, Click! -Hello, Click! -``` - - -## Donate - -The Pallets organization develops and supports Click and other popular -packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, [please -donate today][]. - -[please donate today]: https://palletsprojects.com/donate - diff --git a/.venv/Lib/site-packages/click-8.1.8.dist-info/RECORD b/.venv/Lib/site-packages/click-8.1.8.dist-info/RECORD deleted file mode 100644 index a8eaba9..0000000 --- a/.venv/Lib/site-packages/click-8.1.8.dist-info/RECORD +++ /dev/null @@ -1,38 +0,0 @@ -click-8.1.8.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -click-8.1.8.dist-info/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 -click-8.1.8.dist-info/METADATA,sha256=WJtQ6uGS2ybLfvUE4vC0XIhIBr4yFGwjrMBR2fiCQ-Q,2263 -click-8.1.8.dist-info/RECORD,, -click-8.1.8.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82 -click/__init__.py,sha256=j1DJeCbga4ribkv5uyvIAzI0oFN13fW9mevDKShFelo,3188 -click/__pycache__/__init__.cpython-311.pyc,, -click/__pycache__/_compat.cpython-311.pyc,, -click/__pycache__/_termui_impl.cpython-311.pyc,, -click/__pycache__/_textwrap.cpython-311.pyc,, -click/__pycache__/_winconsole.cpython-311.pyc,, -click/__pycache__/core.cpython-311.pyc,, -click/__pycache__/decorators.cpython-311.pyc,, -click/__pycache__/exceptions.cpython-311.pyc,, -click/__pycache__/formatting.cpython-311.pyc,, -click/__pycache__/globals.cpython-311.pyc,, -click/__pycache__/parser.cpython-311.pyc,, -click/__pycache__/shell_completion.cpython-311.pyc,, -click/__pycache__/termui.cpython-311.pyc,, -click/__pycache__/testing.cpython-311.pyc,, -click/__pycache__/types.cpython-311.pyc,, -click/__pycache__/utils.cpython-311.pyc,, -click/_compat.py,sha256=IGKh_J5QdfKELitnRfTGHneejWxoCw_NX9tfMbdcg3w,18730 -click/_termui_impl.py,sha256=a5z7I9gOFeMmu7Gb6_RPyQ8GPuVP1EeblixcWSPSQPk,24783 -click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 -click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 -click/core.py,sha256=Q1nEVdctZwvIPOlt4vfHko0TYnHCeE40UEEul8Wpyvs,114748 -click/decorators.py,sha256=7t6F-QWowtLh6F_6l-4YV4Y4yNTcqFQEu9i37zIz68s,18925 -click/exceptions.py,sha256=V7zDT6emqJ8iNl0kF1P5kpFmLMWQ1T1L7aNNKM4YR0w,9600 -click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 -click/globals.py,sha256=cuJ6Bbo073lgEEmhjr394PeM-QFmXM-Ci-wmfsd7H5g,1954 -click/parser.py,sha256=h4sndcpF5OHrZQN8vD8IWb5OByvW7ABbhRToxovrqS8,19067 -click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -click/shell_completion.py,sha256=TR0dXEGcvWb9Eo3aaQEXGhnvNS3FF4H4QcuLnvAvYo4,18636 -click/termui.py,sha256=dLxiS70UOvIYBda_nEEZaPAFOVDVmRs1sEPMuLDowQo,28310 -click/testing.py,sha256=3RA8anCf7TZ8-5RAF5it2Te-aWXBAL5VLasQnMiC2ZQ,16282 -click/types.py,sha256=BD5Qqq4h-8kawBmOIzJlmq4xzThAf4wCvaOLZSBDNx0,36422 -click/utils.py,sha256=ce-IrO9ilII76LGkU354pOdHbepM8UftfNH7SfMU_28,20330 diff --git a/.venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL b/.venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL deleted file mode 100644 index e3c6fee..0000000 --- a/.venv/Lib/site-packages/click-8.1.8.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.10.1 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/Lib/site-packages/click/__init__.py b/.venv/Lib/site-packages/click/__init__.py deleted file mode 100644 index 2610d0e..0000000 --- a/.venv/Lib/site-packages/click/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Click is a simple Python module inspired by the stdlib optparse to make -writing command line scripts fun. Unlike other modules, it's based -around a simple API that does not come with too much magic and is -composable. -""" - -from .core import Argument as Argument -from .core import BaseCommand as BaseCommand -from .core import Command as Command -from .core import CommandCollection as CommandCollection -from .core import Context as Context -from .core import Group as Group -from .core import MultiCommand as MultiCommand -from .core import Option as Option -from .core import Parameter as Parameter -from .decorators import argument as argument -from .decorators import command as command -from .decorators import confirmation_option as confirmation_option -from .decorators import group as group -from .decorators import help_option as help_option -from .decorators import HelpOption as HelpOption -from .decorators import make_pass_decorator as make_pass_decorator -from .decorators import option as option -from .decorators import pass_context as pass_context -from .decorators import pass_obj as pass_obj -from .decorators import password_option as password_option -from .decorators import version_option as version_option -from .exceptions import Abort as Abort -from .exceptions import BadArgumentUsage as BadArgumentUsage -from .exceptions import BadOptionUsage as BadOptionUsage -from .exceptions import BadParameter as BadParameter -from .exceptions import ClickException as ClickException -from .exceptions import FileError as FileError -from .exceptions import MissingParameter as MissingParameter -from .exceptions import NoSuchOption as NoSuchOption -from .exceptions import UsageError as UsageError -from .formatting import HelpFormatter as HelpFormatter -from .formatting import wrap_text as wrap_text -from .globals import get_current_context as get_current_context -from .parser import OptionParser as OptionParser -from .termui import clear as clear -from .termui import confirm as confirm -from .termui import echo_via_pager as echo_via_pager -from .termui import edit as edit -from .termui import getchar as getchar -from .termui import launch as launch -from .termui import pause as pause -from .termui import progressbar as progressbar -from .termui import prompt as prompt -from .termui import secho as secho -from .termui import style as style -from .termui import unstyle as unstyle -from .types import BOOL as BOOL -from .types import Choice as Choice -from .types import DateTime as DateTime -from .types import File as File -from .types import FLOAT as FLOAT -from .types import FloatRange as FloatRange -from .types import INT as INT -from .types import IntRange as IntRange -from .types import ParamType as ParamType -from .types import Path as Path -from .types import STRING as STRING -from .types import Tuple as Tuple -from .types import UNPROCESSED as UNPROCESSED -from .types import UUID as UUID -from .utils import echo as echo -from .utils import format_filename as format_filename -from .utils import get_app_dir as get_app_dir -from .utils import get_binary_stream as get_binary_stream -from .utils import get_text_stream as get_text_stream -from .utils import open_file as open_file - -__version__ = "8.1.8" diff --git a/.venv/Lib/site-packages/click/_compat.py b/.venv/Lib/site-packages/click/_compat.py deleted file mode 100644 index 9153d15..0000000 --- a/.venv/Lib/site-packages/click/_compat.py +++ /dev/null @@ -1,623 +0,0 @@ -import codecs -import io -import os -import re -import sys -import typing as t -from weakref import WeakKeyDictionary - -CYGWIN = sys.platform.startswith("cygwin") -WIN = sys.platform.startswith("win") -auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None -_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") - - -def _make_text_stream( - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if encoding is None: - encoding = get_best_encoding(stream) - if errors is None: - errors = "replace" - return _NonClosingTextIOWrapper( - stream, - encoding, - errors, - line_buffering=True, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def is_ascii_encoding(encoding: str) -> bool: - """Checks if a given encoding is ascii.""" - try: - return codecs.lookup(encoding).name == "ascii" - except LookupError: - return False - - -def get_best_encoding(stream: t.IO[t.Any]) -> str: - """Returns the default stream encoding if not found.""" - rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() - if is_ascii_encoding(rv): - return "utf-8" - return rv - - -class _NonClosingTextIOWrapper(io.TextIOWrapper): - def __init__( - self, - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, - **extra: t.Any, - ) -> None: - self._stream = stream = t.cast( - t.BinaryIO, _FixupStream(stream, force_readable, force_writable) - ) - super().__init__(stream, encoding, errors, **extra) - - def __del__(self) -> None: - try: - self.detach() - except Exception: - pass - - def isatty(self) -> bool: - # https://bitbucket.org/pypy/pypy/issue/1803 - return self._stream.isatty() - - -class _FixupStream: - """The new io interface needs more from streams than streams - traditionally implement. As such, this fix-up code is necessary in - some circumstances. - - The forcing of readable and writable flags are there because some tools - put badly patched objects on sys (one such offender are certain version - of jupyter notebook). - """ - - def __init__( - self, - stream: t.BinaryIO, - force_readable: bool = False, - force_writable: bool = False, - ): - self._stream = stream - self._force_readable = force_readable - self._force_writable = force_writable - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._stream, name) - - def read1(self, size: int) -> bytes: - f = getattr(self._stream, "read1", None) - - if f is not None: - return t.cast(bytes, f(size)) - - return self._stream.read(size) - - def readable(self) -> bool: - if self._force_readable: - return True - x = getattr(self._stream, "readable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.read(0) - except Exception: - return False - return True - - def writable(self) -> bool: - if self._force_writable: - return True - x = getattr(self._stream, "writable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.write("") # type: ignore - except Exception: - try: - self._stream.write(b"") - except Exception: - return False - return True - - def seekable(self) -> bool: - x = getattr(self._stream, "seekable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.seek(self._stream.tell()) - except Exception: - return False - return True - - -def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - return isinstance(stream.read(0), bytes) - except Exception: - return default - # This happens in some cases where the stream was already - # closed. In this case, we assume the default. - - -def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - stream.write(b"") - except Exception: - try: - stream.write("") - return False - except Exception: - pass - return default - return True - - -def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_reader(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_reader(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_writer(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_writer(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _stream_is_misconfigured(stream: t.TextIO) -> bool: - """A stream is misconfigured if its encoding is ASCII.""" - # If the stream does not have an encoding set, we assume it's set - # to ASCII. This appears to happen in certain unittest - # environments. It's not quite clear what the correct behavior is - # but this at least will force Click to recover somehow. - return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") - - -def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: - """A stream attribute is compatible if it is equal to the - desired value or the desired value is unset and the attribute - has a value. - """ - stream_value = getattr(stream, attr, None) - return stream_value == value or (value is None and stream_value is not None) - - -def _is_compatible_text_stream( - stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> bool: - """Check if a stream's encoding and errors attributes are - compatible with the desired values. - """ - return _is_compat_stream_attr( - stream, "encoding", encoding - ) and _is_compat_stream_attr(stream, "errors", errors) - - -def _force_correct_text_stream( - text_stream: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - is_binary: t.Callable[[t.IO[t.Any], bool], bool], - find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if is_binary(text_stream, False): - binary_reader = t.cast(t.BinaryIO, text_stream) - else: - text_stream = t.cast(t.TextIO, text_stream) - # If the stream looks compatible, and won't default to a - # misconfigured ascii encoding, return it as-is. - if _is_compatible_text_stream(text_stream, encoding, errors) and not ( - encoding is None and _stream_is_misconfigured(text_stream) - ): - return text_stream - - # Otherwise, get the underlying binary reader. - possible_binary_reader = find_binary(text_stream) - - # If that's not possible, silently use the original reader - # and get mojibake instead of exceptions. - if possible_binary_reader is None: - return text_stream - - binary_reader = possible_binary_reader - - # Default errors to replace instead of strict in order to get - # something that works. - if errors is None: - errors = "replace" - - # Wrap the binary stream in a text stream with the correct - # encoding parameters. - return _make_text_stream( - binary_reader, - encoding, - errors, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def _force_correct_text_reader( - text_reader: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_reader, - encoding, - errors, - _is_binary_reader, - _find_binary_reader, - force_readable=force_readable, - ) - - -def _force_correct_text_writer( - text_writer: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_writable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_writer, - encoding, - errors, - _is_binary_writer, - _find_binary_writer, - force_writable=force_writable, - ) - - -def get_binary_stdin() -> t.BinaryIO: - reader = _find_binary_reader(sys.stdin) - if reader is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdin.") - return reader - - -def get_binary_stdout() -> t.BinaryIO: - writer = _find_binary_writer(sys.stdout) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdout.") - return writer - - -def get_binary_stderr() -> t.BinaryIO: - writer = _find_binary_writer(sys.stderr) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stderr.") - return writer - - -def get_text_stdin( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdin, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) - - -def get_text_stdout( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdout, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) - - -def get_text_stderr( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stderr, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) - - -def _wrap_io_open( - file: t.Union[str, "os.PathLike[str]", int], - mode: str, - encoding: t.Optional[str], - errors: t.Optional[str], -) -> t.IO[t.Any]: - """Handles not passing ``encoding`` and ``errors`` in binary mode.""" - if "b" in mode: - return open(file, mode) - - return open(file, mode, encoding=encoding, errors=errors) - - -def open_stream( - filename: "t.Union[str, os.PathLike[str]]", - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, -) -> t.Tuple[t.IO[t.Any], bool]: - binary = "b" in mode - filename = os.fspath(filename) - - # Standard streams first. These are simple because they ignore the - # atomic flag. Use fsdecode to handle Path("-"). - if os.fsdecode(filename) == "-": - if any(m in mode for m in ["w", "a", "x"]): - if binary: - return get_binary_stdout(), False - return get_text_stdout(encoding=encoding, errors=errors), False - if binary: - return get_binary_stdin(), False - return get_text_stdin(encoding=encoding, errors=errors), False - - # Non-atomic writes directly go out through the regular open functions. - if not atomic: - return _wrap_io_open(filename, mode, encoding, errors), True - - # Some usability stuff for atomic writes - if "a" in mode: - raise ValueError( - "Appending to an existing file is not supported, because that" - " would involve an expensive `copy`-operation to a temporary" - " file. Open the file in normal `w`-mode and copy explicitly" - " if that's what you're after." - ) - if "x" in mode: - raise ValueError("Use the `overwrite`-parameter instead.") - if "w" not in mode: - raise ValueError("Atomic writes only make sense with `w`-mode.") - - # Atomic writes are more complicated. They work by opening a file - # as a proxy in the same folder and then using the fdopen - # functionality to wrap it in a Python file. Then we wrap it in an - # atomic file that moves the file over on close. - import errno - import random - - try: - perm: t.Optional[int] = os.stat(filename).st_mode - except OSError: - perm = None - - flags = os.O_RDWR | os.O_CREAT | os.O_EXCL - - if binary: - flags |= getattr(os, "O_BINARY", 0) - - while True: - tmp_filename = os.path.join( - os.path.dirname(filename), - f".__atomic-write{random.randrange(1 << 32):08x}", - ) - try: - fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) - break - except OSError as e: - if e.errno == errno.EEXIST or ( - os.name == "nt" - and e.errno == errno.EACCES - and os.path.isdir(e.filename) - and os.access(e.filename, os.W_OK) - ): - continue - raise - - if perm is not None: - os.chmod(tmp_filename, perm) # in case perm includes bits in umask - - f = _wrap_io_open(fd, mode, encoding, errors) - af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) - return t.cast(t.IO[t.Any], af), True - - -class _AtomicFile: - def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: - self._f = f - self._tmp_filename = tmp_filename - self._real_filename = real_filename - self.closed = False - - @property - def name(self) -> str: - return self._real_filename - - def close(self, delete: bool = False) -> None: - if self.closed: - return - self._f.close() - os.replace(self._tmp_filename, self._real_filename) - self.closed = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._f, name) - - def __enter__(self) -> "_AtomicFile": - return self - - def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: - self.close(delete=exc_type is not None) - - def __repr__(self) -> str: - return repr(self._f) - - -def strip_ansi(value: str) -> str: - return _ansi_re.sub("", value) - - -def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: - while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): - stream = stream._stream - - return stream.__class__.__module__.startswith("ipykernel.") - - -def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None -) -> bool: - if color is None: - if stream is None: - stream = sys.stdin - return not isatty(stream) and not _is_jupyter_kernel_output(stream) - return not color - - -# On Windows, wrap the output streams with colorama to support ANSI -# color codes. -# NOTE: double check is needed so mypy does not analyze this on Linux -if sys.platform.startswith("win") and WIN: - from ._winconsole import _get_windows_console_stream - - def _get_argv_encoding() -> str: - import locale - - return locale.getpreferredencoding() - - _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def auto_wrap_for_ansi( - stream: t.TextIO, color: t.Optional[bool] = None - ) -> t.TextIO: - """Support ANSI color and style codes on Windows by wrapping a - stream with colorama. - """ - try: - cached = _ansi_stream_wrappers.get(stream) - except Exception: - cached = None - - if cached is not None: - return cached - - import colorama - - strip = should_strip_ansi(stream, color) - ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) - rv = t.cast(t.TextIO, ansi_wrapper.stream) - _write = rv.write - - def _safe_write(s): - try: - return _write(s) - except BaseException: - ansi_wrapper.reset_all() - raise - - rv.write = _safe_write - - try: - _ansi_stream_wrappers[stream] = rv - except Exception: - pass - - return rv - -else: - - def _get_argv_encoding() -> str: - return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() - - def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] - ) -> t.Optional[t.TextIO]: - return None - - -def term_len(x: str) -> int: - return len(strip_ansi(x)) - - -def isatty(stream: t.IO[t.Any]) -> bool: - try: - return stream.isatty() - except Exception: - return False - - -def _make_cached_stream_func( - src_func: t.Callable[[], t.Optional[t.TextIO]], - wrapper_func: t.Callable[[], t.TextIO], -) -> t.Callable[[], t.Optional[t.TextIO]]: - cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def func() -> t.Optional[t.TextIO]: - stream = src_func() - - if stream is None: - return None - - try: - rv = cache.get(stream) - except Exception: - rv = None - if rv is not None: - return rv - rv = wrapper_func() - try: - cache[stream] = rv - except Exception: - pass - return rv - - return func - - -_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) -_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) -_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) - - -binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { - "stdin": get_binary_stdin, - "stdout": get_binary_stdout, - "stderr": get_binary_stderr, -} - -text_streams: t.Mapping[ - str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] -] = { - "stdin": get_text_stdin, - "stdout": get_text_stdout, - "stderr": get_text_stderr, -} diff --git a/.venv/Lib/site-packages/click/_termui_impl.py b/.venv/Lib/site-packages/click/_termui_impl.py deleted file mode 100644 index ad9f8f6..0000000 --- a/.venv/Lib/site-packages/click/_termui_impl.py +++ /dev/null @@ -1,788 +0,0 @@ -""" -This module contains implementations for the termui module. To keep the -import time of Click down, some infrequently used functionality is -placed in this module and only imported as needed. -""" - -import contextlib -import math -import os -import sys -import time -import typing as t -from gettext import gettext as _ -from io import StringIO -from shutil import which -from types import TracebackType - -from ._compat import _default_text_stdout -from ._compat import CYGWIN -from ._compat import get_best_encoding -from ._compat import isatty -from ._compat import open_stream -from ._compat import strip_ansi -from ._compat import term_len -from ._compat import WIN -from .exceptions import ClickException -from .utils import echo - -V = t.TypeVar("V") - -if os.name == "nt": - BEFORE_BAR = "\r" - AFTER_BAR = "\n" -else: - BEFORE_BAR = "\r\033[?25l" - AFTER_BAR = "\033[?25h\n" - - -class ProgressBar(t.Generic[V]): - def __init__( - self, - iterable: t.Optional[t.Iterable[V]], - length: t.Optional[int] = None, - fill_char: str = "#", - empty_char: str = " ", - bar_template: str = "%(bar)s", - info_sep: str = " ", - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - label: t.Optional[str] = None, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, - width: int = 30, - ) -> None: - self.fill_char = fill_char - self.empty_char = empty_char - self.bar_template = bar_template - self.info_sep = info_sep - self.show_eta = show_eta - self.show_percent = show_percent - self.show_pos = show_pos - self.item_show_func = item_show_func - self.label: str = label or "" - - if file is None: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - file = StringIO() - - self.file = file - self.color = color - self.update_min_steps = update_min_steps - self._completed_intervals = 0 - self.width: int = width - self.autowidth: bool = width == 0 - - if length is None: - from operator import length_hint - - length = length_hint(iterable, -1) - - if length == -1: - length = None - if iterable is None: - if length is None: - raise TypeError("iterable or length is required") - iterable = t.cast(t.Iterable[V], range(length)) - self.iter: t.Iterable[V] = iter(iterable) - self.length = length - self.pos = 0 - self.avg: t.List[float] = [] - self.last_eta: float - self.start: float - self.start = self.last_eta = time.time() - self.eta_known: bool = False - self.finished: bool = False - self.max_width: t.Optional[int] = None - self.entered: bool = False - self.current_item: t.Optional[V] = None - self.is_hidden: bool = not isatty(self.file) - self._last_line: t.Optional[str] = None - - def __enter__(self) -> "ProgressBar[V]": - self.entered = True - self.render_progress() - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.render_finish() - - def __iter__(self) -> t.Iterator[V]: - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - self.render_progress() - return self.generator() - - def __next__(self) -> V: - # Iteration is defined in terms of a generator function, - # returned by iter(self); use that to define next(). This works - # because `self.iter` is an iterable consumed by that generator, - # so it is re-entry safe. Calling `next(self.generator())` - # twice works and does "what you want". - return next(iter(self)) - - def render_finish(self) -> None: - if self.is_hidden: - return - self.file.write(AFTER_BAR) - self.file.flush() - - @property - def pct(self) -> float: - if self.finished: - return 1.0 - return min(self.pos / (float(self.length or 1) or 1), 1.0) - - @property - def time_per_iteration(self) -> float: - if not self.avg: - return 0.0 - return sum(self.avg) / float(len(self.avg)) - - @property - def eta(self) -> float: - if self.length is not None and not self.finished: - return self.time_per_iteration * (self.length - self.pos) - return 0.0 - - def format_eta(self) -> str: - if self.eta_known: - t = int(self.eta) - seconds = t % 60 - t //= 60 - minutes = t % 60 - t //= 60 - hours = t % 24 - t //= 24 - if t > 0: - return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" - else: - return f"{hours:02}:{minutes:02}:{seconds:02}" - return "" - - def format_pos(self) -> str: - pos = str(self.pos) - if self.length is not None: - pos += f"/{self.length}" - return pos - - def format_pct(self) -> str: - return f"{int(self.pct * 100): 4}%"[1:] - - def format_bar(self) -> str: - if self.length is not None: - bar_length = int(self.pct * self.width) - bar = self.fill_char * bar_length - bar += self.empty_char * (self.width - bar_length) - elif self.finished: - bar = self.fill_char * self.width - else: - chars = list(self.empty_char * (self.width or 1)) - if self.time_per_iteration != 0: - chars[ - int( - (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) - * self.width - ) - ] = self.fill_char - bar = "".join(chars) - return bar - - def format_progress_line(self) -> str: - show_percent = self.show_percent - - info_bits = [] - if self.length is not None and show_percent is None: - show_percent = not self.show_pos - - if self.show_pos: - info_bits.append(self.format_pos()) - if show_percent: - info_bits.append(self.format_pct()) - if self.show_eta and self.eta_known and not self.finished: - info_bits.append(self.format_eta()) - if self.item_show_func is not None: - item_info = self.item_show_func(self.current_item) - if item_info is not None: - info_bits.append(item_info) - - return ( - self.bar_template - % { - "label": self.label, - "bar": self.format_bar(), - "info": self.info_sep.join(info_bits), - } - ).rstrip() - - def render_progress(self) -> None: - import shutil - - if self.is_hidden: - # Only output the label as it changes if the output is not a - # TTY. Use file=stderr if you expect to be piping stdout. - if self._last_line != self.label: - self._last_line = self.label - echo(self.label, file=self.file, color=self.color) - - return - - buf = [] - # Update width in case the terminal has been resized - if self.autowidth: - old_width = self.width - self.width = 0 - clutter_length = term_len(self.format_progress_line()) - new_width = max(0, shutil.get_terminal_size().columns - clutter_length) - if new_width < old_width: - buf.append(BEFORE_BAR) - buf.append(" " * self.max_width) # type: ignore - self.max_width = new_width - self.width = new_width - - clear_width = self.width - if self.max_width is not None: - clear_width = self.max_width - - buf.append(BEFORE_BAR) - line = self.format_progress_line() - line_len = term_len(line) - if self.max_width is None or self.max_width < line_len: - self.max_width = line_len - - buf.append(line) - buf.append(" " * (clear_width - line_len)) - line = "".join(buf) - # Render the line only if it changed. - - if line != self._last_line: - self._last_line = line - echo(line, file=self.file, color=self.color, nl=False) - self.file.flush() - - def make_step(self, n_steps: int) -> None: - self.pos += n_steps - if self.length is not None and self.pos >= self.length: - self.finished = True - - if (time.time() - self.last_eta) < 1.0: - return - - self.last_eta = time.time() - - # self.avg is a rolling list of length <= 7 of steps where steps are - # defined as time elapsed divided by the total progress through - # self.length. - if self.pos: - step = (time.time() - self.start) / self.pos - else: - step = time.time() - self.start - - self.avg = self.avg[-6:] + [step] - - self.eta_known = self.length is not None - - def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: - """Update the progress bar by advancing a specified number of - steps, and optionally set the ``current_item`` for this new - position. - - :param n_steps: Number of steps to advance. - :param current_item: Optional item to set as ``current_item`` - for the updated position. - - .. versionchanged:: 8.0 - Added the ``current_item`` optional parameter. - - .. versionchanged:: 8.0 - Only render when the number of steps meets the - ``update_min_steps`` threshold. - """ - if current_item is not None: - self.current_item = current_item - - self._completed_intervals += n_steps - - if self._completed_intervals >= self.update_min_steps: - self.make_step(self._completed_intervals) - self.render_progress() - self._completed_intervals = 0 - - def finish(self) -> None: - self.eta_known = False - self.current_item = None - self.finished = True - - def generator(self) -> t.Iterator[V]: - """Return a generator which yields the items added to the bar - during construction, and updates the progress bar *after* the - yielded block returns. - """ - # WARNING: the iterator interface for `ProgressBar` relies on - # this and only works because this is a simple generator which - # doesn't create or manage additional state. If this function - # changes, the impact should be evaluated both against - # `iter(bar)` and `next(bar)`. `next()` in particular may call - # `self.generator()` repeatedly, and this must remain safe in - # order for that interface to work. - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - - if self.is_hidden: - yield from self.iter - else: - for rv in self.iter: - self.current_item = rv - - # This allows show_item_func to be updated before the - # item is processed. Only trigger at the beginning of - # the update interval. - if self._completed_intervals == 0: - self.render_progress() - - yield rv - self.update(1) - - self.finish() - self.render_progress() - - -def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: - """Decide what method to use for paging through text.""" - stdout = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if stdout is None: - stdout = StringIO() - - if not isatty(sys.stdin) or not isatty(stdout): - return _nullpager(stdout, generator, color) - pager_cmd = (os.environ.get("PAGER", None) or "").strip() - if pager_cmd: - if WIN: - if _tempfilepager(generator, pager_cmd, color): - return - elif _pipepager(generator, pager_cmd, color): - return - if os.environ.get("TERM") in ("dumb", "emacs"): - return _nullpager(stdout, generator, color) - if (WIN or sys.platform.startswith("os2")) and _tempfilepager( - generator, "more", color - ): - return - if _pipepager(generator, "less", color): - return - - import tempfile - - fd, filename = tempfile.mkstemp() - os.close(fd) - try: - if _pipepager(generator, "more", color): - return - return _nullpager(stdout, generator, color) - finally: - os.unlink(filename) - - -def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> bool: - """Page through text by feeding it to another program. Invoking a - pager through this might support colors. - - Returns True if the command was found, False otherwise and thus another - pager should be attempted. - """ - cmd_absolute = which(cmd) - if cmd_absolute is None: - return False - - import subprocess - - env = dict(os.environ) - - # If we're piping to less we might support colors under the - # condition that - cmd_detail = cmd.rsplit("/", 1)[-1].split() - if color is None and cmd_detail[0] == "less": - less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" - if not less_flags: - env["LESS"] = "-R" - color = True - elif "r" in less_flags or "R" in less_flags: - color = True - - c = subprocess.Popen( - [cmd_absolute], - shell=True, - stdin=subprocess.PIPE, - env=env, - errors="replace", - text=True, - ) - assert c.stdin is not None - try: - for text in generator: - if not color: - text = strip_ansi(text) - - c.stdin.write(text) - except (OSError, KeyboardInterrupt): - pass - else: - c.stdin.close() - - # Less doesn't respect ^C, but catches it for its own UI purposes (aborting - # search or other commands inside less). - # - # That means when the user hits ^C, the parent process (click) terminates, - # but less is still alive, paging the output and messing up the terminal. - # - # If the user wants to make the pager exit on ^C, they should set - # `LESS='-K'`. It's not our decision to make. - while True: - try: - c.wait() - except KeyboardInterrupt: - pass - else: - break - - return True - - -def _tempfilepager( - generator: t.Iterable[str], - cmd: str, - color: t.Optional[bool], -) -> bool: - """Page through text by invoking a program on a temporary file. - - Returns True if the command was found, False otherwise and thus another - pager should be attempted. - """ - # Which is necessary for Windows, it is also recommended in the Popen docs. - cmd_absolute = which(cmd) - if cmd_absolute is None: - return False - - import subprocess - import tempfile - - fd, filename = tempfile.mkstemp() - # TODO: This never terminates if the passed generator never terminates. - text = "".join(generator) - if not color: - text = strip_ansi(text) - encoding = get_best_encoding(sys.stdout) - with open_stream(filename, "wb")[0] as f: - f.write(text.encode(encoding)) - try: - subprocess.call([cmd_absolute, filename]) - except OSError: - # Command not found - pass - finally: - os.close(fd) - os.unlink(filename) - - return True - - -def _nullpager( - stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] -) -> None: - """Simply print unformatted text. This is the ultimate fallback.""" - for text in generator: - if not color: - text = strip_ansi(text) - stream.write(text) - - -class Editor: - def __init__( - self, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - ) -> None: - self.editor = editor - self.env = env - self.require_save = require_save - self.extension = extension - - def get_editor(self) -> str: - if self.editor is not None: - return self.editor - for key in "VISUAL", "EDITOR": - rv = os.environ.get(key) - if rv: - return rv - if WIN: - return "notepad" - for editor in "sensible-editor", "vim", "nano": - if which(editor) is not None: - return editor - return "vi" - - def edit_file(self, filename: str) -> None: - import subprocess - - editor = self.get_editor() - environ: t.Optional[t.Dict[str, str]] = None - - if self.env: - environ = os.environ.copy() - environ.update(self.env) - - try: - c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) - exit_code = c.wait() - if exit_code != 0: - raise ClickException( - _("{editor}: Editing failed").format(editor=editor) - ) - except OSError as e: - raise ClickException( - _("{editor}: Editing failed: {e}").format(editor=editor, e=e) - ) from e - - def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: - import tempfile - - if not text: - data = b"" - elif isinstance(text, (bytes, bytearray)): - data = text - else: - if text and not text.endswith("\n"): - text += "\n" - - if WIN: - data = text.replace("\n", "\r\n").encode("utf-8-sig") - else: - data = text.encode("utf-8") - - fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) - f: t.BinaryIO - - try: - with os.fdopen(fd, "wb") as f: - f.write(data) - - # If the filesystem resolution is 1 second, like Mac OS - # 10.12 Extended, or 2 seconds, like FAT32, and the editor - # closes very fast, require_save can fail. Set the modified - # time to be 2 seconds in the past to work around this. - os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) - # Depending on the resolution, the exact value might not be - # recorded, so get the new recorded value. - timestamp = os.path.getmtime(name) - - self.edit_file(name) - - if self.require_save and os.path.getmtime(name) == timestamp: - return None - - with open(name, "rb") as f: - rv = f.read() - - if isinstance(text, (bytes, bytearray)): - return rv - - return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore - finally: - os.unlink(name) - - -def open_url(url: str, wait: bool = False, locate: bool = False) -> int: - import subprocess - - def _unquote_file(url: str) -> str: - from urllib.parse import unquote - - if url.startswith("file://"): - url = unquote(url[7:]) - - return url - - if sys.platform == "darwin": - args = ["open"] - if wait: - args.append("-W") - if locate: - args.append("-R") - args.append(_unquote_file(url)) - null = open("/dev/null", "w") - try: - return subprocess.Popen(args, stderr=null).wait() - finally: - null.close() - elif WIN: - if locate: - url = _unquote_file(url) - args = ["explorer", f"/select,{url}"] - else: - args = ["start"] - if wait: - args.append("/WAIT") - args.append("") - args.append(url) - try: - return subprocess.call(args) - except OSError: - # Command not found - return 127 - elif CYGWIN: - if locate: - url = _unquote_file(url) - args = ["cygstart", os.path.dirname(url)] - else: - args = ["cygstart"] - if wait: - args.append("-w") - args.append(url) - try: - return subprocess.call(args) - except OSError: - # Command not found - return 127 - - try: - if locate: - url = os.path.dirname(_unquote_file(url)) or "." - else: - url = _unquote_file(url) - c = subprocess.Popen(["xdg-open", url]) - if wait: - return c.wait() - return 0 - except OSError: - if url.startswith(("http://", "https://")) and not locate and not wait: - import webbrowser - - webbrowser.open(url) - return 0 - return 1 - - -def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: - if ch == "\x03": - raise KeyboardInterrupt() - - if ch == "\x04" and not WIN: # Unix-like, Ctrl+D - raise EOFError() - - if ch == "\x1a" and WIN: # Windows, Ctrl+Z - raise EOFError() - - return None - - -if WIN: - import msvcrt - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - yield -1 - - def getchar(echo: bool) -> str: - # The function `getch` will return a bytes object corresponding to - # the pressed character. Since Windows 10 build 1803, it will also - # return \x00 when called a second time after pressing a regular key. - # - # `getwch` does not share this probably-bugged behavior. Moreover, it - # returns a Unicode object by default, which is what we want. - # - # Either of these functions will return \x00 or \xe0 to indicate - # a special key, and you need to call the same function again to get - # the "rest" of the code. The fun part is that \u00e0 is - # "latin small letter a with grave", so if you type that on a French - # keyboard, you _also_ get a \xe0. - # E.g., consider the Up arrow. This returns \xe0 and then \x48. The - # resulting Unicode string reads as "a with grave" + "capital H". - # This is indistinguishable from when the user actually types - # "a with grave" and then "capital H". - # - # When \xe0 is returned, we assume it's part of a special-key sequence - # and call `getwch` again, but that means that when the user types - # the \u00e0 character, `getchar` doesn't return until a second - # character is typed. - # The alternative is returning immediately, but that would mess up - # cross-platform handling of arrow keys and others that start with - # \xe0. Another option is using `getch`, but then we can't reliably - # read non-ASCII characters, because return values of `getch` are - # limited to the current 8-bit codepage. - # - # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` - # is doing the right thing in more situations than with `getch`. - func: t.Callable[[], str] - - if echo: - func = msvcrt.getwche # type: ignore - else: - func = msvcrt.getwch # type: ignore - - rv = func() - - if rv in ("\x00", "\xe0"): - # \x00 and \xe0 are control characters that indicate special key, - # see above. - rv += func() - - _translate_ch_to_exc(rv) - return rv - -else: - import termios - import tty - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - f: t.Optional[t.TextIO] - fd: int - - if not isatty(sys.stdin): - f = open("/dev/tty") - fd = f.fileno() - else: - fd = sys.stdin.fileno() - f = None - - try: - old_settings = termios.tcgetattr(fd) - - try: - tty.setraw(fd) - yield fd - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - sys.stdout.flush() - - if f is not None: - f.close() - except termios.error: - pass - - def getchar(echo: bool) -> str: - with raw_terminal() as fd: - ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") - - if echo and isatty(sys.stdout): - sys.stdout.write(ch) - - _translate_ch_to_exc(ch) - return ch diff --git a/.venv/Lib/site-packages/click/_textwrap.py b/.venv/Lib/site-packages/click/_textwrap.py deleted file mode 100644 index b47dcbd..0000000 --- a/.venv/Lib/site-packages/click/_textwrap.py +++ /dev/null @@ -1,49 +0,0 @@ -import textwrap -import typing as t -from contextlib import contextmanager - - -class TextWrapper(textwrap.TextWrapper): - def _handle_long_word( - self, - reversed_chunks: t.List[str], - cur_line: t.List[str], - cur_len: int, - width: int, - ) -> None: - space_left = max(width - cur_len, 1) - - if self.break_long_words: - last = reversed_chunks[-1] - cut = last[:space_left] - res = last[space_left:] - cur_line.append(cut) - reversed_chunks[-1] = res - elif not cur_line: - cur_line.append(reversed_chunks.pop()) - - @contextmanager - def extra_indent(self, indent: str) -> t.Iterator[None]: - old_initial_indent = self.initial_indent - old_subsequent_indent = self.subsequent_indent - self.initial_indent += indent - self.subsequent_indent += indent - - try: - yield - finally: - self.initial_indent = old_initial_indent - self.subsequent_indent = old_subsequent_indent - - def indent_only(self, text: str) -> str: - rv = [] - - for idx, line in enumerate(text.splitlines()): - indent = self.initial_indent - - if idx > 0: - indent = self.subsequent_indent - - rv.append(f"{indent}{line}") - - return "\n".join(rv) diff --git a/.venv/Lib/site-packages/click/_winconsole.py b/.venv/Lib/site-packages/click/_winconsole.py deleted file mode 100644 index 6b20df3..0000000 --- a/.venv/Lib/site-packages/click/_winconsole.py +++ /dev/null @@ -1,279 +0,0 @@ -# This module is based on the excellent work by Adam Bartoš who -# provided a lot of what went into the implementation here in -# the discussion to issue1602 in the Python bug tracker. -# -# There are some general differences in regards to how this works -# compared to the original patches as we do not need to patch -# the entire interpreter but just work in our little world of -# echo and prompt. -import io -import sys -import time -import typing as t -from ctypes import byref -from ctypes import c_char -from ctypes import c_char_p -from ctypes import c_int -from ctypes import c_ssize_t -from ctypes import c_ulong -from ctypes import c_void_p -from ctypes import POINTER -from ctypes import py_object -from ctypes import Structure -from ctypes.wintypes import DWORD -from ctypes.wintypes import HANDLE -from ctypes.wintypes import LPCWSTR -from ctypes.wintypes import LPWSTR - -from ._compat import _NonClosingTextIOWrapper - -assert sys.platform == "win32" -import msvcrt # noqa: E402 -from ctypes import windll # noqa: E402 -from ctypes import WINFUNCTYPE # noqa: E402 - -c_ssize_p = POINTER(c_ssize_t) - -kernel32 = windll.kernel32 -GetStdHandle = kernel32.GetStdHandle -ReadConsoleW = kernel32.ReadConsoleW -WriteConsoleW = kernel32.WriteConsoleW -GetConsoleMode = kernel32.GetConsoleMode -GetLastError = kernel32.GetLastError -GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) -CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ("CommandLineToArgvW", windll.shell32) -) -LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) - -STDIN_HANDLE = GetStdHandle(-10) -STDOUT_HANDLE = GetStdHandle(-11) -STDERR_HANDLE = GetStdHandle(-12) - -PyBUF_SIMPLE = 0 -PyBUF_WRITABLE = 1 - -ERROR_SUCCESS = 0 -ERROR_NOT_ENOUGH_MEMORY = 8 -ERROR_OPERATION_ABORTED = 995 - -STDIN_FILENO = 0 -STDOUT_FILENO = 1 -STDERR_FILENO = 2 - -EOF = b"\x1a" -MAX_BYTES_WRITTEN = 32767 - -try: - from ctypes import pythonapi -except ImportError: - # On PyPy we cannot get buffers so our ability to operate here is - # severely limited. - get_buffer = None -else: - - class Py_buffer(Structure): - _fields_ = [ - ("buf", c_void_p), - ("obj", py_object), - ("len", c_ssize_t), - ("itemsize", c_ssize_t), - ("readonly", c_int), - ("ndim", c_int), - ("format", c_char_p), - ("shape", c_ssize_p), - ("strides", c_ssize_p), - ("suboffsets", c_ssize_p), - ("internal", c_void_p), - ] - - PyObject_GetBuffer = pythonapi.PyObject_GetBuffer - PyBuffer_Release = pythonapi.PyBuffer_Release - - def get_buffer(obj, writable=False): - buf = Py_buffer() - flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE - PyObject_GetBuffer(py_object(obj), byref(buf), flags) - - try: - buffer_type = c_char * buf.len - return buffer_type.from_address(buf.buf) - finally: - PyBuffer_Release(byref(buf)) - - -class _WindowsConsoleRawIOBase(io.RawIOBase): - def __init__(self, handle): - self.handle = handle - - def isatty(self): - super().isatty() - return True - - -class _WindowsConsoleReader(_WindowsConsoleRawIOBase): - def readable(self): - return True - - def readinto(self, b): - bytes_to_be_read = len(b) - if not bytes_to_be_read: - return 0 - elif bytes_to_be_read % 2: - raise ValueError( - "cannot read odd number of bytes from UTF-16-LE encoded console" - ) - - buffer = get_buffer(b, writable=True) - code_units_to_be_read = bytes_to_be_read // 2 - code_units_read = c_ulong() - - rv = ReadConsoleW( - HANDLE(self.handle), - buffer, - code_units_to_be_read, - byref(code_units_read), - None, - ) - if GetLastError() == ERROR_OPERATION_ABORTED: - # wait for KeyboardInterrupt - time.sleep(0.1) - if not rv: - raise OSError(f"Windows error: {GetLastError()}") - - if buffer[0] == EOF: - return 0 - return 2 * code_units_read.value - - -class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): - def writable(self): - return True - - @staticmethod - def _get_error_message(errno): - if errno == ERROR_SUCCESS: - return "ERROR_SUCCESS" - elif errno == ERROR_NOT_ENOUGH_MEMORY: - return "ERROR_NOT_ENOUGH_MEMORY" - return f"Windows error {errno}" - - def write(self, b): - bytes_to_be_written = len(b) - buf = get_buffer(b) - code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 - code_units_written = c_ulong() - - WriteConsoleW( - HANDLE(self.handle), - buf, - code_units_to_be_written, - byref(code_units_written), - None, - ) - bytes_written = 2 * code_units_written.value - - if bytes_written == 0 and bytes_to_be_written > 0: - raise OSError(self._get_error_message(GetLastError())) - return bytes_written - - -class ConsoleStream: - def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: - self._text_stream = text_stream - self.buffer = byte_stream - - @property - def name(self) -> str: - return self.buffer.name - - def write(self, x: t.AnyStr) -> int: - if isinstance(x, str): - return self._text_stream.write(x) - try: - self.flush() - except Exception: - pass - return self.buffer.write(x) - - def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: - for line in lines: - self.write(line) - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._text_stream, name) - - def isatty(self) -> bool: - return self.buffer.isatty() - - def __repr__(self): - return f"" - - -def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { - 0: _get_text_stdin, - 1: _get_text_stdout, - 2: _get_text_stderr, -} - - -def _is_console(f: t.TextIO) -> bool: - if not hasattr(f, "fileno"): - return False - - try: - fileno = f.fileno() - except (OSError, io.UnsupportedOperation): - return False - - handle = msvcrt.get_osfhandle(fileno) - return bool(GetConsoleMode(handle, byref(DWORD()))) - - -def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> t.Optional[t.TextIO]: - if ( - get_buffer is not None - and encoding in {"utf-16-le", None} - and errors in {"strict", None} - and _is_console(f) - ): - func = _stream_factories.get(f.fileno()) - if func is not None: - b = getattr(f, "buffer", None) - - if b is None: - return None - - return func(b) diff --git a/.venv/Lib/site-packages/click/core.py b/.venv/Lib/site-packages/click/core.py deleted file mode 100644 index e630501..0000000 --- a/.venv/Lib/site-packages/click/core.py +++ /dev/null @@ -1,3047 +0,0 @@ -import enum -import errno -import inspect -import os -import sys -import typing as t -from collections import abc -from contextlib import contextmanager -from contextlib import ExitStack -from functools import update_wrapper -from gettext import gettext as _ -from gettext import ngettext -from itertools import repeat -from types import TracebackType - -from . import types -from .exceptions import Abort -from .exceptions import BadParameter -from .exceptions import ClickException -from .exceptions import Exit -from .exceptions import MissingParameter -from .exceptions import UsageError -from .formatting import HelpFormatter -from .formatting import join_options -from .globals import pop_context -from .globals import push_context -from .parser import _flag_needs_value -from .parser import OptionParser -from .parser import split_opt -from .termui import confirm -from .termui import prompt -from .termui import style -from .utils import _detect_program_name -from .utils import _expand_args -from .utils import echo -from .utils import make_default_short_help -from .utils import make_str -from .utils import PacifyFlushWrapper - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .decorators import HelpOption - from .shell_completion import CompletionItem - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -V = t.TypeVar("V") - - -def _complete_visible_commands( - ctx: "Context", incomplete: str -) -> t.Iterator[t.Tuple[str, "Command"]]: - """List all the subcommands of a group that start with the - incomplete value and aren't hidden. - - :param ctx: Invocation context for the group. - :param incomplete: Value being completed. May be empty. - """ - multi = t.cast(MultiCommand, ctx.command) - - for name in multi.list_commands(ctx): - if name.startswith(incomplete): - command = multi.get_command(ctx, name) - - if command is not None and not command.hidden: - yield name, command - - -def _check_multicommand( - base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False -) -> None: - if not base_command.chain or not isinstance(cmd, MultiCommand): - return - if register: - hint = ( - "It is not possible to add multi commands as children to" - " another multi command that is in chain mode." - ) - else: - hint = ( - "Found a multi command as subcommand to a multi command" - " that is in chain mode. This is not supported." - ) - raise RuntimeError( - f"{hint}. Command {base_command.name!r} is set to chain and" - f" {cmd_name!r} was added as a subcommand but it in itself is a" - f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" - f" within a chained {type(base_command).__name__} named" - f" {base_command.name!r})." - ) - - -def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: - return list(zip(*repeat(iter(iterable), batch_size))) - - -@contextmanager -def augment_usage_errors( - ctx: "Context", param: t.Optional["Parameter"] = None -) -> t.Iterator[None]: - """Context manager that attaches extra information to exceptions.""" - try: - yield - except BadParameter as e: - if e.ctx is None: - e.ctx = ctx - if param is not None and e.param is None: - e.param = param - raise - except UsageError as e: - if e.ctx is None: - e.ctx = ctx - raise - - -def iter_params_for_processing( - invocation_order: t.Sequence["Parameter"], - declaration_order: t.Sequence["Parameter"], -) -> t.List["Parameter"]: - """Returns all declared parameters in the order they should be processed. - - The declared parameters are re-shuffled depending on the order in which - they were invoked, as well as the eagerness of each parameters. - - The invocation order takes precedence over the declaration order. I.e. the - order in which the user provided them to the CLI is respected. - - This behavior and its effect on callback evaluation is detailed at: - https://click.palletsprojects.com/en/stable/advanced/#callback-evaluation-order - """ - - def sort_key(item: "Parameter") -> t.Tuple[bool, float]: - try: - idx: float = invocation_order.index(item) - except ValueError: - idx = float("inf") - - return not item.is_eager, idx - - return sorted(declaration_order, key=sort_key) - - -class ParameterSource(enum.Enum): - """This is an :class:`~enum.Enum` that indicates the source of a - parameter's value. - - Use :meth:`click.Context.get_parameter_source` to get the - source for a parameter by name. - - .. versionchanged:: 8.0 - Use :class:`~enum.Enum` and drop the ``validate`` method. - - .. versionchanged:: 8.0 - Added the ``PROMPT`` value. - """ - - COMMANDLINE = enum.auto() - """The value was provided by the command line args.""" - ENVIRONMENT = enum.auto() - """The value was provided with an environment variable.""" - DEFAULT = enum.auto() - """Used the default specified by the parameter.""" - DEFAULT_MAP = enum.auto() - """Used a default provided by :attr:`Context.default_map`.""" - PROMPT = enum.auto() - """Used a prompt to confirm a default or provide a value.""" - - -class Context: - """The context is a special internal object that holds state relevant - for the script execution at every single level. It's normally invisible - to commands unless they opt-in to getting access to it. - - The context is useful as it can pass internal objects around and can - control special execution features such as reading data from - environment variables. - - A context can be used as context manager in which case it will call - :meth:`close` on teardown. - - :param command: the command class for this context. - :param parent: the parent context. - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it is usually - the name of the script, for commands below it it's - the name of the script. - :param obj: an arbitrary object of user data. - :param auto_envvar_prefix: the prefix to use for automatic environment - variables. If this is `None` then reading - from environment variables is disabled. This - does not affect manually set environment - variables which are always read. - :param default_map: a dictionary (like object) with default values - for parameters. - :param terminal_width: the width of the terminal. The default is - inherit from parent context. If no context - defines the terminal width then auto - detection will be applied. - :param max_content_width: the maximum width for content rendered by - Click (this currently only affects help - pages). This defaults to 80 characters if - not overridden. In other words: even if the - terminal is larger than that, Click will not - format things wider than 80 characters by - default. In addition to that, formatters might - add some safety mapping on the right. - :param resilient_parsing: if this flag is enabled then Click will - parse without any interactivity or callback - invocation. Default values will also be - ignored. This is useful for implementing - things such as completion support. - :param allow_extra_args: if this is set to `True` then extra arguments - at the end will not raise an error and will be - kept on the context. The default is to inherit - from the command. - :param allow_interspersed_args: if this is set to `False` then options - and arguments cannot be mixed. The - default is to inherit from the command. - :param ignore_unknown_options: instructs click to ignore options it does - not know and keeps them for later - processing. - :param help_option_names: optionally a list of strings that define how - the default help parameter is named. The - default is ``['--help']``. - :param token_normalize_func: an optional function that is used to - normalize tokens (options, choices, - etc.). This for instance can be used to - implement case insensitive behavior. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are used in texts that Click prints which is by - default not the case. This for instance would affect - help output. - :param show_default: Show the default value for commands. If this - value is not set, it defaults to the value from the parent - context. ``Command.show_default`` overrides this default for the - specific command. - - .. versionchanged:: 8.1 - The ``show_default`` parameter is overridden by - ``Command.show_default``, instead of the other way around. - - .. versionchanged:: 8.0 - The ``show_default`` parameter defaults to the value from the - parent context. - - .. versionchanged:: 7.1 - Added the ``show_default`` parameter. - - .. versionchanged:: 4.0 - Added the ``color``, ``ignore_unknown_options``, and - ``max_content_width`` parameters. - - .. versionchanged:: 3.0 - Added the ``allow_extra_args`` and ``allow_interspersed_args`` - parameters. - - .. versionchanged:: 2.0 - Added the ``resilient_parsing``, ``help_option_names``, and - ``token_normalize_func`` parameters. - """ - - #: The formatter class to create with :meth:`make_formatter`. - #: - #: .. versionadded:: 8.0 - formatter_class: t.Type["HelpFormatter"] = HelpFormatter - - def __init__( - self, - command: "Command", - parent: t.Optional["Context"] = None, - info_name: t.Optional[str] = None, - obj: t.Optional[t.Any] = None, - auto_envvar_prefix: t.Optional[str] = None, - default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, - terminal_width: t.Optional[int] = None, - max_content_width: t.Optional[int] = None, - resilient_parsing: bool = False, - allow_extra_args: t.Optional[bool] = None, - allow_interspersed_args: t.Optional[bool] = None, - ignore_unknown_options: t.Optional[bool] = None, - help_option_names: t.Optional[t.List[str]] = None, - token_normalize_func: t.Optional[t.Callable[[str], str]] = None, - color: t.Optional[bool] = None, - show_default: t.Optional[bool] = None, - ) -> None: - #: the parent context or `None` if none exists. - self.parent = parent - #: the :class:`Command` for this context. - self.command = command - #: the descriptive information name - self.info_name = info_name - #: Map of parameter names to their parsed values. Parameters - #: with ``expose_value=False`` are not stored. - self.params: t.Dict[str, t.Any] = {} - #: the leftover arguments. - self.args: t.List[str] = [] - #: protected arguments. These are arguments that are prepended - #: to `args` when certain parsing scenarios are encountered but - #: must be never propagated to another arguments. This is used - #: to implement nested parsing. - self.protected_args: t.List[str] = [] - #: the collected prefixes of the command's options. - self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() - - if obj is None and parent is not None: - obj = parent.obj - - #: the user object stored. - self.obj: t.Any = obj - self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) - - #: A dictionary (-like object) with defaults for parameters. - if ( - default_map is None - and info_name is not None - and parent is not None - and parent.default_map is not None - ): - default_map = parent.default_map.get(info_name) - - self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map - - #: This flag indicates if a subcommand is going to be executed. A - #: group callback can use this information to figure out if it's - #: being executed directly or because the execution flow passes - #: onwards to a subcommand. By default it's None, but it can be - #: the name of the subcommand to execute. - #: - #: If chaining is enabled this will be set to ``'*'`` in case - #: any commands are executed. It is however not possible to - #: figure out which ones. If you require this knowledge you - #: should use a :func:`result_callback`. - self.invoked_subcommand: t.Optional[str] = None - - if terminal_width is None and parent is not None: - terminal_width = parent.terminal_width - - #: The width of the terminal (None is autodetection). - self.terminal_width: t.Optional[int] = terminal_width - - if max_content_width is None and parent is not None: - max_content_width = parent.max_content_width - - #: The maximum width of formatted content (None implies a sensible - #: default which is 80 for most things). - self.max_content_width: t.Optional[int] = max_content_width - - if allow_extra_args is None: - allow_extra_args = command.allow_extra_args - - #: Indicates if the context allows extra args or if it should - #: fail on parsing. - #: - #: .. versionadded:: 3.0 - self.allow_extra_args = allow_extra_args - - if allow_interspersed_args is None: - allow_interspersed_args = command.allow_interspersed_args - - #: Indicates if the context allows mixing of arguments and - #: options or not. - #: - #: .. versionadded:: 3.0 - self.allow_interspersed_args: bool = allow_interspersed_args - - if ignore_unknown_options is None: - ignore_unknown_options = command.ignore_unknown_options - - #: Instructs click to ignore options that a command does not - #: understand and will store it on the context for later - #: processing. This is primarily useful for situations where you - #: want to call into external programs. Generally this pattern is - #: strongly discouraged because it's not possibly to losslessly - #: forward all arguments. - #: - #: .. versionadded:: 4.0 - self.ignore_unknown_options: bool = ignore_unknown_options - - if help_option_names is None: - if parent is not None: - help_option_names = parent.help_option_names - else: - help_option_names = ["--help"] - - #: The names for the help options. - self.help_option_names: t.List[str] = help_option_names - - if token_normalize_func is None and parent is not None: - token_normalize_func = parent.token_normalize_func - - #: An optional normalization function for tokens. This is - #: options, choices, commands etc. - self.token_normalize_func: t.Optional[t.Callable[[str], str]] = ( - token_normalize_func - ) - - #: Indicates if resilient parsing is enabled. In that case Click - #: will do its best to not cause any failures and default values - #: will be ignored. Useful for completion. - self.resilient_parsing: bool = resilient_parsing - - # If there is no envvar prefix yet, but the parent has one and - # the command on this level has a name, we can expand the envvar - # prefix automatically. - if auto_envvar_prefix is None: - if ( - parent is not None - and parent.auto_envvar_prefix is not None - and self.info_name is not None - ): - auto_envvar_prefix = ( - f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" - ) - else: - auto_envvar_prefix = auto_envvar_prefix.upper() - - if auto_envvar_prefix is not None: - auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") - - self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix - - if color is None and parent is not None: - color = parent.color - - #: Controls if styling output is wanted or not. - self.color: t.Optional[bool] = color - - if show_default is None and parent is not None: - show_default = parent.show_default - - #: Show option default values when formatting help text. - self.show_default: t.Optional[bool] = show_default - - self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] - self._depth = 0 - self._parameter_source: t.Dict[str, ParameterSource] = {} - self._exit_stack = ExitStack() - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire CLI - structure. - - .. code-block:: python - - with Context(cli) as ctx: - info = ctx.to_info_dict() - - .. versionadded:: 8.0 - """ - return { - "command": self.command.to_info_dict(self), - "info_name": self.info_name, - "allow_extra_args": self.allow_extra_args, - "allow_interspersed_args": self.allow_interspersed_args, - "ignore_unknown_options": self.ignore_unknown_options, - "auto_envvar_prefix": self.auto_envvar_prefix, - } - - def __enter__(self) -> "Context": - self._depth += 1 - push_context(self) - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self._depth -= 1 - if self._depth == 0: - self.close() - pop_context() - - @contextmanager - def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: - """This helper method can be used with the context object to promote - it to the current thread local (see :func:`get_current_context`). - The default behavior of this is to invoke the cleanup functions which - can be disabled by setting `cleanup` to `False`. The cleanup - functions are typically used for things such as closing file handles. - - If the cleanup is intended the context object can also be directly - used as a context manager. - - Example usage:: - - with ctx.scope(): - assert get_current_context() is ctx - - This is equivalent:: - - with ctx: - assert get_current_context() is ctx - - .. versionadded:: 5.0 - - :param cleanup: controls if the cleanup functions should be run or - not. The default is to run these functions. In - some situations the context only wants to be - temporarily pushed in which case this can be disabled. - Nested pushes automatically defer the cleanup. - """ - if not cleanup: - self._depth += 1 - try: - with self as rv: - yield rv - finally: - if not cleanup: - self._depth -= 1 - - @property - def meta(self) -> t.Dict[str, t.Any]: - """This is a dictionary which is shared with all the contexts - that are nested. It exists so that click utilities can store some - state here if they need to. It is however the responsibility of - that code to manage this dictionary well. - - The keys are supposed to be unique dotted strings. For instance - module paths are a good choice for it. What is stored in there is - irrelevant for the operation of click. However what is important is - that code that places data here adheres to the general semantics of - the system. - - Example usage:: - - LANG_KEY = f'{__name__}.lang' - - def set_language(value): - ctx = get_current_context() - ctx.meta[LANG_KEY] = value - - def get_language(): - return get_current_context().meta.get(LANG_KEY, 'en_US') - - .. versionadded:: 5.0 - """ - return self._meta - - def make_formatter(self) -> HelpFormatter: - """Creates the :class:`~click.HelpFormatter` for the help and - usage output. - - To quickly customize the formatter class used without overriding - this method, set the :attr:`formatter_class` attribute. - - .. versionchanged:: 8.0 - Added the :attr:`formatter_class` attribute. - """ - return self.formatter_class( - width=self.terminal_width, max_width=self.max_content_width - ) - - def with_resource(self, context_manager: t.ContextManager[V]) -> V: - """Register a resource as if it were used in a ``with`` - statement. The resource will be cleaned up when the context is - popped. - - Uses :meth:`contextlib.ExitStack.enter_context`. It calls the - resource's ``__enter__()`` method and returns the result. When - the context is popped, it closes the stack, which calls the - resource's ``__exit__()`` method. - - To register a cleanup function for something that isn't a - context manager, use :meth:`call_on_close`. Or use something - from :mod:`contextlib` to turn it into a context manager first. - - .. code-block:: python - - @click.group() - @click.option("--name") - @click.pass_context - def cli(ctx): - ctx.obj = ctx.with_resource(connect_db(name)) - - :param context_manager: The context manager to enter. - :return: Whatever ``context_manager.__enter__()`` returns. - - .. versionadded:: 8.0 - """ - return self._exit_stack.enter_context(context_manager) - - def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - """Register a function to be called when the context tears down. - - This can be used to close resources opened during the script - execution. Resources that support Python's context manager - protocol which would be used in a ``with`` statement should be - registered with :meth:`with_resource` instead. - - :param f: The function to execute on teardown. - """ - return self._exit_stack.callback(f) - - def close(self) -> None: - """Invoke all close callbacks registered with - :meth:`call_on_close`, and exit all context managers entered - with :meth:`with_resource`. - """ - self._exit_stack.close() - # In case the context is reused, create a new exit stack. - self._exit_stack = ExitStack() - - @property - def command_path(self) -> str: - """The computed command path. This is used for the ``usage`` - information on the help page. It's automatically created by - combining the info names of the chain of contexts to the root. - """ - rv = "" - if self.info_name is not None: - rv = self.info_name - if self.parent is not None: - parent_command_path = [self.parent.command_path] - - if isinstance(self.parent.command, Command): - for param in self.parent.command.get_params(self): - parent_command_path.extend(param.get_usage_pieces(self)) - - rv = f"{' '.join(parent_command_path)} {rv}" - return rv.lstrip() - - def find_root(self) -> "Context": - """Finds the outermost context.""" - node = self - while node.parent is not None: - node = node.parent - return node - - def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: - """Finds the closest object of a given type.""" - node: t.Optional[Context] = self - - while node is not None: - if isinstance(node.obj, object_type): - return node.obj - - node = node.parent - - return None - - def ensure_object(self, object_type: t.Type[V]) -> V: - """Like :meth:`find_object` but sets the innermost object to a - new instance of `object_type` if it does not exist. - """ - rv = self.find_object(object_type) - if rv is None: - self.obj = rv = object_type() - return rv - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[False]" = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: - """Get the default for a parameter from :attr:`default_map`. - - :param name: Name of the parameter. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - if self.default_map is not None: - value = self.default_map.get(name) - - if call and callable(value): - return value() - - return value - - return None - - def fail(self, message: str) -> "te.NoReturn": - """Aborts the execution of the program with a specific error - message. - - :param message: the error message to fail with. - """ - raise UsageError(message, self) - - def abort(self) -> "te.NoReturn": - """Aborts the script.""" - raise Abort() - - def exit(self, code: int = 0) -> "te.NoReturn": - """Exits the application with a given exit code.""" - raise Exit(code) - - def get_usage(self) -> str: - """Helper method to get formatted usage string for the current - context and command. - """ - return self.command.get_usage(self) - - def get_help(self) -> str: - """Helper method to get formatted help page for the current - context and command. - """ - return self.command.get_help(self) - - def _make_sub_context(self, command: "Command") -> "Context": - """Create a new context of the same type as this context, but - for a new command. - - :meta private: - """ - return type(self)(command, info_name=command.name, parent=self) - - @t.overload - def invoke( - __self, - __callback: "t.Callable[..., V]", - *args: t.Any, - **kwargs: t.Any, - ) -> V: ... - - @t.overload - def invoke( - __self, - __callback: "Command", - *args: t.Any, - **kwargs: t.Any, - ) -> t.Any: ... - - def invoke( - __self, - __callback: t.Union["Command", "t.Callable[..., V]"], - *args: t.Any, - **kwargs: t.Any, - ) -> t.Union[t.Any, V]: - """Invokes a command callback in exactly the way it expects. There - are two ways to invoke this method: - - 1. the first argument can be a callback and all other arguments and - keyword arguments are forwarded directly to the function. - 2. the first argument is a click command object. In that case all - arguments are forwarded as well but proper click parameters - (options and click arguments) must be keyword arguments and Click - will fill in defaults. - - Note that before Click 3.2 keyword arguments were not properly filled - in against the intention of this code and no context was created. For - more information about this change and why it was done in a bugfix - release see :ref:`upgrade-to-3.2`. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if :meth:`forward` is called at multiple levels. - """ - if isinstance(__callback, Command): - other_cmd = __callback - - if other_cmd.callback is None: - raise TypeError( - "The given command does not have a callback that can be invoked." - ) - else: - __callback = t.cast("t.Callable[..., V]", other_cmd.callback) - - ctx = __self._make_sub_context(other_cmd) - - for param in other_cmd.params: - if param.name not in kwargs and param.expose_value: - kwargs[param.name] = param.type_cast_value( # type: ignore - ctx, param.get_default(ctx) - ) - - # Track all kwargs as params, so that forward() will pass - # them on in subsequent calls. - ctx.params.update(kwargs) - else: - ctx = __self - - with augment_usage_errors(__self): - with ctx: - return __callback(*args, **kwargs) - - def forward(__self, __cmd: "Command", *args: t.Any, **kwargs: t.Any) -> t.Any: - """Similar to :meth:`invoke` but fills in default keyword - arguments from the current context if the other command expects - it. This cannot invoke callbacks directly, only other commands. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if ``forward`` is called at multiple levels. - """ - # Can only forward to other commands, not direct callbacks. - if not isinstance(__cmd, Command): - raise TypeError("Callback is not a command.") - - for param in __self.params: - if param not in kwargs: - kwargs[param] = __self.params[param] - - return __self.invoke(__cmd, *args, **kwargs) - - def set_parameter_source(self, name: str, source: ParameterSource) -> None: - """Set the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - :param name: The name of the parameter. - :param source: A member of :class:`~click.core.ParameterSource`. - """ - self._parameter_source[name] = source - - def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: - """Get the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - This can be useful for determining when a user specified a value - on the command line that is the same as the default value. It - will be :attr:`~click.core.ParameterSource.DEFAULT` only if the - value was actually taken from the default. - - :param name: The name of the parameter. - :rtype: ParameterSource - - .. versionchanged:: 8.0 - Returns ``None`` if the parameter was not provided from any - source. - """ - return self._parameter_source.get(name) - - -class BaseCommand: - """The base command implements the minimal API contract of commands. - Most code will never use this as it does not implement a lot of useful - functionality but it can act as the direct subclass of alternative - parsing methods that do not depend on the Click parser. - - For instance, this can be used to bridge Click and other systems like - argparse or docopt. - - Because base commands do not implement a lot of the API that other - parts of Click take for granted, they are not supported for all - operations. For instance, they cannot be used with the decorators - usually and they have no built-in callback system. - - .. versionchanged:: 2.0 - Added the `context_settings` parameter. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - """ - - #: The context class to create with :meth:`make_context`. - #: - #: .. versionadded:: 8.0 - context_class: t.Type[Context] = Context - #: the default for the :attr:`Context.allow_extra_args` flag. - allow_extra_args = False - #: the default for the :attr:`Context.allow_interspersed_args` flag. - allow_interspersed_args = True - #: the default for the :attr:`Context.ignore_unknown_options` flag. - ignore_unknown_options = False - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - ) -> None: - #: the name the command thinks it has. Upon registering a command - #: on a :class:`Group` the group will default the command name - #: with this information. You should instead use the - #: :class:`Context`\'s :attr:`~Context.info_name` attribute. - self.name = name - - if context_settings is None: - context_settings = {} - - #: an optional dictionary with defaults passed to the context. - self.context_settings: t.MutableMapping[str, t.Any] = context_settings - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire structure - below this command. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - :param ctx: A :class:`Context` representing this command. - - .. versionadded:: 8.0 - """ - return {"name": self.name} - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def get_usage(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get usage") - - def get_help(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get help") - - def make_context( - self, - info_name: t.Optional[str], - args: t.List[str], - parent: t.Optional[Context] = None, - **extra: t.Any, - ) -> Context: - """This function when given an info name and arguments will kick - off the parsing and create a new :class:`Context`. It does not - invoke the actual command callback though. - - To quickly customize the context class used without overriding - this method, set the :attr:`context_class` attribute. - - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it's usually - the name of the script, for commands below it's - the name of the command. - :param args: the arguments to parse as list of strings. - :param parent: the parent context if available. - :param extra: extra keyword arguments forwarded to the context - constructor. - - .. versionchanged:: 8.0 - Added the :attr:`context_class` attribute. - """ - for key, value in self.context_settings.items(): - if key not in extra: - extra[key] = value - - ctx = self.context_class( - self, # type: ignore[arg-type] - info_name=info_name, - parent=parent, - **extra, - ) - - with ctx.scope(cleanup=False): - self.parse_args(ctx, args) - return ctx - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - """Given a context and a list of arguments this creates the parser - and parses the arguments, then modifies the context as necessary. - This is automatically invoked by :meth:`make_context`. - """ - raise NotImplementedError("Base commands do not know how to parse arguments.") - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the command. The default - implementation is raising a not implemented error. - """ - raise NotImplementedError("Base commands are not invocable by default") - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of chained multi-commands. - - Any command could be part of a chained multi-command, so sibling - commands are valid at any point during command completion. Other - command classes will return more completions. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List[CompletionItem] = [] - - while ctx.parent is not None: - ctx = ctx.parent - - if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - results.extend( - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - if name not in ctx.protected_args - ) - - return results - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: "te.Literal[True]" = True, - **extra: t.Any, - ) -> "te.NoReturn": ... - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = ..., - **extra: t.Any, - ) -> t.Any: ... - - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = True, - windows_expand_args: bool = True, - **extra: t.Any, - ) -> t.Any: - """This is the way to invoke a script with all the bells and - whistles as a command line application. This will always terminate - the application after a call. If this is not wanted, ``SystemExit`` - needs to be caught. - - This method is also available by directly calling the instance of - a :class:`Command`. - - :param args: the arguments that should be used for parsing. If not - provided, ``sys.argv[1:]`` is used. - :param prog_name: the program name that should be used. By default - the program name is constructed by taking the file - name from ``sys.argv[0]``. - :param complete_var: the environment variable that controls the - bash completion support. The default is - ``"__COMPLETE"`` with prog_name in - uppercase. - :param standalone_mode: the default behavior is to invoke the script - in standalone mode. Click will then - handle exceptions and convert them into - error messages and the function will never - return but shut down the interpreter. If - this is set to `False` they will be - propagated to the caller and the return - value of this function is the return value - of :meth:`invoke`. - :param windows_expand_args: Expand glob patterns, user dir, and - env vars in command line args on Windows. - :param extra: extra keyword arguments are forwarded to the context - constructor. See :class:`Context` for more information. - - .. versionchanged:: 8.0.1 - Added the ``windows_expand_args`` parameter to allow - disabling command line arg expansion on Windows. - - .. versionchanged:: 8.0 - When taking arguments from ``sys.argv`` on Windows, glob - patterns, user dir, and env vars are expanded. - - .. versionchanged:: 3.0 - Added the ``standalone_mode`` parameter. - """ - if args is None: - args = sys.argv[1:] - - if os.name == "nt" and windows_expand_args: - args = _expand_args(args) - else: - args = list(args) - - if prog_name is None: - prog_name = _detect_program_name() - - # Process shell completion requests and exit early. - self._main_shell_completion(extra, prog_name, complete_var) - - try: - try: - with self.make_context(prog_name, args, **extra) as ctx: - rv = self.invoke(ctx) - if not standalone_mode: - return rv - # it's not safe to `ctx.exit(rv)` here! - # note that `rv` may actually contain data like "1" which - # has obvious effects - # more subtle case: `rv=[None, None]` can come out of - # chained commands which all returned `None` -- so it's not - # even always obvious that `rv` indicates success/failure - # by its truthiness/falsiness - ctx.exit() - except (EOFError, KeyboardInterrupt) as e: - echo(file=sys.stderr) - raise Abort() from e - except ClickException as e: - if not standalone_mode: - raise - e.show() - sys.exit(e.exit_code) - except OSError as e: - if e.errno == errno.EPIPE: - sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) - sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) - sys.exit(1) - else: - raise - except Exit as e: - if standalone_mode: - sys.exit(e.exit_code) - else: - # in non-standalone mode, return the exit code - # note that this is only reached if `self.invoke` above raises - # an Exit explicitly -- thus bypassing the check there which - # would return its result - # the results of non-standalone execution may therefore be - # somewhat ambiguous: if there are codepaths which lead to - # `ctx.exit(1)` and to `return 1`, the caller won't be able to - # tell the difference between the two - return e.exit_code - except Abort: - if not standalone_mode: - raise - echo(_("Aborted!"), file=sys.stderr) - sys.exit(1) - - def _main_shell_completion( - self, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: t.Optional[str] = None, - ) -> None: - """Check if the shell is asking for tab completion, process - that, then exit early. Called from :meth:`main` before the - program is invoked. - - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. Defaults to - ``_{PROG_NAME}_COMPLETE``. - - .. versionchanged:: 8.2.0 - Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). - """ - if complete_var is None: - complete_name = prog_name.replace("-", "_").replace(".", "_") - complete_var = f"_{complete_name}_COMPLETE".upper() - - instruction = os.environ.get(complete_var) - - if not instruction: - return - - from .shell_completion import shell_complete - - rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) - sys.exit(rv) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: - """Alias for :meth:`main`.""" - return self.main(*args, **kwargs) - - -class Command(BaseCommand): - """Commands are the basic building block of command line interfaces in - Click. A basic command handles command line parsing and might dispatch - more parsing to commands nested below it. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - :param callback: the callback to invoke. This is optional. - :param params: the parameters to register with this command. This can - be either :class:`Option` or :class:`Argument` objects. - :param help: the help string to use for this command. - :param epilog: like the help string but it's printed at the end of the - help page after everything else. - :param short_help: the short help to use for this command. This is - shown on the command listing of the parent command. - :param add_help_option: by default each command registers a ``--help`` - option. This can be disabled by this parameter. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is disabled by default. - If enabled this will add ``--help`` as argument - if no arguments are passed - :param hidden: hide this command from help outputs. - - :param deprecated: issues a message indicating that - the command is deprecated. - - .. versionchanged:: 8.1 - ``help``, ``epilog``, and ``short_help`` are stored unprocessed, - all formatting is done when outputting help text, not at init, - and is done even if not using the ``@command`` decorator. - - .. versionchanged:: 8.0 - Added a ``repr`` showing the command name. - - .. versionchanged:: 7.1 - Added the ``no_args_is_help`` parameter. - - .. versionchanged:: 2.0 - Added the ``context_settings`` parameter. - """ - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - callback: t.Optional[t.Callable[..., t.Any]] = None, - params: t.Optional[t.List["Parameter"]] = None, - help: t.Optional[str] = None, - epilog: t.Optional[str] = None, - short_help: t.Optional[str] = None, - options_metavar: t.Optional[str] = "[OPTIONS]", - add_help_option: bool = True, - no_args_is_help: bool = False, - hidden: bool = False, - deprecated: bool = False, - ) -> None: - super().__init__(name, context_settings) - #: the callback to execute when the command fires. This might be - #: `None` in which case nothing happens. - self.callback = callback - #: the list of parameters for this command in the order they - #: should show up in the help page and execute. Eager parameters - #: will automatically be handled before non eager ones. - self.params: t.List[Parameter] = params or [] - self.help = help - self.epilog = epilog - self.options_metavar = options_metavar - self.short_help = short_help - self.add_help_option = add_help_option - self._help_option: t.Optional[HelpOption] = None - self.no_args_is_help = no_args_is_help - self.hidden = hidden - self.deprecated = deprecated - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - info_dict.update( - params=[param.to_info_dict() for param in self.get_params(ctx)], - help=self.help, - epilog=self.epilog, - short_help=self.short_help, - hidden=self.hidden, - deprecated=self.deprecated, - ) - return info_dict - - def get_usage(self, ctx: Context) -> str: - """Formats the usage line into a string and returns it. - - Calls :meth:`format_usage` internally. - """ - formatter = ctx.make_formatter() - self.format_usage(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_params(self, ctx: Context) -> t.List["Parameter"]: - rv = self.params - help_option = self.get_help_option(ctx) - - if help_option is not None: - rv = [*rv, help_option] - - return rv - - def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the usage line into the formatter. - - This is a low-level method called by :meth:`get_usage`. - """ - pieces = self.collect_usage_pieces(ctx) - formatter.write_usage(ctx.command_path, " ".join(pieces)) - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - """Returns all the pieces that go into the usage line and returns - it as a list of strings. - """ - rv = [self.options_metavar] if self.options_metavar else [] - - for param in self.get_params(ctx): - rv.extend(param.get_usage_pieces(ctx)) - - return rv - - def get_help_option_names(self, ctx: Context) -> t.List[str]: - """Returns the names for the help option.""" - all_names = set(ctx.help_option_names) - for param in self.params: - all_names.difference_update(param.opts) - all_names.difference_update(param.secondary_opts) - return list(all_names) - - def get_help_option(self, ctx: Context) -> t.Optional["Option"]: - """Returns the help option object. - - Unless ``add_help_option`` is ``False``. - - .. versionchanged:: 8.1.8 - The help option is now cached to avoid creating it multiple times. - """ - help_options = self.get_help_option_names(ctx) - - if not help_options or not self.add_help_option: - return None - - # Cache the help option object in private _help_option attribute to - # avoid creating it multiple times. Not doing this will break the - # callback odering by iter_params_for_processing(), which relies on - # object comparison. - if self._help_option is None: - # Avoid circular import. - from .decorators import HelpOption - - self._help_option = HelpOption(help_options) - - return self._help_option - - def make_parser(self, ctx: Context) -> OptionParser: - """Creates the underlying option parser for this command.""" - parser = OptionParser(ctx) - for param in self.get_params(ctx): - param.add_to_parser(parser, ctx) - return parser - - def get_help(self, ctx: Context) -> str: - """Formats the help into a string and returns it. - - Calls :meth:`format_help` internally. - """ - formatter = ctx.make_formatter() - self.format_help(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_short_help_str(self, limit: int = 45) -> str: - """Gets short help for the command or makes it by shortening the - long help string. - """ - if self.short_help: - text = inspect.cleandoc(self.short_help) - elif self.help: - text = make_default_short_help(self.help, limit) - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - return text.strip() - - def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help into the formatter if it exists. - - This is a low-level method called by :meth:`get_help`. - - This calls the following methods: - - - :meth:`format_usage` - - :meth:`format_help_text` - - :meth:`format_options` - - :meth:`format_epilog` - """ - self.format_usage(ctx, formatter) - self.format_help_text(ctx, formatter) - self.format_options(ctx, formatter) - self.format_epilog(ctx, formatter) - - def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help text to the formatter if it exists.""" - if self.help is not None: - # truncate the help text to the first form feed - text = inspect.cleandoc(self.help).partition("\f")[0] - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - if text: - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(text) - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes all the options into the formatter if they exist.""" - opts = [] - for param in self.get_params(ctx): - rv = param.get_help_record(ctx) - if rv is not None: - opts.append(rv) - - if opts: - with formatter.section(_("Options")): - formatter.write_dl(opts) - - def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the epilog into the formatter if it exists.""" - if self.epilog: - epilog = inspect.cleandoc(self.epilog) - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(epilog) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - parser = self.make_parser(ctx) - opts, args, param_order = parser.parse_args(args=args) - - for param in iter_params_for_processing(param_order, self.get_params(ctx)): - value, args = param.handle_parse_result(ctx, opts, args) - - if args and not ctx.allow_extra_args and not ctx.resilient_parsing: - ctx.fail( - ngettext( - "Got unexpected extra argument ({args})", - "Got unexpected extra arguments ({args})", - len(args), - ).format(args=" ".join(map(str, args))) - ) - - ctx.args = args - ctx._opt_prefixes.update(parser._opt_prefixes) - return args - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the attached callback (if it exists) - in the right way. - """ - if self.deprecated: - message = _( - "DeprecationWarning: The command {name!r} is deprecated." - ).format(name=self.name) - echo(style(message, fg="red"), err=True) - - if self.callback is not None: - return ctx.invoke(self.callback, **ctx.params) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options and chained multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List[CompletionItem] = [] - - if incomplete and not incomplete[0].isalnum(): - for param in self.get_params(ctx): - if ( - not isinstance(param, Option) - or param.hidden - or ( - not param.multiple - and ctx.get_parameter_source(param.name) # type: ignore - is ParameterSource.COMMANDLINE - ) - ): - continue - - results.extend( - CompletionItem(name, help=param.help) - for name in [*param.opts, *param.secondary_opts] - if name.startswith(incomplete) - ) - - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class MultiCommand(Command): - """A multi command is the basic implementation of a command that - dispatches to subcommands. The most common version is the - :class:`Group`. - - :param invoke_without_command: this controls how the multi command itself - is invoked. By default it's only invoked - if a subcommand is provided. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is enabled by default if - `invoke_without_command` is disabled or disabled - if it's enabled. If enabled this will add - ``--help`` as argument if no arguments are - passed. - :param subcommand_metavar: the string that is used in the documentation - to indicate the subcommand place. - :param chain: if this is set to `True` chaining of multiple subcommands - is enabled. This restricts the form of commands in that - they cannot have optional arguments but it allows - multiple commands to be chained together. - :param result_callback: The result callback to attach to this multi - command. This can be set or changed later with the - :meth:`result_callback` decorator. - :param attrs: Other command arguments described in :class:`Command`. - """ - - allow_extra_args = True - allow_interspersed_args = False - - def __init__( - self, - name: t.Optional[str] = None, - invoke_without_command: bool = False, - no_args_is_help: t.Optional[bool] = None, - subcommand_metavar: t.Optional[str] = None, - chain: bool = False, - result_callback: t.Optional[t.Callable[..., t.Any]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if no_args_is_help is None: - no_args_is_help = not invoke_without_command - - self.no_args_is_help = no_args_is_help - self.invoke_without_command = invoke_without_command - - if subcommand_metavar is None: - if chain: - subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." - else: - subcommand_metavar = "COMMAND [ARGS]..." - - self.subcommand_metavar = subcommand_metavar - self.chain = chain - # The result callback that is stored. This can be set or - # overridden with the :func:`result_callback` decorator. - self._result_callback = result_callback - - if self.chain: - for param in self.params: - if isinstance(param, Argument) and not param.required: - raise RuntimeError( - "Multi commands in chain mode cannot have" - " optional arguments." - ) - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - commands = {} - - for name in self.list_commands(ctx): - command = self.get_command(ctx, name) - - if command is None: - continue - - sub_ctx = ctx._make_sub_context(command) - - with sub_ctx.scope(cleanup=False): - commands[name] = command.to_info_dict(sub_ctx) - - info_dict.update(commands=commands, chain=self.chain) - return info_dict - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - rv = super().collect_usage_pieces(ctx) - rv.append(self.subcommand_metavar) - return rv - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - super().format_options(ctx, formatter) - self.format_commands(ctx, formatter) - - def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: - """Adds a result callback to the command. By default if a - result callback is already registered this will chain them but - this can be disabled with the `replace` parameter. The result - callback is invoked with the return value of the subcommand - (or the list of return values from all subcommands if chaining - is enabled) as well as the parameters as they would be passed - to the main callback. - - Example:: - - @click.group() - @click.option('-i', '--input', default=23) - def cli(input): - return 42 - - @cli.result_callback() - def process_result(result, input): - return result + input - - :param replace: if set to `True` an already existing result - callback will be removed. - - .. versionchanged:: 8.0 - Renamed from ``resultcallback``. - - .. versionadded:: 3.0 - """ - - def decorator(f: F) -> F: - old_callback = self._result_callback - - if old_callback is None or replace: - self._result_callback = f - return f - - def function(__value, *args, **kwargs): # type: ignore - inner = old_callback(__value, *args, **kwargs) - return f(inner, *args, **kwargs) - - self._result_callback = rv = update_wrapper(t.cast(F, function), f) - return rv # type: ignore[return-value] - - return decorator - - def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: - """Extra format methods for multi methods that adds all the commands - after the options. - """ - commands = [] - for subcommand in self.list_commands(ctx): - cmd = self.get_command(ctx, subcommand) - # What is this, the tool lied about a command. Ignore it - if cmd is None: - continue - if cmd.hidden: - continue - - commands.append((subcommand, cmd)) - - # allow for 3 times the default spacing - if len(commands): - limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) - - rows = [] - for subcommand, cmd in commands: - help = cmd.get_short_help_str(limit) - rows.append((subcommand, help)) - - if rows: - with formatter.section(_("Commands")): - formatter.write_dl(rows) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - rest = super().parse_args(ctx, args) - - if self.chain: - ctx.protected_args = rest - ctx.args = [] - elif rest: - ctx.protected_args, ctx.args = rest[:1], rest[1:] - - return ctx.args - - def invoke(self, ctx: Context) -> t.Any: - def _process_result(value: t.Any) -> t.Any: - if self._result_callback is not None: - value = ctx.invoke(self._result_callback, value, **ctx.params) - return value - - if not ctx.protected_args: - if self.invoke_without_command: - # No subcommand was invoked, so the result callback is - # invoked with the group return value for regular - # groups, or an empty list for chained groups. - with ctx: - rv = super().invoke(ctx) - return _process_result([] if self.chain else rv) - ctx.fail(_("Missing command.")) - - # Fetch args back out - args = [*ctx.protected_args, *ctx.args] - ctx.args = [] - ctx.protected_args = [] - - # If we're not in chain mode, we only allow the invocation of a - # single command but we also inform the current context about the - # name of the command to invoke. - if not self.chain: - # Make sure the context is entered so we do not clean up - # resources until the result processor has worked. - with ctx: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - ctx.invoked_subcommand = cmd_name - super().invoke(ctx) - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) - with sub_ctx: - return _process_result(sub_ctx.command.invoke(sub_ctx)) - - # In chain mode we create the contexts step by step, but after the - # base command has been invoked. Because at that point we do not - # know the subcommands yet, the invoked subcommand attribute is - # set to ``*`` to inform the command that subcommands are executed - # but nothing else. - with ctx: - ctx.invoked_subcommand = "*" if args else None - super().invoke(ctx) - - # Otherwise we make every single context and invoke them in a - # chain. In that case the return value to the result processor - # is the list of all invoked subcommand's results. - contexts = [] - while args: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - sub_ctx = cmd.make_context( - cmd_name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - ) - contexts.append(sub_ctx) - args, sub_ctx.args = sub_ctx.args, [] - - rv = [] - for sub_ctx in contexts: - with sub_ctx: - rv.append(sub_ctx.command.invoke(sub_ctx)) - return _process_result(rv) - - def resolve_command( - self, ctx: Context, args: t.List[str] - ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: - cmd_name = make_str(args[0]) - original_cmd_name = cmd_name - - # Get the command - cmd = self.get_command(ctx, cmd_name) - - # If we can't find the command but there is a normalization - # function available, we try with that one. - if cmd is None and ctx.token_normalize_func is not None: - cmd_name = ctx.token_normalize_func(cmd_name) - cmd = self.get_command(ctx, cmd_name) - - # If we don't find the command we want to show an error message - # to the user that it was not provided. However, there is - # something else we should do: if the first argument looks like - # an option we want to kick off parsing again for arguments to - # resolve things like --help which now should go to the main - # place. - if cmd is None and not ctx.resilient_parsing: - if split_opt(cmd_name)[0]: - self.parse_args(ctx, ctx.args) - ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) - return cmd_name if cmd else None, cmd, args[1:] - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - """Given a context and a command name, this returns a - :class:`Command` object if it exists or returns `None`. - """ - raise NotImplementedError - - def list_commands(self, ctx: Context) -> t.List[str]: - """Returns a list of subcommand names in the order they should - appear. - """ - return [] - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options, subcommands, and chained - multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results = [ - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - ] - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class Group(MultiCommand): - """A group allows a command to have subcommands attached. This is - the most common way to implement nesting in Click. - - :param name: The name of the group command. - :param commands: A dict mapping names to :class:`Command` objects. - Can also be a list of :class:`Command`, which will use - :attr:`Command.name` to create the dict. - :param attrs: Other command arguments described in - :class:`MultiCommand`, :class:`Command`, and - :class:`BaseCommand`. - - .. versionchanged:: 8.0 - The ``commands`` argument can be a list of command objects. - """ - - #: If set, this is used by the group's :meth:`command` decorator - #: as the default :class:`Command` class. This is useful to make all - #: subcommands use a custom command class. - #: - #: .. versionadded:: 8.0 - command_class: t.Optional[t.Type[Command]] = None - - #: If set, this is used by the group's :meth:`group` decorator - #: as the default :class:`Group` class. This is useful to make all - #: subgroups use a custom group class. - #: - #: If set to the special value :class:`type` (literally - #: ``group_class = type``), this group's class will be used as the - #: default class. This makes a custom group class continue to make - #: custom groups. - #: - #: .. versionadded:: 8.0 - group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None - # Literal[type] isn't valid, so use Type[type] - - def __init__( - self, - name: t.Optional[str] = None, - commands: t.Optional[ - t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] - ] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if commands is None: - commands = {} - elif isinstance(commands, abc.Sequence): - commands = {c.name: c for c in commands if c.name is not None} - - #: The registered subcommands by their exported names. - self.commands: t.MutableMapping[str, Command] = commands - - def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: - """Registers another :class:`Command` with this group. If the name - is not provided, the name of the command is used. - """ - name = name or cmd.name - if name is None: - raise TypeError("Command has no name.") - _check_multicommand(self, name, cmd, register=True) - self.commands[name] = cmd - - @t.overload - def command(self, __func: t.Callable[..., t.Any]) -> Command: ... - - @t.overload - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command]: ... - - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: - """A shortcut decorator for declaring and attaching a command to - the group. This takes the same arguments as :func:`command` and - immediately registers the created command with this group by - calling :meth:`add_command`. - - To customize the command class used, set the - :attr:`command_class` attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`command_class` attribute. - """ - from .decorators import command - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'command(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.command_class and kwargs.get("cls") is None: - kwargs["cls"] = self.command_class - - def decorator(f: t.Callable[..., t.Any]) -> Command: - cmd: Command = command(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - @t.overload - def group(self, __func: t.Callable[..., t.Any]) -> "Group": ... - - @t.overload - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: ... - - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: - """A shortcut decorator for declaring and attaching a group to - the group. This takes the same arguments as :func:`group` and - immediately registers the created group with this group by - calling :meth:`add_command`. - - To customize the group class used, set the :attr:`group_class` - attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`group_class` attribute. - """ - from .decorators import group - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'group(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.group_class is not None and kwargs.get("cls") is None: - if self.group_class is type: - kwargs["cls"] = type(self) - else: - kwargs["cls"] = self.group_class - - def decorator(f: t.Callable[..., t.Any]) -> "Group": - cmd: Group = group(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - return self.commands.get(cmd_name) - - def list_commands(self, ctx: Context) -> t.List[str]: - return sorted(self.commands) - - -class CommandCollection(MultiCommand): - """A command collection is a multi command that merges multiple multi - commands together into one. This is a straightforward implementation - that accepts a list of different multi commands as sources and - provides all the commands for each of them. - - See :class:`MultiCommand` and :class:`Command` for the description of - ``name`` and ``attrs``. - """ - - def __init__( - self, - name: t.Optional[str] = None, - sources: t.Optional[t.List[MultiCommand]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - #: The list of registered multi commands. - self.sources: t.List[MultiCommand] = sources or [] - - def add_source(self, multi_cmd: MultiCommand) -> None: - """Adds a new multi command to the chain dispatcher.""" - self.sources.append(multi_cmd) - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - for source in self.sources: - rv = source.get_command(ctx, cmd_name) - - if rv is not None: - if self.chain: - _check_multicommand(self, cmd_name, rv) - - return rv - - return None - - def list_commands(self, ctx: Context) -> t.List[str]: - rv: t.Set[str] = set() - - for source in self.sources: - rv.update(source.list_commands(ctx)) - - return sorted(rv) - - -def _check_iter(value: t.Any) -> t.Iterator[t.Any]: - """Check if the value is iterable but not a string. Raises a type - error, or return an iterator over the value. - """ - if isinstance(value, str): - raise TypeError - - return iter(value) - - -class Parameter: - r"""A parameter to a command comes in two versions: they are either - :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently - not supported by design as some of the internals for parsing are - intentionally not finalized. - - Some settings are supported by both options and arguments. - - :param param_decls: the parameter declarations for this option or - argument. This is a list of flags or argument - names. - :param type: the type that should be used. Either a :class:`ParamType` - or a Python type. The latter is converted into the former - automatically if supported. - :param required: controls if this is optional or not. - :param default: the default value if omitted. This can also be a callable, - in which case it's invoked when the default is needed - without any arguments. - :param callback: A function to further process or validate the value - after type conversion. It is called as ``f(ctx, param, value)`` - and must return the value. It is called for all sources, - including prompts. - :param nargs: the number of arguments to match. If not ``1`` the return - value is a tuple instead of single value. The default for - nargs is ``1`` (except if the type is a tuple, then it's - the arity of the tuple). If ``nargs=-1``, all remaining - parameters are collected. - :param metavar: how the value is represented in the help page. - :param expose_value: if this is `True` then the value is passed onwards - to the command callback and stored on the context, - otherwise it's skipped. - :param is_eager: eager values are processed before non eager ones. This - should not be set for arguments or it will inverse the - order of processing. - :param envvar: a string or list of strings that are environment variables - that should be checked. - :param shell_complete: A function that returns custom shell - completions. Used instead of the param's type completion if - given. Takes ``ctx, param, incomplete`` and must return a list - of :class:`~click.shell_completion.CompletionItem` or a list of - strings. - - .. versionchanged:: 8.0 - ``process_value`` validates required parameters and bounded - ``nargs``, and invokes the parameter callback before returning - the value. This allows the callback to validate prompts. - ``full_process_value`` is removed. - - .. versionchanged:: 8.0 - ``autocompletion`` is renamed to ``shell_complete`` and has new - semantics described above. The old name is deprecated and will - be removed in 8.1, until then it will be wrapped to match the - new requirements. - - .. versionchanged:: 8.0 - For ``multiple=True, nargs>1``, the default must be a list of - tuples. - - .. versionchanged:: 8.0 - Setting a default is no longer required for ``nargs>1``, it will - default to ``None``. ``multiple=True`` or ``nargs=-1`` will - default to ``()``. - - .. versionchanged:: 7.1 - Empty environment variables are ignored rather than taking the - empty string value. This makes it possible for scripts to clear - variables if they can't unset them. - - .. versionchanged:: 2.0 - Changed signature for parameter callback to also be passed the - parameter. The old callback format will still work, but it will - raise a warning to give you a chance to migrate the code easier. - """ - - param_type_name = "parameter" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - required: bool = False, - default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, - callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, - nargs: t.Optional[int] = None, - multiple: bool = False, - metavar: t.Optional[str] = None, - expose_value: bool = True, - is_eager: bool = False, - envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, - shell_complete: t.Optional[ - t.Callable[ - [Context, "Parameter", str], - t.Union[t.List["CompletionItem"], t.List[str]], - ] - ] = None, - ) -> None: - self.name: t.Optional[str] - self.opts: t.List[str] - self.secondary_opts: t.List[str] - self.name, self.opts, self.secondary_opts = self._parse_decls( - param_decls or (), expose_value - ) - self.type: types.ParamType = types.convert_type(type, default) - - # Default nargs to what the type tells us if we have that - # information available. - if nargs is None: - if self.type.is_composite: - nargs = self.type.arity - else: - nargs = 1 - - self.required = required - self.callback = callback - self.nargs = nargs - self.multiple = multiple - self.expose_value = expose_value - self.default = default - self.is_eager = is_eager - self.metavar = metavar - self.envvar = envvar - self._custom_shell_complete = shell_complete - - if __debug__: - if self.type.is_composite and nargs != self.type.arity: - raise ValueError( - f"'nargs' must be {self.type.arity} (or None) for" - f" type {self.type!r}, but it was {nargs}." - ) - - # Skip no default or callable default. - check_default = default if not callable(default) else None - - if check_default is not None: - if multiple: - try: - # Only check the first value against nargs. - check_default = next(_check_iter(check_default), None) - except TypeError: - raise ValueError( - "'default' must be a list when 'multiple' is true." - ) from None - - # Can be None for multiple with empty default. - if nargs != 1 and check_default is not None: - try: - _check_iter(check_default) - except TypeError: - if multiple: - message = ( - "'default' must be a list of lists when 'multiple' is" - " true and 'nargs' != 1." - ) - else: - message = "'default' must be a list when 'nargs' != 1." - - raise ValueError(message) from None - - if nargs > 1 and len(check_default) != nargs: - subject = "item length" if multiple else "length" - raise ValueError( - f"'default' {subject} must match nargs={nargs}." - ) - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - return { - "name": self.name, - "param_type_name": self.param_type_name, - "opts": self.opts, - "secondary_opts": self.secondary_opts, - "type": self.type.to_info_dict(), - "required": self.required, - "nargs": self.nargs, - "multiple": self.multiple, - "default": self.default, - "envvar": self.envvar, - } - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - raise NotImplementedError() - - @property - def human_readable_name(self) -> str: - """Returns the human readable name of this parameter. This is the - same as the name for options, but the metavar for arguments. - """ - return self.name # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - - metavar = self.type.get_metavar(self) - - if metavar is None: - metavar = self.type.name.upper() - - if self.nargs != 1: - metavar += "..." - - return metavar - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - """Get the default for the parameter. Tries - :meth:`Context.lookup_default` first, then the local default. - - :param ctx: Current context. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0.2 - Type casting is no longer performed when getting a default. - - .. versionchanged:: 8.0.1 - Type casting can fail in resilient parsing mode. Invalid - defaults will not prevent showing help text. - - .. versionchanged:: 8.0 - Looks at ``ctx.default_map`` first. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - value = ctx.lookup_default(self.name, call=False) # type: ignore - - if value is None: - value = self.default - - if call and callable(value): - value = value() - - return value - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - raise NotImplementedError() - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, t.Any] - ) -> t.Tuple[t.Any, ParameterSource]: - value = opts.get(self.name) # type: ignore - source = ParameterSource.COMMANDLINE - - if value is None: - value = self.value_from_envvar(ctx) - source = ParameterSource.ENVIRONMENT - - if value is None: - value = ctx.lookup_default(self.name) # type: ignore - source = ParameterSource.DEFAULT_MAP - - if value is None: - value = self.get_default(ctx) - source = ParameterSource.DEFAULT - - return value, source - - def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: - """Convert and validate a value against the option's - :attr:`type`, :attr:`multiple`, and :attr:`nargs`. - """ - if value is None: - return () if self.multiple or self.nargs == -1 else None - - def check_iter(value: t.Any) -> t.Iterator[t.Any]: - try: - return _check_iter(value) - except TypeError: - # This should only happen when passing in args manually, - # the parser should construct an iterable when parsing - # the command line. - raise BadParameter( - _("Value must be an iterable."), ctx=ctx, param=self - ) from None - - if self.nargs == 1 or self.type.is_composite: - - def convert(value: t.Any) -> t.Any: - return self.type(value, param=self, ctx=ctx) - - elif self.nargs == -1: - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - return tuple(self.type(x, self, ctx) for x in check_iter(value)) - - else: # nargs > 1 - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - value = tuple(check_iter(value)) - - if len(value) != self.nargs: - raise BadParameter( - ngettext( - "Takes {nargs} values but 1 was given.", - "Takes {nargs} values but {len} were given.", - len(value), - ).format(nargs=self.nargs, len=len(value)), - ctx=ctx, - param=self, - ) - - return tuple(self.type(x, self, ctx) for x in value) - - if self.multiple: - return tuple(convert(x) for x in check_iter(value)) - - return convert(value) - - def value_is_missing(self, value: t.Any) -> bool: - if value is None: - return True - - if (self.nargs != 1 or self.multiple) and value == (): - return True - - return False - - def process_value(self, ctx: Context, value: t.Any) -> t.Any: - value = self.type_cast_value(ctx, value) - - if self.required and self.value_is_missing(value): - raise MissingParameter(ctx=ctx, param=self) - - if self.callback is not None: - value = self.callback(ctx, self, value) - - return value - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - if self.envvar is None: - return None - - if isinstance(self.envvar, str): - rv = os.environ.get(self.envvar) - - if rv: - return rv - else: - for envvar in self.envvar: - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is not None and self.nargs != 1: - rv = self.type.split_envvar_value(rv) - - return rv - - def handle_parse_result( - self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] - ) -> t.Tuple[t.Any, t.List[str]]: - with augment_usage_errors(ctx, param=self): - value, source = self.consume_value(ctx, opts) - ctx.set_parameter_source(self.name, source) # type: ignore - - try: - value = self.process_value(ctx, value) - except Exception: - if not ctx.resilient_parsing: - raise - - value = None - - if self.expose_value: - ctx.params[self.name] = value # type: ignore - - return value, args - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - pass - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [] - - def get_error_hint(self, ctx: Context) -> str: - """Get a stringified version of the param for use in error messages to - indicate which param caused the error. - """ - hint_list = self.opts or [self.human_readable_name] - return " / ".join(f"'{x}'" for x in hint_list) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. If a - ``shell_complete`` function was given during init, it is used. - Otherwise, the :attr:`type` - :meth:`~click.types.ParamType.shell_complete` function is used. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - if self._custom_shell_complete is not None: - results = self._custom_shell_complete(ctx, self, incomplete) - - if results and isinstance(results[0], str): - from click.shell_completion import CompletionItem - - results = [CompletionItem(c) for c in results] - - return t.cast(t.List["CompletionItem"], results) - - return self.type.shell_complete(ctx, self, incomplete) - - -class Option(Parameter): - """Options are usually optional values on the command line and - have some extra features that arguments don't have. - - All other parameters are passed onwards to the parameter constructor. - - :param show_default: Show the default value for this option in its - help text. Values are not shown by default, unless - :attr:`Context.show_default` is ``True``. If this value is a - string, it shows that string in parentheses instead of the - actual value. This is particularly useful for dynamic options. - For single option boolean flags, the default remains hidden if - its value is ``False``. - :param show_envvar: Controls if an environment variable should be - shown on the help page. Normally, environment variables are not - shown. - :param prompt: If set to ``True`` or a non empty string then the - user will be prompted for input. If set to ``True`` the prompt - will be the option name capitalized. - :param confirmation_prompt: Prompt a second time to confirm the - value if it was prompted for. Can be set to a string instead of - ``True`` to customize the message. - :param prompt_required: If set to ``False``, the user will be - prompted for input only when the option was specified as a flag - without a value. - :param hide_input: If this is ``True`` then the input on the prompt - will be hidden from the user. This is useful for password input. - :param is_flag: forces this option to act as a flag. The default is - auto detection. - :param flag_value: which value should be used for this flag if it's - enabled. This is set to a boolean automatically if - the option string contains a slash to mark two options. - :param multiple: if this is set to `True` then the argument is accepted - multiple times and recorded. This is similar to ``nargs`` - in how it works but supports arbitrary number of - arguments. - :param count: this flag makes an option increment an integer. - :param allow_from_autoenv: if this is enabled then the value of this - parameter will be pulled from an environment - variable in case a prefix is defined on the - context. - :param help: the help string. - :param hidden: hide this option from help outputs. - :param attrs: Other command arguments described in :class:`Parameter`. - - .. versionchanged:: 8.1.0 - Help text indentation is cleaned here instead of only in the - ``@option`` decorator. - - .. versionchanged:: 8.1.0 - The ``show_default`` parameter overrides - ``Context.show_default``. - - .. versionchanged:: 8.1.0 - The default of a single option boolean flag is not shown if the - default value is ``False``. - - .. versionchanged:: 8.0.1 - ``type`` is detected from ``flag_value`` if given. - """ - - param_type_name = "option" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - show_default: t.Union[bool, str, None] = None, - prompt: t.Union[bool, str] = False, - confirmation_prompt: t.Union[bool, str] = False, - prompt_required: bool = True, - hide_input: bool = False, - is_flag: t.Optional[bool] = None, - flag_value: t.Optional[t.Any] = None, - multiple: bool = False, - count: bool = False, - allow_from_autoenv: bool = True, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - help: t.Optional[str] = None, - hidden: bool = False, - show_choices: bool = True, - show_envvar: bool = False, - **attrs: t.Any, - ) -> None: - if help: - help = inspect.cleandoc(help) - - default_is_missing = "default" not in attrs - super().__init__(param_decls, type=type, multiple=multiple, **attrs) - - if prompt is True: - if self.name is None: - raise TypeError("'name' is required with 'prompt=True'.") - - prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() - elif prompt is False: - prompt_text = None - else: - prompt_text = prompt - - self.prompt = prompt_text - self.confirmation_prompt = confirmation_prompt - self.prompt_required = prompt_required - self.hide_input = hide_input - self.hidden = hidden - - # If prompt is enabled but not required, then the option can be - # used as a flag to indicate using prompt or flag_value. - self._flag_needs_value = self.prompt is not None and not self.prompt_required - - if is_flag is None: - if flag_value is not None: - # Implicitly a flag because flag_value was set. - is_flag = True - elif self._flag_needs_value: - # Not a flag, but when used as a flag it shows a prompt. - is_flag = False - else: - # Implicitly a flag because flag options were given. - is_flag = bool(self.secondary_opts) - elif is_flag is False and not self._flag_needs_value: - # Not a flag, and prompt is not enabled, can be used as a - # flag if flag_value is set. - self._flag_needs_value = flag_value is not None - - self.default: t.Union[t.Any, t.Callable[[], t.Any]] - - if is_flag and default_is_missing and not self.required: - if multiple: - self.default = () - else: - self.default = False - - if flag_value is None: - flag_value = not self.default - - self.type: types.ParamType - if is_flag and type is None: - # Re-guess the type from the flag value instead of the - # default. - self.type = types.convert_type(None, flag_value) - - self.is_flag: bool = is_flag - self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) - self.flag_value: t.Any = flag_value - - # Counting - self.count = count - if count: - if type is None: - self.type = types.IntRange(min=0) - if default_is_missing: - self.default = 0 - - self.allow_from_autoenv = allow_from_autoenv - self.help = help - self.show_default = show_default - self.show_choices = show_choices - self.show_envvar = show_envvar - - if __debug__: - if self.nargs == -1: - raise TypeError("nargs=-1 is not supported for options.") - - if self.prompt and self.is_flag and not self.is_bool_flag: - raise TypeError("'prompt' is not valid for non-boolean flag.") - - if not self.is_bool_flag and self.secondary_opts: - raise TypeError("Secondary flag is not valid for non-boolean flag.") - - if self.is_bool_flag and self.hide_input and self.prompt is not None: - raise TypeError( - "'prompt' with 'hide_input' is not valid for boolean flag." - ) - - if self.count: - if self.multiple: - raise TypeError("'count' is not valid with 'multiple'.") - - if self.is_flag: - raise TypeError("'count' is not valid with 'is_flag'.") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - help=self.help, - prompt=self.prompt, - is_flag=self.is_flag, - flag_value=self.flag_value, - count=self.count, - hidden=self.hidden, - ) - return info_dict - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - opts = [] - secondary_opts = [] - name = None - possible_names = [] - - for decl in decls: - if decl.isidentifier(): - if name is not None: - raise TypeError(f"Name '{name}' defined twice") - name = decl - else: - split_char = ";" if decl[:1] == "/" else "/" - if split_char in decl: - first, second = decl.split(split_char, 1) - first = first.rstrip() - if first: - possible_names.append(split_opt(first)) - opts.append(first) - second = second.lstrip() - if second: - secondary_opts.append(second.lstrip()) - if first == second: - raise ValueError( - f"Boolean option {decl!r} cannot use the" - " same flag for true/false." - ) - else: - possible_names.append(split_opt(decl)) - opts.append(decl) - - if name is None and possible_names: - possible_names.sort(key=lambda x: -len(x[0])) # group long options first - name = possible_names[0][1].replace("-", "_").lower() - if not name.isidentifier(): - name = None - - if name is None: - if not expose_value: - return None, opts, secondary_opts - raise TypeError( - f"Could not determine name for option with declarations {decls!r}" - ) - - if not opts and not secondary_opts: - raise TypeError( - f"No options defined but a name was passed ({name})." - " Did you mean to declare an argument instead? Did" - f" you mean to pass '--{name}'?" - ) - - return name, opts, secondary_opts - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - if self.multiple: - action = "append" - elif self.count: - action = "count" - else: - action = "store" - - if self.is_flag: - action = f"{action}_const" - - if self.is_bool_flag and self.secondary_opts: - parser.add_option( - obj=self, opts=self.opts, dest=self.name, action=action, const=True - ) - parser.add_option( - obj=self, - opts=self.secondary_opts, - dest=self.name, - action=action, - const=False, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - const=self.flag_value, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - nargs=self.nargs, - ) - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - if self.hidden: - return None - - any_prefix_is_slash = False - - def _write_opts(opts: t.Sequence[str]) -> str: - nonlocal any_prefix_is_slash - - rv, any_slashes = join_options(opts) - - if any_slashes: - any_prefix_is_slash = True - - if not self.is_flag and not self.count: - rv += f" {self.make_metavar()}" - - return rv - - rv = [_write_opts(self.opts)] - - if self.secondary_opts: - rv.append(_write_opts(self.secondary_opts)) - - help = self.help or "" - extra = [] - - if self.show_envvar: - envvar = self.envvar - - if envvar is None: - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - - if envvar is not None: - var_str = ( - envvar - if isinstance(envvar, str) - else ", ".join(str(d) for d in envvar) - ) - extra.append(_("env var: {var}").format(var=var_str)) - - # Temporarily enable resilient parsing to avoid type casting - # failing for the default. Might be possible to extend this to - # help formatting in general. - resilient = ctx.resilient_parsing - ctx.resilient_parsing = True - - try: - default_value = self.get_default(ctx, call=False) - finally: - ctx.resilient_parsing = resilient - - show_default = False - show_default_is_str = False - - if self.show_default is not None: - if isinstance(self.show_default, str): - show_default_is_str = show_default = True - else: - show_default = self.show_default - elif ctx.show_default is not None: - show_default = ctx.show_default - - if show_default_is_str or (show_default and (default_value is not None)): - if show_default_is_str: - default_string = f"({self.show_default})" - elif isinstance(default_value, (list, tuple)): - default_string = ", ".join(str(d) for d in default_value) - elif inspect.isfunction(default_value): - default_string = _("(dynamic)") - elif self.is_bool_flag and self.secondary_opts: - # For boolean flags that have distinct True/False opts, - # use the opt without prefix instead of the value. - default_string = split_opt( - (self.opts if default_value else self.secondary_opts)[0] - )[1] - elif self.is_bool_flag and not self.secondary_opts and not default_value: - default_string = "" - elif default_value == "": - default_string = '""' - else: - default_string = str(default_value) - - if default_string: - extra.append(_("default: {default}").format(default=default_string)) - - if ( - isinstance(self.type, types._NumberRangeBase) - # skip count with default range type - and not (self.count and self.type.min == 0 and self.type.max is None) - ): - range_str = self.type._describe_range() - - if range_str: - extra.append(range_str) - - if self.required: - extra.append(_("required")) - - if extra: - extra_str = "; ".join(extra) - help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" - - return ("; " if any_prefix_is_slash else " / ").join(rv), help - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - # If we're a non boolean flag our default is more complex because - # we need to look at all flags in the same group to figure out - # if we're the default one in which case we return the flag - # value as default. - if self.is_flag and not self.is_bool_flag: - for param in ctx.command.params: - if param.name == self.name and param.default: - return t.cast(Option, param).flag_value - - return None - - return super().get_default(ctx, call=call) - - def prompt_for_value(self, ctx: Context) -> t.Any: - """This is an alternative flow that can be activated in the full - value processing if a value does not exist. It will prompt the - user until a valid value exists and then returns the processed - value as result. - """ - assert self.prompt is not None - - # Calculate the default before prompting anything to be stable. - default = self.get_default(ctx) - - # If this is a prompt for a flag we need to handle this - # differently. - if self.is_bool_flag: - return confirm(self.prompt, default) - - return prompt( - self.prompt, - default=default, - type=self.type, - hide_input=self.hide_input, - show_choices=self.show_choices, - confirmation_prompt=self.confirmation_prompt, - value_proc=lambda x: self.process_value(ctx, x), - ) - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - rv = super().resolve_envvar_value(ctx) - - if rv is not None: - return rv - - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is None: - return None - - value_depth = (self.nargs != 1) + bool(self.multiple) - - if value_depth > 0: - rv = self.type.split_envvar_value(rv) - - if self.multiple and self.nargs != 1: - rv = batch(rv, self.nargs) - - return rv - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, "Parameter"] - ) -> t.Tuple[t.Any, ParameterSource]: - value, source = super().consume_value(ctx, opts) - - # The parser will emit a sentinel value if the option can be - # given as a flag without a value. This is different from None - # to distinguish from the flag not being given at all. - if value is _flag_needs_value: - if self.prompt is not None and not ctx.resilient_parsing: - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - else: - value = self.flag_value - source = ParameterSource.COMMANDLINE - - elif ( - self.multiple - and value is not None - and any(v is _flag_needs_value for v in value) - ): - value = [self.flag_value if v is _flag_needs_value else v for v in value] - source = ParameterSource.COMMANDLINE - - # The value wasn't set, or used the param's default, prompt if - # prompting is enabled. - elif ( - source in {None, ParameterSource.DEFAULT} - and self.prompt is not None - and (self.required or self.prompt_required) - and not ctx.resilient_parsing - ): - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - - return value, source - - -class Argument(Parameter): - """Arguments are positional parameters to a command. They generally - provide fewer features than options but can have infinite ``nargs`` - and are required by default. - - All parameters are passed onwards to the constructor of :class:`Parameter`. - """ - - param_type_name = "argument" - - def __init__( - self, - param_decls: t.Sequence[str], - required: t.Optional[bool] = None, - **attrs: t.Any, - ) -> None: - if required is None: - if attrs.get("default") is not None: - required = False - else: - required = attrs.get("nargs", 1) > 0 - - if "multiple" in attrs: - raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") - - super().__init__(param_decls, required=required, **attrs) - - if __debug__: - if self.default is not None and self.nargs == -1: - raise TypeError("'default' is not supported for nargs=-1.") - - @property - def human_readable_name(self) -> str: - if self.metavar is not None: - return self.metavar - return self.name.upper() # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - var = self.type.get_metavar(self) - if not var: - var = self.name.upper() # type: ignore - if not self.required: - var = f"[{var}]" - if self.nargs != 1: - var += "..." - return var - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - if not decls: - if not expose_value: - return None, [], [] - raise TypeError("Argument is marked as exposed, but does not have a name.") - if len(decls) == 1: - name = arg = decls[0] - name = name.replace("-", "_").lower() - else: - raise TypeError( - "Arguments take exactly one parameter declaration, got" - f" {len(decls)}." - ) - return name, [arg], [] - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [self.make_metavar()] - - def get_error_hint(self, ctx: Context) -> str: - return f"'{self.make_metavar()}'" - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/.venv/Lib/site-packages/click/decorators.py b/.venv/Lib/site-packages/click/decorators.py deleted file mode 100644 index bcf8906..0000000 --- a/.venv/Lib/site-packages/click/decorators.py +++ /dev/null @@ -1,562 +0,0 @@ -import inspect -import types -import typing as t -from functools import update_wrapper -from gettext import gettext as _ - -from .core import Argument -from .core import Command -from .core import Context -from .core import Group -from .core import Option -from .core import Parameter -from .globals import get_current_context -from .utils import echo - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") -T = t.TypeVar("T") -_AnyCallable = t.Callable[..., t.Any] -FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) - - -def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": - """Marks a callback as wanting to receive the current context - object as first argument. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context(), *args, **kwargs) - - return update_wrapper(new_func, f) - - -def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - """Similar to :func:`pass_context`, but only pass the object on the - context onwards (:attr:`Context.obj`). This is useful if that object - represents the state of a nested system. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context().obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - -def make_pass_decorator( - object_type: t.Type[T], ensure: bool = False -) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: - """Given an object type this creates a decorator that will work - similar to :func:`pass_obj` but instead of passing the object of the - current context, it will find the innermost context of type - :func:`object_type`. - - This generates a decorator that works roughly like this:: - - from functools import update_wrapper - - def decorator(f): - @pass_context - def new_func(ctx, *args, **kwargs): - obj = ctx.find_object(object_type) - return ctx.invoke(f, obj, *args, **kwargs) - return update_wrapper(new_func, f) - return decorator - - :param object_type: the type of the object to pass. - :param ensure: if set to `True`, a new object will be created and - remembered on the context if it's not there yet. - """ - - def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - ctx = get_current_context() - - obj: t.Optional[T] - if ensure: - obj = ctx.ensure_object(object_type) - else: - obj = ctx.find_object(object_type) - - if obj is None: - raise RuntimeError( - "Managed to invoke callback without a context" - f" object of type {object_type.__name__!r}" - " existing." - ) - - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - return decorator - - -def pass_meta_key( - key: str, *, doc_description: t.Optional[str] = None -) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": - """Create a decorator that passes a key from - :attr:`click.Context.meta` as the first argument to the decorated - function. - - :param key: Key in ``Context.meta`` to pass. - :param doc_description: Description of the object being passed, - inserted into the decorator's docstring. Defaults to "the 'key' - key from Context.meta". - - .. versionadded:: 8.0 - """ - - def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: - ctx = get_current_context() - obj = ctx.meta[key] - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - if doc_description is None: - doc_description = f"the {key!r} key from :attr:`click.Context.meta`" - - decorator.__doc__ = ( - f"Decorator that passes {doc_description} as the first argument" - " to the decorated function." - ) - return decorator - - -CmdType = t.TypeVar("CmdType", bound=Command) - - -# variant: no call, directly as decorator for a function. -@t.overload -def command(name: _AnyCallable) -> Command: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) -@t.overload -def command( - name: t.Optional[str], - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) -@t.overload -def command( - name: None = None, - *, - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def command( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Command]: ... - - -def command( - name: t.Union[t.Optional[str], _AnyCallable] = None, - cls: t.Optional[t.Type[CmdType]] = None, - **attrs: t.Any, -) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: - r"""Creates a new :class:`Command` and uses the decorated function as - callback. This will also automatically attach all decorated - :func:`option`\s and :func:`argument`\s as parameters to the command. - - The name of the command defaults to the name of the function with - underscores replaced by dashes. If you want to change that, you can - pass the intended name as the first argument. - - All keyword arguments are forwarded to the underlying command class. - For the ``params`` argument, any decorated params are appended to - the end of the list. - - Once decorated the function turns into a :class:`Command` instance - that can be invoked as a command line utility or be attached to a - command :class:`Group`. - - :param name: the name of the command. This defaults to the function - name with underscores replaced by dashes. - :param cls: the command class to instantiate. This defaults to - :class:`Command`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.1 - The ``params`` argument can be used. Decorated params are - appended to the end of the list. - """ - - func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None - - if callable(name): - func = name - name = None - assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." - assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." - - if cls is None: - cls = t.cast(t.Type[CmdType], Command) - - def decorator(f: _AnyCallable) -> CmdType: - if isinstance(f, Command): - raise TypeError("Attempted to convert a callback into a command twice.") - - attr_params = attrs.pop("params", None) - params = attr_params if attr_params is not None else [] - - try: - decorator_params = f.__click_params__ # type: ignore - except AttributeError: - pass - else: - del f.__click_params__ # type: ignore - params.extend(reversed(decorator_params)) - - if attrs.get("help") is None: - attrs["help"] = f.__doc__ - - if t.TYPE_CHECKING: - assert cls is not None - assert not callable(name) - - cmd = cls( - name=name or f.__name__.lower().replace("_", "-"), - callback=f, - params=params, - **attrs, - ) - cmd.__doc__ = f.__doc__ - return cmd - - if func is not None: - return decorator(func) - - return decorator - - -GrpType = t.TypeVar("GrpType", bound=Group) - - -# variant: no call, directly as decorator for a function. -@t.overload -def group(name: _AnyCallable) -> Group: ... - - -# variant: with positional name and with positional or keyword cls argument: -# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) -@t.overload -def group( - name: t.Optional[str], - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) -@t.overload -def group( - name: None = None, - *, - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def group( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Group]: ... - - -def group( - name: t.Union[str, _AnyCallable, None] = None, - cls: t.Optional[t.Type[GrpType]] = None, - **attrs: t.Any, -) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: - """Creates a new :class:`Group` with a function as callback. This - works otherwise the same as :func:`command` just that the `cls` - parameter is set to :class:`Group`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - """ - if cls is None: - cls = t.cast(t.Type[GrpType], Group) - - if callable(name): - return command(cls=cls, **attrs)(name) - - return command(name, cls, **attrs) - - -def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: - if isinstance(f, Command): - f.params.append(param) - else: - if not hasattr(f, "__click_params__"): - f.__click_params__ = [] # type: ignore - - f.__click_params__.append(param) # type: ignore - - -def argument( - *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an argument to the command. All positional arguments are - passed as parameter declarations to :class:`Argument`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Argument` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default argument class, refer to :class:`Argument` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the argument class to instantiate. This defaults to - :class:`Argument`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Argument - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def option( - *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an option to the command. All positional arguments are - passed as parameter declarations to :class:`Option`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Option` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default option class, refer to :class:`Option` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the option class to instantiate. This defaults to - :class:`Option`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Option - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--yes`` option which shows a prompt before continuing if - not passed. If the prompt is declined, the program will exit. - - :param param_decls: One or more option names. Defaults to the single - value ``"--yes"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value: - ctx.abort() - - if not param_decls: - param_decls = ("--yes",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("callback", callback) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("prompt", "Do you want to continue?") - kwargs.setdefault("help", "Confirm the action without prompting.") - return option(*param_decls, **kwargs) - - -def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--password`` option which prompts for a password, hiding - input and asking to enter the value again for confirmation. - - :param param_decls: One or more option names. Defaults to the single - value ``"--password"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - if not param_decls: - param_decls = ("--password",) - - kwargs.setdefault("prompt", True) - kwargs.setdefault("confirmation_prompt", True) - kwargs.setdefault("hide_input", True) - return option(*param_decls, **kwargs) - - -def version_option( - version: t.Optional[str] = None, - *param_decls: str, - package_name: t.Optional[str] = None, - prog_name: t.Optional[str] = None, - message: t.Optional[str] = None, - **kwargs: t.Any, -) -> t.Callable[[FC], FC]: - """Add a ``--version`` option which immediately prints the version - number and exits the program. - - If ``version`` is not provided, Click will try to detect it using - :func:`importlib.metadata.version` to get the version for the - ``package_name``. On Python < 3.8, the ``importlib_metadata`` - backport must be installed. - - If ``package_name`` is not provided, Click will try to detect it by - inspecting the stack frames. This will be used to detect the - version, so it must match the name of the installed package. - - :param version: The version number to show. If not provided, Click - will try to detect it. - :param param_decls: One or more option names. Defaults to the single - value ``"--version"``. - :param package_name: The package name to detect the version from. If - not provided, Click will try to detect it. - :param prog_name: The name of the CLI to show in the message. If not - provided, it will be detected from the command. - :param message: The message to show. The values ``%(prog)s``, - ``%(package)s``, and ``%(version)s`` are available. Defaults to - ``"%(prog)s, version %(version)s"``. - :param kwargs: Extra arguments are passed to :func:`option`. - :raise RuntimeError: ``version`` could not be detected. - - .. versionchanged:: 8.0 - Add the ``package_name`` parameter, and the ``%(package)s`` - value for messages. - - .. versionchanged:: 8.0 - Use :mod:`importlib.metadata` instead of ``pkg_resources``. The - version is detected based on the package name, not the entry - point name. The Python package name must match the installed - package name, or be passed with ``package_name=``. - """ - if message is None: - message = _("%(prog)s, version %(version)s") - - if version is None and package_name is None: - frame = inspect.currentframe() - f_back = frame.f_back if frame is not None else None - f_globals = f_back.f_globals if f_back is not None else None - # break reference cycle - # https://docs.python.org/3/library/inspect.html#the-interpreter-stack - del frame - - if f_globals is not None: - package_name = f_globals.get("__name__") - - if package_name == "__main__": - package_name = f_globals.get("__package__") - - if package_name: - package_name = package_name.partition(".")[0] - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return - - nonlocal prog_name - nonlocal version - - if prog_name is None: - prog_name = ctx.find_root().info_name - - if version is None and package_name is not None: - metadata: t.Optional[types.ModuleType] - - try: - from importlib import metadata - except ImportError: - # Python < 3.8 - import importlib_metadata as metadata # type: ignore - - try: - version = metadata.version(package_name) # type: ignore - except metadata.PackageNotFoundError: # type: ignore - raise RuntimeError( - f"{package_name!r} is not installed. Try passing" - " 'package_name' instead." - ) from None - - if version is None: - raise RuntimeError( - f"Could not determine the version for {package_name!r} automatically." - ) - - echo( - message % {"prog": prog_name, "package": package_name, "version": version}, - color=ctx.color, - ) - ctx.exit() - - if not param_decls: - param_decls = ("--version",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show the version and exit.")) - kwargs["callback"] = callback - return option(*param_decls, **kwargs) - - -class HelpOption(Option): - """Pre-configured ``--help`` option which immediately prints the help page - and exits the program. - """ - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - **kwargs: t.Any, - ) -> None: - if not param_decls: - param_decls = ("--help",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs.setdefault("callback", self.show_help) - - super().__init__(param_decls, **kwargs) - - @staticmethod - def show_help(ctx: Context, param: Parameter, value: bool) -> None: - """Callback that print the help page on ```` and exits.""" - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Decorator for the pre-configured ``--help`` option defined above. - - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - kwargs.setdefault("cls", HelpOption) - return option(*param_decls, **kwargs) diff --git a/.venv/Lib/site-packages/click/exceptions.py b/.venv/Lib/site-packages/click/exceptions.py deleted file mode 100644 index 0b83151..0000000 --- a/.venv/Lib/site-packages/click/exceptions.py +++ /dev/null @@ -1,296 +0,0 @@ -import typing as t -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import get_text_stderr -from .globals import resolve_color_default -from .utils import echo -from .utils import format_filename - -if t.TYPE_CHECKING: - from .core import Command - from .core import Context - from .core import Parameter - - -def _join_param_hints( - param_hint: t.Optional[t.Union[t.Sequence[str], str]], -) -> t.Optional[str]: - if param_hint is not None and not isinstance(param_hint, str): - return " / ".join(repr(x) for x in param_hint) - - return param_hint - - -class ClickException(Exception): - """An exception that Click can handle and show to the user.""" - - #: The exit code for this exception. - exit_code = 1 - - def __init__(self, message: str) -> None: - super().__init__(message) - # The context will be removed by the time we print the message, so cache - # the color settings here to be used later on (in `show`) - self.show_color: t.Optional[bool] = resolve_color_default() - self.message = message - - def format_message(self) -> str: - return self.message - - def __str__(self) -> str: - return self.message - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=self.show_color, - ) - - -class UsageError(ClickException): - """An internal exception that signals a usage error. This typically - aborts any further handling. - - :param message: the error message to display. - :param ctx: optionally the context that caused this error. Click will - fill in the context automatically in some situations. - """ - - exit_code = 2 - - def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: - super().__init__(message) - self.ctx = ctx - self.cmd: t.Optional[Command] = self.ctx.command if self.ctx else None - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - color = None - hint = "" - if ( - self.ctx is not None - and self.ctx.command.get_help_option(self.ctx) is not None - ): - hint = _("Try '{command} {option}' for help.").format( - command=self.ctx.command_path, option=self.ctx.help_option_names[0] - ) - hint = f"{hint}\n" - if self.ctx is not None: - color = self.ctx.color - echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=color, - ) - - -class BadParameter(UsageError): - """An exception that formats out a standardized error message for a - bad parameter. This is useful when thrown from a callback or type as - Click will attach contextual information to it (for instance, which - parameter it is). - - .. versionadded:: 2.0 - - :param param: the parameter object that caused this error. This can - be left out, and Click will attach this info itself - if possible. - :param param_hint: a string that shows up as parameter name. This - can be used as alternative to `param` in cases - where custom validation should happen. If it is - a string it's used as such, if it's a list then - each item is quoted and separated. - """ - - def __init__( - self, - message: str, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - ) -> None: - super().__init__(message, ctx) - self.param = param - self.param_hint = param_hint - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - return _("Invalid value: {message}").format(message=self.message) - - return _("Invalid value for {param_hint}: {message}").format( - param_hint=_join_param_hints(param_hint), message=self.message - ) - - -class MissingParameter(BadParameter): - """Raised if click required an option or argument but it was not - provided when invoking the script. - - .. versionadded:: 4.0 - - :param param_type: a string that indicates the type of the parameter. - The default is to inherit the parameter type from - the given `param`. Valid values are ``'parameter'``, - ``'option'`` or ``'argument'``. - """ - - def __init__( - self, - message: t.Optional[str] = None, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - param_type: t.Optional[str] = None, - ) -> None: - super().__init__(message or "", ctx, param, param_hint) - self.param_type = param_type - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint: t.Optional[str] = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - param_hint = None - - param_hint = _join_param_hints(param_hint) - param_hint = f" {param_hint}" if param_hint else "" - - param_type = self.param_type - if param_type is None and self.param is not None: - param_type = self.param.param_type_name - - msg = self.message - if self.param is not None: - msg_extra = self.param.type.get_missing_message(self.param) - if msg_extra: - if msg: - msg += f". {msg_extra}" - else: - msg = msg_extra - - msg = f" {msg}" if msg else "" - - # Translate param_type for known types. - if param_type == "argument": - missing = _("Missing argument") - elif param_type == "option": - missing = _("Missing option") - elif param_type == "parameter": - missing = _("Missing parameter") - else: - missing = _("Missing {param_type}").format(param_type=param_type) - - return f"{missing}{param_hint}.{msg}" - - def __str__(self) -> str: - if not self.message: - param_name = self.param.name if self.param else None - return _("Missing parameter: {param_name}").format(param_name=param_name) - else: - return self.message - - -class NoSuchOption(UsageError): - """Raised if click attempted to handle an option that does not - exist. - - .. versionadded:: 4.0 - """ - - def __init__( - self, - option_name: str, - message: t.Optional[str] = None, - possibilities: t.Optional[t.Sequence[str]] = None, - ctx: t.Optional["Context"] = None, - ) -> None: - if message is None: - message = _("No such option: {name}").format(name=option_name) - - super().__init__(message, ctx) - self.option_name = option_name - self.possibilities = possibilities - - def format_message(self) -> str: - if not self.possibilities: - return self.message - - possibility_str = ", ".join(sorted(self.possibilities)) - suggest = ngettext( - "Did you mean {possibility}?", - "(Possible options: {possibilities})", - len(self.possibilities), - ).format(possibility=possibility_str, possibilities=possibility_str) - return f"{self.message} {suggest}" - - -class BadOptionUsage(UsageError): - """Raised if an option is generally supplied but the use of the option - was incorrect. This is for instance raised if the number of arguments - for an option is not correct. - - .. versionadded:: 4.0 - - :param option_name: the name of the option being used incorrectly. - """ - - def __init__( - self, option_name: str, message: str, ctx: t.Optional["Context"] = None - ) -> None: - super().__init__(message, ctx) - self.option_name = option_name - - -class BadArgumentUsage(UsageError): - """Raised if an argument is generally supplied but the use of the argument - was incorrect. This is for instance raised if the number of values - for an argument is not correct. - - .. versionadded:: 6.0 - """ - - -class FileError(ClickException): - """Raised if a file cannot be opened.""" - - def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: - if hint is None: - hint = _("unknown error") - - super().__init__(hint) - self.ui_filename: str = format_filename(filename) - self.filename = filename - - def format_message(self) -> str: - return _("Could not open file {filename!r}: {message}").format( - filename=self.ui_filename, message=self.message - ) - - -class Abort(RuntimeError): - """An internal signalling exception that signals Click to abort.""" - - -class Exit(RuntimeError): - """An exception that indicates that the application should exit with some - status code. - - :param code: the status code to exit with. - """ - - __slots__ = ("exit_code",) - - def __init__(self, code: int = 0) -> None: - self.exit_code: int = code diff --git a/.venv/Lib/site-packages/click/formatting.py b/.venv/Lib/site-packages/click/formatting.py deleted file mode 100644 index ddd2a2f..0000000 --- a/.venv/Lib/site-packages/click/formatting.py +++ /dev/null @@ -1,301 +0,0 @@ -import typing as t -from contextlib import contextmanager -from gettext import gettext as _ - -from ._compat import term_len -from .parser import split_opt - -# Can force a width. This is used by the test system -FORCED_WIDTH: t.Optional[int] = None - - -def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: - widths: t.Dict[int, int] = {} - - for row in rows: - for idx, col in enumerate(row): - widths[idx] = max(widths.get(idx, 0), term_len(col)) - - return tuple(y for x, y in sorted(widths.items())) - - -def iter_rows( - rows: t.Iterable[t.Tuple[str, str]], col_count: int -) -> t.Iterator[t.Tuple[str, ...]]: - for row in rows: - yield row + ("",) * (col_count - len(row)) - - -def wrap_text( - text: str, - width: int = 78, - initial_indent: str = "", - subsequent_indent: str = "", - preserve_paragraphs: bool = False, -) -> str: - """A helper function that intelligently wraps text. By default, it - assumes that it operates on a single paragraph of text but if the - `preserve_paragraphs` parameter is provided it will intelligently - handle paragraphs (defined by two empty lines). - - If paragraphs are handled, a paragraph can be prefixed with an empty - line containing the ``\\b`` character (``\\x08``) to indicate that - no rewrapping should happen in that block. - - :param text: the text that should be rewrapped. - :param width: the maximum width for the text. - :param initial_indent: the initial indent that should be placed on the - first line as a string. - :param subsequent_indent: the indent string that should be placed on - each consecutive line. - :param preserve_paragraphs: if this flag is set then the wrapping will - intelligently handle paragraphs. - """ - from ._textwrap import TextWrapper - - text = text.expandtabs() - wrapper = TextWrapper( - width, - initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - replace_whitespace=False, - ) - if not preserve_paragraphs: - return wrapper.fill(text) - - p: t.List[t.Tuple[int, bool, str]] = [] - buf: t.List[str] = [] - indent = None - - def _flush_par() -> None: - if not buf: - return - if buf[0].strip() == "\b": - p.append((indent or 0, True, "\n".join(buf[1:]))) - else: - p.append((indent or 0, False, " ".join(buf))) - del buf[:] - - for line in text.splitlines(): - if not line: - _flush_par() - indent = None - else: - if indent is None: - orig_len = term_len(line) - line = line.lstrip() - indent = orig_len - term_len(line) - buf.append(line) - _flush_par() - - rv = [] - for indent, raw, text in p: - with wrapper.extra_indent(" " * indent): - if raw: - rv.append(wrapper.indent_only(text)) - else: - rv.append(wrapper.fill(text)) - - return "\n\n".join(rv) - - -class HelpFormatter: - """This class helps with formatting text-based help pages. It's - usually just needed for very special internal cases, but it's also - exposed so that developers can write their own fancy outputs. - - At present, it always writes into memory. - - :param indent_increment: the additional increment for each level. - :param width: the width for the text. This defaults to the terminal - width clamped to a maximum of 78. - """ - - def __init__( - self, - indent_increment: int = 2, - width: t.Optional[int] = None, - max_width: t.Optional[int] = None, - ) -> None: - import shutil - - self.indent_increment = indent_increment - if max_width is None: - max_width = 80 - if width is None: - width = FORCED_WIDTH - if width is None: - width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) - self.width = width - self.current_indent = 0 - self.buffer: t.List[str] = [] - - def write(self, string: str) -> None: - """Writes a unicode string into the internal buffer.""" - self.buffer.append(string) - - def indent(self) -> None: - """Increases the indentation.""" - self.current_indent += self.indent_increment - - def dedent(self) -> None: - """Decreases the indentation.""" - self.current_indent -= self.indent_increment - - def write_usage( - self, prog: str, args: str = "", prefix: t.Optional[str] = None - ) -> None: - """Writes a usage line into the buffer. - - :param prog: the program name. - :param args: whitespace separated list of arguments. - :param prefix: The prefix for the first line. Defaults to - ``"Usage: "``. - """ - if prefix is None: - prefix = f"{_('Usage:')} " - - usage_prefix = f"{prefix:>{self.current_indent}}{prog} " - text_width = self.width - self.current_indent - - if text_width >= (term_len(usage_prefix) + 20): - # The arguments will fit to the right of the prefix. - indent = " " * term_len(usage_prefix) - self.write( - wrap_text( - args, - text_width, - initial_indent=usage_prefix, - subsequent_indent=indent, - ) - ) - else: - # The prefix is too long, put the arguments on the next line. - self.write(usage_prefix) - self.write("\n") - indent = " " * (max(self.current_indent, term_len(prefix)) + 4) - self.write( - wrap_text( - args, text_width, initial_indent=indent, subsequent_indent=indent - ) - ) - - self.write("\n") - - def write_heading(self, heading: str) -> None: - """Writes a heading into the buffer.""" - self.write(f"{'':>{self.current_indent}}{heading}:\n") - - def write_paragraph(self) -> None: - """Writes a paragraph into the buffer.""" - if self.buffer: - self.write("\n") - - def write_text(self, text: str) -> None: - """Writes re-indented text into the buffer. This rewraps and - preserves paragraphs. - """ - indent = " " * self.current_indent - self.write( - wrap_text( - text, - self.width, - initial_indent=indent, - subsequent_indent=indent, - preserve_paragraphs=True, - ) - ) - self.write("\n") - - def write_dl( - self, - rows: t.Sequence[t.Tuple[str, str]], - col_max: int = 30, - col_spacing: int = 2, - ) -> None: - """Writes a definition list into the buffer. This is how options - and commands are usually formatted. - - :param rows: a list of two item tuples for the terms and values. - :param col_max: the maximum width of the first column. - :param col_spacing: the number of spaces between the first and - second column. - """ - rows = list(rows) - widths = measure_table(rows) - if len(widths) != 2: - raise TypeError("Expected two columns for definition list") - - first_col = min(widths[0], col_max) + col_spacing - - for first, second in iter_rows(rows, len(widths)): - self.write(f"{'':>{self.current_indent}}{first}") - if not second: - self.write("\n") - continue - if term_len(first) <= first_col - col_spacing: - self.write(" " * (first_col - term_len(first))) - else: - self.write("\n") - self.write(" " * (first_col + self.current_indent)) - - text_width = max(self.width - first_col - 2, 10) - wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) - lines = wrapped_text.splitlines() - - if lines: - self.write(f"{lines[0]}\n") - - for line in lines[1:]: - self.write(f"{'':>{first_col + self.current_indent}}{line}\n") - else: - self.write("\n") - - @contextmanager - def section(self, name: str) -> t.Iterator[None]: - """Helpful context manager that writes a paragraph, a heading, - and the indents. - - :param name: the section name that is written as heading. - """ - self.write_paragraph() - self.write_heading(name) - self.indent() - try: - yield - finally: - self.dedent() - - @contextmanager - def indentation(self) -> t.Iterator[None]: - """A context manager that increases the indentation.""" - self.indent() - try: - yield - finally: - self.dedent() - - def getvalue(self) -> str: - """Returns the buffer contents.""" - return "".join(self.buffer) - - -def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: - """Given a list of option strings this joins them in the most appropriate - way and returns them in the form ``(formatted_string, - any_prefix_is_slash)`` where the second item in the tuple is a flag that - indicates if any of the option prefixes was a slash. - """ - rv = [] - any_prefix_is_slash = False - - for opt in options: - prefix = split_opt(opt)[0] - - if prefix == "/": - any_prefix_is_slash = True - - rv.append((len(prefix), opt)) - - rv.sort(key=lambda x: x[0]) - return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/.venv/Lib/site-packages/click/globals.py b/.venv/Lib/site-packages/click/globals.py deleted file mode 100644 index 191e712..0000000 --- a/.venv/Lib/site-packages/click/globals.py +++ /dev/null @@ -1,67 +0,0 @@ -import typing as t -from threading import local - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Context - -_local = local() - - -@t.overload -def get_current_context(silent: "te.Literal[False]" = False) -> "Context": ... - - -@t.overload -def get_current_context(silent: bool = ...) -> t.Optional["Context"]: ... - - -def get_current_context(silent: bool = False) -> t.Optional["Context"]: - """Returns the current click context. This can be used as a way to - access the current context object from anywhere. This is a more implicit - alternative to the :func:`pass_context` decorator. This function is - primarily useful for helpers such as :func:`echo` which might be - interested in changing its behavior based on the current context. - - To push the current context, :meth:`Context.scope` can be used. - - .. versionadded:: 5.0 - - :param silent: if set to `True` the return value is `None` if no context - is available. The default behavior is to raise a - :exc:`RuntimeError`. - """ - try: - return t.cast("Context", _local.stack[-1]) - except (AttributeError, IndexError) as e: - if not silent: - raise RuntimeError("There is no active click context.") from e - - return None - - -def push_context(ctx: "Context") -> None: - """Pushes a new context to the current stack.""" - _local.__dict__.setdefault("stack", []).append(ctx) - - -def pop_context() -> None: - """Removes the top level from the stack.""" - _local.stack.pop() - - -def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: - """Internal helper to get the default value of the color flag. If a - value is passed it's returned unchanged, otherwise it's looked up from - the current context. - """ - if color is not None: - return color - - ctx = get_current_context(silent=True) - - if ctx is not None: - return ctx.color - - return None diff --git a/.venv/Lib/site-packages/click/parser.py b/.venv/Lib/site-packages/click/parser.py deleted file mode 100644 index 600b843..0000000 --- a/.venv/Lib/site-packages/click/parser.py +++ /dev/null @@ -1,531 +0,0 @@ -""" -This module started out as largely a copy paste from the stdlib's -optparse module with the features removed that we do not need from -optparse because we implement them in Click on a higher level (for -instance type handling, help formatting and a lot more). - -The plan is to remove more and more from here over time. - -The reason this is a different module and not optparse from the stdlib -is that there are differences in 2.x and 3.x about the error messages -generated and optparse in the stdlib uses gettext for no good reason -and might cause us issues. - -Click uses parts of optparse written by Gregory P. Ward and maintained -by the Python Software Foundation. This is limited to code in parser.py. - -Copyright 2001-2006 Gregory P. Ward. All rights reserved. -Copyright 2002-2006 Python Software Foundation. All rights reserved. -""" - -# This code uses parts of optparse written by Gregory P. Ward and -# maintained by the Python Software Foundation. -# Copyright 2001-2006 Gregory P. Ward -# Copyright 2002-2006 Python Software Foundation -import typing as t -from collections import deque -from gettext import gettext as _ -from gettext import ngettext - -from .exceptions import BadArgumentUsage -from .exceptions import BadOptionUsage -from .exceptions import NoSuchOption -from .exceptions import UsageError - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Argument as CoreArgument - from .core import Context - from .core import Option as CoreOption - from .core import Parameter as CoreParameter - -V = t.TypeVar("V") - -# Sentinel value that indicates an option was passed as a flag without a -# value but is not a flag option. Option.consume_value uses this to -# prompt or use the flag_value. -_flag_needs_value = object() - - -def _unpack_args( - args: t.Sequence[str], nargs_spec: t.Sequence[int] -) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: - """Given an iterable of arguments and an iterable of nargs specifications, - it returns a tuple with all the unpacked arguments at the first index - and all remaining arguments as the second. - - The nargs specification is the number of arguments that should be consumed - or `-1` to indicate that this position should eat up all the remainders. - - Missing items are filled with `None`. - """ - args = deque(args) - nargs_spec = deque(nargs_spec) - rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] - spos: t.Optional[int] = None - - def _fetch(c: "te.Deque[V]") -> t.Optional[V]: - try: - if spos is None: - return c.popleft() - else: - return c.pop() - except IndexError: - return None - - while nargs_spec: - nargs = _fetch(nargs_spec) - - if nargs is None: - continue - - if nargs == 1: - rv.append(_fetch(args)) - elif nargs > 1: - x = [_fetch(args) for _ in range(nargs)] - - # If we're reversed, we're pulling in the arguments in reverse, - # so we need to turn them around. - if spos is not None: - x.reverse() - - rv.append(tuple(x)) - elif nargs < 0: - if spos is not None: - raise TypeError("Cannot have two nargs < 0") - - spos = len(rv) - rv.append(None) - - # spos is the position of the wildcard (star). If it's not `None`, - # we fill it with the remainder. - if spos is not None: - rv[spos] = tuple(args) - args = [] - rv[spos + 1 :] = reversed(rv[spos + 1 :]) - - return tuple(rv), list(args) - - -def split_opt(opt: str) -> t.Tuple[str, str]: - first = opt[:1] - if first.isalnum(): - return "", opt - if opt[1:2] == first: - return opt[:2], opt[2:] - return first, opt[1:] - - -def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: - if ctx is None or ctx.token_normalize_func is None: - return opt - prefix, opt = split_opt(opt) - return f"{prefix}{ctx.token_normalize_func(opt)}" - - -def split_arg_string(string: str) -> t.List[str]: - """Split an argument string as with :func:`shlex.split`, but don't - fail if the string is incomplete. Ignores a missing closing quote or - incomplete escape sequence and uses the partial token as-is. - - .. code-block:: python - - split_arg_string("example 'my file") - ["example", "my file"] - - split_arg_string("example my\\") - ["example", "my"] - - :param string: String to split. - """ - import shlex - - lex = shlex.shlex(string, posix=True) - lex.whitespace_split = True - lex.commenters = "" - out = [] - - try: - for token in lex: - out.append(token) - except ValueError: - # Raised when end-of-string is reached in an invalid state. Use - # the partial token as-is. The quote or escape character is in - # lex.state, not lex.token. - out.append(lex.token) - - return out - - -class Option: - def __init__( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ): - self._short_opts = [] - self._long_opts = [] - self.prefixes: t.Set[str] = set() - - for opt in opts: - prefix, value = split_opt(opt) - if not prefix: - raise ValueError(f"Invalid start character for option ({opt})") - self.prefixes.add(prefix[0]) - if len(prefix) == 1 and len(value) == 1: - self._short_opts.append(opt) - else: - self._long_opts.append(opt) - self.prefixes.add(prefix) - - if action is None: - action = "store" - - self.dest = dest - self.action = action - self.nargs = nargs - self.const = const - self.obj = obj - - @property - def takes_value(self) -> bool: - return self.action in ("store", "append") - - def process(self, value: t.Any, state: "ParsingState") -> None: - if self.action == "store": - state.opts[self.dest] = value # type: ignore - elif self.action == "store_const": - state.opts[self.dest] = self.const # type: ignore - elif self.action == "append": - state.opts.setdefault(self.dest, []).append(value) # type: ignore - elif self.action == "append_const": - state.opts.setdefault(self.dest, []).append(self.const) # type: ignore - elif self.action == "count": - state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore - else: - raise ValueError(f"unknown action '{self.action}'") - state.order.append(self.obj) - - -class Argument: - def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): - self.dest = dest - self.nargs = nargs - self.obj = obj - - def process( - self, - value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], - state: "ParsingState", - ) -> None: - if self.nargs > 1: - assert value is not None - holes = sum(1 for x in value if x is None) - if holes == len(value): - value = None - elif holes != 0: - raise BadArgumentUsage( - _("Argument {name!r} takes {nargs} values.").format( - name=self.dest, nargs=self.nargs - ) - ) - - if self.nargs == -1 and self.obj.envvar is not None and value == (): - # Replace empty tuple with None so that a value from the - # environment may be tried. - value = None - - state.opts[self.dest] = value # type: ignore - state.order.append(self.obj) - - -class ParsingState: - def __init__(self, rargs: t.List[str]) -> None: - self.opts: t.Dict[str, t.Any] = {} - self.largs: t.List[str] = [] - self.rargs = rargs - self.order: t.List[CoreParameter] = [] - - -class OptionParser: - """The option parser is an internal class that is ultimately used to - parse options and arguments. It's modelled after optparse and brings - a similar but vastly simplified API. It should generally not be used - directly as the high level Click classes wrap it for you. - - It's not nearly as extensible as optparse or argparse as it does not - implement features that are implemented on a higher level (such as - types or defaults). - - :param ctx: optionally the :class:`~click.Context` where this parser - should go with. - """ - - def __init__(self, ctx: t.Optional["Context"] = None) -> None: - #: The :class:`~click.Context` for this parser. This might be - #: `None` for some advanced use cases. - self.ctx = ctx - #: This controls how the parser deals with interspersed arguments. - #: If this is set to `False`, the parser will stop on the first - #: non-option. Click uses this to implement nested subcommands - #: safely. - self.allow_interspersed_args: bool = True - #: This tells the parser how to deal with unknown options. By - #: default it will error out (which is sensible), but there is a - #: second mode where it will ignore it and continue processing - #: after shifting all the unknown options into the resulting args. - self.ignore_unknown_options: bool = False - - if ctx is not None: - self.allow_interspersed_args = ctx.allow_interspersed_args - self.ignore_unknown_options = ctx.ignore_unknown_options - - self._short_opt: t.Dict[str, Option] = {} - self._long_opt: t.Dict[str, Option] = {} - self._opt_prefixes = {"-", "--"} - self._args: t.List[Argument] = [] - - def add_option( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ) -> None: - """Adds a new option named `dest` to the parser. The destination - is not inferred (unlike with optparse) and needs to be explicitly - provided. Action can be any of ``store``, ``store_const``, - ``append``, ``append_const`` or ``count``. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - opts = [normalize_opt(opt, self.ctx) for opt in opts] - option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) - self._opt_prefixes.update(option.prefixes) - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - def add_argument( - self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 - ) -> None: - """Adds a positional argument named `dest` to the parser. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - self._args.append(Argument(obj, dest=dest, nargs=nargs)) - - def parse_args( - self, args: t.List[str] - ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: - """Parses positional arguments and returns ``(values, args, order)`` - for the parsed options and arguments as well as the leftover - arguments if there are any. The order is a list of objects as they - appear on the command line. If arguments appear multiple times they - will be memorized multiple times as well. - """ - state = ParsingState(args) - try: - self._process_args_for_options(state) - self._process_args_for_args(state) - except UsageError: - if self.ctx is None or not self.ctx.resilient_parsing: - raise - return state.opts, state.largs, state.order - - def _process_args_for_args(self, state: ParsingState) -> None: - pargs, args = _unpack_args( - state.largs + state.rargs, [x.nargs for x in self._args] - ) - - for idx, arg in enumerate(self._args): - arg.process(pargs[idx], state) - - state.largs = args - state.rargs = [] - - def _process_args_for_options(self, state: ParsingState) -> None: - while state.rargs: - arg = state.rargs.pop(0) - arglen = len(arg) - # Double dashes always handled explicitly regardless of what - # prefixes are valid. - if arg == "--": - return - elif arg[:1] in self._opt_prefixes and arglen > 1: - self._process_opts(arg, state) - elif self.allow_interspersed_args: - state.largs.append(arg) - else: - state.rargs.insert(0, arg) - return - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt( - self, opt: str, explicit_value: t.Optional[str], state: ParsingState - ) -> None: - if opt not in self._long_opt: - from difflib import get_close_matches - - possibilities = get_close_matches(opt, self._long_opt) - raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) - - option = self._long_opt[opt] - if option.takes_value: - # At this point it's safe to modify rargs by injecting the - # explicit value, because no exception is raised in this - # branch. This means that the inserted value will be fully - # consumed. - if explicit_value is not None: - state.rargs.insert(0, explicit_value) - - value = self._get_value_from_state(opt, option, state) - - elif explicit_value is not None: - raise BadOptionUsage( - opt, _("Option {name!r} does not take a value.").format(name=opt) - ) - - else: - value = None - - option.process(value, state) - - def _match_short_opt(self, arg: str, state: ParsingState) -> None: - stop = False - i = 1 - prefix = arg[0] - unknown_options = [] - - for ch in arg[1:]: - opt = normalize_opt(f"{prefix}{ch}", self.ctx) - option = self._short_opt.get(opt) - i += 1 - - if not option: - if self.ignore_unknown_options: - unknown_options.append(ch) - continue - raise NoSuchOption(opt, ctx=self.ctx) - if option.takes_value: - # Any characters left in arg? Pretend they're the - # next arg, and stop consuming characters of arg. - if i < len(arg): - state.rargs.insert(0, arg[i:]) - stop = True - - value = self._get_value_from_state(opt, option, state) - - else: - value = None - - option.process(value, state) - - if stop: - break - - # If we got any unknown options we recombine the string of the - # remaining options and re-attach the prefix, then report that - # to the state as new larg. This way there is basic combinatorics - # that can be achieved while still ignoring unknown arguments. - if self.ignore_unknown_options and unknown_options: - state.largs.append(f"{prefix}{''.join(unknown_options)}") - - def _get_value_from_state( - self, option_name: str, option: Option, state: ParsingState - ) -> t.Any: - nargs = option.nargs - - if len(state.rargs) < nargs: - if option.obj._flag_needs_value: - # Option allows omitting the value. - value = _flag_needs_value - else: - raise BadOptionUsage( - option_name, - ngettext( - "Option {name!r} requires an argument.", - "Option {name!r} requires {nargs} arguments.", - nargs, - ).format(name=option_name, nargs=nargs), - ) - elif nargs == 1: - next_rarg = state.rargs[0] - - if ( - option.obj._flag_needs_value - and isinstance(next_rarg, str) - and next_rarg[:1] in self._opt_prefixes - and len(next_rarg) > 1 - ): - # The next arg looks like the start of an option, don't - # use it as the value if omitting the value is allowed. - value = _flag_needs_value - else: - value = state.rargs.pop(0) - else: - value = tuple(state.rargs[:nargs]) - del state.rargs[:nargs] - - return value - - def _process_opts(self, arg: str, state: ParsingState) -> None: - explicit_value = None - # Long option handling happens in two parts. The first part is - # supporting explicitly attached values. In any case, we will try - # to long match the option first. - if "=" in arg: - long_opt, explicit_value = arg.split("=", 1) - else: - long_opt = arg - norm_long_opt = normalize_opt(long_opt, self.ctx) - - # At this point we will match the (assumed) long option through - # the long option matching code. Note that this allows options - # like "-foo" to be matched as long options. - try: - self._match_long_opt(norm_long_opt, explicit_value, state) - except NoSuchOption: - # At this point the long option matching failed, and we need - # to try with short options. However there is a special rule - # which says, that if we have a two character options prefix - # (applies to "--foo" for instance), we do not dispatch to the - # short option code and will instead raise the no option - # error. - if arg[:2] not in self._opt_prefixes: - self._match_short_opt(arg, state) - return - - if not self.ignore_unknown_options: - raise - - state.largs.append(arg) diff --git a/.venv/Lib/site-packages/click/py.typed b/.venv/Lib/site-packages/click/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/click/shell_completion.py b/.venv/Lib/site-packages/click/shell_completion.py deleted file mode 100644 index 07d0f09..0000000 --- a/.venv/Lib/site-packages/click/shell_completion.py +++ /dev/null @@ -1,603 +0,0 @@ -import os -import re -import typing as t -from gettext import gettext as _ - -from .core import Argument -from .core import BaseCommand -from .core import Context -from .core import MultiCommand -from .core import Option -from .core import Parameter -from .core import ParameterSource -from .parser import split_arg_string -from .utils import echo - - -def shell_complete( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - instruction: str, -) -> int: - """Perform shell completion for the given CLI program. - - :param cli: Command being called. - :param ctx_args: Extra arguments to pass to - ``cli.make_context``. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - :param instruction: Value of ``complete_var`` with the completion - instruction and shell, in the form ``instruction_shell``. - :return: Status code to exit with. - """ - shell, _, instruction = instruction.partition("_") - comp_cls = get_completion_class(shell) - - if comp_cls is None: - return 1 - - comp = comp_cls(cli, ctx_args, prog_name, complete_var) - - if instruction == "source": - echo(comp.source()) - return 0 - - if instruction == "complete": - echo(comp.complete()) - return 0 - - return 1 - - -class CompletionItem: - """Represents a completion value and metadata about the value. The - default metadata is ``type`` to indicate special shell handling, - and ``help`` if a shell supports showing a help string next to the - value. - - Arbitrary parameters can be passed when creating the object, and - accessed using ``item.attr``. If an attribute wasn't passed, - accessing it returns ``None``. - - :param value: The completion suggestion. - :param type: Tells the shell script to provide special completion - support for the type. Click uses ``"dir"`` and ``"file"``. - :param help: String shown next to the value if supported. - :param kwargs: Arbitrary metadata. The built-in implementations - don't use this, but custom type completions paired with custom - shell support could use it. - """ - - __slots__ = ("value", "type", "help", "_info") - - def __init__( - self, - value: t.Any, - type: str = "plain", - help: t.Optional[str] = None, - **kwargs: t.Any, - ) -> None: - self.value: t.Any = value - self.type: str = type - self.help: t.Optional[str] = help - self._info = kwargs - - def __getattr__(self, name: str) -> t.Any: - return self._info.get(name) - - -# Only Bash >= 4.4 has the nosort option. -_SOURCE_BASH = """\ -%(complete_func)s() { - local IFS=$'\\n' - local response - - response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ -%(complete_var)s=bash_complete $1) - - for completion in $response; do - IFS=',' read type value <<< "$completion" - - if [[ $type == 'dir' ]]; then - COMPREPLY=() - compopt -o dirnames - elif [[ $type == 'file' ]]; then - COMPREPLY=() - compopt -o default - elif [[ $type == 'plain' ]]; then - COMPREPLY+=($value) - fi - done - - return 0 -} - -%(complete_func)s_setup() { - complete -o nosort -F %(complete_func)s %(prog_name)s -} - -%(complete_func)s_setup; -""" - -_SOURCE_ZSH = """\ -#compdef %(prog_name)s - -%(complete_func)s() { - local -a completions - local -a completions_with_descriptions - local -a response - (( ! $+commands[%(prog_name)s] )) && return 1 - - response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ -%(complete_var)s=zsh_complete %(prog_name)s)}") - - for type key descr in ${response}; do - if [[ "$type" == "plain" ]]; then - if [[ "$descr" == "_" ]]; then - completions+=("$key") - else - completions_with_descriptions+=("$key":"$descr") - fi - elif [[ "$type" == "dir" ]]; then - _path_files -/ - elif [[ "$type" == "file" ]]; then - _path_files -f - fi - done - - if [ -n "$completions_with_descriptions" ]; then - _describe -V unsorted completions_with_descriptions -U - fi - - if [ -n "$completions" ]; then - compadd -U -V unsorted -a completions - fi -} - -if [[ $zsh_eval_context[-1] == loadautofunc ]]; then - # autoload from fpath, call function directly - %(complete_func)s "$@" -else - # eval/source/. command, register function for later - compdef %(complete_func)s %(prog_name)s -fi -""" - -_SOURCE_FISH = """\ -function %(complete_func)s; - set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ -COMP_CWORD=(commandline -t) %(prog_name)s); - - for completion in $response; - set -l metadata (string split "," $completion); - - if test $metadata[1] = "dir"; - __fish_complete_directories $metadata[2]; - else if test $metadata[1] = "file"; - __fish_complete_path $metadata[2]; - else if test $metadata[1] = "plain"; - echo $metadata[2]; - end; - end; -end; - -complete --no-files --command %(prog_name)s --arguments \ -"(%(complete_func)s)"; -""" - - -class ShellComplete: - """Base class for providing shell completion support. A subclass for - a given shell will override attributes and methods to implement the - completion instructions (``source`` and ``complete``). - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - - .. versionadded:: 8.0 - """ - - name: t.ClassVar[str] - """Name to register the shell as with :func:`add_completion_class`. - This is used in completion instructions (``{name}_source`` and - ``{name}_complete``). - """ - - source_template: t.ClassVar[str] - """Completion script template formatted by :meth:`source`. This must - be provided by subclasses. - """ - - def __init__( - self, - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - ) -> None: - self.cli = cli - self.ctx_args = ctx_args - self.prog_name = prog_name - self.complete_var = complete_var - - @property - def func_name(self) -> str: - """The name of the shell function defined by the completion - script. - """ - safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) - return f"_{safe_name}_completion" - - def source_vars(self) -> t.Dict[str, t.Any]: - """Vars for formatting :attr:`source_template`. - - By default this provides ``complete_func``, ``complete_var``, - and ``prog_name``. - """ - return { - "complete_func": self.func_name, - "complete_var": self.complete_var, - "prog_name": self.prog_name, - } - - def source(self) -> str: - """Produce the shell script that defines the completion - function. By default this ``%``-style formats - :attr:`source_template` with the dict returned by - :meth:`source_vars`. - """ - return self.source_template % self.source_vars() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - """Use the env vars defined by the shell script to return a - tuple of ``args, incomplete``. This must be implemented by - subclasses. - """ - raise NotImplementedError - - def get_completions( - self, args: t.List[str], incomplete: str - ) -> t.List[CompletionItem]: - """Determine the context and last complete command or parameter - from the complete args. Call that object's ``shell_complete`` - method to get the completions for the incomplete value. - - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) - obj, incomplete = _resolve_incomplete(ctx, args, incomplete) - return obj.shell_complete(ctx, incomplete) - - def format_completion(self, item: CompletionItem) -> str: - """Format a completion item into the form recognized by the - shell script. This must be implemented by subclasses. - - :param item: Completion item to format. - """ - raise NotImplementedError - - def complete(self) -> str: - """Produce the completion data to send back to the shell. - - By default this calls :meth:`get_completion_args`, gets the - completions, then calls :meth:`format_completion` for each - completion. - """ - args, incomplete = self.get_completion_args() - completions = self.get_completions(args, incomplete) - out = [self.format_completion(item) for item in completions] - return "\n".join(out) - - -class BashComplete(ShellComplete): - """Shell completion for Bash.""" - - name = "bash" - source_template = _SOURCE_BASH - - @staticmethod - def _check_version() -> None: - import shutil - import subprocess - - bash_exe = shutil.which("bash") - - if bash_exe is None: - match = None - else: - output = subprocess.run( - [bash_exe, "--norc", "-c", 'echo "${BASH_VERSION}"'], - stdout=subprocess.PIPE, - ) - match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) - - if match is not None: - major, minor = match.groups() - - if major < "4" or major == "4" and minor < "4": - echo( - _( - "Shell completion is not supported for Bash" - " versions older than 4.4." - ), - err=True, - ) - else: - echo( - _("Couldn't detect Bash version, shell completion is not supported."), - err=True, - ) - - def source(self) -> str: - self._check_version() - return super().source() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type},{item.value}" - - -class ZshComplete(ShellComplete): - """Shell completion for Zsh.""" - - name = "zsh" - source_template = _SOURCE_ZSH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" - - -class FishComplete(ShellComplete): - """Shell completion for Fish.""" - - name = "fish" - source_template = _SOURCE_FISH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - incomplete = os.environ["COMP_CWORD"] - args = cwords[1:] - - # Fish stores the partial word in both COMP_WORDS and - # COMP_CWORD, remove it from complete args. - if incomplete and args and args[-1] == incomplete: - args.pop() - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - if item.help: - return f"{item.type},{item.value}\t{item.help}" - - return f"{item.type},{item.value}" - - -ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) - - -_available_shells: t.Dict[str, t.Type[ShellComplete]] = { - "bash": BashComplete, - "fish": FishComplete, - "zsh": ZshComplete, -} - - -def add_completion_class( - cls: ShellCompleteType, name: t.Optional[str] = None -) -> ShellCompleteType: - """Register a :class:`ShellComplete` subclass under the given name. - The name will be provided by the completion instruction environment - variable during completion. - - :param cls: The completion class that will handle completion for the - shell. - :param name: Name to register the class under. Defaults to the - class's ``name`` attribute. - """ - if name is None: - name = cls.name - - _available_shells[name] = cls - - return cls - - -def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: - """Look up a registered :class:`ShellComplete` subclass by the name - provided by the completion instruction environment variable. If the - name isn't registered, returns ``None``. - - :param shell: Name the class is registered under. - """ - return _available_shells.get(shell) - - -def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: - """Determine if the given parameter is an argument that can still - accept values. - - :param ctx: Invocation context for the command represented by the - parsed complete args. - :param param: Argument object being checked. - """ - if not isinstance(param, Argument): - return False - - assert param.name is not None - # Will be None if expose_value is False. - value = ctx.params.get(param.name) - return ( - param.nargs == -1 - or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE - or ( - param.nargs > 1 - and isinstance(value, (tuple, list)) - and len(value) < param.nargs - ) - ) - - -def _start_of_option(ctx: Context, value: str) -> bool: - """Check if the value looks like the start of an option.""" - if not value: - return False - - c = value[0] - return c in ctx._opt_prefixes - - -def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: - """Determine if the given parameter is an option that needs a value. - - :param args: List of complete args before the incomplete value. - :param param: Option object being checked. - """ - if not isinstance(param, Option): - return False - - if param.is_flag or param.count: - return False - - last_option = None - - for index, arg in enumerate(reversed(args)): - if index + 1 > param.nargs: - break - - if _start_of_option(ctx, arg): - last_option = arg - - return last_option is not None and last_option in param.opts - - -def _resolve_context( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - args: t.List[str], -) -> Context: - """Produce the context hierarchy starting with the command and - traversing the complete arguments. This only follows the commands, - it doesn't trigger input prompts or callbacks. - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param args: List of complete args before the incomplete value. - """ - ctx_args["resilient_parsing"] = True - ctx = cli.make_context(prog_name, args.copy(), **ctx_args) - args = ctx.protected_args + ctx.args - - while args: - command = ctx.command - - if isinstance(command, MultiCommand): - if not command.chain: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) - args = ctx.protected_args + ctx.args - else: - sub_ctx = ctx - - while args: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - sub_ctx = cmd.make_context( - name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - resilient_parsing=True, - ) - args = sub_ctx.args - - ctx = sub_ctx - args = [*sub_ctx.protected_args, *sub_ctx.args] - else: - break - - return ctx - - -def _resolve_incomplete( - ctx: Context, args: t.List[str], incomplete: str -) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: - """Find the Click object that will handle the completion of the - incomplete value. Return the object and the incomplete value. - - :param ctx: Invocation context for the command represented by - the parsed complete args. - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - # Different shells treat an "=" between a long option name and - # value differently. Might keep the value joined, return the "=" - # as a separate item, or return the split name and value. Always - # split and discard the "=" to make completion easier. - if incomplete == "=": - incomplete = "" - elif "=" in incomplete and _start_of_option(ctx, incomplete): - name, _, incomplete = incomplete.partition("=") - args.append(name) - - # The "--" marker tells Click to stop treating values as options - # even if they start with the option character. If it hasn't been - # given and the incomplete arg looks like an option, the current - # command will provide option name completions. - if "--" not in args and _start_of_option(ctx, incomplete): - return ctx.command, incomplete - - params = ctx.command.get_params(ctx) - - # If the last complete arg is an option name with an incomplete - # value, the option will provide value completions. - for param in params: - if _is_incomplete_option(ctx, args, param): - return param, incomplete - - # It's not an option name or value. The first argument without a - # parsed value will provide value completions. - for param in params: - if _is_incomplete_argument(ctx, param): - return param, incomplete - - # There were no unparsed arguments, the command may be a group that - # will provide command name completions. - return ctx.command, incomplete diff --git a/.venv/Lib/site-packages/click/termui.py b/.venv/Lib/site-packages/click/termui.py deleted file mode 100644 index c084f19..0000000 --- a/.venv/Lib/site-packages/click/termui.py +++ /dev/null @@ -1,784 +0,0 @@ -import inspect -import io -import itertools -import sys -import typing as t -from gettext import gettext as _ - -from ._compat import isatty -from ._compat import strip_ansi -from .exceptions import Abort -from .exceptions import UsageError -from .globals import resolve_color_default -from .types import Choice -from .types import convert_type -from .types import ParamType -from .utils import echo -from .utils import LazyFile - -if t.TYPE_CHECKING: - from ._termui_impl import ProgressBar - -V = t.TypeVar("V") - -# The prompt functions to use. The doc tools currently override these -# functions to customize how they work. -visible_prompt_func: t.Callable[[str], str] = input - -_ansi_colors = { - "black": 30, - "red": 31, - "green": 32, - "yellow": 33, - "blue": 34, - "magenta": 35, - "cyan": 36, - "white": 37, - "reset": 39, - "bright_black": 90, - "bright_red": 91, - "bright_green": 92, - "bright_yellow": 93, - "bright_blue": 94, - "bright_magenta": 95, - "bright_cyan": 96, - "bright_white": 97, -} -_ansi_reset_all = "\033[0m" - - -def hidden_prompt_func(prompt: str) -> str: - import getpass - - return getpass.getpass(prompt) - - -def _build_prompt( - text: str, - suffix: str, - show_default: bool = False, - default: t.Optional[t.Any] = None, - show_choices: bool = True, - type: t.Optional[ParamType] = None, -) -> str: - prompt = text - if type is not None and show_choices and isinstance(type, Choice): - prompt += f" ({', '.join(map(str, type.choices))})" - if default is not None and show_default: - prompt = f"{prompt} [{_format_default(default)}]" - return f"{prompt}{suffix}" - - -def _format_default(default: t.Any) -> t.Any: - if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): - return default.name - - return default - - -def prompt( - text: str, - default: t.Optional[t.Any] = None, - hide_input: bool = False, - confirmation_prompt: t.Union[bool, str] = False, - type: t.Optional[t.Union[ParamType, t.Any]] = None, - value_proc: t.Optional[t.Callable[[str], t.Any]] = None, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, - show_choices: bool = True, -) -> t.Any: - """Prompts a user for input. This is a convenience function that can - be used to prompt a user for input later. - - If the user aborts the input by sending an interrupt signal, this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the text to show for the prompt. - :param default: the default value to use if no input happens. If this - is not given it will prompt until it's aborted. - :param hide_input: if this is set to true then the input value will - be hidden. - :param confirmation_prompt: Prompt a second time to confirm the - value. Can be set to a string instead of ``True`` to customize - the message. - :param type: the type to use to check the value against. - :param value_proc: if this parameter is provided it's a function that - is invoked instead of the type conversion to - convert a value. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - :param show_choices: Show or hide choices if the passed type is a Choice. - For example if type is a Choice of either day or week, - show_choices is true and text is "Group by" then the - prompt will be "Group by (day, week): ". - - .. versionadded:: 8.0 - ``confirmation_prompt`` can be a custom string. - - .. versionadded:: 7.0 - Added the ``show_choices`` parameter. - - .. versionadded:: 6.0 - Added unicode support for cmd.exe on Windows. - - .. versionadded:: 4.0 - Added the `err` parameter. - - """ - - def prompt_func(text: str) -> str: - f = hidden_prompt_func if hide_input else visible_prompt_func - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(text.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - return f(" ") - except (KeyboardInterrupt, EOFError): - # getpass doesn't print a newline if the user aborts input with ^C. - # Allegedly this behavior is inherited from getpass(3). - # A doc bug has been filed at https://bugs.python.org/issue24711 - if hide_input: - echo(None, err=err) - raise Abort() from None - - if value_proc is None: - value_proc = convert_type(type, default) - - prompt = _build_prompt( - text, prompt_suffix, show_default, default, show_choices, type - ) - - if confirmation_prompt: - if confirmation_prompt is True: - confirmation_prompt = _("Repeat for confirmation") - - confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) - - while True: - while True: - value = prompt_func(prompt) - if value: - break - elif default is not None: - value = default - break - try: - result = value_proc(value) - except UsageError as e: - if hide_input: - echo(_("Error: The value you entered was invalid."), err=err) - else: - echo(_("Error: {e.message}").format(e=e), err=err) - continue - if not confirmation_prompt: - return result - while True: - value2 = prompt_func(confirmation_prompt) - is_empty = not value and not value2 - if value2 or is_empty: - break - if value == value2: - return result - echo(_("Error: The two entered values do not match."), err=err) - - -def confirm( - text: str, - default: t.Optional[bool] = False, - abort: bool = False, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, -) -> bool: - """Prompts for confirmation (yes/no question). - - If the user aborts the input by sending a interrupt signal this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the question to ask. - :param default: The default value to use when no input is given. If - ``None``, repeat until input is given. - :param abort: if this is set to `True` a negative answer aborts the - exception by raising :exc:`Abort`. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - - .. versionchanged:: 8.0 - Repeat until input is given if ``default`` is ``None``. - - .. versionadded:: 4.0 - Added the ``err`` parameter. - """ - prompt = _build_prompt( - text, - prompt_suffix, - show_default, - "y/n" if default is None else ("Y/n" if default else "y/N"), - ) - - while True: - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(prompt.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - value = visible_prompt_func(" ").lower().strip() - except (KeyboardInterrupt, EOFError): - raise Abort() from None - if value in ("y", "yes"): - rv = True - elif value in ("n", "no"): - rv = False - elif default is not None and value == "": - rv = default - else: - echo(_("Error: invalid input"), err=err) - continue - break - if abort and not rv: - raise Abort() - return rv - - -def echo_via_pager( - text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], - color: t.Optional[bool] = None, -) -> None: - """This function takes a text and shows it via an environment specific - pager on stdout. - - .. versionchanged:: 3.0 - Added the `color` flag. - - :param text_or_generator: the text to page, or alternatively, a - generator emitting the text to page. - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - color = resolve_color_default(color) - - if inspect.isgeneratorfunction(text_or_generator): - i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() - elif isinstance(text_or_generator, str): - i = [text_or_generator] - else: - i = iter(t.cast(t.Iterable[str], text_or_generator)) - - # convert every element of i to a text type if necessary - text_generator = (el if isinstance(el, str) else str(el) for el in i) - - from ._termui_impl import pager - - return pager(itertools.chain(text_generator, "\n"), color) - - -def progressbar( - iterable: t.Optional[t.Iterable[V]] = None, - length: t.Optional[int] = None, - label: t.Optional[str] = None, - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, -) -> "ProgressBar[V]": - """This function creates an iterable context manager that can be used - to iterate over something while showing a progress bar. It will - either iterate over the `iterable` or `length` items (that are counted - up). While iteration happens, this function will print a rendered - progress bar to the given `file` (defaults to stdout) and will attempt - to calculate remaining time and more. By default, this progress bar - will not be rendered if the file is not a terminal. - - The context manager creates the progress bar. When the context - manager is entered the progress bar is already created. With every - iteration over the progress bar, the iterable passed to the bar is - advanced and the bar is updated. When the context manager exits, - a newline is printed and the progress bar is finalized on screen. - - Note: The progress bar is currently designed for use cases where the - total progress can be expected to take at least several seconds. - Because of this, the ProgressBar class object won't display - progress that is considered too fast, and progress where the time - between steps is less than a second. - - No printing must happen or the progress bar will be unintentionally - destroyed. - - Example usage:: - - with progressbar(items) as bar: - for item in bar: - do_something_with(item) - - Alternatively, if no iterable is specified, one can manually update the - progress bar through the `update()` method instead of directly - iterating over the progress bar. The update method accepts the number - of steps to increment the bar with:: - - with progressbar(length=chunks.total_bytes) as bar: - for chunk in chunks: - process_chunk(chunk) - bar.update(chunks.bytes) - - The ``update()`` method also takes an optional value specifying the - ``current_item`` at the new position. This is useful when used - together with ``item_show_func`` to customize the output for each - manual step:: - - with click.progressbar( - length=total_size, - label='Unzipping archive', - item_show_func=lambda a: a.filename - ) as bar: - for archive in zip_file: - archive.extract() - bar.update(archive.size, archive) - - :param iterable: an iterable to iterate over. If not provided the length - is required. - :param length: the number of items to iterate over. By default the - progressbar will attempt to ask the iterator about its - length, which might or might not work. If an iterable is - also provided this parameter can be used to override the - length. If an iterable is not provided the progress bar - will iterate over a range of that length. - :param label: the label to show next to the progress bar. - :param show_eta: enables or disables the estimated time display. This is - automatically disabled if the length cannot be - determined. - :param show_percent: enables or disables the percentage display. The - default is `True` if the iterable has a length or - `False` if not. - :param show_pos: enables or disables the absolute position display. The - default is `False`. - :param item_show_func: A function called with the current item which - can return a string to show next to the progress bar. If the - function returns ``None`` nothing is shown. The current item can - be ``None``, such as when entering and exiting the bar. - :param fill_char: the character to use to show the filled part of the - progress bar. - :param empty_char: the character to use to show the non-filled part of - the progress bar. - :param bar_template: the format string to use as template for the bar. - The parameters in it are ``label`` for the label, - ``bar`` for the progress bar and ``info`` for the - info section. - :param info_sep: the separator between multiple info items (eta etc.) - :param width: the width of the progress bar in characters, 0 means full - terminal width - :param file: The file to write to. If this is not a terminal then - only the label is printed. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are included anywhere in the progress bar output - which is not the case by default. - :param update_min_steps: Render only when this many updates have - completed. This allows tuning for very fast iterators. - - .. versionchanged:: 8.0 - Output is shown even if execution time is less than 0.5 seconds. - - .. versionchanged:: 8.0 - ``item_show_func`` shows the current item, not the previous one. - - .. versionchanged:: 8.0 - Labels are echoed if the output is not a TTY. Reverts a change - in 7.0 that removed all output. - - .. versionadded:: 8.0 - Added the ``update_min_steps`` parameter. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. Added the ``update`` method to - the object. - - .. versionadded:: 2.0 - """ - from ._termui_impl import ProgressBar - - color = resolve_color_default(color) - return ProgressBar( - iterable=iterable, - length=length, - show_eta=show_eta, - show_percent=show_percent, - show_pos=show_pos, - item_show_func=item_show_func, - fill_char=fill_char, - empty_char=empty_char, - bar_template=bar_template, - info_sep=info_sep, - file=file, - label=label, - width=width, - color=color, - update_min_steps=update_min_steps, - ) - - -def clear() -> None: - """Clears the terminal screen. This will have the effect of clearing - the whole visible space of the terminal and moving the cursor to the - top left. This does not do anything if not connected to a terminal. - - .. versionadded:: 2.0 - """ - if not isatty(sys.stdout): - return - - # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor - echo("\033[2J\033[1;1H", nl=False) - - -def _interpret_color( - color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 -) -> str: - if isinstance(color, int): - return f"{38 + offset};5;{color:d}" - - if isinstance(color, (tuple, list)): - r, g, b = color - return f"{38 + offset};2;{r:d};{g:d};{b:d}" - - return str(_ansi_colors[color] + offset) - - -def style( - text: t.Any, - fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bold: t.Optional[bool] = None, - dim: t.Optional[bool] = None, - underline: t.Optional[bool] = None, - overline: t.Optional[bool] = None, - italic: t.Optional[bool] = None, - blink: t.Optional[bool] = None, - reverse: t.Optional[bool] = None, - strikethrough: t.Optional[bool] = None, - reset: bool = True, -) -> str: - """Styles a text with ANSI styles and returns the new string. By - default the styling is self contained which means that at the end - of the string a reset code is issued. This can be prevented by - passing ``reset=False``. - - Examples:: - - click.echo(click.style('Hello World!', fg='green')) - click.echo(click.style('ATTENTION!', blink=True)) - click.echo(click.style('Some things', reverse=True, fg='cyan')) - click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) - - Supported color names: - - * ``black`` (might be a gray) - * ``red`` - * ``green`` - * ``yellow`` (might be an orange) - * ``blue`` - * ``magenta`` - * ``cyan`` - * ``white`` (might be light gray) - * ``bright_black`` - * ``bright_red`` - * ``bright_green`` - * ``bright_yellow`` - * ``bright_blue`` - * ``bright_magenta`` - * ``bright_cyan`` - * ``bright_white`` - * ``reset`` (reset the color code only) - - If the terminal supports it, color may also be specified as: - - - An integer in the interval [0, 255]. The terminal must support - 8-bit/256-color mode. - - An RGB tuple of three integers in [0, 255]. The terminal must - support 24-bit/true-color mode. - - See https://en.wikipedia.org/wiki/ANSI_color and - https://gist.github.com/XVilka/8346728 for more information. - - :param text: the string to style with ansi codes. - :param fg: if provided this will become the foreground color. - :param bg: if provided this will become the background color. - :param bold: if provided this will enable or disable bold mode. - :param dim: if provided this will enable or disable dim mode. This is - badly supported. - :param underline: if provided this will enable or disable underline. - :param overline: if provided this will enable or disable overline. - :param italic: if provided this will enable or disable italic. - :param blink: if provided this will enable or disable blinking. - :param reverse: if provided this will enable or disable inverse - rendering (foreground becomes background and the - other way round). - :param strikethrough: if provided this will enable or disable - striking through text. - :param reset: by default a reset-all code is added at the end of the - string which means that styles do not carry over. This - can be disabled to compose styles. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. - - .. versionchanged:: 8.0 - Added support for 256 and RGB color codes. - - .. versionchanged:: 8.0 - Added the ``strikethrough``, ``italic``, and ``overline`` - parameters. - - .. versionchanged:: 7.0 - Added support for bright colors. - - .. versionadded:: 2.0 - """ - if not isinstance(text, str): - text = str(text) - - bits = [] - - if fg: - try: - bits.append(f"\033[{_interpret_color(fg)}m") - except KeyError: - raise TypeError(f"Unknown color {fg!r}") from None - - if bg: - try: - bits.append(f"\033[{_interpret_color(bg, 10)}m") - except KeyError: - raise TypeError(f"Unknown color {bg!r}") from None - - if bold is not None: - bits.append(f"\033[{1 if bold else 22}m") - if dim is not None: - bits.append(f"\033[{2 if dim else 22}m") - if underline is not None: - bits.append(f"\033[{4 if underline else 24}m") - if overline is not None: - bits.append(f"\033[{53 if overline else 55}m") - if italic is not None: - bits.append(f"\033[{3 if italic else 23}m") - if blink is not None: - bits.append(f"\033[{5 if blink else 25}m") - if reverse is not None: - bits.append(f"\033[{7 if reverse else 27}m") - if strikethrough is not None: - bits.append(f"\033[{9 if strikethrough else 29}m") - bits.append(text) - if reset: - bits.append(_ansi_reset_all) - return "".join(bits) - - -def unstyle(text: str) -> str: - """Removes ANSI styling information from a string. Usually it's not - necessary to use this function as Click's echo function will - automatically remove styling if necessary. - - .. versionadded:: 2.0 - - :param text: the text to remove style information from. - """ - return strip_ansi(text) - - -def secho( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.AnyStr]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, - **styles: t.Any, -) -> None: - """This function combines :func:`echo` and :func:`style` into one - call. As such the following two calls are the same:: - - click.secho('Hello World!', fg='green') - click.echo(click.style('Hello World!', fg='green')) - - All keyword arguments are forwarded to the underlying functions - depending on which one they go with. - - Non-string types will be converted to :class:`str`. However, - :class:`bytes` are passed directly to :meth:`echo` without applying - style. If you want to style bytes that represent text, call - :meth:`bytes.decode` first. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. Bytes are - passed through without style applied. - - .. versionadded:: 2.0 - """ - if message is not None and not isinstance(message, (bytes, bytearray)): - message = style(message, **styles) - - return echo(message, file=file, nl=nl, err=err, color=color) - - -def edit( - text: t.Optional[t.AnyStr] = None, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - filename: t.Optional[str] = None, -) -> t.Optional[t.AnyStr]: - r"""Edits the given text in the defined editor. If an editor is given - (should be the full path to the executable but the regular operating - system search path is used for finding the executable) it overrides - the detected editor. Optionally, some environment variables can be - used. If the editor is closed without changes, `None` is returned. In - case a file is edited directly the return value is always `None` and - `require_save` and `extension` are ignored. - - If the editor cannot be opened a :exc:`UsageError` is raised. - - Note for Windows: to simplify cross-platform usage, the newlines are - automatically converted from POSIX to Windows and vice versa. As such, - the message here will have ``\n`` as newline markers. - - :param text: the text to edit. - :param editor: optionally the editor to use. Defaults to automatic - detection. - :param env: environment variables to forward to the editor. - :param require_save: if this is true, then not saving in the editor - will make the return value become `None`. - :param extension: the extension to tell the editor about. This defaults - to `.txt` but changing this might change syntax - highlighting. - :param filename: if provided it will edit this file instead of the - provided text contents. It will not use a temporary - file as an indirection in that case. - """ - from ._termui_impl import Editor - - ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) - - if filename is None: - return ed.edit(text) - - ed.edit_file(filename) - return None - - -def launch(url: str, wait: bool = False, locate: bool = False) -> int: - """This function launches the given URL (or filename) in the default - viewer application for this file type. If this is an executable, it - might launch the executable in a new session. The return value is - the exit code of the launched application. Usually, ``0`` indicates - success. - - Examples:: - - click.launch('https://click.palletsprojects.com/') - click.launch('/my/downloaded/file', locate=True) - - .. versionadded:: 2.0 - - :param url: URL or filename of the thing to launch. - :param wait: Wait for the program to exit before returning. This - only works if the launched program blocks. In particular, - ``xdg-open`` on Linux does not block. - :param locate: if this is set to `True` then instead of launching the - application associated with the URL it will attempt to - launch a file manager with the file located. This - might have weird effects if the URL does not point to - the filesystem. - """ - from ._termui_impl import open_url - - return open_url(url, wait=wait, locate=locate) - - -# If this is provided, getchar() calls into this instead. This is used -# for unittesting purposes. -_getchar: t.Optional[t.Callable[[bool], str]] = None - - -def getchar(echo: bool = False) -> str: - """Fetches a single character from the terminal and returns it. This - will always return a unicode character and under certain rare - circumstances this might return more than one character. The - situations which more than one character is returned is when for - whatever reason multiple characters end up in the terminal buffer or - standard input was not actually a terminal. - - Note that this will always read from the terminal, even if something - is piped into the standard input. - - Note for Windows: in rare cases when typing non-ASCII characters, this - function might wait for a second character and then return both at once. - This is because certain Unicode characters look like special-key markers. - - .. versionadded:: 2.0 - - :param echo: if set to `True`, the character read will also show up on - the terminal. The default is to not show it. - """ - global _getchar - - if _getchar is None: - from ._termui_impl import getchar as f - - _getchar = f - - return _getchar(echo) - - -def raw_terminal() -> t.ContextManager[int]: - from ._termui_impl import raw_terminal as f - - return f() - - -def pause(info: t.Optional[str] = None, err: bool = False) -> None: - """This command stops execution and waits for the user to press any - key to continue. This is similar to the Windows batch "pause" - command. If the program is not run through a terminal, this command - will instead do nothing. - - .. versionadded:: 2.0 - - .. versionadded:: 4.0 - Added the `err` parameter. - - :param info: The message to print before pausing. Defaults to - ``"Press any key to continue..."``. - :param err: if set to message goes to ``stderr`` instead of - ``stdout``, the same as with echo. - """ - if not isatty(sys.stdin) or not isatty(sys.stdout): - return - - if info is None: - info = _("Press any key to continue...") - - try: - if info: - echo(info, nl=False, err=err) - try: - getchar() - except (KeyboardInterrupt, EOFError): - pass - finally: - if info: - echo(err=err) diff --git a/.venv/Lib/site-packages/click/testing.py b/.venv/Lib/site-packages/click/testing.py deleted file mode 100644 index 772b215..0000000 --- a/.venv/Lib/site-packages/click/testing.py +++ /dev/null @@ -1,483 +0,0 @@ -import contextlib -import io -import os -import shlex -import shutil -import sys -import tempfile -import typing as t -from types import TracebackType - -from . import _compat -from . import formatting -from . import termui -from . import utils -from ._compat import _find_binary_reader - -if t.TYPE_CHECKING: - from .core import BaseCommand - - -class EchoingStdin: - def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: - self._input = input - self._output = output - self._paused = False - - def __getattr__(self, x: str) -> t.Any: - return getattr(self._input, x) - - def _echo(self, rv: bytes) -> bytes: - if not self._paused: - self._output.write(rv) - - return rv - - def read(self, n: int = -1) -> bytes: - return self._echo(self._input.read(n)) - - def read1(self, n: int = -1) -> bytes: - return self._echo(self._input.read1(n)) # type: ignore - - def readline(self, n: int = -1) -> bytes: - return self._echo(self._input.readline(n)) - - def readlines(self) -> t.List[bytes]: - return [self._echo(x) for x in self._input.readlines()] - - def __iter__(self) -> t.Iterator[bytes]: - return iter(self._echo(x) for x in self._input) - - def __repr__(self) -> str: - return repr(self._input) - - -@contextlib.contextmanager -def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: - if stream is None: - yield - else: - stream._paused = True - yield - stream._paused = False - - -class _NamedTextIOWrapper(io.TextIOWrapper): - def __init__( - self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any - ) -> None: - super().__init__(buffer, **kwargs) - self._name = name - self._mode = mode - - @property - def name(self) -> str: - return self._name - - @property - def mode(self) -> str: - return self._mode - - -def make_input_stream( - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str -) -> t.BinaryIO: - # Is already an input stream. - if hasattr(input, "read"): - rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) - - if rv is not None: - return rv - - raise TypeError("Could not find binary reader for input stream.") - - if input is None: - input = b"" - elif isinstance(input, str): - input = input.encode(charset) - - return io.BytesIO(input) - - -class Result: - """Holds the captured result of an invoked CLI script.""" - - def __init__( - self, - runner: "CliRunner", - stdout_bytes: bytes, - stderr_bytes: t.Optional[bytes], - return_value: t.Any, - exit_code: int, - exception: t.Optional[BaseException], - exc_info: t.Optional[ - t.Tuple[t.Type[BaseException], BaseException, TracebackType] - ] = None, - ): - #: The runner that created the result - self.runner = runner - #: The standard output as bytes. - self.stdout_bytes = stdout_bytes - #: The standard error as bytes, or None if not available - self.stderr_bytes = stderr_bytes - #: The value returned from the invoked command. - #: - #: .. versionadded:: 8.0 - self.return_value = return_value - #: The exit code as integer. - self.exit_code = exit_code - #: The exception that happened if one did. - self.exception = exception - #: The traceback - self.exc_info = exc_info - - @property - def output(self) -> str: - """The (standard) output as unicode string.""" - return self.stdout - - @property - def stdout(self) -> str: - """The standard output as unicode string.""" - return self.stdout_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - @property - def stderr(self) -> str: - """The standard error as unicode string.""" - if self.stderr_bytes is None: - raise ValueError("stderr not separately captured") - return self.stderr_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - def __repr__(self) -> str: - exc_str = repr(self.exception) if self.exception else "okay" - return f"<{type(self).__name__} {exc_str}>" - - -class CliRunner: - """The CLI runner provides functionality to invoke a Click command line - script for unittesting purposes in a isolated environment. This only - works in single-threaded systems without any concurrency as it changes the - global interpreter state. - - :param charset: the character set for the input and output data. - :param env: a dictionary with environment variables for overriding. - :param echo_stdin: if this is set to `True`, then reading from stdin writes - to stdout. This is useful for showing examples in - some circumstances. Note that regular prompts - will automatically echo the input. - :param mix_stderr: if this is set to `False`, then stdout and stderr are - preserved as independent streams. This is useful for - Unix-philosophy apps that have predictable stdout and - noisy stderr, such that each may be measured - independently - """ - - def __init__( - self, - charset: str = "utf-8", - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - echo_stdin: bool = False, - mix_stderr: bool = True, - ) -> None: - self.charset = charset - self.env: t.Mapping[str, t.Optional[str]] = env or {} - self.echo_stdin = echo_stdin - self.mix_stderr = mix_stderr - - def get_default_prog_name(self, cli: "BaseCommand") -> str: - """Given a command object it will return the default program name - for it. The default is the `name` attribute or ``"root"`` if not - set. - """ - return cli.name or "root" - - def make_env( - self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None - ) -> t.Mapping[str, t.Optional[str]]: - """Returns the environment overrides for invoking a script.""" - rv = dict(self.env) - if overrides: - rv.update(overrides) - return rv - - @contextlib.contextmanager - def isolation( - self, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - color: bool = False, - ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: - """A context manager that sets up the isolation for invoking of a - command line tool. This sets up stdin with the given input data - and `os.environ` with the overrides from the given dictionary. - This also rebinds some internals in Click to be mocked (like the - prompt functionality). - - This is automatically done in the :meth:`invoke` method. - - :param input: the input stream to put into sys.stdin. - :param env: the environment overrides as dictionary. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - ``stderr`` is opened with ``errors="backslashreplace"`` - instead of the default ``"strict"``. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - """ - bytes_input = make_input_stream(input, self.charset) - echo_input = None - - old_stdin = sys.stdin - old_stdout = sys.stdout - old_stderr = sys.stderr - old_forced_width = formatting.FORCED_WIDTH - formatting.FORCED_WIDTH = 80 - - env = self.make_env(env) - - bytes_output = io.BytesIO() - - if self.echo_stdin: - bytes_input = echo_input = t.cast( - t.BinaryIO, EchoingStdin(bytes_input, bytes_output) - ) - - sys.stdin = text_input = _NamedTextIOWrapper( - bytes_input, encoding=self.charset, name="", mode="r" - ) - - if self.echo_stdin: - # Force unbuffered reads, otherwise TextIOWrapper reads a - # large chunk which is echoed early. - text_input._CHUNK_SIZE = 1 # type: ignore - - sys.stdout = _NamedTextIOWrapper( - bytes_output, encoding=self.charset, name="", mode="w" - ) - - bytes_error = None - if self.mix_stderr: - sys.stderr = sys.stdout - else: - bytes_error = io.BytesIO() - sys.stderr = _NamedTextIOWrapper( - bytes_error, - encoding=self.charset, - name="", - mode="w", - errors="backslashreplace", - ) - - @_pause_echo(echo_input) # type: ignore - def visible_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(prompt or "") - val = text_input.readline().rstrip("\r\n") - sys.stdout.write(f"{val}\n") - sys.stdout.flush() - return val - - @_pause_echo(echo_input) # type: ignore - def hidden_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(f"{prompt or ''}\n") - sys.stdout.flush() - return text_input.readline().rstrip("\r\n") - - @_pause_echo(echo_input) # type: ignore - def _getchar(echo: bool) -> str: - char = sys.stdin.read(1) - - if echo: - sys.stdout.write(char) - - sys.stdout.flush() - return char - - default_color = color - - def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None - ) -> bool: - if color is None: - return not default_color - return not color - - old_visible_prompt_func = termui.visible_prompt_func - old_hidden_prompt_func = termui.hidden_prompt_func - old__getchar_func = termui._getchar - old_should_strip_ansi = utils.should_strip_ansi # type: ignore - old__compat_should_strip_ansi = _compat.should_strip_ansi - termui.visible_prompt_func = visible_input - termui.hidden_prompt_func = hidden_input - termui._getchar = _getchar - utils.should_strip_ansi = should_strip_ansi # type: ignore - _compat.should_strip_ansi = should_strip_ansi - - old_env = {} - try: - for key, value in env.items(): - old_env[key] = os.environ.get(key) - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - yield (bytes_output, bytes_error) - finally: - for key, value in old_env.items(): - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - sys.stdout = old_stdout - sys.stderr = old_stderr - sys.stdin = old_stdin - termui.visible_prompt_func = old_visible_prompt_func - termui.hidden_prompt_func = old_hidden_prompt_func - termui._getchar = old__getchar_func - utils.should_strip_ansi = old_should_strip_ansi # type: ignore - _compat.should_strip_ansi = old__compat_should_strip_ansi - formatting.FORCED_WIDTH = old_forced_width - - def invoke( - self, - cli: "BaseCommand", - args: t.Optional[t.Union[str, t.Sequence[str]]] = None, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - catch_exceptions: bool = True, - color: bool = False, - **extra: t.Any, - ) -> Result: - """Invokes a command in an isolated environment. The arguments are - forwarded directly to the command line script, the `extra` keyword - arguments are passed to the :meth:`~clickpkg.Command.main` function of - the command. - - This returns a :class:`Result` object. - - :param cli: the command to invoke - :param args: the arguments to invoke. It may be given as an iterable - or a string. When given as string it will be interpreted - as a Unix shell command. More details at - :func:`shlex.split`. - :param input: the input data for `sys.stdin`. - :param env: the environment overrides. - :param catch_exceptions: Whether to catch any other exceptions than - ``SystemExit``. - :param extra: the keyword arguments to pass to :meth:`main`. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - The result object has the ``return_value`` attribute with - the value returned from the invoked command. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionchanged:: 3.0 - Added the ``catch_exceptions`` parameter. - - .. versionchanged:: 3.0 - The result object has the ``exc_info`` attribute with the - traceback if available. - """ - exc_info = None - with self.isolation(input=input, env=env, color=color) as outstreams: - return_value = None - exception: t.Optional[BaseException] = None - exit_code = 0 - - if isinstance(args, str): - args = shlex.split(args) - - try: - prog_name = extra.pop("prog_name") - except KeyError: - prog_name = self.get_default_prog_name(cli) - - try: - return_value = cli.main(args=args or (), prog_name=prog_name, **extra) - except SystemExit as e: - exc_info = sys.exc_info() - e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) - - if e_code is None: - e_code = 0 - - if e_code != 0: - exception = e - - if not isinstance(e_code, int): - sys.stdout.write(str(e_code)) - sys.stdout.write("\n") - e_code = 1 - - exit_code = e_code - - except Exception as e: - if not catch_exceptions: - raise - exception = e - exit_code = 1 - exc_info = sys.exc_info() - finally: - sys.stdout.flush() - stdout = outstreams[0].getvalue() - if self.mix_stderr: - stderr = None - else: - stderr = outstreams[1].getvalue() # type: ignore - - return Result( - runner=self, - stdout_bytes=stdout, - stderr_bytes=stderr, - return_value=return_value, - exit_code=exit_code, - exception=exception, - exc_info=exc_info, # type: ignore - ) - - @contextlib.contextmanager - def isolated_filesystem( - self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None - ) -> t.Iterator[str]: - """A context manager that creates a temporary directory and - changes the current working directory to it. This isolates tests - that affect the contents of the CWD to prevent them from - interfering with each other. - - :param temp_dir: Create the temporary directory under this - directory. If given, the created directory is not removed - when exiting. - - .. versionchanged:: 8.0 - Added the ``temp_dir`` parameter. - """ - cwd = os.getcwd() - dt = tempfile.mkdtemp(dir=temp_dir) - os.chdir(dt) - - try: - yield dt - finally: - os.chdir(cwd) - - if temp_dir is None: - try: - shutil.rmtree(dt) - except OSError: - pass diff --git a/.venv/Lib/site-packages/click/types.py b/.venv/Lib/site-packages/click/types.py deleted file mode 100644 index a70fd58..0000000 --- a/.venv/Lib/site-packages/click/types.py +++ /dev/null @@ -1,1093 +0,0 @@ -import os -import stat -import sys -import typing as t -from datetime import datetime -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import _get_argv_encoding -from ._compat import open_stream -from .exceptions import BadParameter -from .utils import format_filename -from .utils import LazyFile -from .utils import safecall - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .core import Context - from .core import Parameter - from .shell_completion import CompletionItem - - -class ParamType: - """Represents the type of a parameter. Validates and converts values - from the command line or Python into the correct type. - - To implement a custom type, subclass and implement at least the - following: - - - The :attr:`name` class attribute must be set. - - Calling an instance of the type with ``None`` must return - ``None``. This is already implemented by default. - - :meth:`convert` must convert string values to the correct type. - - :meth:`convert` must accept values that are already the correct - type. - - It must be able to convert a value if the ``ctx`` and ``param`` - arguments are ``None``. This can occur when converting prompt - input. - """ - - is_composite: t.ClassVar[bool] = False - arity: t.ClassVar[int] = 1 - - #: the descriptive name of this type - name: str - - #: if a list of this type is expected and the value is pulled from a - #: string environment variable, this is what splits it up. `None` - #: means any whitespace. For all parameters the general rule is that - #: whitespace splits them up. The exception are paths and files which - #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on - #: Windows). - envvar_list_splitter: t.ClassVar[t.Optional[str]] = None - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - # The class name without the "ParamType" suffix. - param_type = type(self).__name__.partition("ParamType")[0] - param_type = param_type.partition("ParameterType")[0] - - # Custom subclasses might not remember to set a name. - if hasattr(self, "name"): - name = self.name - else: - name = param_type - - return {"param_type": param_type, "name": name} - - def __call__( - self, - value: t.Any, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> t.Any: - if value is not None: - return self.convert(value, param, ctx) - - def get_metavar(self, param: "Parameter") -> t.Optional[str]: - """Returns the metavar default for this param if it provides one.""" - - def get_missing_message(self, param: "Parameter") -> t.Optional[str]: - """Optionally might return extra information about a missing - parameter. - - .. versionadded:: 2.0 - """ - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - """Convert the value to the correct type. This is not called if - the value is ``None`` (the missing value). - - This must accept string values from the command line, as well as - values that are already the correct type. It may also convert - other compatible types. - - The ``param`` and ``ctx`` arguments may be ``None`` in certain - situations, such as when converting prompt input. - - If the value cannot be converted, call :meth:`fail` with a - descriptive message. - - :param value: The value to convert. - :param param: The parameter that is using this type to convert - its value. May be ``None``. - :param ctx: The current context that arrived at this value. May - be ``None``. - """ - return value - - def split_envvar_value(self, rv: str) -> t.Sequence[str]: - """Given a value from an environment variable this splits it up - into small chunks depending on the defined envvar list splitter. - - If the splitter is set to `None`, which means that whitespace splits, - then leading and trailing whitespace is ignored. Otherwise, leading - and trailing splitters usually lead to empty items being included. - """ - return (rv or "").split(self.envvar_list_splitter) - - def fail( - self, - message: str, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> "t.NoReturn": - """Helper method to fail with an invalid value message.""" - raise BadParameter(message, ctx=ctx, param=param) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a list of - :class:`~click.shell_completion.CompletionItem` objects for the - incomplete value. Most types do not provide completions, but - some do, and this allows custom types to provide custom - completions as well. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - return [] - - -class CompositeParamType(ParamType): - is_composite = True - - @property - def arity(self) -> int: # type: ignore - raise NotImplementedError() - - -class FuncParamType(ParamType): - def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: - self.name: str = func.__name__ - self.func = func - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["func"] = self.func - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self.func(value) - except ValueError: - try: - value = str(value) - except UnicodeError: - value = value.decode("utf-8", "replace") - - self.fail(value, param, ctx) - - -class UnprocessedParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - return value - - def __repr__(self) -> str: - return "UNPROCESSED" - - -class StringParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, bytes): - enc = _get_argv_encoding() - try: - value = value.decode(enc) - except UnicodeError: - fs_enc = sys.getfilesystemencoding() - if fs_enc != enc: - try: - value = value.decode(fs_enc) - except UnicodeError: - value = value.decode("utf-8", "replace") - else: - value = value.decode("utf-8", "replace") - return value - return str(value) - - def __repr__(self) -> str: - return "STRING" - - -class Choice(ParamType): - """The choice type allows a value to be checked against a fixed set - of supported values. All of these values have to be strings. - - You should only pass a list or tuple of choices. Other iterables - (like generators) may lead to surprising results. - - The resulting value will always be one of the originally passed choices - regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` - being specified. - - See :ref:`choice-opts` for an example. - - :param case_sensitive: Set to false to make choices case - insensitive. Defaults to true. - """ - - name = "choice" - - def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: - self.choices = choices - self.case_sensitive = case_sensitive - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["choices"] = self.choices - info_dict["case_sensitive"] = self.case_sensitive - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - choices_str = "|".join(self.choices) - - # Use curly braces to indicate a required argument. - if param.required and param.param_type_name == "argument": - return f"{{{choices_str}}}" - - # Use square braces to indicate an option or optional argument. - return f"[{choices_str}]" - - def get_missing_message(self, param: "Parameter") -> str: - return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - # Match through normalization and case sensitivity - # first do token_normalize_func, then lowercase - # preserve original `value` to produce an accurate message in - # `self.fail` - normed_value = value - normed_choices = {choice: choice for choice in self.choices} - - if ctx is not None and ctx.token_normalize_func is not None: - normed_value = ctx.token_normalize_func(value) - normed_choices = { - ctx.token_normalize_func(normed_choice): original - for normed_choice, original in normed_choices.items() - } - - if not self.case_sensitive: - normed_value = normed_value.casefold() - normed_choices = { - normed_choice.casefold(): original - for normed_choice, original in normed_choices.items() - } - - if normed_value in normed_choices: - return normed_choices[normed_value] - - choices_str = ", ".join(map(repr, self.choices)) - self.fail( - ngettext( - "{value!r} is not {choice}.", - "{value!r} is not one of {choices}.", - len(self.choices), - ).format(value=value, choice=choices_str, choices=choices_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return f"Choice({list(self.choices)})" - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Complete choices that start with the incomplete value. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - str_choices = map(str, self.choices) - - if self.case_sensitive: - matched = (c for c in str_choices if c.startswith(incomplete)) - else: - incomplete = incomplete.lower() - matched = (c for c in str_choices if c.lower().startswith(incomplete)) - - return [CompletionItem(c) for c in matched] - - -class DateTime(ParamType): - """The DateTime type converts date strings into `datetime` objects. - - The format strings which are checked are configurable, but default to some - common (non-timezone aware) ISO 8601 formats. - - When specifying *DateTime* formats, you should only pass a list or a tuple. - Other iterables, like generators, may lead to surprising results. - - The format strings are processed using ``datetime.strptime``, and this - consequently defines the format strings which are allowed. - - Parsing is tried using each format, in order, and the first format which - parses successfully is used. - - :param formats: A list or tuple of date format strings, in the order in - which they should be tried. Defaults to - ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, - ``'%Y-%m-%d %H:%M:%S'``. - """ - - name = "datetime" - - def __init__(self, formats: t.Optional[t.Sequence[str]] = None): - self.formats: t.Sequence[str] = formats or [ - "%Y-%m-%d", - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%d %H:%M:%S", - ] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["formats"] = self.formats - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - return f"[{'|'.join(self.formats)}]" - - def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: - try: - return datetime.strptime(value, format) - except ValueError: - return None - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, datetime): - return value - - for format in self.formats: - converted = self._try_to_convert_date(value, format) - - if converted is not None: - return converted - - formats_str = ", ".join(map(repr, self.formats)) - self.fail( - ngettext( - "{value!r} does not match the format {format}.", - "{value!r} does not match the formats {formats}.", - len(self.formats), - ).format(value=value, format=formats_str, formats=formats_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return "DateTime" - - -class _NumberParamTypeBase(ParamType): - _number_class: t.ClassVar[t.Type[t.Any]] - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self._number_class(value) - except ValueError: - self.fail( - _("{value!r} is not a valid {number_type}.").format( - value=value, number_type=self.name - ), - param, - ctx, - ) - - -class _NumberRangeBase(_NumberParamTypeBase): - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - self.min = min - self.max = max - self.min_open = min_open - self.max_open = max_open - self.clamp = clamp - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - min=self.min, - max=self.max, - min_open=self.min_open, - max_open=self.max_open, - clamp=self.clamp, - ) - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import operator - - rv = super().convert(value, param, ctx) - lt_min: bool = self.min is not None and ( - operator.le if self.min_open else operator.lt - )(rv, self.min) - gt_max: bool = self.max is not None and ( - operator.ge if self.max_open else operator.gt - )(rv, self.max) - - if self.clamp: - if lt_min: - return self._clamp(self.min, 1, self.min_open) # type: ignore - - if gt_max: - return self._clamp(self.max, -1, self.max_open) # type: ignore - - if lt_min or gt_max: - self.fail( - _("{value} is not in the range {range}.").format( - value=rv, range=self._describe_range() - ), - param, - ctx, - ) - - return rv - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - """Find the valid value to clamp to bound in the given - direction. - - :param bound: The boundary value. - :param dir: 1 or -1 indicating the direction to move. - :param open: If true, the range does not include the bound. - """ - raise NotImplementedError - - def _describe_range(self) -> str: - """Describe the range for use in help text.""" - if self.min is None: - op = "<" if self.max_open else "<=" - return f"x{op}{self.max}" - - if self.max is None: - op = ">" if self.min_open else ">=" - return f"x{op}{self.min}" - - lop = "<" if self.min_open else "<=" - rop = "<" if self.max_open else "<=" - return f"{self.min}{lop}x{rop}{self.max}" - - def __repr__(self) -> str: - clamp = " clamped" if self.clamp else "" - return f"<{type(self).__name__} {self._describe_range()}{clamp}>" - - -class IntParamType(_NumberParamTypeBase): - name = "integer" - _number_class = int - - def __repr__(self) -> str: - return "INT" - - -class IntRange(_NumberRangeBase, IntParamType): - """Restrict an :data:`click.INT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "integer range" - - def _clamp( # type: ignore - self, bound: int, dir: "te.Literal[1, -1]", open: bool - ) -> int: - if not open: - return bound - - return bound + dir - - -class FloatParamType(_NumberParamTypeBase): - name = "float" - _number_class = float - - def __repr__(self) -> str: - return "FLOAT" - - -class FloatRange(_NumberRangeBase, FloatParamType): - """Restrict a :data:`click.FLOAT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. This is not supported if either - boundary is marked ``open``. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "float range" - - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - super().__init__( - min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp - ) - - if (min_open or max_open) and clamp: - raise TypeError("Clamping is not supported for open bounds.") - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - if not open: - return bound - - # Could use Python 3.9's math.nextafter here, but clamping an - # open float range doesn't seem to be particularly useful. It's - # left up to the user to write a callback to do it if needed. - raise RuntimeError("Clamping is not supported for open bounds.") - - -class BoolParamType(ParamType): - name = "boolean" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if value in {False, True}: - return bool(value) - - norm = value.strip().lower() - - if norm in {"1", "true", "t", "yes", "y", "on"}: - return True - - if norm in {"0", "false", "f", "no", "n", "off"}: - return False - - self.fail( - _("{value!r} is not a valid boolean.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "BOOL" - - -class UUIDParameterType(ParamType): - name = "uuid" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import uuid - - if isinstance(value, uuid.UUID): - return value - - value = value.strip() - - try: - return uuid.UUID(value) - except ValueError: - self.fail( - _("{value!r} is not a valid UUID.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "UUID" - - -class File(ParamType): - """Declares a parameter to be a file for reading or writing. The file - is automatically closed once the context tears down (after the command - finished working). - - Files can be opened for reading or writing. The special value ``-`` - indicates stdin or stdout depending on the mode. - - By default, the file is opened for reading text data, but it can also be - opened in binary mode or for writing. The encoding parameter can be used - to force a specific encoding. - - The `lazy` flag controls if the file should be opened immediately or upon - first IO. The default is to be non-lazy for standard input and output - streams as well as files opened for reading, `lazy` otherwise. When opening a - file lazily for reading, it is still opened temporarily for validation, but - will not be held open until first IO. lazy is mainly useful when opening - for writing to avoid creating the file until it is needed. - - Files can also be opened atomically in which case all writes go into a - separate file in the same folder and upon completion the file will - be moved over to the original location. This is useful if a file - regularly read by other users is modified. - - See :ref:`file-args` for more information. - - .. versionchanged:: 2.0 - Added the ``atomic`` parameter. - """ - - name = "filename" - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: t.Optional[bool] = None, - atomic: bool = False, - ) -> None: - self.mode = mode - self.encoding = encoding - self.errors = errors - self.lazy = lazy - self.atomic = atomic - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update(mode=self.mode, encoding=self.encoding) - return info_dict - - def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: - if self.lazy is not None: - return self.lazy - if os.fspath(value) == "-": - return False - elif "w" in self.mode: - return True - return False - - def convert( - self, - value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> t.IO[t.Any]: - if _is_file_like(value): - return value - - value = t.cast("t.Union[str, os.PathLike[str]]", value) - - try: - lazy = self.resolve_lazy_flag(value) - - if lazy: - lf = LazyFile( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - if ctx is not None: - ctx.call_on_close(lf.close_intelligently) - - return t.cast(t.IO[t.Any], lf) - - f, should_close = open_stream( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - # If a context is provided, we automatically close the file - # at the end of the context execution (or flush out). If a - # context does not exist, it's the caller's responsibility to - # properly close the file. This for instance happens when the - # type is used with prompts. - if ctx is not None: - if should_close: - ctx.call_on_close(safecall(f.close)) - else: - ctx.call_on_close(safecall(f.flush)) - - return f - except OSError as e: - self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide file path completions. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - return [CompletionItem(incomplete, type="file")] - - -def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": - return hasattr(value, "read") or hasattr(value, "write") - - -class Path(ParamType): - """The ``Path`` type is similar to the :class:`File` type, but - returns the filename instead of an open file. Various checks can be - enabled to validate the type of file and permissions. - - :param exists: The file or directory needs to exist for the value to - be valid. If this is not set to ``True``, and the file does not - exist, then all further checks are silently skipped. - :param file_okay: Allow a file as a value. - :param dir_okay: Allow a directory as a value. - :param readable: if true, a readable check is performed. - :param writable: if true, a writable check is performed. - :param executable: if true, an executable check is performed. - :param resolve_path: Make the value absolute and resolve any - symlinks. A ``~`` is not expanded, as this is supposed to be - done by the shell only. - :param allow_dash: Allow a single dash as a value, which indicates - a standard stream (but does not open it). Use - :func:`~click.open_file` to handle opening this value. - :param path_type: Convert the incoming path value to this type. If - ``None``, keep Python's default, which is ``str``. Useful to - convert to :class:`pathlib.Path`. - - .. versionchanged:: 8.1 - Added the ``executable`` parameter. - - .. versionchanged:: 8.0 - Allow passing ``path_type=pathlib.Path``. - - .. versionchanged:: 6.0 - Added the ``allow_dash`` parameter. - """ - - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - exists: bool = False, - file_okay: bool = True, - dir_okay: bool = True, - writable: bool = False, - readable: bool = True, - resolve_path: bool = False, - allow_dash: bool = False, - path_type: t.Optional[t.Type[t.Any]] = None, - executable: bool = False, - ): - self.exists = exists - self.file_okay = file_okay - self.dir_okay = dir_okay - self.readable = readable - self.writable = writable - self.executable = executable - self.resolve_path = resolve_path - self.allow_dash = allow_dash - self.type = path_type - - if self.file_okay and not self.dir_okay: - self.name: str = _("file") - elif self.dir_okay and not self.file_okay: - self.name = _("directory") - else: - self.name = _("path") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - exists=self.exists, - file_okay=self.file_okay, - dir_okay=self.dir_okay, - writable=self.writable, - readable=self.readable, - allow_dash=self.allow_dash, - ) - return info_dict - - def coerce_path_result( - self, value: "t.Union[str, os.PathLike[str]]" - ) -> "t.Union[str, bytes, os.PathLike[str]]": - if self.type is not None and not isinstance(value, self.type): - if self.type is str: - return os.fsdecode(value) - elif self.type is bytes: - return os.fsencode(value) - else: - return t.cast("os.PathLike[str]", self.type(value)) - - return value - - def convert( - self, - value: "t.Union[str, os.PathLike[str]]", - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> "t.Union[str, bytes, os.PathLike[str]]": - rv = value - - is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") - - if not is_dash: - if self.resolve_path: - # os.path.realpath doesn't resolve symlinks on Windows - # until Python 3.8. Use pathlib for now. - import pathlib - - rv = os.fsdecode(pathlib.Path(rv).resolve()) - - try: - st = os.stat(rv) - except OSError: - if not self.exists: - return self.coerce_path_result(rv) - self.fail( - _("{name} {filename!r} does not exist.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if not self.file_okay and stat.S_ISREG(st.st_mode): - self.fail( - _("{name} {filename!r} is a file.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - if not self.dir_okay and stat.S_ISDIR(st.st_mode): - self.fail( - _("{name} {filename!r} is a directory.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.readable and not os.access(rv, os.R_OK): - self.fail( - _("{name} {filename!r} is not readable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.writable and not os.access(rv, os.W_OK): - self.fail( - _("{name} {filename!r} is not writable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.executable and not os.access(value, os.X_OK): - self.fail( - _("{name} {filename!r} is not executable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - return self.coerce_path_result(rv) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide path completions for only - directories or any paths. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - type = "dir" if self.dir_okay and not self.file_okay else "file" - return [CompletionItem(incomplete, type=type)] - - -class Tuple(CompositeParamType): - """The default behavior of Click is to apply a type on a value directly. - This works well in most cases, except for when `nargs` is set to a fixed - count and different types should be used for different items. In this - case the :class:`Tuple` type can be used. This type can only be used - if `nargs` is set to a fixed number. - - For more information see :ref:`tuple-type`. - - This can be selected by using a Python tuple literal as a type. - - :param types: a list of types that should be used for the tuple items. - """ - - def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: - self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["types"] = [t.to_info_dict() for t in self.types] - return info_dict - - @property - def name(self) -> str: # type: ignore - return f"<{' '.join(ty.name for ty in self.types)}>" - - @property - def arity(self) -> int: # type: ignore - return len(self.types) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - len_type = len(self.types) - len_value = len(value) - - if len_value != len_type: - self.fail( - ngettext( - "{len_type} values are required, but {len_value} was given.", - "{len_type} values are required, but {len_value} were given.", - len_value, - ).format(len_type=len_type, len_value=len_value), - param=param, - ctx=ctx, - ) - - return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) - - -def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: - """Find the most appropriate :class:`ParamType` for the given Python - type. If the type isn't provided, it can be inferred from a default - value. - """ - guessed_type = False - - if ty is None and default is not None: - if isinstance(default, (tuple, list)): - # If the default is empty, ty will remain None and will - # return STRING. - if default: - item = default[0] - - # A tuple of tuples needs to detect the inner types. - # Can't call convert recursively because that would - # incorrectly unwind the tuple to a single type. - if isinstance(item, (tuple, list)): - ty = tuple(map(type, item)) - else: - ty = type(item) - else: - ty = type(default) - - guessed_type = True - - if isinstance(ty, tuple): - return Tuple(ty) - - if isinstance(ty, ParamType): - return ty - - if ty is str or ty is None: - return STRING - - if ty is int: - return INT - - if ty is float: - return FLOAT - - if ty is bool: - return BOOL - - if guessed_type: - return STRING - - if __debug__: - try: - if issubclass(ty, ParamType): - raise AssertionError( - f"Attempted to use an uninstantiated parameter type ({ty})." - ) - except TypeError: - # ty is an instance (correct), so issubclass fails. - pass - - return FuncParamType(ty) - - -#: A dummy parameter type that just does nothing. From a user's -#: perspective this appears to just be the same as `STRING` but -#: internally no string conversion takes place if the input was bytes. -#: This is usually useful when working with file paths as they can -#: appear in bytes and unicode. -#: -#: For path related uses the :class:`Path` type is a better choice but -#: there are situations where an unprocessed type is useful which is why -#: it is is provided. -#: -#: .. versionadded:: 4.0 -UNPROCESSED = UnprocessedParamType() - -#: A unicode string parameter type which is the implicit default. This -#: can also be selected by using ``str`` as type. -STRING = StringParamType() - -#: An integer parameter. This can also be selected by using ``int`` as -#: type. -INT = IntParamType() - -#: A floating point value parameter. This can also be selected by using -#: ``float`` as type. -FLOAT = FloatParamType() - -#: A boolean parameter. This is the default for boolean flags. This can -#: also be selected by using ``bool`` as a type. -BOOL = BoolParamType() - -#: A UUID parameter. -UUID = UUIDParameterType() diff --git a/.venv/Lib/site-packages/click/utils.py b/.venv/Lib/site-packages/click/utils.py deleted file mode 100644 index 836c6f2..0000000 --- a/.venv/Lib/site-packages/click/utils.py +++ /dev/null @@ -1,624 +0,0 @@ -import os -import re -import sys -import typing as t -from functools import update_wrapper -from types import ModuleType -from types import TracebackType - -from ._compat import _default_text_stderr -from ._compat import _default_text_stdout -from ._compat import _find_binary_writer -from ._compat import auto_wrap_for_ansi -from ._compat import binary_streams -from ._compat import open_stream -from ._compat import should_strip_ansi -from ._compat import strip_ansi -from ._compat import text_streams -from ._compat import WIN -from .globals import resolve_color_default - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") - - -def _posixify(name: str) -> str: - return "-".join(name.split()).lower() - - -def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": - """Wraps a function so that it swallows exceptions.""" - - def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: - try: - return func(*args, **kwargs) - except Exception: - pass - return None - - return update_wrapper(wrapper, func) - - -def make_str(value: t.Any) -> str: - """Converts a value into a valid string.""" - if isinstance(value, bytes): - try: - return value.decode(sys.getfilesystemencoding()) - except UnicodeError: - return value.decode("utf-8", "replace") - return str(value) - - -def make_default_short_help(help: str, max_length: int = 45) -> str: - """Returns a condensed version of help string.""" - # Consider only the first paragraph. - paragraph_end = help.find("\n\n") - - if paragraph_end != -1: - help = help[:paragraph_end] - - # Collapse newlines, tabs, and spaces. - words = help.split() - - if not words: - return "" - - # The first paragraph started with a "no rewrap" marker, ignore it. - if words[0] == "\b": - words = words[1:] - - total_length = 0 - last_index = len(words) - 1 - - for i, word in enumerate(words): - total_length += len(word) + (i > 0) - - if total_length > max_length: # too long, truncate - break - - if word[-1] == ".": # sentence end, truncate without "..." - return " ".join(words[: i + 1]) - - if total_length == max_length and i != last_index: - break # not at sentence end, truncate with "..." - else: - return " ".join(words) # no truncation needed - - # Account for the length of the suffix. - total_length += len("...") - - # remove words until the length is short enough - while i > 0: - total_length -= len(words[i]) + (i > 0) - - if total_length <= max_length: - break - - i -= 1 - - return " ".join(words[:i]) + "..." - - -class LazyFile: - """A lazy file works like a regular file but it does not fully open - the file but it does perform some basic checks early to see if the - filename parameter does make sense. This is useful for safely opening - files for writing. - """ - - def __init__( - self, - filename: t.Union[str, "os.PathLike[str]"], - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, - ): - self.name: str = os.fspath(filename) - self.mode = mode - self.encoding = encoding - self.errors = errors - self.atomic = atomic - self._f: t.Optional[t.IO[t.Any]] - self.should_close: bool - - if self.name == "-": - self._f, self.should_close = open_stream(filename, mode, encoding, errors) - else: - if "r" in mode: - # Open and close the file in case we're opening it for - # reading so that we can catch at least some errors in - # some cases early. - open(filename, mode).close() - self._f = None - self.should_close = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self.open(), name) - - def __repr__(self) -> str: - if self._f is not None: - return repr(self._f) - return f"" - - def open(self) -> t.IO[t.Any]: - """Opens the file if it's not yet open. This call might fail with - a :exc:`FileError`. Not handling this error will produce an error - that Click shows. - """ - if self._f is not None: - return self._f - try: - rv, self.should_close = open_stream( - self.name, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - except OSError as e: - from .exceptions import FileError - - raise FileError(self.name, hint=e.strerror) from e - self._f = rv - return rv - - def close(self) -> None: - """Closes the underlying file, no matter what.""" - if self._f is not None: - self._f.close() - - def close_intelligently(self) -> None: - """This function only closes the file if it was opened by the lazy - file wrapper. For instance this will never close stdin. - """ - if self.should_close: - self.close() - - def __enter__(self) -> "LazyFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.close_intelligently() - - def __iter__(self) -> t.Iterator[t.AnyStr]: - self.open() - return iter(self._f) # type: ignore - - -class KeepOpenFile: - def __init__(self, file: t.IO[t.Any]) -> None: - self._file: t.IO[t.Any] = file - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._file, name) - - def __enter__(self) -> "KeepOpenFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - pass - - def __repr__(self) -> str: - return repr(self._file) - - def __iter__(self) -> t.Iterator[t.AnyStr]: - return iter(self._file) - - -def echo( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.Any]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, -) -> None: - """Print a message and newline to stdout or a file. This should be - used instead of :func:`print` because it provides better support - for different data, files, and environments. - - Compared to :func:`print`, this does the following: - - - Ensures that the output encoding is not misconfigured on Linux. - - Supports Unicode in the Windows console. - - Supports writing to binary outputs, and supports writing bytes - to text outputs. - - Supports colors and styles on Windows. - - Removes ANSI color and style codes if the output does not look - like an interactive terminal. - - Always flushes the output. - - :param message: The string or bytes to output. Other objects are - converted to strings. - :param file: The file to write to. Defaults to ``stdout``. - :param err: Write to ``stderr`` instead of ``stdout``. - :param nl: Print a newline after the message. Enabled by default. - :param color: Force showing or hiding colors and other styles. By - default Click will remove color if the output does not look like - an interactive terminal. - - .. versionchanged:: 6.0 - Support Unicode output on the Windows console. Click does not - modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` - will still not support Unicode. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionadded:: 3.0 - Added the ``err`` parameter. - - .. versionchanged:: 2.0 - Support colors on Windows if colorama is installed. - """ - if file is None: - if err: - file = _default_text_stderr() - else: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - return - - # Convert non bytes/text into the native string type. - if message is not None and not isinstance(message, (str, bytes, bytearray)): - out: t.Optional[t.Union[str, bytes]] = str(message) - else: - out = message - - if nl: - out = out or "" - if isinstance(out, str): - out += "\n" - else: - out += b"\n" - - if not out: - file.flush() - return - - # If there is a message and the value looks like bytes, we manually - # need to find the binary stream and write the message in there. - # This is done separately so that most stream types will work as you - # would expect. Eg: you can write to StringIO for other cases. - if isinstance(out, (bytes, bytearray)): - binary_file = _find_binary_writer(file) - - if binary_file is not None: - file.flush() - binary_file.write(out) - binary_file.flush() - return - - # ANSI style code support. For no message or bytes, nothing happens. - # When outputting to a file instead of a terminal, strip codes. - else: - color = resolve_color_default(color) - - if should_strip_ansi(file, color): - out = strip_ansi(out) - elif WIN: - if auto_wrap_for_ansi is not None: - file = auto_wrap_for_ansi(file, color) # type: ignore - elif not color: - out = strip_ansi(out) - - file.write(out) # type: ignore - file.flush() - - -def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: - """Returns a system stream for byte processing. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - """ - opener = binary_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener() - - -def get_text_stream( - name: "te.Literal['stdin', 'stdout', 'stderr']", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", -) -> t.TextIO: - """Returns a system stream for text processing. This usually returns - a wrapped stream around a binary stream returned from - :func:`get_binary_stream` but it also can take shortcuts for already - correctly configured streams. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - :param encoding: overrides the detected default encoding. - :param errors: overrides the default error mode. - """ - opener = text_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener(encoding, errors) - - -def open_file( - filename: t.Union[str, "os.PathLike[str]"], - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: bool = False, - atomic: bool = False, -) -> t.IO[t.Any]: - """Open a file, with extra behavior to handle ``'-'`` to indicate - a standard stream, lazy open on write, and atomic write. Similar to - the behavior of the :class:`~click.File` param type. - - If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is - wrapped so that using it in a context manager will not close it. - This makes it possible to use the function without accidentally - closing a standard stream: - - .. code-block:: python - - with open_file(filename) as f: - ... - - :param filename: The name or Path of the file to open, or ``'-'`` for - ``stdin``/``stdout``. - :param mode: The mode in which to open the file. - :param encoding: The encoding to decode or encode a file opened in - text mode. - :param errors: The error handling mode. - :param lazy: Wait to open the file until it is accessed. For read - mode, the file is temporarily opened to raise access errors - early, then closed until it is read again. - :param atomic: Write to a temporary file and replace the given file - on close. - - .. versionadded:: 3.0 - """ - if lazy: - return t.cast( - t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) - ) - - f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) - - if not should_close: - f = t.cast(t.IO[t.Any], KeepOpenFile(f)) - - return f - - -def format_filename( - filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", - shorten: bool = False, -) -> str: - """Format a filename as a string for display. Ensures the filename can be - displayed by replacing any invalid bytes or surrogate escapes in the name - with the replacement character ``�``. - - Invalid bytes or surrogate escapes will raise an error when written to a - stream with ``errors="strict"``. This will typically happen with ``stdout`` - when the locale is something like ``en_GB.UTF-8``. - - Many scenarios *are* safe to write surrogates though, due to PEP 538 and - PEP 540, including: - - - Writing to ``stderr``, which uses ``errors="backslashreplace"``. - - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens - stdout and stderr with ``errors="surrogateescape"``. - - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. - - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. - Python opens stdout and stderr with ``errors="surrogateescape"``. - - :param filename: formats a filename for UI display. This will also convert - the filename into unicode without failing. - :param shorten: this optionally shortens the filename to strip of the - path that leads up to it. - """ - if shorten: - filename = os.path.basename(filename) - else: - filename = os.fspath(filename) - - if isinstance(filename, bytes): - filename = filename.decode(sys.getfilesystemencoding(), "replace") - else: - filename = filename.encode("utf-8", "surrogateescape").decode( - "utf-8", "replace" - ) - - return filename - - -def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: - r"""Returns the config folder for the application. The default behavior - is to return whatever is most appropriate for the operating system. - - To give you an idea, for an app called ``"Foo Bar"``, something like - the following folders could be returned: - - Mac OS X: - ``~/Library/Application Support/Foo Bar`` - Mac OS X (POSIX): - ``~/.foo-bar`` - Unix: - ``~/.config/foo-bar`` - Unix (POSIX): - ``~/.foo-bar`` - Windows (roaming): - ``C:\Users\\AppData\Roaming\Foo Bar`` - Windows (not roaming): - ``C:\Users\\AppData\Local\Foo Bar`` - - .. versionadded:: 2.0 - - :param app_name: the application name. This should be properly capitalized - and can contain whitespace. - :param roaming: controls if the folder should be roaming or not on Windows. - Has no effect otherwise. - :param force_posix: if this is set to `True` then on any POSIX system the - folder will be stored in the home folder with a leading - dot instead of the XDG config home or darwin's - application support folder. - """ - if WIN: - key = "APPDATA" if roaming else "LOCALAPPDATA" - folder = os.environ.get(key) - if folder is None: - folder = os.path.expanduser("~") - return os.path.join(folder, app_name) - if force_posix: - return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) - if sys.platform == "darwin": - return os.path.join( - os.path.expanduser("~/Library/Application Support"), app_name - ) - return os.path.join( - os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), - _posixify(app_name), - ) - - -class PacifyFlushWrapper: - """This wrapper is used to catch and suppress BrokenPipeErrors resulting - from ``.flush()`` being called on broken pipe during the shutdown/final-GC - of the Python interpreter. Notably ``.flush()`` is always called on - ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any - other cleanup code, and the case where the underlying file is not a broken - pipe, all calls and attributes are proxied. - """ - - def __init__(self, wrapped: t.IO[t.Any]) -> None: - self.wrapped = wrapped - - def flush(self) -> None: - try: - self.wrapped.flush() - except OSError as e: - import errno - - if e.errno != errno.EPIPE: - raise - - def __getattr__(self, attr: str) -> t.Any: - return getattr(self.wrapped, attr) - - -def _detect_program_name( - path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None -) -> str: - """Determine the command used to run the program, for use in help - text. If a file or entry point was executed, the file name is - returned. If ``python -m`` was used to execute a module or package, - ``python -m name`` is returned. - - This doesn't try to be too precise, the goal is to give a concise - name for help text. Files are only shown as their name without the - path. ``python`` is only shown for modules, and the full path to - ``sys.executable`` is not shown. - - :param path: The Python file being executed. Python puts this in - ``sys.argv[0]``, which is used by default. - :param _main: The ``__main__`` module. This should only be passed - during internal testing. - - .. versionadded:: 8.0 - Based on command args detection in the Werkzeug reloader. - - :meta private: - """ - if _main is None: - _main = sys.modules["__main__"] - - if not path: - path = sys.argv[0] - - # The value of __package__ indicates how Python was called. It may - # not exist if a setuptools script is installed as an egg. It may be - # set incorrectly for entry points created with pip on Windows. - # It is set to "" inside a Shiv or PEX zipapp. - if getattr(_main, "__package__", None) in {None, ""} or ( - os.name == "nt" - and _main.__package__ == "" - and not os.path.exists(path) - and os.path.exists(f"{path}.exe") - ): - # Executed a file, like "python app.py". - return os.path.basename(path) - - # Executed a module, like "python -m example". - # Rewritten by Python from "-m script" to "/path/to/script.py". - # Need to look at main module to determine how it was executed. - py_module = t.cast(str, _main.__package__) - name = os.path.splitext(os.path.basename(path))[0] - - # A submodule like "example.cli". - if name != "__main__": - py_module = f"{py_module}.{name}" - - return f"python -m {py_module.lstrip('.')}" - - -def _expand_args( - args: t.Iterable[str], - *, - user: bool = True, - env: bool = True, - glob_recursive: bool = True, -) -> t.List[str]: - """Simulate Unix shell expansion with Python functions. - - See :func:`glob.glob`, :func:`os.path.expanduser`, and - :func:`os.path.expandvars`. - - This is intended for use on Windows, where the shell does not do any - expansion. It may not exactly match what a Unix shell would do. - - :param args: List of command line arguments to expand. - :param user: Expand user home directory. - :param env: Expand environment variables. - :param glob_recursive: ``**`` matches directories recursively. - - .. versionchanged:: 8.1 - Invalid glob patterns are treated as empty expansions rather - than raising an error. - - .. versionadded:: 8.0 - - :meta private: - """ - from glob import glob - - out = [] - - for arg in args: - if user: - arg = os.path.expanduser(arg) - - if env: - arg = os.path.expandvars(arg) - - try: - matches = glob(arg, recursive=glob_recursive) - except re.error: - matches = [] - - if not matches: - out.append(arg) - else: - out.extend(matches) - - return out diff --git a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER b/.venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA b/.venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA deleted file mode 100644 index a1b5c57..0000000 --- a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/METADATA +++ /dev/null @@ -1,441 +0,0 @@ -Metadata-Version: 2.1 -Name: colorama -Version: 0.4.6 -Summary: Cross-platform colored terminal text. -Project-URL: Homepage, https://github.com/tartley/colorama -Author-email: Jonathan Hartley -License-File: LICENSE.txt -Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Console -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Terminals -Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7 -Description-Content-Type: text/x-rst - -.. image:: https://img.shields.io/pypi/v/colorama.svg - :target: https://pypi.org/project/colorama/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/pyversions/colorama.svg - :target: https://pypi.org/project/colorama/ - :alt: Supported Python versions - -.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg - :target: https://github.com/tartley/colorama/actions/workflows/test.yml - :alt: Build Status - -Colorama -======== - -Makes ANSI escape character sequences (for producing colored terminal text and -cursor positioning) work under MS Windows. - -.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif - :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD - :alt: Donate with Paypal - -`PyPI for releases `_ | -`Github for source `_ | -`Colorama for enterprise on Tidelift `_ - -If you find Colorama useful, please |donate| to the authors. Thank you! - -Installation ------------- - -Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8. - -No requirements other than the standard library. - -.. code-block:: bash - - pip install colorama - # or - conda install -c anaconda colorama - -Description ------------ - -ANSI escape character sequences have long been used to produce colored terminal -text and cursor positioning on Unix and Macs. Colorama makes this work on -Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which -would appear as gobbledygook in the output), and converting them into the -appropriate win32 calls to modify the state of the terminal. On other platforms, -Colorama does nothing. - -This has the upshot of providing a simple cross-platform API for printing -colored terminal text from Python, and has the happy side-effect that existing -applications or libraries which use ANSI sequences to produce colored output on -Linux or Macs can now also work on Windows, simply by calling -``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` -(all versions, but may have other side-effects – see below). - -An alternative approach is to install ``ansi.sys`` on Windows machines, which -provides the same behaviour for all applications running in terminals. Colorama -is intended for situations where that isn't easy (e.g., maybe your app doesn't -have an installer.) - -Demo scripts in the source code repository print some colored text using -ANSI sequences. Compare their output under Gnome-terminal's built in ANSI -handling, versus on Windows Command-Prompt using Colorama: - -.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png - :width: 661 - :height: 357 - :alt: ANSI sequences on Ubuntu under gnome-terminal. - -.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png - :width: 668 - :height: 325 - :alt: Same ANSI sequences on Windows, using Colorama. - -These screenshots show that, on Windows, Colorama does not support ANSI 'dim -text'; it looks the same as 'normal text'. - -Usage ------ - -Initialisation -.............. - -If the only thing you want from Colorama is to get ANSI escapes to work on -Windows, then run: - -.. code-block:: python - - from colorama import just_fix_windows_console - just_fix_windows_console() - -If you're on a recent version of Windows 10 or better, and your stdout/stderr -are pointing to a Windows console, then this will flip the magic configuration -switch to enable Windows' built-in ANSI support. - -If you're on an older version of Windows, and your stdout/stderr are pointing to -a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a -magic file object that intercepts ANSI escape sequences and issues the -appropriate Win32 calls to emulate them. - -In all other circumstances, it does nothing whatsoever. Basically the idea is -that this makes Windows act like Unix with respect to ANSI escape handling. - -It's safe to call this function multiple times. It's safe to call this function -on non-Windows platforms, but it won't do anything. It's safe to call this -function when one or both of your stdout/stderr are redirected to a file – it -won't do anything to those streams. - -Alternatively, you can use the older interface with more features (but also more -potential footguns): - -.. code-block:: python - - from colorama import init - init() - -This does the same thing as ``just_fix_windows_console``, except for the -following differences: - -- It's not safe to call ``init`` multiple times; you can end up with multiple - layers of wrapping and broken ANSI support. - -- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, - and if it thinks they don't, then it will wrap ``sys.stdout`` and - ``sys.stderr`` in a magic file object that strips out ANSI escape sequences - before printing them. This happens on all platforms, and can be convenient if - you want to write your code to emit ANSI escape sequences unconditionally, and - let Colorama decide whether they should actually be output. But note that - Colorama's heuristic is not particularly clever. - -- ``init`` also accepts explicit keyword args to enable/disable various - functionality – see below. - -To stop using Colorama before your program exits, simply call ``deinit()``. -This will restore ``stdout`` and ``stderr`` to their original values, so that -Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is -cheaper than calling ``init()`` again (but does the same thing). - -Most users should depend on ``colorama >= 0.4.6``, and use -``just_fix_windows_console``. The old ``init`` interface will be supported -indefinitely for backwards compatibility, but we don't plan to fix any issues -with it, also for backwards compatibility. - -Colored Output -.............. - -Cross-platform printing of colored text can then be done using Colorama's -constant shorthand for ANSI escape sequences. These are deliberately -rudimentary, see below. - -.. code-block:: python - - from colorama import Fore, Back, Style - print(Fore.RED + 'some red text') - print(Back.GREEN + 'and with a green background') - print(Style.DIM + 'and in dim text') - print(Style.RESET_ALL) - print('back to normal now') - -...or simply by manually printing ANSI sequences from your own code: - -.. code-block:: python - - print('\033[31m' + 'some red text') - print('\033[39m') # and reset to default color - -...or, Colorama can be used in conjunction with existing ANSI libraries -such as the venerable `Termcolor `_ -the fabulous `Blessings `_, -or the incredible `_Rich `_. - -If you wish Colorama's Fore, Back and Style constants were more capable, -then consider using one of the above highly capable libraries to generate -colors, etc, and use Colorama just for its primary purpose: to convert -those ANSI sequences to also work on Windows: - -SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. -We are only interested in converting ANSI codes to win32 API calls, not -shortcuts like the above to generate ANSI characters. - -.. code-block:: python - - from colorama import just_fix_windows_console - from termcolor import colored - - # use Colorama to make Termcolor work on Windows too - just_fix_windows_console() - - # then use Termcolor for all colored text output - print(colored('Hello, World!', 'green', 'on_red')) - -Available formatting constants are:: - - Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. - Style: DIM, NORMAL, BRIGHT, RESET_ALL - -``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will -perform this reset automatically on program exit. - -These are fairly well supported, but not part of the standard:: - - Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX - Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX - -Cursor Positioning -.................. - -ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for -an example of how to generate them. - -Init Keyword Args -................. - -``init()`` accepts some ``**kwargs`` to override default behaviour. - -init(autoreset=False): - If you find yourself repeatedly sending reset sequences to turn off color - changes at the end of every print, then ``init(autoreset=True)`` will - automate that: - - .. code-block:: python - - from colorama import init - init(autoreset=True) - print(Fore.RED + 'some red text') - print('automatically back to default color again') - -init(strip=None): - Pass ``True`` or ``False`` to override whether ANSI codes should be - stripped from the output. The default behaviour is to strip if on Windows - or if output is redirected (not a tty). - -init(convert=None): - Pass ``True`` or ``False`` to override whether to convert ANSI codes in the - output into win32 calls. The default behaviour is to convert if on Windows - and output is to a tty (terminal). - -init(wrap=True): - On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` - with proxy objects, which override the ``.write()`` method to do their work. - If this wrapping causes you problems, then this can be disabled by passing - ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or - ``strip`` or ``convert`` are True. - - When wrapping is disabled, colored printing on non-Windows platforms will - continue to work as normal. To do cross-platform colored output, you can - use Colorama's ``AnsiToWin32`` proxy directly: - - .. code-block:: python - - import sys - from colorama import init, AnsiToWin32 - init(wrap=False) - stream = AnsiToWin32(sys.stderr).stream - - # Python 2 - print >>stream, Fore.BLUE + 'blue text on stderr' - - # Python 3 - print(Fore.BLUE + 'blue text on stderr', file=stream) - -Recognised ANSI Sequences -......................... - -ANSI sequences generally take the form:: - - ESC [ ; ... - -Where ```` is an integer, and ```` is a single letter. Zero or -more params are passed to a ````. If no params are passed, it is -generally synonymous with passing a single zero. No spaces exist in the -sequence; they have been inserted here simply to read more easily. - -The only ANSI sequences that Colorama converts into win32 calls are:: - - ESC [ 0 m # reset all (colors and brightness) - ESC [ 1 m # bright - ESC [ 2 m # dim (looks same as normal brightness) - ESC [ 22 m # normal brightness - - # FOREGROUND: - ESC [ 30 m # black - ESC [ 31 m # red - ESC [ 32 m # green - ESC [ 33 m # yellow - ESC [ 34 m # blue - ESC [ 35 m # magenta - ESC [ 36 m # cyan - ESC [ 37 m # white - ESC [ 39 m # reset - - # BACKGROUND - ESC [ 40 m # black - ESC [ 41 m # red - ESC [ 42 m # green - ESC [ 43 m # yellow - ESC [ 44 m # blue - ESC [ 45 m # magenta - ESC [ 46 m # cyan - ESC [ 47 m # white - ESC [ 49 m # reset - - # cursor positioning - ESC [ y;x H # position cursor at x across, y down - ESC [ y;x f # position cursor at x across, y down - ESC [ n A # move cursor n lines up - ESC [ n B # move cursor n lines down - ESC [ n C # move cursor n characters forward - ESC [ n D # move cursor n characters backward - - # clear the screen - ESC [ mode J # clear the screen - - # clear the line - ESC [ mode K # clear the line - -Multiple numeric params to the ``'m'`` command can be combined into a single -sequence:: - - ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background - -All other ANSI sequences of the form ``ESC [ ; ... `` -are silently stripped from the output on Windows. - -Any other form of ANSI sequence, such as single-character codes or alternative -initial characters, are not recognised or stripped. It would be cool to add -them though. Let me know if it would be useful for you, via the Issues on -GitHub. - -Status & Known Problems ------------------------ - -I've personally only tested it on Windows XP (CMD, Console2), Ubuntu -(gnome-terminal, xterm), and OS X. - -Some valid ANSI sequences aren't recognised. - -If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the -explanation there of why we do not want PRs that allow Colorama to generate new -types of ANSI codes. - -See outstanding issues and wish-list: -https://github.com/tartley/colorama/issues - -If anything doesn't work for you, or doesn't do what you expected or hoped for, -I'd love to hear about it on that issues list, would be delighted by patches, -and would be happy to grant commit access to anyone who submits a working patch -or two. - -.. _README-hacking.md: README-hacking.md - -License -------- - -Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see -LICENSE file. - -Professional support --------------------- - -.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png - :alt: Tidelift - :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme - -.. list-table:: - :widths: 10 100 - - * - |tideliftlogo| - - Professional support for colorama is available as part of the - `Tidelift Subscription`_. - Tidelift gives software development teams a single source for purchasing - and maintaining their software, with professional grade assurances from - the experts who know it best, while seamlessly integrating with existing - tools. - -.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme - -Thanks ------- - -See the CHANGELOG for more thanks! - -* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. -* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, - providing a solution to issue #7's setuptools/distutils debate, - and other fixes. -* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. -* Matthew McCormick for politely pointing out a longstanding crash on non-Win. -* Ben Hoyt, for a magnificent fix under 64-bit Windows. -* Jesse at Empty Square for submitting a fix for examples in the README. -* User 'jamessp', an observant documentation fix for cursor positioning. -* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 - fix. -* Julien Stuyck, for wisely suggesting Python3 compatible updates to README. -* Daniel Griffith for multiple fabulous patches. -* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty - output. -* Roger Binns, for many suggestions, valuable feedback, & bug reports. -* Tim Golden for thought and much appreciated feedback on the initial idea. -* User 'Zearin' for updates to the README file. -* John Szakmeister for adding support for light colors -* Charles Merriam for adding documentation to demos -* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes -* Florian Bruhin for a fix when stdout or stderr are None -* Thomas Weininger for fixing ValueError on Windows -* Remi Rampin for better Github integration and fixes to the README file -* Simeon Visser for closing a file handle using 'with' and updating classifiers - to include Python 3.3 and 3.4 -* Andy Neff for fixing RESET of LIGHT_EX colors. -* Jonathan Hartley for the initial idea and implementation. diff --git a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD b/.venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD deleted file mode 100644 index bafc005..0000000 --- a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/RECORD +++ /dev/null @@ -1,31 +0,0 @@ -colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158 -colorama-0.4.6.dist-info/RECORD,, -colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105 -colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491 -colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 -colorama/__pycache__/__init__.cpython-311.pyc,, -colorama/__pycache__/ansi.cpython-311.pyc,, -colorama/__pycache__/ansitowin32.cpython-311.pyc,, -colorama/__pycache__/initialise.cpython-311.pyc,, -colorama/__pycache__/win32.cpython-311.pyc,, -colorama/__pycache__/winterm.cpython-311.pyc,, -colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 -colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 -colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 -colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 -colorama/tests/__pycache__/__init__.cpython-311.pyc,, -colorama/tests/__pycache__/ansi_test.cpython-311.pyc,, -colorama/tests/__pycache__/ansitowin32_test.cpython-311.pyc,, -colorama/tests/__pycache__/initialise_test.cpython-311.pyc,, -colorama/tests/__pycache__/isatty_test.cpython-311.pyc,, -colorama/tests/__pycache__/utils.cpython-311.pyc,, -colorama/tests/__pycache__/winterm_test.cpython-311.pyc,, -colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 -colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 -colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 -colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 -colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 -colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 -colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 -colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 diff --git a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL b/.venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL deleted file mode 100644 index d79189f..0000000 --- a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.11.1 -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any diff --git a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt b/.venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt deleted file mode 100644 index 3105888..0000000 --- a/.venv/Lib/site-packages/colorama-0.4.6.dist-info/licenses/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2010 Jonathan Hartley -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holders, nor those of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/Lib/site-packages/colorama/__init__.py b/.venv/Lib/site-packages/colorama/__init__.py deleted file mode 100644 index 383101c..0000000 --- a/.venv/Lib/site-packages/colorama/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# 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/site-packages/colorama/ansi.py b/.venv/Lib/site-packages/colorama/ansi.py deleted file mode 100644 index 11ec695..0000000 --- a/.venv/Lib/site-packages/colorama/ansi.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -''' -This module generates ANSI character codes to printing colors to terminals. -See: http://en.wikipedia.org/wiki/ANSI_escape_code -''' - -CSI = '\033[' -OSC = '\033]' -BEL = '\a' - - -def code_to_chars(code): - return CSI + str(code) + 'm' - -def set_title(title): - return OSC + '2;' + title + BEL - -def clear_screen(mode=2): - return CSI + str(mode) + 'J' - -def clear_line(mode=2): - return CSI + str(mode) + 'K' - - -class AnsiCodes(object): - def __init__(self): - # the subclasses declare class attributes which are numbers. - # Upon instantiation we define instance attributes, which are the same - # as the class attributes but wrapped with the ANSI escape sequence - for name in dir(self): - if not name.startswith('_'): - value = getattr(self, name) - setattr(self, name, code_to_chars(value)) - - -class AnsiCursor(object): - def UP(self, n=1): - return CSI + str(n) + 'A' - def DOWN(self, n=1): - return CSI + str(n) + 'B' - def FORWARD(self, n=1): - return CSI + str(n) + 'C' - def BACK(self, n=1): - return CSI + str(n) + 'D' - def POS(self, x=1, y=1): - return CSI + str(y) + ';' + str(x) + 'H' - - -class AnsiFore(AnsiCodes): - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - RESET = 39 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 90 - LIGHTRED_EX = 91 - LIGHTGREEN_EX = 92 - LIGHTYELLOW_EX = 93 - LIGHTBLUE_EX = 94 - LIGHTMAGENTA_EX = 95 - LIGHTCYAN_EX = 96 - LIGHTWHITE_EX = 97 - - -class AnsiBack(AnsiCodes): - BLACK = 40 - RED = 41 - GREEN = 42 - YELLOW = 43 - BLUE = 44 - MAGENTA = 45 - CYAN = 46 - WHITE = 47 - RESET = 49 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 100 - LIGHTRED_EX = 101 - LIGHTGREEN_EX = 102 - LIGHTYELLOW_EX = 103 - LIGHTBLUE_EX = 104 - LIGHTMAGENTA_EX = 105 - LIGHTCYAN_EX = 106 - LIGHTWHITE_EX = 107 - - -class AnsiStyle(AnsiCodes): - BRIGHT = 1 - DIM = 2 - NORMAL = 22 - RESET_ALL = 0 - -Fore = AnsiFore() -Back = AnsiBack() -Style = AnsiStyle() -Cursor = AnsiCursor() diff --git a/.venv/Lib/site-packages/colorama/ansitowin32.py b/.venv/Lib/site-packages/colorama/ansitowin32.py deleted file mode 100644 index abf209e..0000000 --- a/.venv/Lib/site-packages/colorama/ansitowin32.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import re -import sys -import os - -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL -from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle -from .win32 import windll, winapi_test - - -winterm = None -if windll is not None: - winterm = WinTerm() - - -class StreamWrapper(object): - ''' - Wraps a stream (such as stdout), acting as a transparent proxy for all - attribute access apart from method 'write()', which is delegated to our - Converter instance. - ''' - def __init__(self, wrapped, converter): - # double-underscore everything to prevent clashes with names of - # attributes on the wrapped stream object. - self.__wrapped = wrapped - self.__convertor = converter - - def __getattr__(self, name): - return getattr(self.__wrapped, name) - - def __enter__(self, *args, **kwargs): - # special method lookup bypasses __getattr__/__getattribute__, see - # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit - # thus, contextlib magic methods are not proxied via __getattr__ - return self.__wrapped.__enter__(*args, **kwargs) - - def __exit__(self, *args, **kwargs): - return self.__wrapped.__exit__(*args, **kwargs) - - def __setstate__(self, state): - self.__dict__ = state - - def __getstate__(self): - return self.__dict__ - - def write(self, text): - self.__convertor.write(text) - - def isatty(self): - stream = self.__wrapped - if 'PYCHARM_HOSTED' in os.environ: - if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): - return True - try: - stream_isatty = stream.isatty - except AttributeError: - return False - else: - return stream_isatty() - - @property - def closed(self): - stream = self.__wrapped - try: - return stream.closed - # AttributeError in the case that the stream doesn't support being closed - # ValueError for the case that the stream has already been detached when atexit runs - except (AttributeError, ValueError): - return True - - -class AnsiToWin32(object): - ''' - Implements a 'write()' method which, on Windows, will strip ANSI character - sequences from the text, and if outputting to a tty, will convert them into - win32 function calls. - ''' - ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command - - def __init__(self, wrapped, convert=None, strip=None, autoreset=False): - # The wrapped stream (normally sys.stdout or sys.stderr) - self.wrapped = wrapped - - # should we reset colors to defaults after every .write() - self.autoreset = autoreset - - # create the proxy wrapping our output stream - self.stream = StreamWrapper(wrapped, self) - - on_windows = os.name == 'nt' - # We test if the WinAPI works, because even if we are on Windows - # we may be using a terminal that doesn't support the WinAPI - # (e.g. Cygwin Terminal). In this case it's up to the terminal - # to support the ANSI codes. - conversion_supported = on_windows and winapi_test() - try: - fd = wrapped.fileno() - except Exception: - fd = -1 - system_has_native_ansi = not on_windows or enable_vt_processing(fd) - have_tty = not self.stream.closed and self.stream.isatty() - need_conversion = conversion_supported and not system_has_native_ansi - - # should we strip ANSI sequences from our output? - if strip is None: - strip = need_conversion or not have_tty - self.strip = strip - - # should we should convert ANSI sequences into win32 calls? - if convert is None: - convert = need_conversion and have_tty - self.convert = convert - - # dict of ansi codes to win32 functions and parameters - self.win32_calls = self.get_win32_calls() - - # are we wrapping stderr? - self.on_stderr = self.wrapped is sys.stderr - - def should_wrap(self): - ''' - True if this class is actually needed. If false, then the output - stream will not be affected, nor will win32 calls be issued, so - wrapping stdout is not actually required. This will generally be - False on non-Windows platforms, unless optional functionality like - autoreset has been requested using kwargs to init() - ''' - return self.convert or self.strip or self.autoreset - - def get_win32_calls(self): - if self.convert and winterm: - return { - AnsiStyle.RESET_ALL: (winterm.reset_all, ), - AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), - AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), - AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), - AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), - AnsiFore.RED: (winterm.fore, WinColor.RED), - AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), - AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), - AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), - AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), - AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), - AnsiFore.WHITE: (winterm.fore, WinColor.GREY), - AnsiFore.RESET: (winterm.fore, ), - AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), - AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), - AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), - AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), - AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), - AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), - AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), - AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), - AnsiBack.BLACK: (winterm.back, WinColor.BLACK), - AnsiBack.RED: (winterm.back, WinColor.RED), - AnsiBack.GREEN: (winterm.back, WinColor.GREEN), - AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), - AnsiBack.BLUE: (winterm.back, WinColor.BLUE), - AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), - AnsiBack.CYAN: (winterm.back, WinColor.CYAN), - AnsiBack.WHITE: (winterm.back, WinColor.GREY), - AnsiBack.RESET: (winterm.back, ), - AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), - AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), - AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), - AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), - AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), - AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), - AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), - AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), - } - return dict() - - def write(self, text): - if self.strip or self.convert: - self.write_and_convert(text) - else: - self.wrapped.write(text) - self.wrapped.flush() - if self.autoreset: - self.reset_all() - - - def reset_all(self): - if self.convert: - self.call_win32('m', (0,)) - elif not self.strip and not self.stream.closed: - self.wrapped.write(Style.RESET_ALL) - - - def write_and_convert(self, text): - ''' - Write the given text to our wrapped stream, stripping any ANSI - sequences from the text, and optionally converting them into win32 - calls. - ''' - cursor = 0 - text = self.convert_osc(text) - for match in self.ANSI_CSI_RE.finditer(text): - start, end = match.span() - self.write_plain_text(text, cursor, start) - self.convert_ansi(*match.groups()) - cursor = end - self.write_plain_text(text, cursor, len(text)) - - - def write_plain_text(self, text, start, end): - if start < end: - self.wrapped.write(text[start:end]) - self.wrapped.flush() - - - def convert_ansi(self, paramstring, command): - if self.convert: - params = self.extract_params(command, paramstring) - self.call_win32(command, params) - - - def extract_params(self, command, paramstring): - if command in 'Hf': - params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) - while len(params) < 2: - # defaults: - params = params + (1,) - else: - params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) - if len(params) == 0: - # defaults: - if command in 'JKm': - params = (0,) - elif command in 'ABCD': - params = (1,) - - return params - - - def call_win32(self, command, params): - if command == 'm': - for param in params: - if param in self.win32_calls: - func_args = self.win32_calls[param] - func = func_args[0] - args = func_args[1:] - kwargs = dict(on_stderr=self.on_stderr) - func(*args, **kwargs) - elif command in 'J': - winterm.erase_screen(params[0], on_stderr=self.on_stderr) - elif command in 'K': - winterm.erase_line(params[0], on_stderr=self.on_stderr) - elif command in 'Hf': # cursor position - absolute - winterm.set_cursor_position(params, on_stderr=self.on_stderr) - elif command in 'ABCD': # cursor position - relative - n = params[0] - # A - up, B - down, C - forward, D - back - x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] - winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) - - - def convert_osc(self, text): - for match in self.ANSI_OSC_RE.finditer(text): - start, end = match.span() - text = text[:start] + text[end:] - paramstring, command = match.groups() - if command == BEL: - if paramstring.count(";") == 1: - params = paramstring.split(";") - # 0 - change title and icon (we will only change title) - # 1 - change icon (we don't support this) - # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) - return text - - - def flush(self): - self.wrapped.flush() diff --git a/.venv/Lib/site-packages/colorama/initialise.py b/.venv/Lib/site-packages/colorama/initialise.py deleted file mode 100644 index d5fd4b7..0000000 --- a/.venv/Lib/site-packages/colorama/initialise.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import atexit -import contextlib -import sys - -from .ansitowin32 import AnsiToWin32 - - -def _wipe_internal_state_for_tests(): - global orig_stdout, orig_stderr - orig_stdout = None - orig_stderr = None - - global wrapped_stdout, wrapped_stderr - wrapped_stdout = None - wrapped_stderr = None - - global atexit_done - atexit_done = False - - global fixed_windows_console - fixed_windows_console = False - - try: - # no-op if it wasn't registered - atexit.unregister(reset_all) - except AttributeError: - # python 2: no atexit.unregister. Oh well, we did our best. - pass - - -def reset_all(): - if AnsiToWin32 is not None: # Issue #74: objects might become None at exit - AnsiToWin32(orig_stdout).reset_all() - - -def init(autoreset=False, convert=None, strip=None, wrap=True): - - if not wrap and any([autoreset, convert, strip]): - raise ValueError('wrap=False conflicts with any other arg=True') - - global wrapped_stdout, wrapped_stderr - global orig_stdout, orig_stderr - - orig_stdout = sys.stdout - orig_stderr = sys.stderr - - if sys.stdout is None: - wrapped_stdout = None - else: - sys.stdout = wrapped_stdout = \ - wrap_stream(orig_stdout, convert, strip, autoreset, wrap) - if sys.stderr is None: - wrapped_stderr = None - else: - sys.stderr = wrapped_stderr = \ - wrap_stream(orig_stderr, convert, strip, autoreset, wrap) - - global atexit_done - if not atexit_done: - atexit.register(reset_all) - atexit_done = True - - -def deinit(): - if orig_stdout is not None: - sys.stdout = orig_stdout - if orig_stderr is not None: - sys.stderr = orig_stderr - - -def just_fix_windows_console(): - global fixed_windows_console - - if sys.platform != "win32": - return - if fixed_windows_console: - return - if wrapped_stdout is not None or wrapped_stderr is not None: - # Someone already ran init() and it did stuff, so we won't second-guess them - return - - # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the - # native ANSI support in the console as a side-effect. We only need to actually - # replace sys.stdout/stderr if we're in the old-style conversion mode. - new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) - if new_stdout.convert: - sys.stdout = new_stdout - new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) - if new_stderr.convert: - sys.stderr = new_stderr - - fixed_windows_console = True - -@contextlib.contextmanager -def colorama_text(*args, **kwargs): - init(*args, **kwargs) - try: - yield - finally: - deinit() - - -def reinit(): - if wrapped_stdout is not None: - sys.stdout = wrapped_stdout - if wrapped_stderr is not None: - sys.stderr = wrapped_stderr - - -def wrap_stream(stream, convert, strip, autoreset, wrap): - if wrap: - wrapper = AnsiToWin32(stream, - convert=convert, strip=strip, autoreset=autoreset) - if wrapper.should_wrap(): - stream = wrapper.stream - return stream - - -# Use this for initial setup as well, to reduce code duplication -_wipe_internal_state_for_tests() diff --git a/.venv/Lib/site-packages/colorama/tests/__init__.py b/.venv/Lib/site-packages/colorama/tests/__init__.py deleted file mode 100644 index 8c5661e..0000000 --- a/.venv/Lib/site-packages/colorama/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/.venv/Lib/site-packages/colorama/tests/ansi_test.py b/.venv/Lib/site-packages/colorama/tests/ansi_test.py deleted file mode 100644 index 0a20c80..0000000 --- a/.venv/Lib/site-packages/colorama/tests/ansi_test.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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/site-packages/colorama/tests/ansitowin32_test.py b/.venv/Lib/site-packages/colorama/tests/ansitowin32_test.py deleted file mode 100644 index 91ca551..0000000 --- a/.venv/Lib/site-packages/colorama/tests/ansitowin32_test.py +++ /dev/null @@ -1,294 +0,0 @@ -# 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/site-packages/colorama/tests/initialise_test.py b/.venv/Lib/site-packages/colorama/tests/initialise_test.py deleted file mode 100644 index 89f9b07..0000000 --- a/.venv/Lib/site-packages/colorama/tests/initialise_test.py +++ /dev/null @@ -1,189 +0,0 @@ -# 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/site-packages/colorama/tests/isatty_test.py b/.venv/Lib/site-packages/colorama/tests/isatty_test.py deleted file mode 100644 index 0f84e4b..0000000 --- a/.venv/Lib/site-packages/colorama/tests/isatty_test.py +++ /dev/null @@ -1,57 +0,0 @@ -# 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/site-packages/colorama/tests/utils.py b/.venv/Lib/site-packages/colorama/tests/utils.py deleted file mode 100644 index 472fafb..0000000 --- a/.venv/Lib/site-packages/colorama/tests/utils.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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/site-packages/colorama/tests/winterm_test.py b/.venv/Lib/site-packages/colorama/tests/winterm_test.py deleted file mode 100644 index d0955f9..0000000 --- a/.venv/Lib/site-packages/colorama/tests/winterm_test.py +++ /dev/null @@ -1,131 +0,0 @@ -# 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/site-packages/colorama/win32.py b/.venv/Lib/site-packages/colorama/win32.py deleted file mode 100644 index 841b0e2..0000000 --- a/.venv/Lib/site-packages/colorama/win32.py +++ /dev/null @@ -1,180 +0,0 @@ -# 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/site-packages/colorama/winterm.py b/.venv/Lib/site-packages/colorama/winterm.py deleted file mode 100644 index aad867e..0000000 --- a/.venv/Lib/site-packages/colorama/winterm.py +++ /dev/null @@ -1,195 +0,0 @@ -# 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/site-packages/cryptography-44.0.0.dist-info/INSTALLER b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/METADATA b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/METADATA deleted file mode 100644 index 6798c61..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/METADATA +++ /dev/null @@ -1,140 +0,0 @@ -Metadata-Version: 2.3 -Name: cryptography -Version: 44.0.0 -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: License :: OSI Approved :: BSD License -Classifier: Natural Language :: English -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX -Classifier: Operating System :: POSIX :: BSD -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: Microsoft :: Windows -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 :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Security :: Cryptography -Requires-Dist: cffi >=1.12 ; platform_python_implementation != 'PyPy' -Requires-Dist: bcrypt >=3.1.5 ; extra == 'ssh' -Requires-Dist: nox >=2024.4.15 ; extra == 'nox' -Requires-Dist: nox[uv] >=2024.3.2 ; python_version >= '3.8' and extra == 'nox' -Requires-Dist: cryptography-vectors ==44.0.0 ; extra == 'test' -Requires-Dist: pytest >=7.4.0 ; extra == 'test' -Requires-Dist: pytest-benchmark >=4.0 ; extra == 'test' -Requires-Dist: pytest-cov >=2.10.1 ; extra == 'test' -Requires-Dist: pytest-xdist >=3.5.0 ; extra == 'test' -Requires-Dist: pretend >=0.7 ; extra == 'test' -Requires-Dist: certifi >=2024 ; extra == 'test' -Requires-Dist: pytest-randomly ; extra == 'test-randomorder' -Requires-Dist: sphinx >=5.3.0 ; extra == 'docs' -Requires-Dist: sphinx-rtd-theme >=3.0.0 ; python_version >= '3.8' and extra == 'docs' -Requires-Dist: pyenchant >=3 ; extra == 'docstest' -Requires-Dist: readme-renderer >=30.0 ; extra == 'docstest' -Requires-Dist: sphinxcontrib-spelling >=7.3.1 ; extra == 'docstest' -Requires-Dist: build >=1.0.0 ; extra == 'sdist' -Requires-Dist: ruff >=0.3.6 ; extra == 'pep8test' -Requires-Dist: mypy >=1.4 ; extra == 'pep8test' -Requires-Dist: check-sdist ; python_version >= '3.8' and extra == 'pep8test' -Requires-Dist: click >=8.0.1 ; extra == 'pep8test' -Provides-Extra: ssh -Provides-Extra: nox -Provides-Extra: test -Provides-Extra: test-randomorder -Provides-Extra: docs -Provides-Extra: docstest -Provides-Extra: sdist -Provides-Extra: pep8test -License-File: LICENSE -License-File: LICENSE.APACHE -License-File: LICENSE.BSD -Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. -Author: The cryptography developers -Author-email: The Python Cryptographic Authority and individual contributors -License: Apache-2.0 OR BSD-3-Clause -Requires-Python: >=3.7, !=3.9.0, !=3.9.1 -Description-Content-Type: text/x-rst; charset=UTF-8 -Project-URL: homepage, https://github.com/pyca/cryptography -Project-URL: documentation, https://cryptography.io/ -Project-URL: source, https://github.com/pyca/cryptography/ -Project-URL: issues, https://github.com/pyca/cryptography/issues -Project-URL: changelog, https://cryptography.io/en/latest/changelog/ - -pyca/cryptography -================= - -.. image:: https://img.shields.io/pypi/v/cryptography.svg - :target: https://pypi.org/project/cryptography/ - :alt: Latest Version - -.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest - :target: https://cryptography.io - :alt: Latest Docs - -.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main - :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain - - -``cryptography`` is a package which provides cryptographic recipes and -primitives to Python developers. Our goal is for it to be your "cryptographic -standard library". It supports Python 3.7+ and PyPy3 7.3.11+. - -``cryptography`` includes both high level recipes and low level interfaces to -common cryptographic algorithms such as symmetric ciphers, message digests, and -key derivation functions. For example, to encrypt something with -``cryptography``'s high level symmetric encryption recipe: - -.. code-block:: pycon - - >>> from cryptography.fernet import Fernet - >>> # Put this somewhere safe! - >>> key = Fernet.generate_key() - >>> f = Fernet(key) - >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") - >>> token - b'...' - >>> f.decrypt(token) - b'A really secret message. Not for prying eyes.' - -You can find more information in the `documentation`_. - -You can install ``cryptography`` with: - -.. code-block:: console - - $ pip install cryptography - -For full details see `the installation documentation`_. - -Discussion -~~~~~~~~~~ - -If you run into bugs, you can file them in our `issue tracker`_. - -We maintain a `cryptography-dev`_ mailing list for development discussion. - -You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get -involved. - -Security -~~~~~~~~ - -Need to report a security issue? Please consult our `security reporting`_ -documentation. - - -.. _`documentation`: https://cryptography.io/ -.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ -.. _`issue tracker`: https://github.com/pyca/cryptography/issues -.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`security reporting`: https://cryptography.io/en/latest/security/ - diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/RECORD b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/RECORD deleted file mode 100644 index 01d5869..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/RECORD +++ /dev/null @@ -1,182 +0,0 @@ -cryptography-44.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cryptography-44.0.0.dist-info/METADATA,sha256=2V9JHtQY3DAtsDgE2vkzXOIbLfRwRYfmhR7wPh-E2JU,5724 -cryptography-44.0.0.dist-info/RECORD,, -cryptography-44.0.0.dist-info/WHEEL,sha256=Hn9bytZpOGoR6M4U5xUTHC1AJpPD9B1xPrM4STxljEU,94 -cryptography-44.0.0.dist-info/licenses/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197 -cryptography-44.0.0.dist-info/licenses/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 -cryptography-44.0.0.dist-info/licenses/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 -cryptography/__about__.py,sha256=fcUqF1IcadxBSH0us1vCvob0OJOrPV3h30yZD8wsHo4,445 -cryptography/__init__.py,sha256=XsRL_PxbU6UgoyoglAgJQSrJCP97ovBA8YIEQ2-uI68,762 -cryptography/__pycache__/__about__.cpython-311.pyc,, -cryptography/__pycache__/__init__.cpython-311.pyc,, -cryptography/__pycache__/exceptions.cpython-311.pyc,, -cryptography/__pycache__/fernet.cpython-311.pyc,, -cryptography/__pycache__/utils.cpython-311.pyc,, -cryptography/exceptions.py,sha256=835EWILc2fwxw-gyFMriciC2SqhViETB10LBSytnDIc,1087 -cryptography/fernet.py,sha256=aMU2HyDJ5oRGjg8AkFvHwE7BSmHY4fVUCaioxZcd8gA,6933 -cryptography/hazmat/__init__.py,sha256=5IwrLWrVp0AjEr_4FdWG_V057NSJGY_W4egNNsuct0g,455 -cryptography/hazmat/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/__pycache__/_oid.cpython-311.pyc,, -cryptography/hazmat/_oid.py,sha256=xcGtygUQX1p2ozVjhqKk016E5--BC7ituI1EGuoiWds,15294 -cryptography/hazmat/backends/__init__.py,sha256=O5jvKFQdZnXhKeqJ-HtulaEL9Ni7mr1mDzZY5kHlYhI,361 -cryptography/hazmat/backends/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/backends/openssl/__init__.py,sha256=p3jmJfnCag9iE5sdMrN6VvVEu55u46xaS_IjoI0SrmA,305 -cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-311.pyc,, -cryptography/hazmat/backends/openssl/backend.py,sha256=Bk_inezh7fBN3jsxMu1YIkf10zryfup6opBDLVFiNms,9413 -cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/bindings/_rust.pyd,sha256=oihcPy9-Y7qKF6tdCjAnQOat9-YI4HB6dzfB7DvYzsw,8292864 -cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=s73-NWxZs-5r2vAzDT9Eqo9mRiWE__A4VJKyFBkjHdM,879 -cryptography/hazmat/bindings/_rust/_openssl.pyi,sha256=mpNJLuYLbCVrd5i33FBTmWwL_55Dw7JPkSLlSX9Q7oI,230 -cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=BrGjC8J6nwuS-r3EVcdXJB8ndotfY9mbQYOfpbPG0HA,354 -cryptography/hazmat/bindings/_rust/exceptions.pyi,sha256=exXr2xw_0pB1kk93cYbM3MohbzoUkjOms1ZMUi0uQZE,640 -cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=mNrMO5sYEnftD_b2-NvvR6M8QdYGZ1jpTdazpgzXgl0,4004 -cryptography/hazmat/bindings/_rust/openssl/__init__.pyi,sha256=FS2gi2eALVzqTTic8an8enD431pkwKbRxeAZaNMV4Ts,1410 -cryptography/hazmat/bindings/_rust/openssl/aead.pyi,sha256=i0gA3jUQ4rkJXTGGZrq-AuY-VQLN31lyDeWuDZ0zJYw,2553 -cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi,sha256=iK0ZhQ-WyCQbjaraaFgK6q4PpD-7Rf5RDHkFD3YEW_g,1301 -cryptography/hazmat/bindings/_rust/openssl/cmac.pyi,sha256=nPH0X57RYpsAkRowVpjQiHE566ThUTx7YXrsadmrmHk,564 -cryptography/hazmat/bindings/_rust/openssl/dh.pyi,sha256=Z3TC-G04-THtSdAOPLM1h2G7ml5bda1ElZUcn5wpuhk,1564 -cryptography/hazmat/bindings/_rust/openssl/dsa.pyi,sha256=qBtkgj2albt2qFcnZ9UDrhzoNhCVO7HTby5VSf1EXMI,1299 -cryptography/hazmat/bindings/_rust/openssl/ec.pyi,sha256=zJy0pRa5n-_p2dm45PxECB_-B6SVZyNKfjxFDpPqT38,1691 -cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi,sha256=OJsrblS2nHptZctva-pAKFL5q8yPEAkhmjPZpJ6TA94,493 -cryptography/hazmat/bindings/_rust/openssl/ed448.pyi,sha256=SkPHK2HdbYN02TVQEUOgW3iTdiEY7HBE4DijpdkAzmk,475 -cryptography/hazmat/bindings/_rust/openssl/hashes.pyi,sha256=p8sdf41mPBlV_W9v_18JItuMoHE8UkBxj9Tuqi0WiTE,639 -cryptography/hazmat/bindings/_rust/openssl/hmac.pyi,sha256=ZmLJ73pmxcZFC1XosWEiXMRYtvJJor3ZLdCQOJu85Cw,662 -cryptography/hazmat/bindings/_rust/openssl/kdf.pyi,sha256=hvZSV2C3MQd9jC1Tuh5Lsb0iGBgcLVF2xFYdTo7USO4,1129 -cryptography/hazmat/bindings/_rust/openssl/keys.pyi,sha256=JSrlGNaW49ZCZ1hcb-YJdS1EAbsMwRbVEcLL0P9OApA,872 -cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi,sha256=9iogF7Q4i81IkOS-IMXp6HvxFF_3cNy_ucrAjVQnn14,540 -cryptography/hazmat/bindings/_rust/openssl/rsa.pyi,sha256=2OQCNSXkxgc-3uw1xiCCloIQTV6p9_kK79Yu0rhZgPc,1364 -cryptography/hazmat/bindings/_rust/openssl/x25519.pyi,sha256=2BKdbrddM_9SMUpdvHKGhb9MNjURCarPxccbUDzHeoA,484 -cryptography/hazmat/bindings/_rust/openssl/x448.pyi,sha256=AoRMWNvCJTiH5L-lkIkCdPlrPLUdJvvfXpIvf1GmxpM,466 -cryptography/hazmat/bindings/_rust/pkcs12.pyi,sha256=afhB_6M8xI1MIE5vxkaDF1jSxA48ib1--NiOxtf6boM,1394 -cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=Ag9coB8kRwrUJEg1do6BJABs9DqxZiY8WJIFUVa7StE,1545 -cryptography/hazmat/bindings/_rust/test_support.pyi,sha256=FXe7t_tqI3e9ULirYcr5Zlw5szGY7TiZyb7W83ak0Nk,718 -cryptography/hazmat/bindings/_rust/x509.pyi,sha256=0p-Ak_zj-9WfyZKPo08YT6cOx1c-lhjeYd0jJ8c4oT0,8318 -cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-311.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-311.pyc,, -cryptography/hazmat/bindings/openssl/_conditional.py,sha256=dkGKGU-22uR2ZKeOOwaSxEJCGaafgUjb2romWcu03QE,5163 -cryptography/hazmat/bindings/openssl/binding.py,sha256=e1gnFAZBPrkJ3CsiZV-ug6kaPdNTAEROaUFiFrUh71M,4042 -cryptography/hazmat/decrepit/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 -cryptography/hazmat/decrepit/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/decrepit/ciphers/__init__.py,sha256=wHCbWfaefa-fk6THSw9th9fJUsStJo7245wfFBqmduA,216 -cryptography/hazmat/decrepit/ciphers/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/decrepit/ciphers/__pycache__/algorithms.cpython-311.pyc,, -cryptography/hazmat/decrepit/ciphers/algorithms.py,sha256=HWA4PKDS2w4D2dQoRerpLRU7Kntt5vJeJC7j--AlZVU,2520 -cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/_serialization.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/cmac.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/constant_time.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/hashes.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/hmac.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/keywrap.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/padding.cpython-311.pyc,, -cryptography/hazmat/primitives/__pycache__/poly1305.cpython-311.pyc,, -cryptography/hazmat/primitives/_asymmetric.py,sha256=RhgcouUB6HTiFDBrR1LxqkMjpUxIiNvQ1r_zJjRG6qQ,532 -cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=gKa0WrLz6K4fqhnGbfBYKDSxgLxsPU0uj_EK2UT47W4,1495 -cryptography/hazmat/primitives/_serialization.py,sha256=qrozc8fw2WZSbjk3DAlSl3ResxpauwJ74ZgGoUL-mj0,5142 -cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-311.pyc,, -cryptography/hazmat/primitives/asymmetric/dh.py,sha256=OOCjMClH1Bf14Sy7jAdwzEeCxFPb8XUe2qePbExvXwc,3420 -cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=xBwdf0pZOgvqjUKcO7Q0L3NxwalYj0SJDUqThemhSmI,3945 -cryptography/hazmat/primitives/asymmetric/ec.py,sha256=lwZmtAwi3PM8lsY1MsNaby_bVi--49OCxwE_1yqKC-A,10428 -cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=kl63fg7myuMjNTmMoVFeH6iVr0x5FkjNmggxIRTloJk,3423 -cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=2UzEDzzfkPn83UFVFlMZfIMbAixxY09WmQyrwinWTn8,3456 -cryptography/hazmat/primitives/asymmetric/padding.py,sha256=eZcvUqVLbe3u48SunLdeniaPlV4-k6pwBl67OW4jSy8,2885 -cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=dvj4i2js78qpgotEKn3SU5Eh2unDSMiZpTVo2kx_NWU,7668 -cryptography/hazmat/primitives/asymmetric/types.py,sha256=LnsOJym-wmPUJ7Knu_7bCNU3kIiELCd6krOaW_JU08I,2996 -cryptography/hazmat/primitives/asymmetric/utils.py,sha256=DPTs6T4F-UhwzFQTh-1fSEpQzazH2jf2xpIro3ItF4o,790 -cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=VGYuRdIYuVBtizpFdNWd2bTrT10JRa1admQdBr08xz8,3341 -cryptography/hazmat/primitives/asymmetric/x448.py,sha256=GKKJBqYLr03VewMF18bXIM941aaWcZIQ4rC02GLLEmw,3374 -cryptography/hazmat/primitives/ciphers/__init__.py,sha256=eyEXmjk6_CZXaOPYDr7vAYGXr29QvzgWL2-4CSolLFs,680 -cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-311.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-311.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-311.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-311.pyc,, -cryptography/hazmat/primitives/ciphers/aead.py,sha256=Fzlyx7w8KYQakzDp1zWgJnIr62zgZrgVh1u2h4exB54,634 -cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=cPzrUizm_RfUi7DDqf3WNezkFy2IxfllsJv6s16bWS8,4493 -cryptography/hazmat/primitives/ciphers/base.py,sha256=tg-XNaKUyETBi7ounGDEL1_ICn-s4FF9LR7moV58blI,4211 -cryptography/hazmat/primitives/ciphers/modes.py,sha256=BFpxEGSaxoeZjrQ4sqpyPDvKClrqfDKIBv7kYtFURhE,8192 -cryptography/hazmat/primitives/cmac.py,sha256=sz_s6H_cYnOvx-VNWdIKhRhe3Ymp8z8J0D3CBqOX3gg,338 -cryptography/hazmat/primitives/constant_time.py,sha256=xdunWT0nf8OvKdcqUhhlFKayGp4_PgVJRU2W1wLSr_A,422 -cryptography/hazmat/primitives/hashes.py,sha256=EvDIJBhj83Z7f-oHbsA0TzZLFSDV_Yv8hQRdM4o8FD0,5091 -cryptography/hazmat/primitives/hmac.py,sha256=RpB3z9z5skirCQrm7zQbtnp9pLMnAjrlTUvKqF5aDDc,423 -cryptography/hazmat/primitives/kdf/__init__.py,sha256=4XibZnrYq4hh5xBjWiIXzaYW6FKx8hPbVaa_cB9zS64,750 -cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/argon2.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-311.pyc,, -cryptography/hazmat/primitives/kdf/argon2.py,sha256=UFDNXG0v-rw3DqAQTB1UQAsQC2M5Ejg0k_6OCyhLKus,460 -cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=bcn4NGXse-EsFl7nlU83e5ilop7TSHcX-CJJS107W80,3686 -cryptography/hazmat/primitives/kdf/hkdf.py,sha256=uhN5L87w4JvtAqQcPh_Ji2TPSc18IDThpaYJiHOWy3A,3015 -cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=eSuLK1sATkamgCAit794jLr7sDNlu5X0USdcWhwJdmk,9146 -cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=Xj3YIeX30h2BUaoJAtOo1RMXV_em0-eCG0PU_0FHJzM,1950 -cryptography/hazmat/primitives/kdf/scrypt.py,sha256=XyWUdUUmhuI9V6TqAPOvujCSMGv1XQdg0a21IWCmO-U,590 -cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=wCpWmwQjZ2vAu2rlk3R_PX0nINl8WGXYBmlyMOC5iPw,1992 -cryptography/hazmat/primitives/keywrap.py,sha256=XV4Pj2fqSeD-RqZVvY2cA3j5_7RwJSFygYuLfk2ujCo,5650 -cryptography/hazmat/primitives/padding.py,sha256=Qu1VVsCiqfQMPPqU0qU6ig9Y802jZlXVOUDLIxN5KeQ,4932 -cryptography/hazmat/primitives/poly1305.py,sha256=P5EPQV-RB_FJPahpg01u0Ts4S_PnAmsroxIGXbGeRRo,355 -cryptography/hazmat/primitives/serialization/__init__.py,sha256=jyNx_7NcOEbVRBY4nP9ks0IVXBafbcYnTK27vafPLW8,1653 -cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-311.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-311.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-311.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-311.pyc,, -cryptography/hazmat/primitives/serialization/base.py,sha256=ikq5MJIwp_oUnjiaBco_PmQwOTYuGi-XkYUYHKy8Vo0,615 -cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=7vVXbiP7qhhvKAHJT_M8-LBZdbpOwrpWRHWxNrNqzXE,4492 -cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=n25jEw__vkZWSlumwgYnqJ0lzyPh5xljMsJDyp2QomM,12346 -cryptography/hazmat/primitives/serialization/ssh.py,sha256=VKscMrVdYK5B9PQISjjdRMglRvqa_L3sDNm5vdjVHJY,51915 -cryptography/hazmat/primitives/twofactor/__init__.py,sha256=tmMZGB-g4IU1r7lIFqASU019zr0uPp_wEBYcwdDCKCA,258 -cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-311.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-311.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-311.pyc,, -cryptography/hazmat/primitives/twofactor/hotp.py,sha256=rv507uNznUs22XlaqGBbZKkkGjmiTUAcwghTYMem6uM,3219 -cryptography/hazmat/primitives/twofactor/totp.py,sha256=BQ0oPTp2JW1SMZqdgv95NBG3u_ODiDtzVJENHWYhvXY,1613 -cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -cryptography/utils.py,sha256=Rp7ppg4XIBVVzNQ6XngGndwkICJoYp6FoFOOgTWLJ7g,3925 -cryptography/x509/__init__.py,sha256=Q8P-MnUGrgFxRt5423bE-gzSvgZLAdddWuPheHnuA_c,8132 -cryptography/x509/__pycache__/__init__.cpython-311.pyc,, -cryptography/x509/__pycache__/base.cpython-311.pyc,, -cryptography/x509/__pycache__/certificate_transparency.cpython-311.pyc,, -cryptography/x509/__pycache__/extensions.cpython-311.pyc,, -cryptography/x509/__pycache__/general_name.cpython-311.pyc,, -cryptography/x509/__pycache__/name.cpython-311.pyc,, -cryptography/x509/__pycache__/ocsp.cpython-311.pyc,, -cryptography/x509/__pycache__/oid.cpython-311.pyc,, -cryptography/x509/__pycache__/verification.cpython-311.pyc,, -cryptography/x509/base.py,sha256=-F5KWjxbyjSqluUSr7LRC_sqN_s-qHP5K0rW-41PI4E,26909 -cryptography/x509/certificate_transparency.py,sha256=JqoOIDhlwInrYMFW6IFn77WJ0viF-PB_rlZV3vs9MYc,797 -cryptography/x509/extensions.py,sha256=iX-3WFm4yFjstFIs1F30f3tixIp6i0WgGdc6GwJ-QiQ,76158 -cryptography/x509/general_name.py,sha256=sP_rV11Qlpsk4x3XXGJY_Mv0Q_s9dtjeLckHsjpLQoQ,7836 -cryptography/x509/name.py,sha256=MYCxCSTQTpzhjxFPZaANqJ9fGrhESH73vPkoay8HSWM,14830 -cryptography/x509/ocsp.py,sha256=vbrg3p1hBJQEEFIZ35GHcjbGwTrsxPhlot-OVpyP-C8,11390 -cryptography/x509/oid.py,sha256=X8EbhkRTLrGuv9vHZSGqPd9zpvRVsonU_joWAL5LLY8,885 -cryptography/x509/verification.py,sha256=alfx3VaTSb2bMz7_7s788oL90vzgHwBjVINssdz0Gv0,796 -rust/Cargo.toml,sha256=gaBJTn9TwBCG7U3JgETYbTmK8DNUxl4gKKS65nDWuwM,1320 -rust/cryptography-cffi/Cargo.toml,sha256=CjVBJTYW1TwzXgLgY8TZ92NP_9XSmHzSfRIzVaZh9Bk,386 -rust/cryptography-keepalive/Cargo.toml,sha256=_ABt1o-uFnxDqhb7YzNynb6YEQ2eW2QpnPD1RXBUsrI,210 -rust/cryptography-key-parsing/Cargo.toml,sha256=yLWh172kspq6BJVZA2PjFw17Rt0xTYKn_TTzp3IVhxg,455 -rust/cryptography-openssl/Cargo.toml,sha256=mI0cIDv-kQTl24C-bLvDCqiWn6QobBdqCMYSi_UWPE0,545 -rust/cryptography-x509-verification/Cargo.toml,sha256=vECbxPiNu-dQhW4baTuSPzgqaBnBgwZYnJCSaJQbIUA,426 -rust/cryptography-x509/Cargo.toml,sha256=wAuwnc1eKnSUNFjf4GpQM__FTig-hqF2ZPXJPmqb6cA,248 diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/WHEEL b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/WHEEL deleted file mode 100644 index 3e276aa..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: maturin (1.7.5) -Root-Is-Purelib: false -Tag: cp39-abi3-win_amd64 diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE deleted file mode 100644 index b11f379..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made -under the terms of *both* these licenses. diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.APACHE b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.APACHE deleted file mode 100644 index 62589ed..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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 - - https://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. diff --git a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.BSD b/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.BSD deleted file mode 100644 index ec1a29d..0000000 --- a/.venv/Lib/site-packages/cryptography-44.0.0.dist-info/licenses/LICENSE.BSD +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of PyCA Cryptography nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/Lib/site-packages/cryptography/__about__.py b/.venv/Lib/site-packages/cryptography/__about__.py deleted file mode 100644 index 99fc2d1..0000000 --- a/.venv/Lib/site-packages/cryptography/__about__.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -__all__ = [ - "__author__", - "__copyright__", - "__version__", -] - -__version__ = "44.0.0" - - -__author__ = "The Python Cryptographic Authority and individual contributors" -__copyright__ = f"Copyright 2013-2024 {__author__}" diff --git a/.venv/Lib/site-packages/cryptography/__init__.py b/.venv/Lib/site-packages/cryptography/__init__.py deleted file mode 100644 index f37370e..0000000 --- a/.venv/Lib/site-packages/cryptography/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import sys -import warnings - -from cryptography import utils -from cryptography.__about__ import __author__, __copyright__, __version__ - -__all__ = [ - "__author__", - "__copyright__", - "__version__", -] - -if sys.version_info[:2] == (3, 7): - warnings.warn( - "Python 3.7 is no longer supported by the Python core team " - "and support for it is deprecated in cryptography. A future " - "release of cryptography will remove support for Python 3.7.", - utils.CryptographyDeprecationWarning, - stacklevel=2, - ) diff --git a/.venv/Lib/site-packages/cryptography/exceptions.py b/.venv/Lib/site-packages/cryptography/exceptions.py deleted file mode 100644 index fe125ea..0000000 --- a/.venv/Lib/site-packages/cryptography/exceptions.py +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.bindings._rust import exceptions as rust_exceptions - -if typing.TYPE_CHECKING: - from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -_Reasons = rust_exceptions._Reasons - - -class UnsupportedAlgorithm(Exception): - def __init__(self, message: str, reason: _Reasons | None = None) -> None: - super().__init__(message) - self._reason = reason - - -class AlreadyFinalized(Exception): - pass - - -class AlreadyUpdated(Exception): - pass - - -class NotYetFinalized(Exception): - pass - - -class InvalidTag(Exception): - pass - - -class InvalidSignature(Exception): - pass - - -class InternalError(Exception): - def __init__( - self, msg: str, err_code: list[rust_openssl.OpenSSLError] - ) -> None: - super().__init__(msg) - self.err_code = err_code - - -class InvalidKey(Exception): - pass diff --git a/.venv/Lib/site-packages/cryptography/fernet.py b/.venv/Lib/site-packages/cryptography/fernet.py deleted file mode 100644 index 868ecb2..0000000 --- a/.venv/Lib/site-packages/cryptography/fernet.py +++ /dev/null @@ -1,223 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import base64 -import binascii -import os -import time -import typing - -from cryptography import utils -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes, padding -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.hmac import HMAC - - -class InvalidToken(Exception): - pass - - -_MAX_CLOCK_SKEW = 60 - - -class Fernet: - def __init__( - self, - key: bytes | str, - backend: typing.Any = None, - ) -> None: - try: - key = base64.urlsafe_b64decode(key) - except binascii.Error as exc: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) from exc - if len(key) != 32: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) - - self._signing_key = key[:16] - self._encryption_key = key[16:] - - @classmethod - def generate_key(cls) -> bytes: - return base64.urlsafe_b64encode(os.urandom(32)) - - def encrypt(self, data: bytes) -> bytes: - return self.encrypt_at_time(data, int(time.time())) - - def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: - iv = os.urandom(16) - return self._encrypt_from_parts(data, current_time, iv) - - def _encrypt_from_parts( - self, data: bytes, current_time: int, iv: bytes - ) -> bytes: - utils._check_bytes("data", data) - - padder = padding.PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(data) + padder.finalize() - encryptor = Cipher( - algorithms.AES(self._encryption_key), - modes.CBC(iv), - ).encryptor() - ciphertext = encryptor.update(padded_data) + encryptor.finalize() - - basic_parts = ( - b"\x80" - + current_time.to_bytes(length=8, byteorder="big") - + iv - + ciphertext - ) - - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(basic_parts) - hmac = h.finalize() - return base64.urlsafe_b64encode(basic_parts + hmac) - - def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(token) - if ttl is None: - time_info = None - else: - time_info = (ttl, int(time.time())) - return self._decrypt_data(data, timestamp, time_info) - - def decrypt_at_time( - self, token: bytes | str, ttl: int, current_time: int - ) -> bytes: - if ttl is None: - raise ValueError( - "decrypt_at_time() can only be used with a non-None ttl" - ) - timestamp, data = Fernet._get_unverified_token_data(token) - return self._decrypt_data(data, timestamp, (ttl, current_time)) - - def extract_timestamp(self, token: bytes | str) -> int: - timestamp, data = Fernet._get_unverified_token_data(token) - # Verify the token was not tampered with. - self._verify_signature(data) - return timestamp - - @staticmethod - def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]: - if not isinstance(token, (str, bytes)): - raise TypeError("token must be bytes or str") - - try: - data = base64.urlsafe_b64decode(token) - except (TypeError, binascii.Error): - raise InvalidToken - - if not data or data[0] != 0x80: - raise InvalidToken - - if len(data) < 9: - raise InvalidToken - - timestamp = int.from_bytes(data[1:9], byteorder="big") - return timestamp, data - - def _verify_signature(self, data: bytes) -> None: - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(data[:-32]) - try: - h.verify(data[-32:]) - except InvalidSignature: - raise InvalidToken - - def _decrypt_data( - self, - data: bytes, - timestamp: int, - time_info: tuple[int, int] | None, - ) -> bytes: - if time_info is not None: - ttl, current_time = time_info - if timestamp + ttl < current_time: - raise InvalidToken - - if current_time + _MAX_CLOCK_SKEW < timestamp: - raise InvalidToken - - self._verify_signature(data) - - iv = data[9:25] - ciphertext = data[25:-32] - decryptor = Cipher( - algorithms.AES(self._encryption_key), modes.CBC(iv) - ).decryptor() - plaintext_padded = decryptor.update(ciphertext) - try: - plaintext_padded += decryptor.finalize() - except ValueError: - raise InvalidToken - unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() - - unpadded = unpadder.update(plaintext_padded) - try: - unpadded += unpadder.finalize() - except ValueError: - raise InvalidToken - return unpadded - - -class MultiFernet: - def __init__(self, fernets: typing.Iterable[Fernet]): - fernets = list(fernets) - if not fernets: - raise ValueError( - "MultiFernet requires at least one Fernet instance" - ) - self._fernets = fernets - - def encrypt(self, msg: bytes) -> bytes: - return self.encrypt_at_time(msg, int(time.time())) - - def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: - return self._fernets[0].encrypt_at_time(msg, current_time) - - def rotate(self, msg: bytes | str) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(msg) - for f in self._fernets: - try: - p = f._decrypt_data(data, timestamp, None) - break - except InvalidToken: - pass - else: - raise InvalidToken - - iv = os.urandom(16) - return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) - - def decrypt(self, msg: bytes | str, ttl: int | None = None) -> bytes: - for f in self._fernets: - try: - return f.decrypt(msg, ttl) - except InvalidToken: - pass - raise InvalidToken - - def decrypt_at_time( - self, msg: bytes | str, ttl: int, current_time: int - ) -> bytes: - for f in self._fernets: - try: - return f.decrypt_at_time(msg, ttl, current_time) - except InvalidToken: - pass - raise InvalidToken - - def extract_timestamp(self, msg: bytes | str) -> int: - for f in self._fernets: - try: - return f.extract_timestamp(msg) - except InvalidToken: - pass - raise InvalidToken diff --git a/.venv/Lib/site-packages/cryptography/hazmat/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/__init__.py deleted file mode 100644 index b9f1187..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -""" -Hazardous Materials - -This is a "Hazardous Materials" module. You should ONLY use it if you're -100% absolutely sure that you know what you're doing because this module -is full of land mines, dragons, and dinosaurs with laser guns. -""" diff --git a/.venv/Lib/site-packages/cryptography/hazmat/_oid.py b/.venv/Lib/site-packages/cryptography/hazmat/_oid.py deleted file mode 100644 index 8bd240d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/_oid.py +++ /dev/null @@ -1,315 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import ( - ObjectIdentifier as ObjectIdentifier, -) -from cryptography.hazmat.primitives import hashes - - -class ExtensionOID: - SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") - SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") - KEY_USAGE = ObjectIdentifier("2.5.29.15") - SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") - ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") - BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") - NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") - CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") - CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") - POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") - AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") - POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") - EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") - FRESHEST_CRL = ObjectIdentifier("2.5.29.46") - INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") - ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") - AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") - SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") - OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") - TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") - CRL_NUMBER = ObjectIdentifier("2.5.29.20") - DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") - PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( - "1.3.6.1.4.1.11129.2.4.2" - ) - PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") - SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") - MS_CERTIFICATE_TEMPLATE = ObjectIdentifier("1.3.6.1.4.1.311.21.7") - ADMISSIONS = ObjectIdentifier("1.3.36.8.3.3") - - -class OCSPExtensionOID: - NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") - ACCEPTABLE_RESPONSES = ObjectIdentifier("1.3.6.1.5.5.7.48.1.4") - - -class CRLEntryExtensionOID: - CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") - CRL_REASON = ObjectIdentifier("2.5.29.21") - INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") - - -class NameOID: - COMMON_NAME = ObjectIdentifier("2.5.4.3") - COUNTRY_NAME = ObjectIdentifier("2.5.4.6") - LOCALITY_NAME = ObjectIdentifier("2.5.4.7") - STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") - STREET_ADDRESS = ObjectIdentifier("2.5.4.9") - ORGANIZATION_IDENTIFIER = ObjectIdentifier("2.5.4.97") - ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") - ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") - SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") - SURNAME = ObjectIdentifier("2.5.4.4") - GIVEN_NAME = ObjectIdentifier("2.5.4.42") - TITLE = ObjectIdentifier("2.5.4.12") - INITIALS = ObjectIdentifier("2.5.4.43") - GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") - X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") - DN_QUALIFIER = ObjectIdentifier("2.5.4.46") - PSEUDONYM = ObjectIdentifier("2.5.4.65") - USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") - DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") - EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") - JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") - JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") - JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( - "1.3.6.1.4.1.311.60.2.1.2" - ) - BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") - POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") - POSTAL_CODE = ObjectIdentifier("2.5.4.17") - INN = ObjectIdentifier("1.2.643.3.131.1.1") - OGRN = ObjectIdentifier("1.2.643.100.1") - SNILS = ObjectIdentifier("1.2.643.100.3") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -class SignatureAlgorithmOID: - RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") - RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") - # This is an alternate OID for RSA with SHA1 that is occasionally seen - _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") - RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") - RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") - RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") - RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") - RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") - RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") - RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") - RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") - RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") - ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") - ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") - ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") - ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") - ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") - ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") - ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") - ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") - ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") - DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") - DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") - DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") - DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") - DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") - ED25519 = ObjectIdentifier("1.3.101.112") - ED448 = ObjectIdentifier("1.3.101.113") - GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") - GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") - GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") - - -_SIG_OIDS_TO_HASH: dict[ObjectIdentifier, hashes.HashAlgorithm | None] = { - SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), - SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ED25519: None, - SignatureAlgorithmOID.ED448: None, - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, -} - - -class PublicKeyAlgorithmOID: - DSA = ObjectIdentifier("1.2.840.10040.4.1") - EC_PUBLIC_KEY = ObjectIdentifier("1.2.840.10045.2.1") - RSAES_PKCS1_v1_5 = ObjectIdentifier("1.2.840.113549.1.1.1") - RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") - X25519 = ObjectIdentifier("1.3.101.110") - X448 = ObjectIdentifier("1.3.101.111") - ED25519 = ObjectIdentifier("1.3.101.112") - ED448 = ObjectIdentifier("1.3.101.113") - - -class ExtendedKeyUsageOID: - SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") - CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") - CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") - EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") - TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") - OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") - ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") - SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") - KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") - IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") - CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") - - -class AuthorityInformationAccessOID: - CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") - OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") - - -class SubjectInformationAccessOID: - CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") - - -class CertificatePoliciesOID: - CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") - CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") - ANY_POLICY = ObjectIdentifier("2.5.29.32.0") - - -class AttributeOID: - CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -_OID_NAMES = { - NameOID.COMMON_NAME: "commonName", - NameOID.COUNTRY_NAME: "countryName", - NameOID.LOCALITY_NAME: "localityName", - NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", - NameOID.STREET_ADDRESS: "streetAddress", - NameOID.ORGANIZATION_NAME: "organizationName", - NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", - NameOID.SERIAL_NUMBER: "serialNumber", - NameOID.SURNAME: "surname", - NameOID.GIVEN_NAME: "givenName", - NameOID.TITLE: "title", - NameOID.GENERATION_QUALIFIER: "generationQualifier", - NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", - NameOID.DN_QUALIFIER: "dnQualifier", - NameOID.PSEUDONYM: "pseudonym", - NameOID.USER_ID: "userID", - NameOID.DOMAIN_COMPONENT: "domainComponent", - NameOID.EMAIL_ADDRESS: "emailAddress", - NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", - NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", - NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( - "jurisdictionStateOrProvinceName" - ), - NameOID.BUSINESS_CATEGORY: "businessCategory", - NameOID.POSTAL_ADDRESS: "postalAddress", - NameOID.POSTAL_CODE: "postalCode", - NameOID.INN: "INN", - NameOID.OGRN: "OGRN", - NameOID.SNILS: "SNILS", - NameOID.UNSTRUCTURED_NAME: "unstructuredName", - SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", - SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS", - SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", - SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", - SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", - SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", - SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", - SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", - SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", - SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", - SignatureAlgorithmOID.ED25519: "ed25519", - SignatureAlgorithmOID.ED448: "ed448", - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( - "GOST R 34.11-94 with GOST R 34.10-2001" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" - ), - PublicKeyAlgorithmOID.DSA: "dsaEncryption", - PublicKeyAlgorithmOID.EC_PUBLIC_KEY: "id-ecPublicKey", - PublicKeyAlgorithmOID.RSAES_PKCS1_v1_5: "rsaEncryption", - PublicKeyAlgorithmOID.RSASSA_PSS: "rsassaPss", - PublicKeyAlgorithmOID.X25519: "X25519", - PublicKeyAlgorithmOID.X448: "X448", - ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", - ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", - ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", - ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", - ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", - ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", - ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", - ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", - ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", - ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", - ExtensionOID.KEY_USAGE: "keyUsage", - ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", - ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", - ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.PRECERT_POISON: "ctPoison", - ExtensionOID.MS_CERTIFICATE_TEMPLATE: "msCertificateTemplate", - ExtensionOID.ADMISSIONS: "Admissions", - CRLEntryExtensionOID.CRL_REASON: "cRLReason", - CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", - CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", - ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", - ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", - ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", - ExtensionOID.POLICY_MAPPINGS: "policyMappings", - ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", - ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", - ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", - ExtensionOID.FRESHEST_CRL: "freshestCRL", - ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", - ExtensionOID.ISSUING_DISTRIBUTION_POINT: "issuingDistributionPoint", - ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", - ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", - ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", - ExtensionOID.CRL_NUMBER: "cRLNumber", - ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", - ExtensionOID.TLS_FEATURE: "TLSFeature", - AuthorityInformationAccessOID.OCSP: "OCSP", - AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", - SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", - CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", - CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", - OCSPExtensionOID.NONCE: "OCSPNonce", - AttributeOID.CHALLENGE_PASSWORD: "challengePassword", -} diff --git a/.venv/Lib/site-packages/cryptography/hazmat/backends/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/backends/__init__.py deleted file mode 100644 index b4400aa..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/backends/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from typing import Any - - -def default_backend() -> Any: - from cryptography.hazmat.backends.openssl.backend import backend - - return backend diff --git a/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py deleted file mode 100644 index 51b0447..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.backends.openssl.backend import backend - -__all__ = ["backend"] diff --git a/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py b/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py deleted file mode 100644 index 7899684..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py +++ /dev/null @@ -1,285 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.bindings.openssl import binding -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import ec -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.hazmat.primitives.asymmetric.padding import ( - MGF1, - OAEP, - PSS, - PKCS1v15, -) -from cryptography.hazmat.primitives.ciphers import ( - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.algorithms import ( - AES, -) -from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, - Mode, -) - - -class Backend: - """ - OpenSSL API binding interfaces. - """ - - name = "openssl" - - # TripleDES encryption is disallowed/deprecated throughout 2023 in - # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). - _fips_ciphers = (AES,) - # Sometimes SHA1 is still permissible. That logic is contained - # within the various *_supported methods. - _fips_hashes = ( - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA512_224, - hashes.SHA512_256, - hashes.SHA3_224, - hashes.SHA3_256, - hashes.SHA3_384, - hashes.SHA3_512, - hashes.SHAKE128, - hashes.SHAKE256, - ) - _fips_ecdh_curves = ( - ec.SECP224R1, - ec.SECP256R1, - ec.SECP384R1, - ec.SECP521R1, - ) - _fips_rsa_min_key_size = 2048 - _fips_rsa_min_public_exponent = 65537 - _fips_dsa_min_modulus = 1 << 2048 - _fips_dh_min_key_size = 2048 - _fips_dh_min_modulus = 1 << _fips_dh_min_key_size - - def __init__(self) -> None: - self._binding = binding.Binding() - self._ffi = self._binding.ffi - self._lib = self._binding.lib - self._fips_enabled = rust_openssl.is_fips_enabled() - - def __repr__(self) -> str: - return ( - f"" - ) - - def openssl_assert(self, ok: bool) -> None: - return binding._openssl_assert(ok) - - def _enable_fips(self) -> None: - # This function enables FIPS mode for OpenSSL 3.0.0 on installs that - # have the FIPS provider installed properly. - rust_openssl.enable_fips(rust_openssl._providers) - assert rust_openssl.is_fips_enabled() - self._fips_enabled = rust_openssl.is_fips_enabled() - - def openssl_version_text(self) -> str: - """ - Friendly string name of the loaded OpenSSL library. This is not - necessarily the same version as it was compiled against. - - Example: OpenSSL 3.2.1 30 Jan 2024 - """ - return rust_openssl.openssl_version_text() - - def openssl_version_number(self) -> int: - return rust_openssl.openssl_version() - - def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): - return False - - return rust_openssl.hashes.hash_supported(algorithm) - - def signature_hash_supported( - self, algorithm: hashes.HashAlgorithm - ) -> bool: - # Dedicated check for hashing algorithm use in message digest for - # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return False - return self.hash_supported(algorithm) - - def scrypt_supported(self) -> bool: - if self._fips_enabled: - return False - else: - return hasattr(rust_openssl.kdf.Scrypt, "derive") - - def argon2_supported(self) -> bool: - if self._fips_enabled: - return False - else: - return hasattr(rust_openssl.kdf.Argon2id, "derive") - - def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - # FIPS mode still allows SHA1 for HMAC - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return True - - return self.hash_supported(algorithm) - - def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: - if self._fips_enabled: - # FIPS mode requires AES. TripleDES is disallowed/deprecated in - # FIPS 140-3. - if not isinstance(cipher, self._fips_ciphers): - return False - - return rust_openssl.ciphers.cipher_supported(cipher, mode) - - def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - return self.hmac_supported(algorithm) - - def _consume_errors(self) -> list[rust_openssl.OpenSSLError]: - return rust_openssl.capture_error_stack() - - def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return False - - return isinstance( - algorithm, - ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ) - - def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: - if isinstance(padding, PKCS1v15): - return True - elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): - # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked - # as signature algorithm. - if self._fips_enabled and isinstance( - padding._mgf._algorithm, hashes.SHA1 - ): - return True - else: - return self.hash_supported(padding._mgf._algorithm) - elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): - return self._oaep_hash_supported( - padding._mgf._algorithm - ) and self._oaep_hash_supported(padding._algorithm) - else: - return False - - def rsa_encryption_supported(self, padding: AsymmetricPadding) -> bool: - if self._fips_enabled and isinstance(padding, PKCS1v15): - return False - else: - return self.rsa_padding_supported(padding) - - def dsa_supported(self) -> bool: - return ( - not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - and not self._fips_enabled - ) - - def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if not self.dsa_supported(): - return False - return self.signature_hash_supported(algorithm) - - def cmac_algorithm_supported(self, algorithm) -> bool: - return self.cipher_supported( - algorithm, CBC(b"\x00" * algorithm.block_size) - ) - - def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: - if self._fips_enabled and not isinstance( - curve, self._fips_ecdh_curves - ): - return False - - return rust_openssl.ec.curve_supported(curve) - - def elliptic_curve_signature_algorithm_supported( - self, - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, - curve: ec.EllipticCurve, - ) -> bool: - # We only support ECDSA right now. - if not isinstance(signature_algorithm, ec.ECDSA): - return False - - return self.elliptic_curve_supported(curve) and ( - isinstance(signature_algorithm.algorithm, asym_utils.Prehashed) - or self.hash_supported(signature_algorithm.algorithm) - ) - - def elliptic_curve_exchange_algorithm_supported( - self, algorithm: ec.ECDH, curve: ec.EllipticCurve - ) -> bool: - return self.elliptic_curve_supported(curve) and isinstance( - algorithm, ec.ECDH - ) - - def dh_supported(self) -> bool: - return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - - def dh_x942_serialization_supported(self) -> bool: - return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 - - def x25519_supported(self) -> bool: - if self._fips_enabled: - return False - return True - - def x448_supported(self) -> bool: - if self._fips_enabled: - return False - return ( - not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL - and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - ) - - def ed25519_supported(self) -> bool: - if self._fips_enabled: - return False - return True - - def ed448_supported(self) -> bool: - if self._fips_enabled: - return False - return ( - not rust_openssl.CRYPTOGRAPHY_IS_LIBRESSL - and not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - ) - - def ecdsa_deterministic_supported(self) -> bool: - return ( - rust_openssl.CRYPTOGRAPHY_OPENSSL_320_OR_GREATER - and not self._fips_enabled - ) - - def poly1305_supported(self) -> bool: - if self._fips_enabled: - return False - return True - - def pkcs7_supported(self) -> bool: - return not rust_openssl.CRYPTOGRAPHY_IS_BORINGSSL - - -backend = Backend() diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/bindings/__init__.py deleted file mode 100644 index b509336..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust.pyd b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust.pyd deleted file mode 100644 index 1a7008978cafbd233447127e5290f14e22f4043d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8292864 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 ry`PWO|LD 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ÊTl& zKi5Q|4=yO{v+W%TD-e$#Mh#EjuXhF-hrnA$pkS$FBz`9Czr*CMWB--nm%45U?LQ`= zYS!|5Mr7%^4?mE)VgFUI1KB_Pz9?$9<6-CDiNB7G$5G>?2>mc&A95i{!dlLqjO?oU zHqqBEL^|yQwNH0a z`*F{fuc;jr{N>t=8(Zz@W{<7*WZ(U@t#%K2Lo9dZ?{~{q8^Zj&CWCmO@KnRkauhDS zp&_8Ip5wOIV!s2A+4KckO>q7j`RZByinyfyac0t1n~E1nTaCC0J(4H{p2X=OGZFmN zTc%*E313LJ)rfCc$8M0v;)U{9x{MEPtF1s1Ga$GUTg(>JyBoxd%vYFMAyK`IEq0N% z)jIhRx52ic0Q%${A%$Agm$7$YtDTyA$XSdOSi)W)OiVv?XB%JpKU;haV(o=8T>Y2v>ISZIE>9LxT}fXp0FR{jAdg5EJ{Z#-V?HNJwQ(vpW3 zR9<_HQ)%oq!V>2Dacdl2DE3;PPrei{jNe0hEkfm~^At^fS7YowD>6p(!EX zoR7^!!+ohf|JT6;sDL`!iB(|VJh`|^%Q!U*S1vlZT^-2aZYtz>jm=|cAAO6kqM>-$ z`NQ!yg%vI2z*9}fJ}hM1O<&;HBt#v27~v~t=GUPO^&==Ua7o&@>kNU#mTLSHV&Ij+FNdWEoV=|%6rir0J=n}a+4|HRXMNgu3v$p*LVgUy z4-870j(oneEB=5K%+BLr?c70l*!hRxuQ9ZjW`x$KO-X3I)GW~2EU!G!nhZ@6hBoIv zK^XJU-0nim>F=wb?S!$NK!tNoci3Ef6zt0XN8Go-M_F8bCqOV>a5rEyUeHBR6GaVP z19;g$7N6)wq7pzWh!>F7s!=zJwdi89nC;_MT1};I8%-}LeXTaOg7Fef5EDeSQLCa@ z4OZ){@e;I!fMUM?|I9qQ&n6pg`h8!1u+MESXU?2C=ggTiXIQGHX#qlQ{x1K zCk+Ci8F*@wMW8;@b4GHC#>C51PtTc8{dGA7)B-&{KOHFG^c>+=br7izBuSZ`qc=D_ zfsoaJNM)aau8VGoKPJW&{q8@fi$cL4SfZ9Zz+vu48gRGwjTlfU{l-86h>=C>p)wE}F?FqiMvKEod7 zqP#Q(?KC_`eaHO&-n^Uj8|pr0{BF;;g9>!I{z^HqEP@ zv$rI)xW|`4i|hSrL^^o?@6F$jhXmX1{Jr_0zVmmYY2l~YoW>+Qjj0y8*KB1b20xVy za;&~9x4(FrGxxGiRT-e4N~eyIn`dI=DumVQL0B!En$~}v_J=Zi(&udI)z{OSBUF8!ycSf1Z42ZF1fqarv7g`poXND0QseT04?uULDL%d_Hq8hB+&!nVDJXJ>p|( zx7=qEX#iMenokwSO!LF~w$C)5-J{l@0zaBUp#GIp2{>lHcrmsmIp0Wn?YhrMlC1}% zbXa~7!K?`t@Gp#}8$>)Lfz}2@M45vKRfoMNz<83lwaS|?`WrL^FbdpgIOho$8brxI zH!aqz^aC06?Ts6Z+y-WORt`x|Bk`XMdC1 zAeXkwuZHd4mtQ$c4GTZj#QEpZVe}!$wi6dO&kFeAXX5Y2tzJhLVDOZym40%o@=Pp$ z50@_*u4*i1J4%5U>SI9K6vsebT zdgoslx|G*ZS+J!A}OyX&c zequer*mAz)K>_&^)Hzp@Nv3J3qTx1oj^V~5W z-%8PSOn)uXV^YX;eEb{I@%k{Y_8F^BWP7%#U-7XW79OU$EgR#w5q;i*K1T>t*MZ%_ zFGyvHm7-MqtZQ@Q=M()z@cSX={`rEK+h+-4uAgfVbJ$!5G1bxmf*1$>1Y-6ExI_#` zy;a=~I@+S=tdt7s@P% zzCv1Hy1j{X+~|T>11i9rO;e}r<5ACj^-XX5A-o5<{yOzdZKm|Du&5u{?(eSDIK7w?+?pyY4-j`=IUWB#@8X$~*3Bz6P-?&H2&`@i{T z-}>F!_xJ5T`RK+S$g?v%-?uz_{xQQ$z8v@6<=L~x8F{uGH|m#T(#o^xc->E)ts1x6 z^6ZdCj*>D*p6&I|-QpS0SA^i7J9a>Mrr-qyj$-85R{gXDqii5~cHa%cwwFy2wtd27 z2GjEpsC>urY$^chE6*O?lvbW)At~gU-!0GlWZU;6y(16s_^)Gtd0f}?YM;>qM+`o; z@$q|*XW1$EImm^dzJmUh>jg1?nJ$R=9fDShN;TzD2Qk}|XA=N!UwKyd-?Z}V(*l>E zH}XsbeFW*#%d-y|({CVo_TK)xD9;u=;?SnZvuoekE!x~&dG^JpNqKfPAkoTfY}j>w z7V$D~iCetSm7j8xD)cs zm;c@PdFRpJEq}vz%>T_>edWnO`>*=V$%H-mzGMAk`qiH*&m2u+;iuYG^GeVjkFCq0 z^R4JxLHmYmEB4#bjnt5$Z{rjJRrcBNj7>H22I{$RMtc$_tu>>=NaXQO%JvkLjIPAg z2wS7Y-I`YX_&fZ#B1`=!W%V@t5kiG=WWS8hpo`v9`fs0^oZmjV2%o{QCVSbD`5x~N5QX5kqZ{zWQ*iHS4exJ9_sBDJJx@V! zbfKqU>S({G;F8gpLR>O>Hd~*~S;MG5pNMkzKw52cj%AvHKzUGPwOx_U59HEuHCq{ z;Hq$4jcX^a9bl+UYF2y)`ow2L6E$l(VEu(@&e#3VFesDH{aY6|aJ=05bmdiU_q<}y zF0|+8Y6tBZnXdkH?b&ZXwC9VDcGRAiv7aDifK)i~Zy8$8v3TP6gyzMVM&i0W%qePn zq!Jfe6N1q;j2tWa5@@vP9LI22dfQN01?*pi1HlogW%sQ) zelY659F(S!Qu{n^p}F~p>4Hl?Dv*6QbMZn=I$X>Bt600eoM_=`k@vO)C!BH|5_s-y z&29}11IA_}AOE@vO@T+@vlKLv1#KOr3u1sA90bDD9N*93HEfJxqLEOsUvfeFH$TRt z7n{ZB%#3o65G&`Pix!;IVo^Q&!bgu++39dS;!f?BNPu z>UM#jvfPxOG1!7#vk35g<+&}z4_KcfLkIt_=6Zg-QHS@5N&FGi*5sUnxKW)&tC4n> zw7{sZ{L2u5x&dXPzDBvoP6@On(t<}9C3N61zLC4Fn=~TD#+gA-V_D-ziZ6p${M#d5 zyd=*99`oe8rnOj|qy|C=FguVjGjENI^E0@^tbxj>^(P`~JyCL=*$xAh6J z*sYG1pf6YRQs4}=b!?s!DQZF`fFY#sJMgjr|4 zur&nSAs-N72D>+pJhJNua}Y5vm}~koRBHqCO%3Sk08c?GgWabB=l96Idv}AR$oSm5rS>TIWZs6X% z@*78{l>a+<{7&Ut)y+g&hM3C*eL-EQi=&KBt@YS}Yc=cj+rgLG6Z5qT{rAfrbvc4jN*meA>TfW^Tc(5UI`i?-E*9Vk-`+v97UhL#G z-4T~ z&n)OmuVt9CY^ZGT4>|;IldBrYPewcJ50vj^Me5U|X$30MYp|7ONy88Gb~(oK>~u>S zXA6PIIi+Mp{9{R!M(XExGqlqJzmy2|8>f4D4Od7xxGx#YX$t^??~E`t*Eyv}{8GaQ z<%94;`S=1Cb=!Z3qWo+8IOEX@+Bn40E}J2B@tjfzdO$rHX9r&kC4MeW=bM}tjGpH+ z=b}nfrU>dpOdOY(WUEfK^zp!JAkoI-e-Vg z7`?^a?fT1~rZ_BKk=ex~2Zzo|nc>{O&dhKMQ95QgBMbV?aNzuC&XrE?;(MA}nD~VT z#%}659IUm<|1>93c*HsgfkxyNLWL8VJ9)uL)0^_>5wt;lj%%Q1Q)mP%K0~p5Pm9k# z@NR5N*AL*+S7lkTtQGl$19tlSfRD@1cA${EY@;*LDjo%@`-4gmkG{%#Xb4-}g#K*7 zno>8e9k|lc--)ZY35`Q>j`JpV)TKK>c}l+O{TIzs67Wf#4UmjSCvk(_ces#sC&Fr` z;@>r;nh&S%H1Sm`oQNQ3p`0Tn@&n7h;+3i{U-PY;Dn=P0dwCKSrQgrv1lg_rv|o8d zxmt7pjcVG6_!GI;!dHikk|Eu{DuWkjq z>IKWPsgNlB)1LC}ADwC+JaRl6YdT z8LbLW#0eENd`mq5Tqn;L-wm93#sn#=e3A8`LlCMEI~nUas?{+YgsA2IhFl?PP%{w- zYQ{2pI(v_{_2ehdJ7d#TOS_()?nFF5sci^q<7#+nJU|hBJB$SwnTHzr2MNL3eiGsb z_2j@>Lbi|M{IUh9_5K=)8!ajfrgi*DZymMKbTZ>UFf=&Sgqg2hVqf9TDIT=L59<7= z9DP83?^MRPfYOOx(Iv5iN4hOVmfYlQ>W`0QEp{E7gXzh9w#qc%rl&?AG++x#PKE{) zxu`<}cBX~~1argcE;hv=doA)L3d%MZqdqiHy~*%@z)&;sKR zcpTe;SvO&eKY$+yA?XUt0XmTn?Nz)K#LDr}t|Lk4P+R!&jeKD=&={STfzCqQ9-?pk zxE-T!p<~2&qAL>2w(Ekpi&j*2-2^%M?6ed)I_*^>M=wPheO&huv{2~q*U6~VgvAx+ z&$R8`9O(3zLAT>1-=1ta!dHeo%yQdkq1LU0?%M*mfj*)Xl2SJp?$>z4q>N=_zdKv) zg#KfrMTF*~f%z4F#QZBq5@~*pZhB6gG&`qWnw$?$ubc>*40R) zr4Nvg$$)VPOa!k!npSV4ScZf_7F5LHroLTi3~bIE%#nBf(^oS6L;`@rl5r38%ZPQKs9M6;Q%8G^5l$?X zPJPmVLC8-ID6ziCYzMAS(!b_OO%MHA`!hBXf}}rVAs&qQy8hd7w6zT+8>9yC1wRTPm^W3TAwV_MJ7@L0Gef!xJ=gwga2}YAKhfIH(pS68v^Q$)qE)B;;-wsX^HUA5wTRo@n{%#FZnp6}GNc z8KZG-qy$5F{kEkM<3plXQ}1|1h0^pOV&@;V*jHA<7a=_S+bibl2NH)3iNb^&7UWR?VX z4C4u`Q)0-|B*KX*MfU~I!I}b>PVytnhy4aj4hjMlorkdo75c7q zGJKgs4H@PPUy`qA8wNbr7(VyYX&GH7p}U(*9NfxOVK*Dhw;XK5pCF7fzj|=_H@)uj zKT_AG!p+k4p@Yy`4Z4gCkb=;_mf56%lrQ=S`{ejcEe{04v88;?ntRaqV|)IGNUz(! zj~>lBz1Cs}TFs_g1v3 z5F9IvTmFHsP@gYU&zwmHTBRop2;l>_M;m_CQ3)ju+GIo*9#R}&3EgOD53+WvtO*zf z?d>ol_Qqp?>`BpK2cPG)nyTj9SUKmK>uyBaBCF}L8|GX)=c-$Nj3>XnN}+pXy92$b9%j8zfx z?&79weB?U9+i!Aj(?;y!MPyX<5})j{d8Mrzih_HiDK^cgFjb6Q4U0ulY36FnuJBu} zt;4L$mBF#CR%?sb8rxCY+Tyb^*OX?q;8Tt@_Eo&kwOUvDf@53C#;&#&e%Xuam^*VN zGn<@}ZEOc}G8=R2oXEw@-#I-XGi4K=(x|{$I(BvG*p^ZNUET97dVzFM>Y3PM0U4Fq z+m>bbdB`hvMJD1en~+z5tqoB2MrJu;ezhJykd1x%E3G|2Q~&!zH1!)J0q~FQJKbzRLfs9_b2xdlq5<4* zHooF>SsrlEpbmncAW-hx6v)#mF6_ExMHraYKq~J2%x+KdTGg%PuqVz|Zw<|b4riU! zh9cbBhj+#aV7Lcj-%0`YuZ;8L%(QBG0?2~dsJ#KT=czycj><$}*``24X6P`mg+|m4 zB^$2S@bv;)=muNp)EdPj=P+sqK{ z9gVny{@5ZHnPm)w=RH`fJ>9!$5VY)UK2)!S{;2o&bD;m_;ysa|tP==S5_fx#u`kB| zC{t1p~Wd9ojAlow=HwBjIVFuY(wi-xZ zm)iZa5;IAOfnkf~l%=sG_1W*a-=1jar1AOyM)NXhc(mT65yqwhce5WBU>Kvzbw89R z`(d`}2Tm(8x`Hr?5NnuaHt-_z(h5wNv<&>@e<|MEEn6JzB_esTWVDQs{L^#8VM~G| zgyc>v6KuCv-&RLwIK4_-%8Vcx|EnLrlg3Ghe9=2qIz;okME_lg4ouivtDD)LjL?B8 zfSMMlEzffD9nsKg3Q5{n^>_~>cd~l}*p&o19EEMXLILC5`08}5QtzB3{8M-AkwEwW z9h)!_bRDndRc$$m$X+~ym*|3S75)=2?fC6-ril0_Xsr&LOppvS>#yJRq2ID@&Cj0y zR^!T-!q2$Bb2aNs2#B4x|9^I(G33Q5{{{WFZFSJTOZ0m;vbpKku@BH;#Kkezf#5Y)bUYI9f`1k~ z5;zzVUX6SY+v5l=d&$Vv^gX!V#9O`IM7l8RO)a>EcGMx4x=vow>=WOgz(kwpu_7G1 zlBilAKogTCB2|1m`&ulgPGvhjhswQl*~sFtQX`isbYy&V>oo0mxD+?)jxAGVa!8orFMBjY*Vf*)FSC8sv->LnqVLVte-Orax?;3+0m zQ@YkF5#vSmN%*kl?dny7KP6bYg8kPYasgl|BsvO^- zAcEA#rmSt=%mrTz0U6UEJgJw8_J-cMpfQW6#0y!oZNi%E94jV=aY5I`df8$ra*JBB z7sYzL!2`=Le3n@nav30Q5VG zW6s7U^5$;1Qx+5v&YU`_I&&FKAQ}PQ$Py$)8w%0vT7B!o?R>+!g735U4zJLc&m90F>QFw4BUd(%#%yqd|uM< znr4Q6i#B(%zGO&lQ#{s*_(8oHET|;8oC>TwyZBJ({wsw)w zhZIcQWO&Uu+^GHkt9eZ~%D@N~-&`#9uKy27zMji_IJa#MVng)|u7VZljCXP0Rn-01G0WC=CW=rW3D_5<5atEAgyYx>yoQV0plP zGwe&ZW43xXGDPdR-5ZvT`*10VMjqy+cIjVmK|LDhelP4u6D*%+;c_6?jVK4{^!9ICn=G#_PR5lp1g-8`DJAII+ z=2FxKWV1Jb>{tj_bWXWkByeGIbp$Rwtz_Vww7_AHOWdZ^8dz1h+s(#rR5{3+ayKFV6hYfpQ`SEfZHvEGORt|JR#*u!fdn z+~wPW*@$~l620y8vdD+rxp*2k6S)h>5%=mj>_~~fjyo%sHR&d%cefd(lG;hY zq)=@DCGjRI$^8{G8O7-Emr>XvB-G9XKHRBqUxG716-xi8N=lcWaQUI=Gcc2-uSTY1 z=|#G9W>TxxqNHQZHg2V>&5l)y)fLAnHk(~-aAXLF9mmV|upfI=uS!RRO4Mfk=th}s2yq#*}u3;40_<%<60oq2#K z=LcB)y3pszITw;(FqDumnhjCUZPqImi3`m-B0~GcnTWBT$ROYvp79;>Ng@}uMear& z*tdYnw<E*$wK$@Ds0wo`4klagc`*~dB zZOSGU8HI>*U4$Z_CFbK2e;sw%Szk?;$&)9tl9j}=*5f&TC_X_%ddH-Q$Yl>25xES= z0K1-j1DPcz>;7!AY4Bi{i+{W=1nXA*C$7_ojZ?%Xfqpc2qwObP{(o)6mQ32UsjObafY=fYqfPwlAc3;4BK*O>$9P) zSEk3WMl8xE^{L8_$Zmzd#KrMiQ~rf~tq*~R0bXGa%hW)JPc9XTgx>Hd30-^A<|D%< znJ;9T;;Jq~SF#yc!wsVay8u}pA$#*fLZ(Y!Blr3QcLVqn@=7WIVo8d;gmm)~9^#4c2!kIhES92cFq zBozt8m$BSUOl|hF-T~+Pu1dqqFv@;*9W;f{p2y1{pxhEE7tEy{G%FuR z%7U}wI2LrYO9#ngA^RJWb-mBPd+a;nUtfD#%6^{2I-|k%CI2Gk9WUqE0LQHVB=nTwB zwP}FYJCDCjT2O@!b;x!EDs$EzlDO+v@h zstGNcEhGW0P$%`G*?6;z#DPCc9An@h15qE&rBU&kW#StHuN;?ejLZ3)J0TtC^QEY+ z)ULp+@DUcs!qnE^QdMbys?va0*u4qAJUC^bv>=uW#UT==q*6HJSh2_x%mqeYY?t|< z9e8T~!93ANm=%mIAKwbC7{(W!FHw3h7RXoD!4;6b4UoORVKUSJ8(dW*yd78#{QDwd zytf1EbgZr7$1_Xps&}!qu>r43H@Cynuh&ykkB6??x&ARaMRtvUz{swDBLVopmp>v$ zNl05g=?ddpyR0GLMyA#JyhNFrTCtIDT!7d_lb{_fGu2x5crvfwygwD2E7^XiTn#&G|_{ll^8^25-<+Lw_IrN)OFp4CmjdPdivu`TY2ITn&QG570< zdAUr?Z#tbDIgdk~dS_${@_}!XNK*Kw={^^0n)9rlAt58zpBdFBxxWxSk=zgI!(fuW z7b_KmfVK0F%m?}xt3fmN{BAVNp?=Uf znUkh^vt~%)$UpG&LqZ}a$p7$kyndow15wJ$#9oF2B}4AlN6KD?ci-H~*rFXd6dYyq z)w?)BuzJCK&?2--;mS;I!#!|{i7UGHpW2fCTNVxySb}GWZW-DA{Rmm+>Wit#AndbU zA7*JhtrsJpzMLiKy+W2fA5}cSH$nReJ?izzh8V|1x62_Jx*2-2N1bHVCBSxm5MU5e z?V16J+=EIWoVhNf^sNuKoGjq27q_eREmVYbd(*sP?ei4O?1l?nCk(xCyFxr6a}f7m z-eq&qn5Z-cB-#S;-T_a#PHb=LhEiP1(sT57w;Z$GO&7^SHgk0C`2ZwG!z6o*HKh6C zRYv$B#ihNmiutPPN5r3~?SPn_3V{5$rM81xC}5%+<92ovUS-5Nsd8JrH3>j7w+0?acOnCzqI$2&{YVGtFTACyDbyOkJm!ZQrnpS84iv=rH2NQ{Y&iw z`g|THpQm>O$9?eA$C>XhNW4zlBvkjWhi7!%kDdynGWFNR-}ZJv6w78!ar3fTKNza{ zAIHf`q{dgl4hl*bHuR~If0VCc`&7YqrlQ4JSckIDIDI{ItvzsaEN)m#0X`-;r7*~R z>`%$BfF$Z0YPTEx;j-$06A)0n%|%avychy7w0g^ZFs~OPc_QGpp%@*Z{h=k_^sY^b z7m~*Q;S7+UzpVwk+bok-AM9hFLjH0anIM06ix4LE({I#3EJQ-px66OOOEIaUesFnje>@#%{y{Sd{>X_rOa%Ie-M;;zAvS zmG=r@Hd(K6xKIn%iRV?mgv^t=?NhQ$+rD}eZHtwG`$%aj3k{=JK+O%bc0huxLxT7m zKIlcMbyBtVZj3>dF&G~7g9g7b$I2fV@9kK3my_bq#0EAwqtwh;Amug#mN zPhcE81L%5Noy-q*y}TOtX4Pq66;^a5xj9)4cyschFGQ4{V?;v#q7?AB_d<&BJ3$F3gnbbbz0cOkReQel`lQF&g zeAS4IuA}fYhfke+%EQxPh+K}%#D%y#m4gqU;1vsKl{%dsJh$Dv4gR!CrGmn z80qU%2du#E6gU4)Egz2(Z>+>bsn&VSZMXcnvW+iOf6E+#jJBaP+n?MwV`_ zS&M(XD;vOQu4&qdX)uaHtEIWv389~wPz0vP4f=`5!BZoiz<<}_$%@8W_=Fe`o(LEJ zIP+v;>>iej91HKs7r#I_8_ed*0%PIRYEpq{r(ji5P*mH|ZEdFqOU%9%;+qbD(UpO* zEox7!DIjejE2W|bIErv@l*Xs;rWzm5nx6yj`y6bm z$lQ?QgYq4z^fz?#|&96@K;JQ*?|<$A;1mAesB3XW258V<7ibxFFvH z)vIZ@Q4P@8#*Qxmc{1no4f@uH+r|18+E2pIGyW$>AxL9?)-tQe*|b(Ie#v?vcr}Vr zZ?4taftY!uHc_gMGW6!Bfl?V#eQX&MkQ-{RG zeFX-*4PF@VD)CW!%G_del(YbCop=XRO9w8VnoUwz?L2wcq%T5=D*xvFpp3&Ki~F2b zssb!OWB(=$m}8h0d#HkT6|^c0EOW||-dW~ju@bUOGe7QYE6~duUa$@A&w~VB+_D34 z>%%Pp#egj|0~Xz;!PIW-u+`#wHaZm|um!g|xduCyTVD%3F@F*HXN(C|a4i~J{WX?m z^*}JI0*h)<0yr0QW1>4Bw*o@WbIB=QN4&8%er8ujHzW9Q;q0C;3Ytz#cYzEF|6AIL=};; zSc<4vrcSCJgQe7ZmW&X0d<{3JCH!hPH{b%g`P06j8z1s%x+#td-FRvkT|5{q3Pxvx z(a!hQ&S2vqoKf}UY-Hutiwl4RUSe4t@SDM{PnIYX0mfzhT(F?pvE-ZRX90Ja?%_aV zr9k9#@3V{0y`?oG9+#=gH2^4q=TMfq;zbU*g{wF&T!sqz^|nvW6NuV}lJ6vQRjuiI z88eCi?9s~iCrn~SBK}|pDn(E}_K>#u`jAIm4`*?+9MyMzkQ|h*ZHtBi*Sx3=1wI!< zJC(m$0LZ8s0cM<41Qa5$9w1Vs)gwfKO(`(VdX}+=_>vro6sb8B@6^ewHHUH+BH$n*+^;uhmzJ`lBhizusjZN|a~EB;!FZ4ca|; zbQEOuzi1T~j@($-sr~^wRG+o?hdPB}f4}pG&oN^ptOLd~SpXVMt)F6Q;ip!rq-X)g zQ|1nS_C!pWb5fZWZW7BarH26)>{a3Gm^i?vBQys58CD-o8B!vSf)SpV2WFU9|EiP@ z7Jg}IFZ!u%Mzuc|H>%?Wt=bn6Jm_q{#WVPi#cZ$;-P>YKIODHKsOJhp#`bmu+W^;WU^ z$T7j5h*khyE(z((np#J@6s9nLs_J(zEntZJEKqG#{tHF*)0Jy^;|NSGh#ZGyt*L62 zr@sQ2PA2tJ1KHum@FzUpt%`}N3BG*MUF6H613{QB1C+uWqs<)WXpIFc)e?byiz<1n zkITAt9VyUp0(A&#?Yl1f+0kl^*xWnL6=kdgBfq#wl(82>qKxsoqfP2fH}m<1!I!++ z#}|!qDP!P*y(bD6Y(hFU>9!FWaWCh;E+x}x*)?W2I)T?MW6dyy>6Vo_EmPMtLYBap z3dB(Ete<#guf9SCysgCB9?7fiG>LX?9PeruW@s5|qu82^Dx-DHooQG6Tq>EV{JW2C z3I6YDdFcYp|J`Aj5@Y`RoeAS2 z1iJ^Lk@~n2IbqlpD-C117&q#)R?V2;GsSfC2$xPb!AW-_(|Js~w{<#E$aT6APP)CA z?&B6+-pLqYC~tnUyslQ%d$48aet;YGoJr>=o^(2>in{uGmQIcFCL|)ayjHGT)LdRQ zX5h?-ph%@)fhYYhLW-@3d;|oXep1yGs8@}+#c*##DHvfy9`LkSE_DsORCjIt`l&^pq{}dG)>YL@Ro|rBuv2%~cFQLJ)jN60e zA_GSE;3OD#A-Gh~&iW@_wMEX>3538<6suAP-!TGai)L4MdTP!^=ag33C+#zGVc3A-nShK5y%%PI4z1C6(Nm@f9 zBxr&k^Nk69tZ%(C9Z80Y>V_>Eb~Hq)N}Yjt^0lhBa1}K`ZTT=x$Ou*NoT2pO2-0Ad z`2Z?!Re<|Q;LBLOz?WHdh2vsE9HrQ=3;6~|+sh^5qjl4&YE6^GLaNC4iZM+imq#3A#daJV!q&Nl$w_rym8z_G-rr%{SX3 zlL;<`DQZJ|h)cL&5Mx+=AP-ef6f>?`3UkE78Mr~0G%#0GNFc#XKW2e;jIth|*!2K- z8J<8L9KMEG(!9%Wn$e;H(h76 z3NbCfI&ms7d1{^oaNmWXq(k6F>;q7kEal;XUH(Kyd=;m8hQ}s4TX<}uv#Ip8MP?v1 zv255SNl`sEjqze0;j3U&Nlb9=VmFiljKfp8Hy)(yH!47ah6Vj&EA~TM>sVcaGJlC| zY%6IIlMA`jX^FU_$weInM*3#1MW3=JLW(tR0r-&)RE1g4YFu>t&|$iL(K_NtwVo+f zceW28F*7D`KIXDz`i!Ad$S0vN7+F#FQZl6Agr%hID!5zfO0b^{6L3G>-1~4p!Q8`(FzYb6AClcxl6}^m7_9`3dHiUs zfEJyaU0&L|3JN&^gLq}fWytW8UN*yz=ynai+REV>jhRsyFY&*F`3GZ5rQ@S@UN!{U zue$dVc3!|It*;R#W5Y8o(&CBav#O|QAGnZ%ltn+nj0XdT59*ddL!clC+1N{oR^VMr z>P(Nfl&f8Y5|T!!U`4DENor&Hs2RKaACredJXH5UE!+(1Lc=8k{0nqGvMH3p%FtD! zOa8EtPJpwJ4f_(~n3a`9KUMyZ_d=Kq0+Qj09KqBvqcZq6Lf8SC@*IzM#wdvRM$8FX zFem7Mn{wx-2!Y8AO@#e`kkm^3rg(?lvAG0G{jB65f*}YrigERpL7|i5Hwb)!6HsLy zo)QplP>(l^$!OXKkU2U{&8xbpY!zH7gcTdS<&S(odcGX`Oy* z;FDMSiOc`;RNo;<>Q^&KPF<5+w5ZWCCPA*+OknG=go!p%8Ww`qbJWboo8l)j4eN4< z*i&=o7Q(4jLeO2!&GIZn;3DRyuNj^ZzeIl`^{QEqg6kW3bph_dGAaS9b#)zC{=8u3 zC*TU%NZEBsY4r>6jc|?1$X}#Y9F-tC-h=4mVk|^cvwfbNU^djfA=xFh!R)hw7=r#_ z^pb2V7%g!R#?o(XhIYMVPN7E(O_Y;tj_py9|sw_=8VK70VoOZD>8*#qaylsojMk64iFXhGI_y7Xm zp;fUARkS8MX|VVQYBjbIKM66_HiG32yzHYDr&qE2*$Fezr&f&HCNO%8SNIN9#280I z{2f4RxTa+nLz^mvYO8hlXyo^VD3vFgsWLp1@y(^WK`^j9@G*9DZKOdzu=i^tzhhd% zKMNklJb>%;7!KMd5_tlMapYnjd$J7K9ok9vL~(}W$@EouyQt$0``wwGsmg|_Bgp}h znkH@uF7okh7`OZ*l@Zif)&nDD_RdDiTDmB+Sl=)Sj5u@C&7B4 zpO^*TgqP(+n)FkL{e`l(w#DLlcJLEagR3hy-FYe1#<053rYm#JlHqfcZ1 z37*tlmx)rFrGraw_p+l>32SpvPiwYmG>?eVIvi@W_!Uy*RvWP}K#TI&*02`Dk%dt#rf| zWjzh>V$x3MmK#ML9E^FmPGiv=m9z7TAF1me@;=Ld90B z4BOlI+F=^&@fE!^1Bio+_JcO(;T99U@-|sz<1iprjl4t}A8mUjwU2Pi_1u!^(rEWRC+-E*kRDpCTuK(r)rX#k&{(WKbjx*% zQnrTY#q2HO)IQJm(P{wd{lv6=>}Nnl?;4?rydarhfp6cIgxo%N^HJd>T2au zzl4{3y`GU1S*D--Iztnl082TZ&|9nd6w}^CFy1_k`A$`Ho~WdjBYO zQ?XQcUnIqlnK1@qjm#)qQLbfLWY_V#8llUR$^Jtu&rJ5$-!AuLO6odz$fNPX2k%2Y z@t3*~UXl|2DcV2P7u9f3+6N+JGTedG^)=h~M;hq7%k87->MN3HdjALCVr3eK5*Nbf z#7eQ^rLI@I7k&Gk-?@D}e|@>sCwRVl;nwe^zU`5Ljrs;`lOM#^e?Kq01(N%DvE5pD zn!5br0gVz=iK}`Hd`p;!l$U z^UWFGWBF!)uV~O)!TXP`U4~cq_=$kgu=W8^k?Mb(+^?QWk8lc=;HTRkwk4MihXO{$ zV!+D6U|Bmio&^4tb7=LPjS`*N%K#+@e$b#-Nc*6iGP3pdkjM@$-p z?1xhaIygH$d)~$I;3ycU{uWb@^um3Y#+QStfB%N_0JrUbz|wcAeDn|L7xc}tfh1AA z_A95V6rn(aeklEuFhjGOY|+JPWi1Tj;Bus^t>;8>0HAh3pD`wbP5;C8(nn?BGW`H< zhx&vm??EZ=?C)G2j2&!etxW6iAWawh$0or>X6R&S^@1_8n~eCE^BUha>B#jZDPv0EEENw5#;V;y{?9HtZcr zstpf-NX6S3x0 za_p2z<4ca8Qgw2)Q<6yjOIjgPDhxnQA4oclFvDT9IRr$!K5PYQ=Ejd3gXivoT zoIESq8jQXc&qIjmu`P*G<9Y8TOPPkv7GWZRr&GN;_BFNPUW^_Twl`hb5kUSievAc2 z`&G_0@Ea@{?Z@$BmR&O1g1=zN=yJ;rj-KuD21n2H6a+{6?5U&YLxdFK`qQop;SIIl z+bP~qf4&e37K;BNtOE0nOBj|DE>LhvgJW7DDwQZ)E&51ybTEJzc9mq9oXvxf;cAb= zv-6Q2t~~jGrx5!)qSH=qlC+3$y>L|+xn@1@vPWDBkN1SJa+$IuGFkA+sRR)09d5{B6`HQxB09HV?4av)} zdJ$O#&;e{(|L*@uE+DV+G93@BJRUl7O$&S}*b0{m<52Fp_E>hAGIIC7=j(k$l{B(2 zT#L=jXSk_Rr5jBs4&-Yw#{;PtGomwibt93^<=zO%6 z1FMFs0x_#MyyY;@otGeO%Z9<>Eq+fmjp>Y;^|~slMe@_(fWQC;B{BcE#o?YkJa-;} z1QTQXyugC6$)NQ0I2M6N7a&IS9ser{t!?=Hvi`ia^z~0lYNw7q26+d0?%;q#N#7*% zI|+_04sRKB!;ed1*4O=Wk74c`rXc6m{yC46oF^jZmi{@9mz=ol5N>pZ{AH$+HAP_b*svsb85xR^j|$-_I}RX# z^>nL@QnQgXc^u(d?jM>>);&8>d$g>xEwCBvcGy)%BmVTy{=gM#!l2ZrF{*}OQI7Yd ziES4vYf+cK2v=tT3TL6NnoW>ONXwiL>C-)bgo<(>K2o4j>IMtZTmqY6T3F@l3x6U# z4p$8?*ql){RHstSAV_YAL=Gc=wg>+ngn!NNlkkt(4*XxP+9~+EMjQD5diKu19}xIM z2QUPp=Gj)ZkpUaeHG4^&wy;Ipr-x@_NqU%lSArhSj_nFP6zCRotM1AD=>aMb2(Q$O z<7~aqQ>wS@T{Xdaa#G-esil)kCs`+DTTjVd@zj8IQu$zO&o$V!`8-q^XtANJL+_b} z>su{n^!7pn#|NlQDdfI(nshA~oSkFvah`*Z4d)C8ah-=94h@dsY9qC!oQr%M!+P}JIR29xC z!dQ45P9-=9w0f0)=JV=kDB)eB&}^Sj`MkZb05cCInBaR5+p0u>gOHBZF5^HhVH#SF z5^1Q0`e()uM#6scFGJ7%zNLO%njis3Uv`fdzl?BG<7@D3G^9Up`Kb`8pC`*VNaVq_ z(2h4TZV&Z-%ic47$j_!Ga7_GyX}Gx&Fo;y>mF`jCYfqPOH~|qh4&8|5zRPzHIq$0H z=i|r)^SKZRDV!M}|5BmAUC2S6$`2s+P>^IA=44WPne@UG(xulGU%`rxN5uvwPWzzG z!-26XQ>*LHl(XX5rky88r)J3f20PtlcH^@Qr$sDnxaiNqq~Yt{&?Q0pdat#4eOYvK z>E`!K+gEjiR>pqTbpY*q=fs8_VfD7a39+nTGiurhBeOkp;`kr?yx0E=)SDd~wP9jg ziO&mb)lm2`^15#@b4%}AR_}RvT`d$iR?Nz@qHC8x(uQ4{&CxUw+AwH6ciUmW^6H6+F8AVHobMh_ zzRP2V^YEbqAM_yjm3*}@#~|(a(I)8RwoF`xa`^Rj@E2WhEMwg&Z%6d}lQyekl0-d2;P@(Rm>n*i^DU49jja?fwN(Iq}nSe?y2@cf* z{Bl)yX8fjrMzK5*VSVgTS}V=BVn6a*_JU45A$#Alt9n95h`}eBJ~m`MCT4Bw9t$8~ zb~NtmsxH){qpdmpmr`DW6b|SQp?bGOL#FeuUF)2Lm><4nPFB@*45bNrmBS|2i3KN931%# zj-Xy?(2HlqFK|IpJS)+EAf-O?$M8q$N5{jm+Ly=gg>npY(~2D9d!;4<48B68SDfgd0r-)$S2Pfwlai*r&VWfH#22CfutF`oV&8fn>+^mwxDyG+_JXul1Yq^EqDAl>aail*Z1RuNVQ3UAC#% zpINmxDJEfCDYlsTQ}E>SB1t{ zj?MGAT}bP+-RfTGFsT&Vty5in*1-B|VV@l8Oe8immx3>6y!X+F)xOE}uJM>knrYiR zDs-l4@3GZA$5fr3XvbO%#6D2zcKqQ?4H_CgV{5NO&B5)vUx~C3d+p=_sX%26AYQd-#kKn%+NkyzhX%KRsgy|MBG66SB<>4w zzYdTnkY9!sJEa!KDR=E}+GoSiNevmNTzSWlbyp~BLdCk!@fISwwRUE4hy-Ip;Jb;0 zy(_IKzT3e}jQ+c~(M;Al;N}f{z~$;7AYVIiasP zNaOe^hX%0@8iaysz@a1>giBt%ZMAO5vNAWS>*wz!_El&Qag^zd5~8!OeCtzw64Vi? zpsLvEhG5MIU2N88UG`MKXHXW)J@YF_ythv?N}kG8`CwgW}N$hc}HphP~l-O zk9S6O+VM;M!ptiWQia9!Ek@a<1T&;7xL{L6l_D9fo#GU1g^_iwSk~qHVHU&il{nex zP<&~NJcQp&HSt8oX>Y(iW_B{uvnOMf2op9tr_9Fg6p_sIb%2JmY{%G12U+~&>$T( zp7;zg{06hAH87nHG*@4Vrq7^DNN_X?$ zl=7ea!FJ1stM)_Zrz!g1+_z(j?#mzC<%gTue;E=D+ONhuss}eAO8V=#(>v&yIF`|- zrSDBOBx#59fFCoizg_H;xJ9w4qkTGRDm8_pC&Qy>#o0%~s16P&pMYUtXQSFje;RF$ z8kKL^=394wJ}!p^?VGdB$rdS}U*%^SfZyTb)$@}0Qjpv2m|p)hQIS*F9}=ly;b#Mc zo5k05x6DDRu9cIk<;hcck zd#WR0GQ#rhp+WoXyx@e%c~$#a_B6mU#jEy`PcZ(;x*IBnO+8to%zqbt+LJm0EAePX zAeKBwJ%c4rauyu`;>lAvEs_vn;1!-49#}W6z$VJY|!w$brd8+4p8}XaNa=~eIW&nA-h!N^NZ@$O-t6E8TKeyc= zy7Aw!f3gLs8Zpcoes(`8#ftrEnOp=kb(R~hfIk7094(_EB45}ZI6S`^=U5luJnI73 z-SYztU=r9CiJJBCiP)CJ;fp)dG@v=$W`P2MVWJjY;p0vUQlzAJqTCU|ELZo%ICR`gEJTB1(~2q5K!t4KVc z=9y+`nv|keO%)~r=(ygHur~+?6Bc+6_6Sk*8N);$eukilNsgDI#p*LK11D=a7d-bP-@hKkzGQTPZpL`gQK6Q(H3hgU0L{2^%BzhA+a!=kpp+H;c)Sm;_MdM z3is^5eE{p<4AN2ERDuyzS9a&}(T36?z_ zT;x2jTBSdM!AwSb&dw);nU3~g`t>LI#2R?yS04tW>vOP%6L5yBClN3Pi@tCSK;Sof z8jQTeLj3o5-9JC=!^S$CZ`s#FRl`9g;(|Jl@;rkRrrx%$ez7PvuXof7!AzKxT34&bF%dyf0a&KS%mw3I=g=R^*@NYjE^G^9 zYdYe!du8<`^wH*yuE(s{kzDYV|Z}?qsz3ZU$bAQ!9zH}qko4XJWry_69GZ{EDdal=Pgyd`HiyJ zRbDGLa{Zpu-Rm8ayavhV1|pwT9kG~MhK!!eJf#cUbqiN{ix*q=Dvj-fJ>6wJrFL+S)pV#R8zZLY9!zO4#@d`&t&Zi+&&I4|(y>NyaRd3h_-lPX^g7=~uS?XGU+e_E zVxMw4dY!);^!nlP--TX9ASDKZ-!;lTpH#B`&A&Whe{t@1KObFg<`(XXLwbrv@C08Nu z;#BZ1b>hD;-HQY>yFn_;Nc5`vv%WLF9<}y3ZufU+c>v7rkd_yr6cS=8EhqKgH1wA` z_3TlTOcA0-I)mi-wOEhJ=6cjvu17tUCq4gx!>OEgDDdt*6P${RQJcodVpKvtcvfVN zA5?V~W`aI0-krroEohzIDhRj>z_d%V)!%NXtmry{3TSUD8GP?b<4d?iK81!QxOrn{ z*MFgZ`^Qg)?_o< zdt`>!?Pb@yD&&Pe+s~e>ief3Yg@T|C#>J&Y6OmdV0_PkL&hp$%bZc#a5x#`4f&l7i2 zQJRZciIW=YvZVfnl=Z-H;q8|sK4+WHH?XkkuQRI-MTH}fC{2auAr0wB9AyNf34H4Q zIx&Gy-D}$xh=iwF;)8SEZK@;VsPyNuH*LELncff!tU!l zITMbl<}YRa)}XbS!`h0WNx|5Wf5V=6c>CNur#Lnl1F_}wLeHcZ@S-P(Nm3Hk6ei&! zU_0Df=(+O~6maepL!Jp{cJiz>5A9&T#GunYhH=qm4+|p^q~Oh%^xvK*rS<8(LX?MY zffHdFf>0mMCz6q?D;!)8#W%>)bcJ-C2B4nxp{mmA-b`vlSgJ}>#PdjZj(z%RaSz~3 z&VS|AH<(L#RzQ*_iNLA(E@aa^s2ygxye!lxN<=C(&L${EyP)4u{0fN8SOpP*A0f)T-0uMSFb z{|?xjX`n44pCZ?fp;1M*5*tc8pUjr@M!N{E{RotSh z7!V^DrKCPrrv}5jJ--W1H)*@r6@B~Yzb8A5*4B!` zr1wMoZGN-QA_jT~KhXe-uun_k8(~~_@oyuZ?%qRYN;g`uDf6t@w0TCp7R?tXeu)u8 z;}?-1R+TxDN83Eq1v>Mojs%*ph=Qo~L|Oj*pLWJ>|zo@X0&Klf$GZ#)r5*9u?mNw&P?x zI?nA>&eNgscld;i-gDs0eF2ObnVuX30n0hxhk}lbKkZ69BL0Z$>4f+LuBV*%FYshl zkB4v09=wBXI{K<={uBfd-5B45a0C!)6VEz*P-rB!D$EQ<%bGpjiGI{u?%1SCJWrf6 z*A0vJ;uAbM7vcZ;fQvw7medxXl`tyh6#MbI97{&cR`mRtwZ(G~QlxZlob?0vedFJv zR;Q(V#3e+GcVafGghU@?MbXmYY?P3P#K*>8MPl4|;;Z$|KJn*V*$;^`@|jcE!SO%1 zp0eU~uBYMgJ6%shfk56$u?H4b`Q{srEXCH!zaURG-n;54Z zP7`AQvwPSV$1tIN0IWt0YJu>r0G?;U$~tK_>}%bgoJr-lcH#k%WyB+CPEDelZB|hWwyCzBKwq(7rqyxc+cy zO3(h(=jz!DnAfiG39f%H%j4`?9JRXZxGsyHSr(m}T^ha3TN<5}SBl*9>Bqfzh7vUc;EyMMA0v4~cZ8>JdEFz^wa+E|aGO|f@~pUi4rVmm$bc;k+QYfTGikMF#ZMsp&ZhM1C>E>3 z#FfQGt)9q3Aoj*gruE!81S|~+UJS-^4<1foxX;hZGG8t3-2m&YC-QHM@%Y>Lf%MGx zMBc(3CuHp8S@DAr!3Xb8@I;>Dd)Y4MiJXJNExL+hX(;{&o%MJqpty@OloE1`@zg~4 z$cTA(Jlqqx4R^_cKZr}g2YG5{G1+B0*)UIJdP=hOFhn7HfhQuN=@W2`pxq|iGtyIY z7_%R)v*&nf7v#O5pQyILs#UJh+B)0QtYaqMT zQ*$eFmiSuY+|Pq;f|u(e_tiRc{6hUWTAv7~qh5u3vOP5?;zOL?aR3KL*>i(V{3!i6 z6enu)@c{jJq^Bl}k6b-L;-ftgZUag-@s}S<6A$&&yodMk4fsK_A=-Ere@#DnJvA-- z`n-NT*i*y(jPb|yBNY4J^Kq$u^muCS;p4CLBUIoTKHjMxeW6>rZbuErRo#N^0-5-~ z7q$$nhV8rc3>t&GF2%>ALQ}gg#&vKg*mVJ}!$T!q=iquk==83MxY89U(Mg4$OA8JR z`MQq6x4ckZ*P*x`67qKKkLwX3IECVRY!&1J>`Ul`MBe8S*$gHCSmpfKD2o3Mxqk|F z;U0l0>j~AZc}Sh7u7Vm4HTFPQHLy53IlIbZ*$DgWQTtxQ-ghmvK=<~J$F@G0F+P)G zxxZuKrwu@fOO4GlYoE0h2h_Y4@`;^rP*tg&b?r-tL)kOf6WJmevZmp=YkxcIDChk~ z{eE9OTXqq*B$i%06{^f_*Z?Lc-$_?lvA_XPZyK;5f5^2wOE9nux7YudtLpH5X%DQ! zAmJ{}Zd?EuQbxrES%G%&T-JyUj~%LeIFAqv98eno*U7r|eh6A0zH1d~#DNgB)F%Fn zSI|=+cMxGV$ATJ!lDBtU;GO~pP6e2b^oipFA!!;=5|{a40-w3&1}>bPic8o3;qG1F z>nf@}{)7@pfpSg?g_MT`D3F3kih!kvB+#@ckhBpW&CG$sk96@Zk5X^1rnlOw?DYTZS!#|$#Foll81r@6K%}KbS%HkQ z0$DTzZGtVYx@9WBhaI~8Uw7kG31#_9CJr-R85fj<7waYKI<;y>qt|o=dwGs4cA~r_ zYird`+Pg-?Is^hYqvI#;ru<~VPgI}8udT`ThE}>&uUyk!Jxfiym6*JvrN#0ZMbI1+{D^{ z7klT*-kgy);ZgQfpKvDa60O`Mbq11baH}#E{ zt%pXIjfXOz*0lf3ycpBYSpLTCeQM;^N&1keD^3>tbAxqk?JzEQiRb=L(x97ope{cn zNK^0er!&;U2XyC@lRDe{_R*2nZjej4g%I??ZGPDd-Qcqr!<})@xBeb-PXV3ZZ#_KZ zo@dFI%{?X>U^C;Mvrj+Bn2b$fyTLubs8b~jug3BHA2T+(%ih(6_o!krIkE9$E!)R8 zp3<`Y=%&Lo*^NHN3Q4qi5-h!5maPqJ(=v_=RZzQSS1IT9lJeD9^86AOQi}b%emi&} zrPyydcywO>Ls?w(oh&Yz_PTM=^%jI&^qaIa80vp$tHc9*1~i#W`$!i12UDE*%V|*+ zmk#uQa;wbQ`9&W8MerV&X+=gq9puxXu6WtSj)^mcg!{dhzSb*JWh^DJV2IH~ohbmz zDxB3&a4L(pRhRAET%F&8-8=Cs=Ewe|aoTB&TKFfpHCb@MBf${#mT`5#hly7w%XBj6 zA1)F{HI?&E-x~8d$5ERrx*nr!|J1SKsLeFqfv^x9wH0}kmnDxj!%<~^KVE2CDwXU? z6_C`W7ABK$oU6gMP{%Z6#^8%4p;oy+m*P`1$mDN$cQpN~a$+o5@ZaB(xE!^bAUWm% zeszp9f&!79sH3B@GMlJT@BEe$dUc!q6LsWMvcTfWQQx+V-`_#TFNGQ3;6NqrK)BH2 z_#S$76MgScSDVJ;7)7_TvR6iNNLIubBVn|b;YT;4>xT5ou6~JN@R5^CWf{$Jf&ul4CbT)c^D?9l@a=c6JhFJhP4txji6BosNo;V1YFcL0sS_`X>Rd|0si8!Xu}<%F393k^BKODVIJh9 z->ts~{=2Z9o9T&2bsrBzdVq44{10bQS?MSG$L&`7a{r&T1L3>zJ$vbtF+;Yapx5dC z!>HmL;)MiZ2lTvR3EY+=_M$Q3dd35+L;>z;)&;yWVzGqRp_tvq@FtRv#P-77A%<&~ z5|gK%z;8NIZD)5yaT%I{@!Ir-b8A&ZwRiU-VqARCBiJqGuEmJd;s7@&rSy^K_UB>y zMnLJ6hK-ZX&gaa2LF5#eNA=m=)soHA(OK9OXJ3k^nrx5dv@CAV@n-}fxmxBRlzqGT zdA-#)AKth$=6PdP>7tKw_B{DAR;=amS;AOW{I~oAUN2D1R{{j2y7;igDvq_v!{BW0 zCL3j+jPDDL^ZhH2FhZ;{2&?51r;D9x3AqjG#?4mAYe)~wS%RrbKf_*LS801i!JawO z`P0eYV)R2ieU4PY5zz9$_1e^Ce(9&_0NjqwkQkVI+Pukr?+Yh}pU0gTTL>Q*o;j>r)Ged6X7lEtLS-MP`kPb&CS;IYMc*YN@pr5&oY#LLZM% zSN7KIvY((re&u1tCh81SVi7s*{x3lin*ddLj60-kWYv!g8dQ$><~_BPbr!vXVXG5= z@!!TYlI}0BTU*+tx_EDQC?j2JV|+h4h9Q@xh-};j;pJ+Mx1((T$+4iA^z8YDYF!<- zg8aHRLa0UTW)G^^9AFdwj21@=9`BZP9@R|%(X`6|f{ghI(P}SVphCP+&BXlc9+%|y zM4>bi-1n>=EBMukKUXJ@*1e8fFHl3@>0aI`QgI->2&YA;5{TpQ?x*rK_$43CR4>zE zg}tK&JxIq^QludHtAJXSc&94y`#?e^3n~oB(@zahpWskGb^z-C{3WP&{YnGx>H(-< z!2(T9@(gk2R6{tZg|+QKO#cGyg%{NjQ~IBSEPz`{&C;m83#CF--#yBUkgD!rM&q<`eV3_L!P?AdlUIfC^N%V-0rGqHN00 zaTY-uArEO(tUnFIVL+O+*geunQ?tnbQx!#+ucuQ*n`J@!eEU9U z7CK0bdH+PRVa#@8s-1l0#!qJr5ErZP)E{}!U{y+Pjs|s*?Vedi+Fo9|W7!k&K0UWz z%x}ch z{l!FiUZv-2^z5t`)ro~V$ZH|$-i;-w1Hw*~Od4)F%xezY=Il~5f06?s8v7sOnd|yY zGr^iqlMJxt2OVoSiIDn~&Ff9X>FOL+m#cFWWb1%|-NiAP5qzF4;1+!7D;Sfa5Z@V- z*6%agiP(1Im{Gs}vy?=X+Va9PNYp<8`_tRW0a-*fOxPCQ+{$MAFfzvtj-sPNC&bMRIP-mB+`|83SiOtb77|8H?Hbcv%= zRy+XftlAtMD(BmQ^Wk0TADzPg_+g95UjG~9e>UHQ`;W(|FJjl}L&!V34#%B(y~O|vrnm6Qp9m-9>o$v%i>Z@HOhtUk6j2Dz;xvf6Cc1122HP+8AI)a*~K>s8(4lzM6t1uk16N#(W(M)9#9AKxq#qx-N= z62sq&l1Ow;8RmyRy&d1yYk}8ef$bqg%dN(^bI12}P?BvYWa3dOj9a!AG%chuT;j+? zSeml`=zcbxu;**emLas1$>blQJN2uFYeaYXsDOMK6)mTpvL|6QuEKbXlfnjFO_!Hy z<%i;2(}cP7RTGZ>ra*5C07i8&2w z`?s@=o|%1<)XN&_@w1$v)RraE`CYFH_Wy2Fr827%<*pNPrpBZ^P90>y*+1`L@doRC z$;Dm96*FVWBX9etD*vqiSxV?X70;$Z?C2e_+th#3qj0wAmCpX z#uIb20Bn=Y?^p}NHAOcyyGPn-3N6i6{h}!}Gq&s^cwL6|C}&+p>-y+FnnMJoiC8|)InJ0sCTw%^RraP#L^*} zY6J54cps1c|LkM3F=~Y_?yB~h>SG=ketO*kTAe&@%_Po`94OCQx_z=D|2{z57~m!fr(|6( zt_Lv>uZ>7$FJ<}{U}8)c=&XTk&?c=bX_9iaEcWvn>llux3}QVxRn@OGjGyzL>nR)x zpfnBO9-fi~1wS;i;+7wnS+VKF{!awslkj}{Bf|Gi~N1`ppRG9SV(Z#{G%|!hom72V27W~5);~;y&!T;A8 zswCDnyWOuQ;e(Z$#6NLw8=ma>NfL#XX{~@2z`0Wh*^;IkKBCrN7$`dUkZXc{nlZY! z)$)awFW(ZI~Sf}T&&l{%3Z9_#cEw_vy0UeP=21ubL&pA zq}`pET1Tr_C;AxzU|Nnv3002DMLb4bHa9%FBMFk$RH-HQ<^Ltj&7$r`qTsaIA1xO=IM}5eXWcEa6t8 zO5NI2sl&Kv3i{eK^tKt8Yv;sBYjrJ}GK9ug>4Z}8E7WvtmY zR^*z$X6aK#uV0>;8HWeBAWs(VJJiRFu3!8&^MCaH=eIU~XTtl&-$dsx-+z8rfAS-s zjjtMV!`fB-))?f2Ibj6)X|`&+^ff76p?-vU3O|<(m8Iz!->;JeM|?L}oqPyhgNybi z9!nNWNqwM9d^mfnZA|-=@o5v2#>zrqDvauZuvl@kJXcQGUeRrDz1baTM_c7`#~dtK zQ*wUdfIbc#u1?G?uGq36zv+gm#6My!?-gEuO_kR;F2Abf9qwGebwgF;ynnjclmFmD zV|DDsZ!|*RcD58R`r+SE07|M7J^tR?H0W`g>ruioS+id3O$4P|cGEJjW#9elLWgiZi9O0Lo{X%LgIjR#)C6?=LbYi1@t6Z*koKp>!(%Bp=f4p<;faQ;` zy$(6lBF%=*sOk-pX0~E-GH`AAqp~mX@MVqr>h!|Lg`{-r9zPWV-k6FG>1v}6BM^qw zUS*+Q`4g&}taMz`dbpYJGxQuV7s~k7rsPTpsvjSSYU(vZRh5afzU<0uUoUiNi1Nan zp!;1?8oK`6lZ6w`TyA$oT`?D%aIfD(uK;jduggFi^3EDKh|0CeH zVgcI`znv$n;WbL!>i^|~q)u?M5dtASGs}qfEF1U-4;W+=Kb#jB#na{IkpF}2x%AS{ zQ{XUGwn6iyU4u6sc)4wC25$H4_2BpHNqY{Sgv}$VZRpNWx_MZ@IpuY+aevxAw*v0U zme#ko2hw_xlh!v852SSw1tG2H!>|9ffUZS?y!(Kb=b!nPpljWwOzyP3c^mk$K7I?o zT$E8$Dp#cmPiP`gS0?*Z<0*7|3{o{Q0b;K=`wZ-3*1}a05rkZ)R%9MCUCfNY*s?MdYha^u^ZsBN$Smk(PFF ztJw0}iso1L{W%-OUV0LlZN5o`2j^4SMYixu);E8$wo*bNO+^WrrmALtU%IIv&Ob_3Lu)gu z@himdQ&rq105#Q-zNAFWe1D_Tni49O9HfMfIS@xrMC_lxX(E;euk`(wS7kL>>pUl7 zhhd=%^54R3H1;TJsw3$-|Lu_ooN@Q6X7K>o0!3^pn5y!-}#$-h;8gMZHZ&)?jwzLwWGKD&_}_xq>4 zT9me}Wdwid@SQPwI(r)zPH0DgW;3BS$?8EDzz18~v={Y3|9IMu?pw@YRf3XJDzQSJ zpUAP@<#1~ZeWF*=psALDu9sK`DpI8N+iDOGNAh`>%EN4N$(G7udiU3OO)l4qxQr*8 z$J5RvHx{NA!b9}#m4cE83=fsF=o$0r8H*y(_T>UpG-nY2=F=T#(H)tjne*{P0P&TK z@Fw#-tgO^zMhV>zk+ZNYrR9q1BsN1&J_^kmn7T?L98A)-H_6MXB^h%>rgQdHRRv$|HO-A!BSUrQyjJbDxUinGGiC< z($?g-BbXVKA=;FB3WacHP|mbQbT)(>Vo){})*JK1U!oWVC#;sg$XQTsbB|joN?Mhx zlTi63zZRsh!UBDH{ZGFBt`<&8S{4`QHyt*R4B95ecB!Ju zzJ6S#ZO;e0n|m1aL~R+%e%!AS#+~^3%CSaN+i=AlJHzcw$tZ;Vb}1s9A_EgpjJmfA zib+a!4<{*YV@J0Cw&as_qL$Z6!qp0I%%#7`5a-xG1Us+>Kmc-DJ`K(vl>Y(BmzBIE zD`QUmXJ~#A#s9-G?>oP%e~$c_705yM`k1W;mo(Z^Zo)TQaPWhuHyBU%6ewN@9@N3 z2lc${0f9)A!yZ_Ojb`t@@KL35Fx1%OT!WluX{L79-7j&g7lPhrr}*d=@nx!{ohLR2sOQ zM-%VV7~>VLMN+=dKanwn+#{qQK-#PsNE&>wW||XYw>y|X!Y0Ab1-f7G+r|3+;#@{ zLsfyvxiQK?OsNb3Gydn1Vr*N`@Z%3<`rfFz<#<=H z{@W&&%our_Wl!=7YDg9uc^`e=jJ(%Amu2MreV)zEbISiA=T(5Hb%7U-kcz9`3J(^@7?@?^{QtpD~{%(mz3-MAW6vR2KY!Fc5(TG(7{oBkTl1#N_8jtzgI-iOOY zk~;|20E&ll3}&LGIxshpb=ce4waJ37e#PO|`dL|=6N@LhBF9|Id&a#B2!sIY{B(cK zHw3HFVQ|(?F_3&=+v8~tgIfQj0G0m52w0*6lRvjn|$DxV=$q{7?~5u zbJuD<2yB)nd<<3$(zwQx_Pevj8>Ib0lJ6Fa@v`X=o-drapCsJhSnD@g>OfJo>kB5s z3{Jltn!enx+^zHz{V}_hzSjTKp;?MA_*jP-tgjYkdX4r&c4|Y^hWILiVCKsqW?Z** zQlzy6)^6EahqdY?S&)j3jKp8#iSMIZdj8(R$u!GeAdYXrS00D^!;SUW+0Q+QpYu7e zx1ZBQ{?-$oDssAALK?q|O#1%e@cqAfIo~%DzeT`WZa9yvRINO=+;9O;H}KSa!*ovE zXwc`h56nt=k}xqpa)j;Lv}m%s!~9FyS1FKfE`H}RJb7bo`LcM`Q!4*QWv2Hj>6WvCpRl4kyGue{VLA2MPTwQOatp_IXvt>f5{ zd7{fSd&Q1)qpi2QO_%625#Y$XklZ%zPnD1icF1FRhYF(@l@&Tou7~0GjN6f4A zZH;%NzO9XaKrdU~qGgh%VYcaWEYUWbnAVY%c)V?a&;qlImGcc#yJ1J@2`|DnVMN$2 zTwvw)GE29_wt*72wP!-}V`$#CUnKq=O?Z-{ULGS^?Jc4?{U0C9y8nlHYQAAKq} zkUjUH79G2D1@LStu%%Sr4~D?6+0&SPBk`AcBIO}1TgxJ?Hy@#t19*QIJyZH=9oLem3C>^GIEO&{{pE#&{H1Ixsg*~fv=f)!OOY|=B6ddj)G93Qs z$wN83>M*5@mx~0eEp&?7y*26beqh^%yGmQpV!WiTR z#MD^^aZVHq4<<6(yCr0Y<2B|k283dk%!u*BG$&wIW<;uay(?>qcJGP7s}ncH8DW>- z?D0DDkYW!qoSUcM|bn5WR3*ATmdNb(P79UgX4MI+Q68pYwynrw!tdTHYfq zCL=T}d0cu!HKmM<#3$od^|@FNF~=2qjpfN}E8%^H9Tphd5N}g9!gghVh{iUAUK!~L+sa+oUQ0MR zVeB(GvHDHeyLoKklv-vg=cjrr8FOb@y1#GyT8B6mxXdW9rZ z*d@Xdp~K-|)Jm-Ww&5TNnlLhfgY^urP3(+iKcgLesV?>Bq)9`B;*pFg;w;I=z_$wr92b+0D5~a}&|44)y9I&pTM!CEyF-RfXpWc? zikkkbQ}z%p_l^^kL|o^U1%#x*^vQ2qWE4_O=<%w*Gm9KLj;BC){^tu4An{r-k0shm z)uR)u%LrnL?uiOK8<6pzS9`@g>-!X7DZRU+ytLy2UMb{jGHzKU1C~gfm|@CZG2Y(A zNom^3Ne&L-X1KjVNs?3RDZU<>qvOjcRO=qgc`UcbeNp@|3e7A*@t0AkOmEqKZ-E!J zAdZX?fcdn>^hNx9eK`amyu(b#@V=3HiA_yJN=?%VooiRrU7^EXRNj(lSr?f_;6Bkb zx$F_5Ao)5mPGGo7%$4*SjK@5w8)R9%i14HPH64mUdrHLlOdNcm^xn;OkXvM}ceUkf z6BPL339&$F=!@3lVk%#q(eXooG`)@2*4tcYNDKK6OShzLg$uoA7seY1m&bpnSi*an z08vYm>9xOfp&V0JJD#wvR@L$zqvE9Ft&WAXPDRW1f+h}^KYj6mt|AwVQbktJ5_h{L z1t;^9)^0Fe7u2`YK4EkWIHFX5*u%1{3K z_{~rYGoA46B(47_3RZwOOe9_w-n4@-j>O+QPK?_v6xYXf;&Q&f_F3cX7_TH$M134t zfkgqUSE&5-*%j3;%}QR{0M;NK%$V0+85YXvkBFuvE?o&8mfpq`uUj;w^|p|24!suQ7eBP-w8Pr&E4NxS+X-F3mw!l zi3sh45}LO_Bsf2pQ&NPhYh|k-ha9Vwg}Yi=RZ%O~yU?l%)d#n-YhD%N|Ahl7bFt-n zM>|A7YQ3FJlJCFx%xEbe^@dHn-0Ef9|3SEcm=F=Uj6TG*9?HIWp0Fd{uZrQ|s9X|L zzdV5lYiYe&AHRcwuwO_P8a zXXfbyj(`|HC50n^eyhht6>zPdf=kAoLMfL@yP7$cpXnp{nYs^vAcdh%-jj3y9NQm_?j25l2~I=Z!4Yi=A&H0YXcZ z*HThlQUqezxWr9l%XfC&;5r0?S=qa%t5nkNzXcy%bg5z zt4H}?`?=9ypDp)CgWUVM+lDEOP?WTXrlkPm|@E?Kg3*PRwXt`vzhm6txDzZ%MD7 zqWn+pD?_C41hfQkjJLXkr)%CTK zp>=IL#_B3*Kz2!WvRfrNR44%E>XTJhaZos^w>rJds5iJyUjnk|1`z+IeN-pm6V$1e zx2n_lOr3xz?IsdmCX|w<4N1p|AHiL+;HE4=)o8q}U%WQdyZnQx7YVYvdTSLbDI(jN zhR{8Paks|L5GyOa| z!^}%OIm65|`pikYUwTp;tofv5`Jnmaah$V9@vrG5QR1{>D;nC8)k_^c2R}V3$0%!7jwc6I?x4m+1 zSRM{sX<2mvDRv~QDk4i}pF)BqbrN4oY9z~+V0_IXuWZrr@BXj=ntfG5o39D0NiGAPm+%HlP?YmOv%#RP9?GUb@yRURD^yug!Vf+zKP z$SgDR+jOhrr{(00%`aJAUTw{8?S1kAWcJ34T$Je!1xF6)4)OR%k_BSeDHs*auX1C8 z(?}Bze!^M*23YN~reuqMkL9k(G5U@Pkw=Yk5yBT6IYRuNM@NXKcyfezRG)_s;tUo} z0`}K%0nfq(CUK1b9s7#_&OUk8Swm;CNzIUgntNk@wlG7d-yc4dPFF`PwG;ME5gSMj zC-Wu8KkzHaaoDbq;|-3)$VHB4c^{DD*E~9MJj|0LhfdNMN{)a1PULXokdt``1(5@F zReA!{d{Ax{TgdyM zcK^YntKFx0ag#?8Cyq-gQ|@XNe>S8`^0Du-3SumQ|}UV4D~In0kl!jYB}}6u)T{lnXzuYYy={iARSY zSDpm;9jec1{Hz~9zl-S{fy9DV>K5s~84lPbBB#l(?}DDXnTQ__(PXv+h%I$qZ_E|f zWO(ZMLx=Lz_ci{f#k=VS&ambee*0jI5+=`rH~ZL|LLm^m*_$^N%h!j|0*h`4qe@?~ z{E;xaFVXVl>92xcvHaoi>qw%@Ipo}_ef#TUWX%)hku}e(d%1ArW2|Nf6^1ZVH7nNj z6;_PgNRnT#dnr13zq%cWQXL z_o&`kB5)vgRbcRWn(zO^g0yOuF67jJ)3W1W+H(|L5YtK`Ym?ks&C!rPmE zkR|f7>N!cN)Ajtz)tP!OJa}k5_uch+u4OY?>K^*pPFwNI2j;HlwY(4Nc{z`+p0jy! z^}I-*htzWg7YPM@n7N4hF!LJ?v+RU5?aK|bt`FC!I-G-=RqN)1hSa)sEuxBA3^UFR z9NI#M16-QcF<|Vp@}5gHWO!@heNg!;cyyJo=gC!mhCT-@eB_gA+HB1NwE-LD3mVtt zTQz~i2sBXvd9! z_wLDwg$Lu|ow)Ic)C{y~s&@XN{9JfKxSDkF%{f<-4hs^0WYB8TUQ)?hZkWnc_Fo8h z2+dN`>rzvPaM@vO3JJK(anfg$*Bf)+Rhj;CMv*wl6yMN7(fR^B3yL|LVz81FauSPW z*MkH^b!5EB$QX_18&RiR!-qFS?V>547U-YxOz5q1VcTz9*#44*OXhrBO^e7*&pMw%nBpwmcEAhs#U=VHE#^|;tp7wdJgz~i8g7$rzmM|^Kq%+!)E?WOmo z?}-FL#3l)PjiF9KdM;oq9G5OOfwz)EfG7L`g!DMtW}cyPTr@w!*s~8DA|m5|)g)xs zl;hjG85q7vf1%m~Ir@KtyWB32)afSa@#h-c+ONrCK@zZ6(|?O{wuA z{i2+VaG9}d;A73Ao)}v`)|?;f9(BIL3LfF$;nvRQ%u?5vD?<8MT`i-?*;;(Bf>I}` zHenwLJ-UPGtUS7{V>yrRF8E#&Oudq@ZIcVz)e|WrM`2LHu}Ft zNoVtP?IwAp%4d7ZV@FJJK9H0Wsw}#zrOI4rJ`=-eG5r&ETYsUFTKVw3zg?sXm+N_{ zdzNzVH}PC?KS=7UjQ0Lz?yGF>_{dqVLQwNBaL=`Re-6*p2@VFou@KIdf+fzD_Cy%y zUklp%h|>?E;(!u<%w)oztkU>q_eHD3dB7u)Ru!Y}u|fP^!ZTS+HvqFmL{nO+K_*+D zcD6$ncqHdpBk3T(fk7P@&xK9}>FJnyUe#{lcapFgdt<3dh440g1^x5y)iFen&G8s zdk-djQAk!5`pzM*t;ue-*8>KTa2sNXrCSPzc{E(YJcjd& z|23F-8c*AOoCoc*aC|xO&llyTe#AW-AuGMl7Q(Qiu?xe>MhmQYbkr@Zp`&gs7;MgI zU^V{#y4=(juCS%hRY7}akGR9I5xg%44F7;vO%62cInO;?Yk$T;%*8>&L#zi%s+^p= ztq-Omhz{@y=MWNa^nn4IZ|YlOVsFe-pUjBc7rF6gux4{XAl!1%d58T< z>URG}cTZY7wI=Pxe%UM5P^RS^#@5^6fDLXP;-(1)!p=V&I8L)p-Te;-W>*?H3ydh} z|Mrg;<@JAf7us2Ps{06ldQ4Du6=h3h02YG5>^MV&?F&Et-w+X^pg#ump@@o4J+2#j zJh#$6&}{LBfBHW!3f68+C_Xd{r;X?M3c#yZ-@xUcr~M3f#vP_|rSG3Oq~&ZZ+o34X4S$IX4T= z{a_5@muJIy@)6*48k|w@@!fx8BQNf7FJ9M+KD{tm@2!r~oS_|$o0Z@4Bd(MeNEni5 zmMbMcyOb*_rN0Imd`l2-24ep?5Pk@_o;R)XUKY5K4&2Xg&#L^9fYX%|J3J2%OVkgW zsR!(*Q|8??!9V1sbo2ZB@tpg3tX%tfhrlyUXno&sFxaBHd-xf=g?kR34YtScVR%o! zxM%Bo)1HH8^*v(`!+Z4)d#t{O-)+ES0-x*NgWctqLOP>AWzXSf{I})zd#t`TT6`aP zfq&yL)!w!HBP6n=@XpwS_zB)Vdl=rD7xoYJb~-_1 z_w+N{-~9B`d#wJNGy69IFWA#C45GU{J{td=v4`-ZKWL*x*dNSd@Z9gzAGE<_7*u!o zarfv=`6?(Z->ej$wDPs+GHm%f__%xJhm+=4f4gVeoB9a1us+sSUk3K_cJ1&Qwmv)f z`2Om12xSga+V1uTn@+EPYR}Lor1wJL1#{?O(A?$zV9Sih?;&`!pV}g9zjF26$DUOC zc{^&q9emuq_S@n%8`=?2rpVP$_%IY2?)7fUJ6n8x^*3q{je+^YfZetBfPMlJ($5xi zzYOfOX$XIfc4#PNv7Gvk>sgbq4^EkqG>09}% z+Fd^?vUcOrZSAz?!VgS%be{7BQ!da34_$60j4qw?kk**Wc+5L)JE>(i;#}Fu<6?TH zhx+hgksda*j$5ISzxA!-WU7*V?3jj(#cAQ?X4jf-0if?Yi8TJ!pV#GuGzq9NIkNV_ zd0qK=6-&1o$_EfX+b}+2LeD(i*_QY?7~4&8vBbsz;|#t_Uv>yz{5}xAW&S7npNtEV)lCsk6Jv{6jx~P+rT&ID4GakIVl$pA20391vyJ`y9Jg#ck+6-CHH2RvA0)=K;$p{qM!MK zqy5hPiraoUdzCP&;^GJJ$L$sjuz8LY6#>f6#@Gh^ElfO|!dS*PJKzj&iPz4TA=fTb z@>f)E-(IzSL(Q50YFbW7DwmQP8?#G2TL#!nsoZ|1Dtm3h*Wb>b>{v3WLYq1CwY*1R zhbU9B+tGDftYuwcMOQib{QZPb;v=!dRyaoOWv8B1Tsj+f|4~1?5sT-ye~b+^VpiW# zE0erP-ouOcRCmvn;5DsE8&>^$G3vnMH<2>5s_w-zw%Z>ivdo}|bOPjFfN(NU)1_pn zyIL9S(sVp3g*ipirR+v^t-0%hh@$uktL&JcDz&S+{{;~2zXo*Qx)SVO{}Hjvh&{ZR z*o%tQ5&N}bzfo)|vEDBedkCKGKa;Z<%QD!%jY$P$Hu62helpKs|N8PQ>}UPdjJSi& z{~dJ4oo9<|{yRU<%beY3#|BFr=aQd;^0!!X$)%t64WGun?hEF~(zh<2`SLyR@ zlUjwaPdzx6bREpod-uDB{A;`j6!zO?BcFer82Y8mS-x4>#rRX%aMDW>|KLj-QWG6%o?Rn2pO)8(J2of))$RK zpM7ksw!F+O;Zq?;nMXOIwz#7C9ZqH|0PMV~$kmT@(O!A|7xI+@mKIc&MAp=lCF>?6 z?84Dx-D!!or%1rrgcCWEw}UW7*hx5xu!nFyVJ{)qiPiM6vb$W#$JcC58idKoxbja< z#GfZfv~AMYQXP+dbF+KwGmrXGV3*c)axY*{q;zg?q-b6}=MUo)J)@lOjH)+M*Bj+p zHl=v^v{EkrBzLmSfKD5qnDN0Fd-9TPJ-pRLH=~2w1mH*B_`h0)*tRiJZ85+DCRurO zvSwVu6P8GIeru9Ay1n67ZoDYZoerFIA3ZC~4v3W>no9E8IuziJMu1n^Dhuw`C^~uh zom~Xnl&aq#ucS%BGd}<|o;yfdhtpm0xZHAS!^FDSzn;CO9LQDxc)WP-fqZfY%CnF3 zQdswkUmBg)+%51UBCY$#$;Ijm(nY=EoD9|9aLfZyZAF!G>c=y|Ly-8~eV*En`S69jGJSKO5A$bS89Z1WD-14izZ@(*} zsu>>KAB)Q7eCeH7Vij-oW=Wb2c0`fHlVp=u@Qpb(=4mgk208*xC1A?(B2W4LbzdA! zr`e>BiPahnNz+T9KX9XtZF=kfV#Gf1aMPkKl@0lg7vfN{oCL@UwIt3vD@YR=Z|5Gx z*kY~B2|HQ&&B%?xY2X~JJl<<|Use!sn9tQ)=0&djduqL2CUHARuGq|Db+UP@C0i{- zKw_2u1h=WSEM8U6RD>Mt)HT2h9>$U7F(vaBgJd1bfq+Vp|27sLD&sPHRwpFseHV_< zEXwLA&s8B@>XjJYgU~Niu?~F|wKqkPV};AnVL39D2@jBtTb*cxa!W^oQmZY*skJO# zJpsgVXa+~g+il7I?f)2^H(|SL5-Q}bAAa-;jBbgoxc+Tc#-$IdwRQ0=6k$Hn*27!L z9|Hg6Pvalwj&N@v&MB-+RBCWdUr;v-#%NQ~P~Rb9w+T{;5cQ9x=>C=!ZajpkT3#y~ zye!;HI-R^y;J@`?W^PqxckrgqySMRnK>o;hx2o)`nAg532R?UeccM)=2Qyl);AC-X z%fq_{&%gD1^n`Hy9$FrLXiJAL>OslHu&5mJa40 zN++3zl}DposTq;D)AGN-X$QMp{+yka?|o;mJlvT)K5Oc3l>djxJFEYY@(13R=)BI; z`l94vMj#h{LIN4_zoF81qWCVp=llLbL~&(xEYY-?PvyLm@Y{-T+w6bzJ)K~(n9CWq z<~N?>bj6x7v`R@6x_b#?rrAA4xpbj{t4``EV?F@t{c|=WNj7YtpKVWVW=*^%d3iL} z)in3IhPhWQxQ=97V_l!VcJ9@4FaP|N#PEu~YQbl({`9pAK1-}H)^*+7#?OD|vvV7X zMQwsqSgTPPo@_j>aC-8K z$Fav{aRx6PcJ*m8xr1UjhS+cU(+JM7d$_pz%vYNp>mOZ(!3ec>aXHo5>Th{fa~JNh zS$j{Xp*{qIgB=EuwJmHC##EF|F~qykV)K{Z-5y!f^4}ydie^*ux9;2OT;ICfw>7!G zecOFoo9kP%`-a(PXnCL2x3%}^2DlvboWfVAPi=G%v#X7;4Qq0|gREothty<0_l*-; zhJO2N!YH5-Zb-f--M4kQzO8ZJ9?bRan|wogU@mxFNsg*~L7%z0^Tz7re_*`3Zeor! zGlMk$F@Sx<@UW2mrRT>x;HM&>WccaBf6V5mqL_Cabu7BV zh+2lQ>Q;&K`JYi!{#>m4-kv2xoK6dQok#B$s5b5XUq1za+kS8Y53ZR^h0b8 zlo_AlV>wsNHlBghofS(if`ewwiY0QPb%p??$v^+)5qbS%EteOI5!OOv*RoG+30wAN z=|W-S9zRWudwUuEr#iW2qJkk3@5!68%XpX=RhN}*NGq@6TXnLgv})Z;6RY=Uw2*BA z14Q+Njn!NlP?f(imP9LCw{BD{|H0}B>tgGAqOl3RRqJ|6V)>6%<@fNZC^q3YydM)= z_h3o&gr1rS*nI!aA?bO_o&TUR2RSX z^i+X#%hvZ8;xx!sMb6zyRcoEfI<-s7Ga6u_a!s{=U4v=WsM8vw2iOt#z6y%Wf;hGQ z^L3_CGj}tEogsD=8Z~zt)NRDSy%}&=NSM*6*D%Ds;BzP0O12*ius>xh1ud_I_j!O^ zxkK)UYcI+%aUE{GD9rEQ@(t<0IrP`z%5(RW&A&xDI6R!FtBrXDqnzGt2EO#9d7wU@ z8=fE8z4Fn?U%z=k(O-3X_`srfxjxO^)|`zap?shJnf@$8Wx5`@{8Q8Po4#`Q=qJ88 z>uTxurl<|MQ3f^G*$cz_4YheNxFaPpesw3ElUa2KK}HE2F?|M&Vb&_XZGxYiDtHH} z^8Mdmsag3Kl`*lpL=s|y8Kp@EWKRPl6LWp=kNJl*!)}R@EjGW(Z##lL_0B8|BdxcpB0Iz~?y!st%arKDxYG&SZmKiF zb%HKLkc(~N3j}#UP6pc3#@Yf;a8r-v~#t+O0MqF7j8pmQDCnN zur5Av6;hKEw1xdYe~uAgypcDwvV=7-rU_c#ANi~#Ee7&!{)1#m-L3MI=SIO=6pKXn z>PNE~)J%QYT83?=uYYH3UjIqJVB!;>vj#&&A3V z6qzLr()U?gCU!~t1X#Q`3q+~w@ue0#v<}Ao9=roZ8d2q7r2Q1?^w}wFMq(HZhiL6E9~5AL4~%~YY8X@!t7E7irFSyn1H!iNi3Y5hEF zd>95^gDNl*P?+po|1=4lHqB%K9(aH6Fz}2Z_x?0DYAbe%kM+fcQ|XHpS7aH8valF# z{LF=qU;Upp1nvesR*4?REz>w2(8Jbc9X-4PyYdjz+CYSM6~6z=6}yEGD#bT%8V;4~ zbo=5!RCY$+=5Awkv%>X?0}uFPZX)N-^Ld20ClHg=pIz*47|`^%nQuU+MtC4iSqGsOkAcYk1M#U?<>HzRLh0!Vt0My zLKph$EL$GbwZ{Xmt5p7!ESDI8N_YlJK_2T+G7<4O#yu9tJgs+9o(e75-_ng)x#b$x zx(m+i-nN}3?;BsD9fiUV==3d%YdJanHo>*YRy3BpfYSzk(>M|jfs69+Qn*}aKU~Q( z1E^}p!TW`=vqpXU?z+6jgJa1txANdOSlUQyn-U-^KdSWJn2$^rg4_J<^CZch^^enQ z!ebSNo6b2DbYGR83Vv2g|`f04O4PrGo52Hbb}jud_n7Qb?Zj3R+x9A&isQ0J~ONAv8&4J@&UA z!UjTCZsW}Wym&&&w`c2G{@68$4CB1~Mh1Sn#@$Jc z0c(TNwp9SkB|dD|Czl?mAnw9T7j58~-sppW-k(L-Zi%F@un91{x;{E~hXrk&3bJoh z&1z-ypGvk{btFvA8rATFKvrDNgFktdm({2i_XV=@O>(N%ucL~W!Ut>g!X6}0)j~p3aQww|7b5ArZTiF0-sDA0ZW0qcI zGN`H&ImJ;213jhpcv zUc#Tf7PR##$T5J96*7r#BU<*dyMd<%=!0MJih4kM4=}>Q)B`pOX4IT$IEBG?B_Prz zK_t%Sq~+W_s)w@>1f%8E-w%4gTps*$?{q!j+m`Mk@|yJ^tmV-~cF~Kf$=yiuY#N2| z?Idwt>LGqe-eWlpMHM7m))agub#30g8Y#7SkmtX3nfYBZ3t6SqiFQGN4Z8>;t$$bK z-t#Iiytryg)p@Z*;fC^k&#Sn&5(X6!+<@!W%r{uK(Hme1DF&JEuUn(u6p}(%XesKtQ>{BI8ggs6^5)nWUkH%l z+X^KaAMYSGs5>|FiaDF5D{WJP0o|F-_MhJkd$RadI<*F0RYrf!Vej<{_O^}S)`=mN zIhvazKDFAF2)eav>=AZ(n46S!t;2 zN(H9(I4NeTj}vc$bPE<6;)kg(AIXo=YvMh+`Lm+1r)ht;dg;&n zG%U-}hRTAe&T_gLPI_DoHS&ynQL2nazKLQoTFWZhNFqUh@F}`d-75dneWth+Z>fyt zo8nS}>VQ?@tl@Xm&LYkH>f8tZb|bi3HXsm z^tE^=$$3PMo4o3;913FGbMzT93rwUn?lNQ$9B=M=OyUicV|Ii?6w3hkP-bboLwQPF zf>M{f2t*!YtW3AI-MmhNPvoWkXmCao3Pzl+I%wm0H^V{>diH+)!ei2`l6QmoD8$fyAONw%Nr>hz$~Emb}!nbfspJ z9w^MV`48V7wu8uJYRL-MzX$mabMPSv9QAJ@lc=vX0+iTH{~B)m{&g&2nvuE~Q1r(L z)?tBt@VCBTs^ltOZHencexn9b*9*KJUNDgMDj22>_EFiIFgRnXzqa*Y5mm*}5FIj& zfA63B(jS{Tbk$wc{^H~bkEZ_164b%r1qYvFx>t|hI72P|800Rv9=+oH%MR!atk>-w zybi6`&-#yIH=;I19#49J0Lbr6jBj$nLLFhbMndc>(h&ZB`0qRB z?@1pW%-_N4%#TnXhUf2;8EA&#?~X6;5`W*hBN&$m#{d8L`%fqAdHx>rjrYgjzhAi{ z{&wS~1W49+*+chAkC%1qaWj3Lm+H$>2qyaHJtL-`-MOW5l^ZF$7%4Y0Qf?Y3r8Co= zKPY?BrZHUsB>&w4IaUDDZOvd>0XTh3v%_nv4VGZrj3Rm8V45&QIAT*r^X^#sJnLWo^{{nBOB)&awpL58U8BcSMzb)Mos@Z=3CRSYy{3`H8>UWR=foqV{!}H+%GTIHF)y|rf zsGX}4PcTaNp&ioH+Rmmw#x;pqR3m-xFZ`Vv+%{)$dov&M8&B7`!V}Y7A;>7*Lr{VK zSjJCH2|tz5szg&^6=qYlP3mao6nOnaCx3aZw596g0}TYRTrZs zx!>eUN?vIA|4!Qj$m&w78ciL`83eBKNuZ?xY zs$J0Pd?Tgv|Bd2SOc+G@)k8V{jJ$$RK1qF)=4gF z)+g2rE+E-*m#owXcFPK4%e{9Ugq4;Idu+UzuZ{RfE$gt<@iI%jOk!er{5?|l9|Wj@ z>6?0K>vG+Cz;$@zUI=T9dq=&{LgQmu-&+bYoLRWNqE_3Ung%rM>sjQfDXtp%k9B{H z#zsDbTDXa?v2`yM$3{NOHBch>^7zG27>0CYw~N`;MU{{)V%!$oO77JH5!q31L3@LO zqQG+DshQzK^@m7N`$h$rC0&wDF1EtOd}5T)L5vc%x^LF6ITBS#0s|16VW?nat@Uy` zZ@KVHc;^bGwbqq+c|I|mO{yK9>T5C@DhOuh<859-=M&=xBC?`u6%l{6Y(!jY=@5yM ziI{jCw2B2{OSARsR^}3lzn8}+5`lwzRTTSF91S-SL(lGWFV%Da z@fC9tK}NA@iw>r^c6IgE=Oe9u<}2h&2sY>XDu~8EVS)EK3)((!fs+H{+xis4d%=RZ zkzq}IJF)d9tnz86m%tY>@P-9#`Jh2NMGfr|;l#D0s*xK+WaE17WQ`;7n5(B4Vv8{X z-LQ#9!^=YafVpy(1?}|&pgCUwh78hkC@&ENInR@m-CrbP$L{0H)W@q-p36~hRWaWf zG71Z-;$T}$;-P8p30wLjh=BM#-5T%55TEbtYHfCdHv^3V!<(*4QWw<95Mc zInAuGk=E;l4{8dVsmr9g?;G1NwQzzOke$$u4jYUDyb?WCj=Ovy-V+d_@esOZugd?A z-dWD2-#-HQX?n|7JM~R|VoX}077Vn?&~*WQ!(s(%M@024=3*6wj}bxI0x!ub#X*G7 zA9FpChBsRY01vxt=UYC?V=PoB&YOA?Xv^u*Mg~M2yI&p9w3twKNc6KOAyEln9EqxG z0ur64LJ-ni6QYEru0e`e`Yg&xB_od{i7F$;3mN8e-qDpb?4xPu3;s{)Ut7%flCQHj z%oR`rmx$UY7#X5&s0JfvK{V(jHq@p>VHPz^q-Y;lL0D4#6=e4Je)~O!Yxm0OzH_B8 zgeV0~WCa`9gMy8HGX+nh;Oq*;yd(ek!5K((HeTH6TRlvN^W*zr*}YR(GkMqhq^E{M z3AUDEe$Pw)sE>Q>JGhhTY=F7JBi7T3FgS5E@Eu?BjUDkPfm0{t_0D_ z4o1#S2F@PF&0bRW>UEK|AtTFXi?xeQN!!QwD32L5vl}gPm}Vi?Xf&({NGGr7(HPat z7;#gUoF&6rriy(#Rf!tSAnI9CX7`~vDKk=QOm|9hU0F^1l}5I!yyTZ9bsdP$+P+kE zSvb?Kvzn85B1r#_xXj(Z@Cptv;NXLTu_xQq-Vk;DQH%sXs%bT{+twh%5%+`ZlseFx z&-o8Pe8?1(`$|aYe?Tyg9M~1y=cqn!|D3G_Wa}$g{!v5sE5l@<>+FUzznq!pWa<>m zcXInPBaZu&f8c!J3Rot5<>omVaN+*LVc>b*DoEn{)3;^LKuD8!=lh!>n;P2p3*X=V zX!4gUe^>SUEWgE$CCQ!#s#gZx)vt*CyZgTf(|d&){d{)8zfXEIDha={tQ$9-7K~D% z5bcE)Swi$nQ*)s|#HU-UNkjZCa<{h|b<*5^^gHjdl~)513r%Lf&SIP#{$oSR!SKXSI;m-imN{qT=x>ni)zMU(X6Sa9wKj%sY1 z$08#U$!9bC`2P8iNPk=G3_r&qGlwz!q?btgKg5J1$`K*bY1v9HZ5AJ$&3CGqj*$A>*@#Ew$fEh-AVz;g6(@<%ZWB4nzRrh{1MKt z@T&jgRJQ@y`wS60>pBS_XIZsZS>h*k$y~D1m38#MvQQ+8P$Y{`Bsp}&4$KK5C$Xrr zJF`{fT8t53%$p}juGf@RbX`Ts{>A5?gwtLbO0~{9sipvS9F@!2&jtq)Qqx8LsLzhZ zy^vZ?0EhXuGMMcPTvS&G23x_vS8=zhxTICB|CIa8Jc2{9DmtPzO5uI}-N)*}mzqWX ze3EXd?8^_5H%~&RTFDcyh*LnB=4qJhV#j(8~6_if?Zk z-;Wcd4xiD|4D}{;wm(;PYLI=kC1fSFzBq!^oU~F|LbIGMY6Yj_zj3%7@pIS_3Mxz7 z8C;E*+fbLb$7Sumr+yqTrET{u6d~K)j`x!rJ!T1;fLQX#2{Hx!PdzNdpV|I&vtZ78 ztF9KZyt-NbW9y7LSD8nf`XA&BJlb+EkM+-YO*KM3RJM(-&o8ics`OhH_vJUqEp9VF zP=GXbVSRf%i@*@d)?WrL>Xy(rWi(FtfX0E`*MBJ_w<=@gK5DXBDZRfQx)1V)JC8iJ z^73!?*R?(GufI;;e$C+idK$0N{q+PQ^w&Isbboz5|KI4Z)k{Sr>#x5W7xvdHp_l)? zDZA8PXV>pefBj>#m@e$EW}Y0>U*A0{=&xNQ{eQQ=o*`t-t2IY|9VKc7{k5Xl`s)=I zg#Gp5%XXo^KDA_*`|B!_?wS6281o*+%iZd))>PU3wa(gUPxjXhH-_Zi_5OM*d4~4a zf5+eA-Cr7UcEjJ|UV6)YvIEA9J{fO|Yun%6skg;pD^%$qPukmJ;sKi01&(|FYu*+! z7Ka2E_Q3)X*f{wpe@Rvc0s)!jZ*dgaMDx(!!t}M_$BT}v@uHaVqQn2@FO0+`TX3G< zHUDefp>nSyv3x3Uz&>Qj`*y(YAFJZ`qyzS-FNTB+Id_PDJy<`icjNU_U)a<9dp6rM z=-hjPe}8`a&iVI~UM~J!H1hv~e{Z`XB>Qgh@1G2U1*?)s}grOnv3i#9BA(oRd{X63L( zI*TLz5lT%>vPtY0;H_Hgqs5K%21@t4>m!EZatmtuNaCQC13t9^n!v1{ zBQJ@oaDUE1_?->u0Zx&4tIA29eZ}=SDueMvzE9{$$&ZriKXTuI>d!tssQ#70#T#|x zVd?6hEifA@i(xf|ZeF{gvXm%rE9uY$&x}!OCVe_Qwz7OfWvxEy%#%l(K+3a2CE>{8 zaIeTVe=8=Pkh7?u7SYD;E%CY-8NdwI&0e<>_7E>5?Cks4h&q*N3afV|84_!xSgWAN681cs>9~ybmQOiGfB86 zvm-@Xe?>z0vpi$B3U+(Bb0Hmn(E*xC2PY_?MvEvHdY}H&>l~8p*FyfR;^?iH{N=sv zsq-KTX8}c8pE5b`_Gob2RO@dPH``L1gn3nBbH&g0@{1?BNo%C_r-JWQvMWtn6$7Us z5M{|uyA_MtI!M6U%nA$Q%>=1?_^I+1GBBsn)>tB~KUSXP=}%niYWRz5s3GpD0W^Pc>b>nc5In9sv{Rp=_0e4s&~9NJUm#`Y zq=1m>zaM=N;5=XDGy!W1G)x~Au3GSMl>%uSfrzC)(ub|P7a>7{O`=*Y))8+WCnBHl z9c}YZy`cTlNp0T$SSA*RJ4S(Dgb6C%({y!uY)zz^%JmQG@fVTJ(9Zs5^$&pcdboNLy+v+S(4rVa=r z=d4L~GFv46dBIp?o?4SyWv|6jxYD`fWP9e;RVDwU84Hw$J7a z(&LFORO}7K82v}BGD~T}y#)6afr0;Zj+2P&uR;zQ*j%Z6>p{*tD|svIVyl6tDCzIO zr-K*ZqoC;#zX1yn`1}%lR`F(1i+=H6Dj<2&_u;;BHZ+#UTDI-gSd8yRBS)k2%DbrC zfAtyBqw!1qqxl$#UypnuK_q^)1eJHoM&QJ}TNU^hL`0=XD<>Cn2-h-mtvRbjK_q^T zk|%F<>lC*r@L#|VB!`M@CXB>Sgt5W0oD`E<^xGuc z4H?rB&ddQMdwkDbzEmRr#K{unLH*i90*2|EMEOBD?F63xn;MRW$Xd@HR9}v^c;LJo zMID}_s9k9<|I<4zFH{RYd8{2^OTF?3)hOd%oQu93U`w97;54q^-jSsO1r5LB{QkOX z=&{rDV?QN=@9O!rAD~BwSB5>mwnk-U|7G1LA}_Wq?W~vH4Cz^${tD+(2O}e_-B1iA}G@O6TmVYONwa_&Eqwke!-zqNLkmj<3m29!n{#WzwrOCcQ){G z7S;YwDaDpc*$q;x0vax>CW6-#Uv7Ir-AWoZxUB)&(yMKaT5CnXs0q|-3#rXQ*6niZ zy$x1vG%8^A58fygsuE2U+p37+MJ?8s;2Y1HB0&&Z5c>cA&djqfNt&cBsP}%FPrLKX zJoC()IdkUBnKNh3Fy_7acG_Gq)~tOVz=TI&1sc{?njdKOW)~CpCuXh2z|6&j9CCIM zgTd@1hG5X67}p_0>;eL8aI7E>&gvTu&RSsSh`D*bL9wb-wFS0B#7|#6F;Hj)rP~`V z=8jtl zWV00(*&^t5ZWHleW#sMq^&^E*M3oto5l*3yN+0hqzjF8-khyH+q8DhUTHryE6(_Lyp+ELU=gXO4ybEAQCK$Xa5L?aD~HgxZScq$p88C&AZ68u~-!s>cGCRyvc? z*5hnaY9SUZYdl1!EoVVbFbAv1J3)a7te&EINi{HwLpk^3Ga>)PZfJ)O6>OspVybM{E4pCh+x?})xw0RkdI zlnfu7lNwk=L(p*f1V%vj9(~9%Q>DA@X$>+UD9k&WuZrd!w(thY9Z7X6SEW1YTIC!8 z&BjlU?u z-%a60BAi?;1*o_Mj&C~`7uBQ$kN*CMvRtb^kGf;F+2`4avw7-SOZXR_%F8_T>>m`iqm*sjv17W8u^?QMG3khZQq)wOjen70mK zo&TbQn|XTt%!ARX8uXi&Fu8CW8qZ}-Zz8`Tm>W}osZZ?$rkr!#6c!a}T}$Q>iyL@P zF?00T3mps@0>AbCeGj$1~=YC zBB_ab%UsA^O+7g6-|^<`F;^zCXZ?uc%aRiw=A2I>IS5uNqnj|HwKl~<5;l_1VGPT; zH~7)p0mz*<0f}va>$y^oonv{PN_st8_T3GrmPKcU^+BCoJT<40Lp^2_)xg1? z=^g@9C&~10f)~C*`YRg|`JsF*5Yt>Ybp9M12M~4pwnbXD3DkW*jI@4>40W@fZvHGI zt4m;Hx>a$`Bsd38=`_7hIvU4>HBHAJvT3?nUeP&7et5)9bR* zYMU1m(5JqSq|T)PF3yXj{va5#j;&ouf%x>w*jV>on>fwM6t@%)7&M>~t3J(sb`6%K z2k~>Kd0i4Nj?b3*uhUw7Hrl#3Pme?|s6_r*Z&)z71`eSHQ0+_$rp-W0jO?I9FFPzd z)?^30XP_GfqwR|nE65Hj^s!`z*pt#5Ozm8O^uLQWXHFYQB<%YO2^S?%3Y=&Olm768 zEl69`X08w-AYG4Q?h+fF>9?`Z?4%w(_eM1BePl#I+GT-_JJz>vxqp6Hu7}S=jgt`* zr>h$0Bb3Iq@CLyya2H49xYuat$X|cS~l$ukCEE}7+iFglu<+WC!{~f+w6qiSyq(xp>HZh&vJ9isaFH zqLN2B&RMNDX&D2s%lh^Xc3JD;b?!vf6ED!Zv4du&F7ND)`!!SbUB5{Rw*zt*Y{YkWd{K>h)w~(KjVCr%yjg- zM0d0%Rj-0G9d^0~DT+lHIU7ji)L&GwXzF;E*hnI=3dPD(tCWRfmeoWvsTurol#Rw@ zQm?V+v(+#(#Y`&Gd2GHUneLd$1Cos3ppqU1l?DK@J{MEX#QI&#s^k=W&#s(qyPYm8sN$FujZz| zH+$<=o`dD+dFs+n&P_isHyzZZQCi<5pphgrUnHIOg!4)(Ep}52LD&wz&gVu&msl@Y z%WX3A^C>y4Rh*w93umTwxSZCw^3le#!db-- z>vOR(1sni$v8am;x>$vRsPzf0KAl)*PG@HB7dUH6+3Dy>+|Futj7039XFKV;vl^*_eEhKy_}ssgPys(0@w`T@>*#J)I81b zq#EGmwQ*#TdZGq-dF`T_^rb6^8tCP<7X!ZFMo3v%({`&Sc#aZNNXCFqUzH{$ea$+9 zmwc+8LkSXbGFcn(sqaV3CqQm14XH~%!BpL5P;Su}hx*ia63;x%N8{5&f{(^A0(Y2= z#%tK5MOMm+z|{!^2`tiQr=NkhY$Y~(t2Vfj=|8w`4*o~#rx3Z-h_W1~Pox(C+)8dS zXh6nYA>{O^yz+P@a)Yqa=b~}G6nu5*bA+6Jl^-cTXVIMW<-(3?S6t{3Kzd%uCk98` z)j|^$Cf4){Jq|F0-Ok`dgUM(P3BJp|y!8{dsp^l0Jzl1RfT*nHfH^dvV}(^J~2I1%Hnpp+sFjum5lpH`Bg10 zHr-;iQh!Z$j}1H;>npFV=TY2~f_Auqc3RL8C@5bVFEy0H6lLDzf#r41HYo)3yoXFF zYy&gfE`Wotuv_)GX^(*YCI`ECg@;`XhALND&@Q?eq7J7FVH#v=5C#A9E>j~Mz<^pE zVL(luS{P8LUmzW4NV}Y|c**(Dky%CTq>2*Roc2u?u)gm;uzOhh?y|m7izhU_CXq$4 zg==GdVp6Jicv{-NyttZ^XHyNQ?X}ZsCJe#d2cHKnl6pHia{56w|Hz+_%`crH*>m`L zrBeAYZ|4i(&|t80X3-td`T2bu=MmQ$+T9(|j|PaqC?(VnxBZjb>?ys+Wxw)99-g(` z;u%hdr>Efa^-gjrE$9H+r!f}n*yApE9#YEK@c53Me%?qY1IK}%OxDP9=lnyfe`Ebf z^?Uj2S=C2AbMF}X$X7yOB??jR?=Ychs$l)u1C5vLjm2%8I7h?z%_6?xqiLUeMvJGp z@t_|g$;ao!Cp4Xol4Pah0i4P&I6xe>6DD?ufM%sTAI?X<#cy3bg-t-vGS6*Y%@la3 zBa_5YCnWS43X%;yi8EjSPQGw^VMpSOq>62RZGkNd%Xu3Q!AxIHL?5!zaiq$z9U{Bk z?1{&`ALzvZz7@fA5&8U1hX%h|$5f}(;D^(-%`t}^@_2rO1K)Gexv<*>jjZdd^=(yn z0d16+Q%Jl$k+tjJiagQ&P`V$yZKw)#CWE_7XW}Y1uF6cQ0^r`l6tM~yYjH7Ew5~@m z`q-=?o@f0z2(u{wE7DtHvsw*u%j|3C7%)5S+@}^u)gfi&?5*M+!H1ug^|Wih-z{+- zXjk%~-)van9*Z{{pu|`G&Ul#G6FhnpYL2LQ>sAWYUyD{8R`;Zn*_$EOshDe-Y6caq zWpl%p1t-%i^)|$S^kP07cx>P?-~%2TdE8^)T7&7$2CHJ)rB4>MiwU_b_}pI)s$ID^ zYZIkZA2D?AW7=zfTWI;-NsfDKF(0~#&yV?MdH>_bWAK~q(}(h#;;C%S?iK$8+6#QA z|L7rnXZ;i-dla=~OIeU-KSC;<{%;TvXt$YS>4nlN>CtV?2RwOMM6#X49}C)dDi~pF z{x(E+op@7yj**=BX3vn0;~{vs&M~A$DkooCA>X6t`h0)nA=vKo{lw){GkkeJ4D)?Q z`9ACOxviD%jfH$)P`(qn_~KK_ce&5kZd))uUx%t^1I0>*JN^rqw6p&*LXF86D|DFQ zRKi?&j@laxobeQUw^c zr?WQ@Y)kE=4S1KCR{Px#gcu9%d52j%h;BlXLapf*9Qbk)n9+8FmK&=09{kL-AXQ0F zgcwjI{N?7T@6hzqY1>jiBW1$2)K3*q@si#`#sB_nSNu!IpUMPQX42RGld3XPZzjl0 z>*iO-T3!z;|Fzzy+oPgEJWW}UtuSbR<)HnggLdkbDgs3=?}8t$hv+hsE+%PPsx+T< zF!?l^f(wu-A+}<4&Oa1%LTPGH@Wxt*oSh8n$oE%g)M!ft(=ZC92c_n+3nFO#$_^=+3SY?`VHRvyjeaoOvOnigsO zDlh6XH8D+iivSDKIWopiGKSZd==@j(#>xp%HB@7P^YG;w5?nOL8f6V!%5&};YEtn; z#))MBob=1tHj(X63`1)I{?VN7o%TL!jl;s5Sa9AMW*nfTA&gLdxXt6#0%_MB)8teW zx9SO@*=fsB{BvAm&`r=en?y$drv3ncxr0+4T9h6*F#YK2bN{4Cb_5W96dyl6W*;;N zD*(aa<5)31=?g{zCVl|G+=4HQgTqJe(GVEsQmnlmGbuwoM|s9Zo%Cd^)R}4+J1zC! zpOfXZAD@tH4`fbYyg*JLC>cU92J8nuJBk`}Nk0sad|FHQtiB6wd=rN3sR|%KE3nFy z>)~-V8;1ls2U5z|W(aFaymUDCq-+~&^^^@D;{8Eq?=$<>c5D8b)Z7H@6%J@sOyT0f z2xpFj!||$j=nKqJNwkM&RDKcUH_$*na&u7$cdu%Xs=%sjnFZ$d&I#Sz7Gs}-5dl#+ z1oSA%o)BBe_0#)It`FfV6hnnJ_^VP@Hany4ohlqbt{v}!vBcD=9~J@<#A2Ai#ZW-5Fg{c+ z$PSSULaMkn4j+3hvTS}cx1=!EHm(LI3OP!8C*OoUztEm1y*h1N{WS!cX>Z_HT>TX^ z#!X)f1~F#X!*L0C^)8@ea-AC0Cvu_p5`0K8SCtXZzYQtFCaJF+Ic$ul7P0Oj*bfJi%`bazHu6v}aoZ@r6Pl&3v!N0-k5331^`eaNRT?7vrEk2TOaR=a z5y*BoqcwbWG_Mc^p4Vo$oJ^3Jb{xM$L;(TAmyUk20%%+6J|T5m>RylA`>wW$QaOX3 zHbZYONkj9MU^y{y7?uxBvho7XptW$)c>*aj^?ZWNw8i|6qgD5~R^9De^` z^KN}+U%Ztom43nYf9|)k0l=)?;bh5GhLfcPnQ1Hd9S2VO98P}haqTz=yvg{f-#d8xdlQ-tnPIH41ypLMT8AYDU8_GH zH}Xi!_1IZRU0HZ+DP)lMKmBb>{oJ+D!JcV1B3gLal>DM58H%Z5F(ZhnBQp7+i$C?s zXP8NgzGs#w0|@4fC;>3<5$yc&SVr4?OD6MZshLT8)>=pICCE&h*y=j6NMJW~G85lX^x)S?+LrPeO1f_QGv#oIlC{X8F`)lY3-ROa2(sR_<2-A}+xQhh zz6$943d<;_WZ}Uib(%i%D~`)KX-5O5TT(bzsPJH_gS==)_kvavDyoB-GB9@pgTSt- zJa>bsE5alvDP()pI1kzDUv*qrOSe+WnwEjWJ+9%8GsRH9mLV)HwDw@i3KW#L|3UA{ zvp=;UB?Ghl2o6JODt8RfpsK)M73=jbfr+q5gr)psf}ru}7Xo>ElsDQOl=a-|V+3p1 z*(N1^N(Mprk#?i15iA0!S4%Xw*q~M+SZTVLnmt4V676tmiH^9kppUS?7RP)SC03|Z zWR7LjAc?_e7CbLM&MpeaStGC)jT0WV9IR?@Af3@tZ$T=qU}OnvD59uaAO!1~0CGP= z+$bu2NP*}=tgYEjf&$Fqq(Sd;W(-`Q>P^P29MjpYyg}t6xfpFp4>5OeV;cQf_+Gp! zSToZ1n@hg$%735I_iU6ZA)We~8hcwxuW_D@PZYgV)x6v6-l=Ndb?IHcgFUV_$pvT3 z&*M7M+6Wdxd{>uZB#-Y3(#4%DF!SB<<6FHG^gp$SojZs7LH{)vW1CH23IK2 zsbg(ErQJ|=aOPe^q7lHW+;db7c-Pk;fG@Kgqo9&XuIKAu28A&o7Xl80?kf&y5{GAR z)qAD`eG?q5Y_}S`ZK?0^ns1buZ?+L*iLq>z&FSwZ$V_`bzeDE2Kr@c>ri~JAk7C)W zZ`4QGcD)PUhqor?!qY7&Wb7?!!GtG<&Eg+`n#HOZMST_=h=LYGXf+-iD)Y}k7#d2$!)s5;)F0Y=>jwgFny1p60(v8|jZHqDS%ftfC zHQj=aN(C6k=>vD+7_kNyOYjc9-$;xa7P*{4JvC^)ayrU6gG>!zm$^VWIPFD*Sy-ES zQu(|58foOO0Flvz-R5npIe*2%`O+e7w@p=z6$%lp0nkc;fF(46yCe^7if~;wMWZN!}OQ+PM0yEq8Jk{C!k+?6u^3z zFdzWEg8=%871Vl;XWJ}j-)KR{W(A?eUTUk0 zS*y!aJBR^`Faj()6@wpJg--@oe$oiYMcv4QfG#N_Am7C{UPuVubi*_I zW^jnTTjW~o^V_I~v$G@zb9YC}q%&y!CU?^?J!-YYa2X6XFg4_EAQ9kP`;E)9=h-LV z{H*n2s%3C!zLEjMOMV7N&Sp*KaA$8j?xlpa-O(W+*avi$K={Exl)+O1`qkG{?C5}R zu&*@dtHwRxEjZ=qz2HD|zLLu5p5P4>v@NAMUr9FUKQ*N=>A&iEg-QR3(`;<_3v~nT z*(Uv8yXQfr-^;Vty=+TK^`cTMRMAha16_?d&8AZy*~NzKb@*r)IEL6$id{=gXA0t= z$?m@$+1}k_b;lPE(q4RYM8D&YQFF9VYUFRnGYjX5#(+<>A5=mcYPcrLi#Lx5>q!0^ zhM!NsJ{`IbHVzgh*>&#OD7Xje(rNz@03L7oR?f^X6b8r8(nf19H{XaX$DqWcbJ4Q4 z4l(~>phjLYR)3qou@fAEPs7E!hYPr9anIIkv?%B#L8@bGJ;}@8%%#}rD-Uy0E7!%{_uduwzqVg6$3+3(H zZSA)2o@l3uv&NNj%hOU34$;&3P(k2c;I4{rW+ z^U=pA?qxLCvUF%E;~rmrQClX``oL#2x@=8*(B=6RvCV{!93l8@L445V`Q@}Np&!@> z8TcjimtsPpwCC_fY1v2MjV-(!l|O<{eHyuSiwdG>o1EuSbi8d#A4zQ;+MF0x^a1cM zeNXu+jPh;5|I?{fzIJ-rI7=yKVJZ7+C3cx+J2J9N6#U=>nPuu+6=E?40sJFFLS-) z)0)<0as;%t5jM2(W6i%Fe}6qi@W`^~z~E`=wr`p^uVeN{!OaxJa~UNCU$jDHPc*#H z@z1aNo)Wb<2^LUdTZh`~gipD1Diw{!XfNYQXo4P9Nc{0_6WBQS;O?m~s+U<>TZgRD zh85138|7`OeQL3h=i1#Nf2Mww{J1lIGkV)2dRs-^Z5=yDs&qfe<*6OPFN_QFzy%gNtvSUUu^Y z9KxmkO}eYhQOV3@(d4YbNbCC$kJ*+;X5SN^JL{Qd4lX?sy!e7+$x}P|P1}yF)`5FG z1ap|_0TuMF+;n*`Og$>SS^AMANi8ohr-pVKsO8Gp6Pl@8^@ep1ZVBtw&2+_ezcF9; z?0nsyKYzbw?D zIKd(7W&`qUfZS+6GIQpE{Pr!bomqV*N6Y&I@u~}lfv9UPag=;^BC|A_IP=90#CaaX zZAYSC;23q9cN`o8!VM3)s{YwQD6imOWzR%C93g4OuH4b~W>u^qp(_c>l$jP0o7MRr|AghnbRUo3J&^CMhmEKUN*wGI63+_IWGz5!FA#)!+JB z)RwuADSa)bz^yHM0`}}NWr(s*5BRxg$J zvb{8A_P1Hjmn9=-?~xmfwtgb!lmDzE9iW0bWrVddPipK|2QL=FmSVz9tuERp1=T24T9 zjc@Sr+GESsu8JilG#_{K1f2b@y<>&5+`_9Om*UpM>M>pCQoq-U2I%`ptIp))Ci1ht zLiYq`g@rcVR8)lF-gBZz>j!Zym22i_|Hb@ej)yJM`c9ppC_obyCB}kda20jKs6~Cb zP}B=XE~+BWqn?>N(vWxjvBwQ4PB@`-H~!dgoOAlRLO^_zbojTl2tIVyCxVx;<}V18CoU&82Pc6>0gZ*udL#M<6?F#ing2~gTc#;ot(54gU->RZ!K zU{xN&QC3BQW7(RtS|=51)pB&`d|J}p`@R2z{kC@)epfjB8k)1y?*9q+U3Ra>Z^oCl zfBe4pfa|LlE6udILu}LDtNI?h$Jck_nDyse4WCRccPE~>{{uKMOiZTUHLCX*>K!QmP(*2Nq76R$Q*PT8X;lF^Ps!CecT<$1{l?ZqS11wA}c^Kr&okAV;Mz5ZVulU;A)o zVVMn|qVb~(=4Q>=*>n=#CpkIRpU523m)Q3my9$x%eQ}@sL-K<1qVgTd<@40h`OtEe)C!ILK9UUe)`{uYf5v=%x`ow+BoS39cECF7CApfXwtI=Vp$1 zLSn`5evG4mecS40{i=Dl`q~$|-mkYx%gY1z0taXbfG-LG?49Q6#+PMc^tkw8DgAsH zKMF>#jlRG!=%+5MSNs=BP9)NOi3~)Vgz0CezV#l?gPC-~Y#Y~8rOA1cRv9%JUgP0Z zx!iGvM7lTF_Sh?I2qc(yr{q=~`NI4m+2Q3sdH1O0pW(_^uF&&lZ?L>j{+iuw0>puw zJ+C5}oy`)UHP%9EGCOG#-z51mbVE!FghPtLKCm>Qcn@C@uU{VdX>9{89B_5f>Ka#G+Zb>ARtxVzeg)_|HGS0@V(DvI`K7Pb zoi>c=@E4q$J?4l+I+94AGr4Zowht~(Y({zGzFcIv<031`3PiTU}_$9mj2)5!GJ z2wm=%-XQR9>-62kG;eQY%;})&?|_s{~tF z?t_l-zV&?w?-5Q;jY@B26Gx%9{|;Y{!ru8-?CZoiH|kJ3OC`L~sAA=F^i91rB;Xeee#*>_vm)olh&CL5sOR`R(TL zzXOfk`cOz?qsePMPmMxf z7yQ+~9EH9P&z~dF*WYCRL{ffM=g<2-W^&p=&Y#EjKy(MWELuNk@Lf$3?*J%8$|M5}i?L$wD&EF2w+dm?` z4VyoY7(RbC-%alxD4E&(dG;wj&0Rrc`{n%b!-bv z$0mb;$Y1gV=wC)sQYCSzuykyRsy&glYo)nikGe?5<}_^B$Y2RCBYDam z)1baKnQhVm4{hPoJ37s<=&tG7IIcXRc^(%dPibywIkT*J348c6_TdydcN4Xaj}~cl zUIq77(}bWg4o{Rl5}Y34gwUHv|KJ&8mnRk*s`HLjS*V#SNkgIgNTO;lo1T!Md0YI+ zu~sM0P-v_x3054YcV^by~3JzNgLJ?9`WRWsIEvTO8YD zr{4Sxo{#!ne!PsLAFO{QKOBbZqkkK|9A*ALEN_m)kN+m}=CiFIJpMEj01i^#{LB`J z?(Y6EWYdYkyr z*z|TaGtS}tw!is)+qSE&XL38bc;D<$x7`+e@?gmBLvHt@rwg)sbeEA`!E$%O1&KAg z&8vLXEwM5-PLM;jrHtX7*|V*StzeFJ)`mxY>iK1BZ?^<&WwW#{61^qX$opjGx>$?y zWmY*OxJBMR5p!4R8W$xqO_*WWZOdQaJIS&>%j`eqv&c%)vP_YTWZS>#9Z89^H?V7+ zM1-sQvAjlb*i)Ou2uWQ0iDP+W*`@j-dwXmp857wJ24g7V><#LULKewRnt7x4&s$@I zf^>UpY!BhdAvDRXf02GN_a|Tf3P}xai#f@Ta+|f-?<`lXi$yiCG8jjoIBP)M#pLKBsD@{qYPc0WP-;#eh*y8fHT({k?ocoNfqzb_zj z)6XCy#1ou6t`P?N)$;T$v4AjB5xK ztBI#K#Nq@Z7bJM+lRa|+S}k7k!>|v37&kwlVKzZr6x1Wu+7L^99x;B=UVtGnYqRHf2*@BsA`Q> z2>NJitdsXLL8$ieE+3Ekc#n_Q`*@#^HxM`J|I7q}{!Gp++dlo63ryTKghKd$ZN{n~ zUH&0d8&H+&959vORPQP`#jNu3r7Q4yOB#M+@#*%0`7j0Zx!9sy0$Ys?7 zCytcrrFCvuh*Wx3vE~kOj<+qrbje>x@LeIT8bjY#IpZ8-BD1zfKKf#HO3|`su++j} zaWZ{lte0}(F4aYz5(~xm75{g4ixTK+y<+pz%`zKgiht(5?iSvML>l5 zGXfI3P=btJzV+)o3lo=v8pjx~eSA0)zbc@js6uE*aDW=D?&aJ5zP0>aFFeYI|eXMCI%lLUuJ~`|T>bKwmfSJD<8Ied`P2Q>mRA zAlp;@1TUtKfGsLWv+e~{w&<7B>6|S}jZ}755Y*sqpjw$STXcdHhX@G*j`G(lNSl*| zOllF4n#@g$3F>-(Ur{&Zd-(iVLaZ&dTrl63T1o_8F7Fi3c;qL>$CHU=QY(mLIxbdH z?{6y-Q@+c$mk?`9y~llfDG|P1=DuxI# zCURT*R|vT;tV>b+i4bJ6xAU9meV`&arI({h-NYidb$^QhH1-g1YuFA8Qg;&6QRv($ zdnl~?E~4sglSQr{wBV)yD7lDp4>5@3%4`{#a4-=ifOgbO2&IA;4mS0DZ&1;eI-RGQ zw(Khjme-`)|Ammd#i|Lo;EUfxZ?2*(dlpZ$MTFJX@p>Z5)7@_%;8LzQK@In?5!6v4 z*KH;Bo}0Pm5lRWz8&rA}xfphWMwNBx``WT|m8q2YFZ%gkL*kF5{zFtu=UZ8+O6#31 zjl?3+?iK<$KU}Yu`M!{63;cY!4>p4g-zB)F;tgHWtu6tH>JdKcayiTFGxZDLb@Ca6 zs{Vmc)G>JqRreGqw8!Ol6uQGjHu5oF{m8x&ZDu;ueNeZ$tw5oDE_;+5^ta~%8dD(` zm|)C2Wl5RrbiGMu7m?kGrl)I|y0IC4ab@ur?e>L9u^(4f&J0=+I}%lWk+pZL9BwzO z`tjFEg%KCzSnhp8j{Th#zhv_eW^sRT)lo*69=B@i)4SCTQ)t)ZIeeXwFwHbd_vl+Sf7u_A6m4~&nqka2%(WGEv@ z6)_b)+b;tF60r>6Qg4w{1Lv%xBdsr{%g>q=Y45?sL%_4UM<$Xwif3HvC>ujc6uf+H zLwlm7Mjs#L1F1veO;+auuXbBF60( zRFh7{TyEiwA}Wd1v~|q148mF_D{Q5+XOWUl&Ehwaos|2ujkpH!4P*&|q7lb1HOR9m zy$`nhAy!y7<*)K9 zrQ8fhg1PDKb-fR>EZUyveYiSu8$o~d?8vP@MuezLOzHJ&8r6DA57LJ`Kv8UXV;`%l zNck1hYglb*X~l|*yBxV}T@|Ej6UlU75<%^JZgi}NXGtP!t6jjfC24GVx^^MCC%fE> z5*#0t{7}0ry|jVMn&#SS-%VzTUu0fRX3cCAy&{?JcbS`N(#sop5x3mUJXO2gmtpve z>AYRR#Nv)bK4fucax)@k3n>yY*SpX;6J2wMgl`#<=dYjxlY=&j+XppBynnw5ck|@Z zP1wf9Jo3@kD_?#U=uwV5+HRFm*8Kf}S0KuXMcDb~A?w2TX=zfZ!c&Aq!Q@1{ z!%>2Ca^g*rSWGUS*kSUAzlkxLolGV8#+`0KN2LNM*TS{6YQzt$t);3u)+@#Z0WM3Y zcn=u-4qSvRG}Qz}oCX9eMk z7~vwM&q@bJrGul=!BKg=0Kl`^O64qXZ?m9dqXpM*wjgDTW!SOhw40_Q<#ua4M4pnu zN0X@qsY(K>chsJelBXtp(@cW8-iHucZ#s*|kmysdS0tqS)EkKLsU!WA6hrBbgwlEk zD%v_sA>;=@YOedxje*oW_n{jGDbp0CuU|xBUGHw{yneBJZ=)f##6{eING-KkR=9V4i^&*(ocQF!f~{=dk;j#g8LQ9GPdck)8(}B3At?yf{<>$mE>{> zejDMTsdp2KbV;V<3l&3pCl*}r#AE?!Aj>oInR=JWS|7P^)UMcTMXB+wuA?W8`Iqe_t#!uQ#pCc9FpuwQ|flQZps zvNCE3pzS>t%TDG)WS6Lds%#wcLE@=!iIpy~p2Up1DJPme%cb>*&JdIoE6?6ZOd`)B zq6j(vt{}ao5BP~KcCmYjnf~L{aJWZWXVGw^?XQoZ{xDFy3?^TMs;|o3#9C=S`iM2C z_Ndhk5Em1}Lmt&+I#e0=nu4@+holFDG-dX8^JKbL5J`7S{NQ_iNNJKkyiBIso}b36 zES;bMHG8Y%8o5un_Tlr(a+k=ve|G9s8&Qkw{qB6{YHp8Wr`<7MSBQ@0k7Gq5`{fo& zlY%4}t3W-%j%1_?txTkcTF=1st#&3!ZE?058Dt3FUC*=MpX_P zzfQ7CK_;OoRJ?gb;C^-x7zCSQ``(dJx)Ioe-yNqyVnj5am=QS1#Z=Axv_-vcqcBzi{ z%4j)PkP<(ED*7yUe$vQb)}F}KYsgj*LXxMHs-hz)`GrWPud@vrsiXH0ck1Y-xU|LM z5D8S#8mf_(wD8M_#U04cgH)gCqm|)^=9Q+8o~+7H!>SPU(YCl;t^&~b*Tc-sQ0qW6 z{&w4?G}_f8X|%8L>hhfi;|7DZpmCf%a6T=&C8m{+G};w}`pavyl^~7w?j0p#WNeLg zoDWAL?`spjkMxEawN@m zZmpyv%#&WDM*cYxo%(`n7LD(BRKK(uo)X}%Pj65*HNPW~b=Y(Hb{FMKl0VEBNqtXW z(4VYC*jnW@t}ZT0a)`a^2V0q>N>1alQG(q7TB#RVOV=5{(MD)L8O5&DTJ^e!TN`1* zh&I9+)eyiljFGMtxzZa5?ToGwARihkIissJxKLWJkTbdoLLJLZDrDfErbwNSROYZM zrxAZ+cxaEn(H;kn0*1#*&h92TlFgP{l5In#y+&=A2mwl2DPnIEc1Tp~wQI7TIA z3uR1SyN2J$Z5v|h^5yAkQ;L#vJ7L?kS1U%>Y*tuayF$+!-Lsvt=7()8U0Sg+67_8; z=Kyj!XGA$G$|uwPnPqWyclEmHmz5z_4)bHx=i2dNJmEOUIAXjS?FGiGQ^sreoPbFq zb8|d+@8=!MiR)N-LL_RNSdr9mGGDO$(WVbFG$})J^>iOr`!J!9GsjMzZc!}9O%=ug z7AYoH(C8lA8F4+s13L*2hZ(_}V%HIFb4STx61k0Hrs<_NcZE#Bpvg}C(0?*^Z}>*I zQ&l2=wAhH+ul&(j%3C#hi{+0Q-_Nt&=U)EMfE~)VJz+uPrIrgDyyXJhq@bz5rAM?} z7?=F7@`tTJ^GcB6^2fE*C;6k_LN})r z%O7Gcl0WpmRQ_oHp2;7|a76R_P5$_;GKbs1%|8ppj&qcJK!fRp45s~x9iQ-G#~uT1 zgYmzD*ztqkII%<1fW(d{q5kq>hY}=qG~@lLNHiZ?>}cS_{m`#mKe!EaZv#Pm2bD(3 zH@InKh?pVSf(hI<(O1RJwpsP8Jmw;;-x*oV7+5rt=&;+14uUTf6^ccP3Z(;(CKLg5 zI^j@}L8687g`$P#Oo+YvT#$ZizilwtU%lYvy*#hjkWL)sk%MZXyGlf21384V%C#*j>w^jTOvng@WL%@ zAl_~X7>5bd2`ri7yqD>WCUlw7)C+Ko#uizKjHJHcDavQb6|!6$W|?S3*_?#%6GYa&e4>Wh!z0_3<(LA_ z*B8kAJIE-zGPSor*pK|$HsvBIZ4tD_wle-j-#vsCAv7`+u@^;lWeJ&car>h!LT#E& zA~$#g4AFB7UmG$Ml3qr&Bx`~OBbx|B9i4hxv}(-r<*mwhV@w_gF(a$sQ6quJQ*� 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

h%cALbG1itB@M5lO!%OGN0@d$mi3mRsqHiTJ( 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�Y02I9-5)a~R4VGkW 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

xK<=$)It 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

O`yGw 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

-^yFrw&%P|HsA(g@6rz(^+v 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

j424H}onUr=|F*N4DY6#mZ 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⪙$I)F;jE|mmC?OiGn3(N#lNwrHQ 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⪙)4@sb2t)ZIZTl}

1W^jjA1c* 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

{3P^silZ-!^eNDt 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∨p-af?3nLJ-LdB#2(0Zd|QdFLwV4XK!3U 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Π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
KWR?eT6Ok!yi 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

sY>rF29oH@3G43foZEu1DcmN%DKs-&e1SZ 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

sy%g%^x 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%0A4lGVbaW0*WRk9VCfz5QN_ZPYi&;`2Icq< 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�G~9sWo&;wQlgkP9=QqOdR3+6Bn52|hiYX^%j+{$Ois1bYpAlC#fG%=} zms)_G5GW<9q7bv9-mrYUh+|n#OZ=skVX*qcrs{L5zNVJPQj;QP5$7{?`g-c}-GdI_ z)1Z(HRKSgh`}oH(Vge>GDwAaR(PpUth`)|ot0#W zHXrJ;<%HQf!fdn1X6~JCiGDt5ExHEJ2Df1hMF0L1zv>`(@iQ6gS)B{Q1rrY<@>K3M zK17^TgmSLZAnzf6(O`D^ba&A9ZTmU(SO^RUK5KJjZ1&*#w8OTii=ji=% zE=UC;Z@9fKG!NH0ke$4qnQph{PIRj0is$S;VqE)hj#JZyTMY8Ka`Kx8b`H+*%Qo5j zdOEY~cxyg-&+0V#OXP{-4z68+&e?4l?)z)_xzOL)ELZ1l^3TuzyZm#x9F+N9^%nEd zZzZSn8YNEYBLjWWCe&Z%JvgJhM>5*#Vz~hHOF>M=O@h)IbNB65-q|BEJ@ofB0+H!2 z?e5!4WctM~Z4x3=T*z3FsgAPdv86UTD>6NLnnkOEf75ei=?m!DnMaIZKA8T_g(Or&nsXN9rO@LbFycJFL<5`GVeULwoJ~hM3d3{!{A0~<*VGs_^=sEuR88#{HdRNQvpy4aUwl&V>D*6E z1)cI6x{mb9`*rFh=L5;9v2ME%9_(7p~kGV|Wf_28l2(*4i` zwOPwE$y&{^fix{nYI2O_#pl+EcV=AESHi4tSY zlP6fvbbXs@K6TY>Cv4VJy+CsSana_?ZJKLU&7X*JY?Qh^e~ek|>bP5Vh%*Dc_p6TU z!a7`6WauRD`qa_&BZgTVOCt|+d`mc8M#vm=a1Y`r*q3?x#CVYKxlO*@52gA8WpOHg zs$bV(78Cl$?X`Vl^7F{NAt*q30mr+yEtcBDJ^+=(MPZJNNv*(fL@`|py*9r#8N zToCKuO~o!);H`-D=d*}Uam+{)BLk?eAI8CO(Yf)#*j`Lg4r<**t*y!>2AwEsZY3Dj ztYBEPf(5Qt1qV04 zXjrLuFW%N8NpKt3F7kv>8S(D+kZ@W(b>;NBdGjkahxRgv>=$(}q_%mN`|0b|K(zUH z{AF|9`$%x&9^JErBL)A@fF<)#xu+H&sF)4)vmUZ_9~lOaa4O0wKHN{8z=J(MiX`$0 zMf~oGb&+c>yv(nhS2y>DkRC`y)I;}^(zPJDe~%$#^kYY7dW^VSj*nQwh^xw%_yL<# zyI(mtfjv1dLFM|DCltz=fSQ(cDc29C14MgEniIh=1&LPj1jK1p=yd$({lr~Hn`bjl zeCC!&&J7tQsvmZ`${1Bjh%}-WGzg;3wZM1M?meYcLkyZ!@iv0~|50 z74U*pQlUjSvTX30#9KM{NkM~6Jc$P?A&=&gWAb?S(62muCb!0re581ZsPgPtx^yF5 z?X+3;m(jD7fHGqPE1uAJ=`v)3KmNIBn?pwbVL3 z2Lw)C;;h{wXRpG4-W$R|=5H3qSyTU6224|q48hb`8nFl6N(wWp(<6yN_L4{EmzHh( zQ|SUFv)9ekZyGNMa;;g%2YQsK_U;Et8Fr#}nKBF5=!1m=vR zTYgLIX~hR?IjW?6E;LLlGI#K#nxCF}a`o7Ga{zE^T5(r_xmJzW!2@@y~UB{CSpB!JiJ73r5(R?zi$SYely(f(7Ue}d}Gqc(N1GSSP5~-Jai?tQTY1Gf& zUyB8U;=wok)D*U%33DjO7^jr8=!*TylOn>ZL3R~Zp5zOw18?f!zrnmDp=(@zDq`&Pwmm>sWi260e`T)GQXOjd9kwplX1!&PXUSc zNYSgpyVPtb!<WOoJCda_O zyIEhukIa5{-WCU&0`d8m-l|+oj!>&flc8K%m$t6 z#}{;JLeZ+H{Px_)=#6{14W>O>XgiK{pFwkej}|Yz<%5B%qb1p1$8iq<8{D4<91FWF zwQ9Jm1YZuciN-cJ_N*aPOo?1B>&J@3QVmdAtsFscG2eR0be=C^YA(&^nk!d?+b-=l z7bUZv#P-Xc#QAZ|oBRZ9V{I^)JM^)#XDJ&i>xi|S68=}KS865`eL``p>Pg%y;<+Us zUx+c@GKy_qAFEpL;&63rU3kV)5ayM?D2>_w<5e89)cB8{VBJ6KZ(lD5nE7b+U{Vck zUGlPQU5NiZ?`!V+VTed8mvK52F4dP*bZ7SiPCAO`khcD@yc`TsyYuFDX+P0h_iO)v zOEX@kRycXD4bL`%3i1^>$K3I(jags{S11rHryld0X*PVTPNFPzc8ugw!dxNRo?OZj z5`QoMHNhO^KJq+0H*R}ij(Q|7C(Kh4=Gg>Zn$xjN;ilIX^6P>JElo3K#19nk%;q^t zT!;*pdp-Z6s&$~7*}Kd343xRwe&0PoT@Lh2pXqwfrbSr%4F_xS%eSNXJKj^sVKx$mJJK8o4uV_0YCud7eMO$jI5_=yiG9Yqf zS!Q|J7z;4#KL#Y@y!^felvGt4adQXXsk5Uv1Rkab>$Wda-9iAb34F^jcO+#?Db2Rm zXx&)Axl7_Dc`!CvNNqeee=qrz?KZLzJDFJb(!6atZBx7U3Mn-*upINm&%&C2s3Jkqs9VbI zyiX3g!c`b2wS|8!E!r%?ZZ?Ol={lk{MzaN-NYShd6ZdrWE~NlhocF2UyJc{u8uObpo?{gGUBgc>>V!FA;^eTVQk>)w}`PYTmR!>>cuYABEV_j@7)>+H;)Cr40X7{cdJk z7d*+j5xsKAtw8dnZAgi(nlm8I{>ls=BF_=+s2{q!FBSU{@h`OV5t-m2dBCZ}Bsu$W zk_@+B(4NSgdH!Fny`bQp1InBjqp`Gs6t*d-CRjH|TwoBjk2ImFiWMfQ`r%e@szQA>>dkA|aYW!XD1 zO1j{SW65|pNKZ!)A=e44B}$xW-RMWO;mtBHfUYbt{G_xdhJ;%xN>I`J9+6;4Cu4LX z!K_KNJEXt(P6%zDx_qClIAmV=QT1%u8B-j0`71Q|+(gc1i#WH0<%5EY_79=gWZfMH z8##QXYf-{%0x+iTQ`!jzYwMr!guq$@Bx{!Qua$r6ykWJQ__u|BIs7a7Dkv*%4w)PH zw}gL7*-E{}w*;}uiKD#64K7sfH9kYgTz-v^XE1l}%^d_o226|p+;v=#3G%|7#Sh?8 z2AXuKqba{oEr^&Di#>Sla=N-4Vk9@81lRegvo`qcH638w3w?{FD>=cGo1ZfKb!FTr2tVgf}~~4 zTVn~g+r1(O!xa>da&)W3!WU4-b#eF+T`BIs+J|7wkTZn)j-22`I`1V%1Uq)&8P3ZTKxY5mNHjm=D8p^C;(dY3TC(LIKL4il<1+dDLPa4>L|7 zBTg_xKU6C@(glapahL12VXjh}%dyN~eC)K;MZ|?PrAT!YoO3HyMk&i}Bc6ZfyF>;B zwTcX$>t;fdw`L&yg58e;@#W0!97S_?ge6l#!FwraZV^YHN{WD3xc{C2louewiEpB_ zZDt>6@Y&?$X<=ke6dq-tK<6S4R&?g^xekD0DO^FX#SEo)RKfkMedcn&J7H8jU1QG{ z@BPYQ)i|45OCQtU9l$9=0cuXk5P+J~SpODhQcVryfWw~SrR<2Rf7w=#R=9JL1IpsAwq0O20){7@V7Lgt?7~zNNI(L(s12M)_8h}JaUZdWY!VWdLx=^Rd*O-?QyV(iF$^rN& z;Spipr7aku9uf8l*Jk&PWuVO?Wm8u)+%np9!bM=&m6cwbj(z>hKGU1QoUT zBdMBY`mNKSS^5KpoYla;CHz}zQe!Moi)~+@06Gj;nBEdua7FH--wQ>%kLUf&cFSt) zqYUlIR{Eyk>SPC@JYovT$BZa(tcvg-K|^XeqN{l7p)DFtyWL+EW8Q3K4lcY=HQD_( zJomOJ8piIe@2DrzOho9J1a31Su6!Uk!DBP?%ge`=myMN_@3gTov;*C z^95Ec+Sbx`e?dSx<*mUi^WT(~&{;g?bJ>1g1TXo1ugmuTH~Z3Z25X*r&F+N3Uj?R` z;Pc=f5x|;YQ`?6~qW)d+8C(EQ<<>BQy;tUxP@?5b#+91vNczjvffBTSpA6nydyY+Y zs9j zjmrgnr=Kz})!9}P1+?0Auqy~BDf;gUhG&W{4EgOAF-@!yh{HZo@GDf|r~x@?-MCa8 z0}(;z>lHTlu{;=QXw`sZb=n;B(^JV5Ok3c$Po6`b921+xCeq610)6UeUMHF8OdU06 zNqaOKp37O5oViM^7M^md^RrI3k$=2uAzl+)V?kcQ6Tc5zHqrWG#^=G-Xz<2AArv{$ z?QJLKPqfA?XA3$MO+#CWAU>BLOdHE@2}uCXsdQk|CP=?Z%pKzex2{qDbiMv( z*lXT)exm-pc>3CqSAHCeBXd3kRff(Dsv!ATopr^|^DdS@ykcR=#`w-& zWf^5$B)CeD#Tt;oU_QZk>PrMSPvVMJ(A(Z9>>6po_s}dNx&~PoJ|x`tgK?x#n8YZzE#U;5Ua$@I`)kfOy{9`|ZKmz;l; zwN19T6SX~omwG`p>wQ}@akOpfsUjkdvb14ngPK}H7yMwc1bp-_G0eQb<1;qJ7TTY~LMpul)NfLFeE$Z*ULHHYcxh z=pxeNwS00Q#2Q*41f6Nd9L!xBfi9iYnvN!9n2B?_rPtzPzJLKcsi6D^H>IM0~m2{xE)B!*TJpE^6uxWS_`!201iMq6F{}p zIG`1&oio%+{cGJ>|Hb!DVo*fdPm3_J4-km){h*aKMVE;e;&D02_eX z^+!CG!$Ub|_qnskc|Srk=A9QT4UnwUih{kCHwVr=C1^V&2ZA_0rxhfh41YL&$bLDE zPb{d0oPUeag%q4sDLOxv7!i0B22hJN_6g0Fi#024e$lkJ~YV2u~7bV9%Li-b79ogXSJ>#g<})iaN0dztPe5 zHD1g6gHrY*`PKmQhdtFLr+D~dMzgV$jV7C4xfYVk35%1D(ZRm-(jIi&KCKR#zddgu zh6P_N;NI>Q>qpwVx=G^=XCScRX9WGZhtgTvp!19 zys?fyA47guPutsjRBTxEwkD)vgY9POb`7?3I9W3LvbR{vfb$DnJN&@;eTDIEWBUre zz_A3?P}N!5ZaJw*fRlC-O!FWt4vfGhmM?G&gJTWO>~636V(zaqPEas)U_hG18qUfZ zlYA8P9@T5;kf`ZgHzLl6-)MV#m>m_2OrKG8MZvMV2CnNuBn$j^EFo0UpjlS*7msGffd z_;-0K(U3|srV^Y}64158I%&$r|B+fdOZH%7Ss$m?&8U3tx$B6jM5{q_NZXkmmv;pR z$-vca8x1#z{JjAs+h86(*pj8u#XaQjvO>txW2GX09PEZ3^Usj{jh`dn+8r1BI#NnV z5p=^H7xuXKj!SPiXB3>PnsqD6JbQ?3YENou`Lm;dd#I_?Xe!p50xsD4S$Axg9rw;p zwQbZaRjoi`nV-61x#Td5WPZqgJ5=!KZtq@QJ@&A~`<*+7Dx1WA+>4%xF3oFv4}OC8 zhh+Vt(`TKD&UP#41u+`1WVbG0+XFrKrXB?Rx*k;6NuUQWz0s!!7pmyPf^_F+WRVfqJw)|xr66o&v9i+2{+U)( zR;_%WU)Rb3nO1(%w-w3vtk$n{2|2J&y2xufgE(tU+-sVC6*g5tnoMgWz^;M5nPC&T z$5&l?k?m-lI%>GX(p3HxRm=#h&=FlT9f{g|HQ<;|g`GvN{n=c(`HI5d2n)YWE?b!P zH^GA0ehN($pB}N-@pS2cAKEVI-M^g9l@K3>HOFguiY&VhYt^X^Q||nlYITaG+W#C! zwe1($YUkK$&vDgqHB?X5>{{(bVYTOHss*W;j#K%JUbDD^A3#=bE853Zw7_dR^D8Pk zIIQUCY(?X9A|PGzU?O5pN$X#@PLd0N&`PAqS)Qlb7uedR*JbqQc}*j~qV~s6aijl` zK3Jgo$XxAzMd%&8t6wCZYBx}n8qc#eUg&C^aGKy7uNW|*VOnMSK}sE<1Aa_ z-l{Rt;5A8}xod?7g%zU2(|8R9-SnpxZ&8?S5aUoD6T<-$5(XJ`Ou!Hs7xjyGkZukt z$qX%3)6rhjU|akHe}T&7UK8Ybw}ckpS-gDD-%m__Dsw!yQ0XzH<(R1P{H*04S9qYy6^)E&vW4hg@53}afQ!t;o13wr@3&o!rEr2r%>VVy72SL zU*^I~6+X~~?^K?>TzG-P16$fW`!ra z@Z}0ux$qW+zwN>gD}1mEj~YN&zWJ#C1I3Gc6P~SjxhYit75>D93*k08-*e&T75|D0 zFHv}{3!kj;Di>~6{s&$7T7{+lq0Tym^;R0;;R@gA!kbj~Y8QT9@jr9nTNVB>VeQ!S zhOvS3jCOkbcVJRGXWD2p0KyS<)+pwG@REhWAqS=z8DL*MkbzRBs0^GcX1!cWcP!qShC zO3n54dozB!FOvA(F!64exWHDct&OybFfDLtm)kTEWYP``(|)Y9L__^v@t~%oSNsec z-)M_i9t8gsVUck*>6-d|D6)YU$#1ZSJ~ONDCec_7 zgafmuz{uF|*6-=SG7nF-903KA52IP};VDbKmTi(Af;d2<+3et~C|P1{xdiRUwmW^8 z&hiMt;9TaR6Wc`}wxsf&sX42VO9$nP2*r$&he(5NC7@VGqg@pDXbKbM|$jK`%9D3^qIo!S5PQg@U zGB3&4Q4(EK^{n%6=Cxyb7KYVJ6p&6`*o&OrfT))?^zx_(sjE&Bwc@r;?d>> zw)GJ|q;;9r{B93(zLrMvyvRmtxBx#%5jsyW=Hc~_MR z*JM(wNtk7Ra&&K8+u}7z5^Hg7{D)Tv*PKM|TSXQDek}wDiST=$f&fPi6Rsgv9D3)y zU{rd<9r{5PCR|I8S(Pi+5MyPq=_ATo0!$j79TCh$Z9g1=7D$}ip5q-_&!%Y0$Ic(x z5tf1tPbuFS*s3k28VyVt)&CT;UH== zSfwZ2K_5T$8|&a&Cgp>?6}idQ|NK$DRlLSO(!=Ps#|jg*UQraM&$mc=L`P$5 z&)wed$e8__3Bv=viu!@YI+UgsU!-g+CJeu3@f<=lo9rkIdG?!8wBj$;wj1DK$NI${ zCB654`cstrykC8Ou%_!9c2Dlw%f9K|HN>)JT?LkdsdmRpUNXg0y$Sy>`!JTSyc$La zEo`RG(sH%h--k;Z_>IbP%Dd`s1pUg>@#C^gq4ZSWj6COkIZEOsWPKIdFCIj?M(M#~ z^)epF_r_ZD!T0SYMnBkL-Xw^7z@`+KprmK{Rwb3m9h9ACV<+QadQx5D8!^^2p_z#y zoZ3veAX+)OpC#e-{V+4&L{dZUuKl8z16NIodrimina$`m9mZ$y4^5xf^4>mkM@P1` z7uFvZ_6*adhRTBa;&5KL3bwyF=5Q$(yv7e<7~;VS4FKw3C-n<1J4ev6tGi!rh6(aAsUQ57y3 zVpnFamP@!YYqzpoZXNnTH$GEYnS+*DzA>I2bLLKVC|0Jlc4C`8y*u0m;{QcE{aw{; zcY#0bVVl}1zVSBUH19HPp{da{)kj+xf0!PBs}))K0O&WX;4CkmO6;B40`Q}V zorczBM!yQF?!1brz)lQldChj+zz=*Db1f}BV+KEu>zQCuNjzOk(nz?5STHFf>N3iz z?SNyBne;cIzeOxRD5?(naj0sIrX%1_fp@jcq1cia9rstXPS-Blw0bI!T0YVJrbGBY zvJ)Ul--}Gk3Y`enPyUoAZ>_kC>xS{PJ-uSAV9iS%=|6gNKZ_Bsf(qG|x$0WPWmKOx zRWVgPu`ZT0MP>?*rhq$5evD?St$zc-@)?XOD2k zM@++AgEKQ;d{<`1e-206d&c{b(u)hu?E9fTg1`o)u2IeQq;S7OF3T4`>t=k*hcs11 zQ+;Oq^bNbu_~KXGjH}knjI*UxXY|8f(!&t9XYM;g=2&yTm>q3UhkXTEd4kizA!#11^E zhqhP3d#j6wnR7<>odMknaWk-Ke$Nc-H_*;N+Aah)0~J+6#-izTh?AzKF?(HDekRn#K!ZHXQb zZX968HAN?`5Q%U3fN{N9m;vEQzt4d1%g_6Qa6Bnr4?+<+>$|BMP+04 zjFHx+^Io#E`6INxL%oMu^r%to>ySUU^_^H(%kk@2UZ&{B9i{xa+&GxP!+jL$-&Z%b|XzFYS-JY-49xM;QZv*i%4O_0?MUQD$x4x#&T6Mvak*lWWEQi4A z_u4l1Tfleb5GQVsdyp5EmYnKL;08Zn*(qm91|PMoBpPgRbTW9~tKKNHvf{EfuW>QW zl(p5*N;mg6zUh*SAXli*NHzJD?*oRo3G1rsLhku zv~5BOgr->cwCb^Phd+G^-AGPBHN&^7dSZX%cFs zqW9xQCDr+<+h;TUZ8atR65j{E1q*BLXSYYrF2Q17QPPnIi)Yy&w^6W5PQJNwFjwWL z&XzEjZ1$olVd`BxGb|D@(_Y}7kUK4Lkx+iWI2!kX?kXn;eitL(tp5z5jXC;nc3QPX z>+D1KH}YQe{6*Gyhi}eL*qr@?vp6<1$n@f+bEE)UpzcS^>H)GvtDhXl8j+}iV?G0u zrF&(=&}ir;4=WT*Gy6&(RsM|TfapXvtH@opUDCnqgwpqXT*7Kz99K zZ6`U7g^w@9W}9EzTf`fDA-n#I-~mN1f!S-MX0(-f*N=?`);KIYttFqxZ8Q5;;Q#0L zB>*qEsi>lj6Q`sqS|_(s4Wf)WoF_O8aKW;Hh>#VK5MaZhy$@y>Czc7dVYaohpr_fp zo#`<(b9#gVi~3o*`Z4F@3|&3$J&0W@4`OG90$;8$Ux}6OnvBbn|9VW8S6QMKD{%{?{i9_|DHM|Ff9cr~!`P@Cj z%QPHjCEl*)=$N!uueWPk&&msAfu=Px+w{{_CDzLv38S8oRx)odBz zdvYzV|AmuP%W}sYb=XO3pc{Kyyo5^tA)ss>CIQ~}!f^ze+AZoX zSQ3gS28)+?JUj^Yuznm-a}>T`gEjbckEmw=cDNAldgbw6cOU1VnKck3~1Yk0OTv+{AOP44LC z!f5L11p=?DEAX@=pbzE#Lpd$`uP22?whEco26H}}Or}S{lXhd=sEu(ryu~kV9X*}; z%Jy%%HykThl|HWyq9uNcI|*Gbw#wKIu(T>X(cAPsb93Bl}~L!ateYA zkN~$uthGs$ECzY&$J_3LPJe$B9Ar#>dS-WY<^ISs)onk0Qm*;c``qP8A-)iLHCUu|uqj$~EXhHC+q|IciuaeHQkAU9QcTA?gykti$4^GYY zQs62N^$$gvTpP^`56Q(&hiE{0!~(SrX<5F5R~W&C?B&;Ne_#A#2oG2PfL`^d7YRPu zKDqwf)i?k4+MfQT7tMh}XY;dPlz?ZZK2w4_#K?PN<>nx)9a-;4Ae`fl-`y?YFK*my z4>K%ApU!6cduXP%locZjCVSz_gml4UPqz(8f|%%UE5e&x*rVWvrQm%kb54-C+5OB9cVc2KnW0hf9zsahe|eP-l-UB+f%kceXN zeiNdZtzPwp`uu3J1M)e1a4%+=Qx4a-By+4y^!*8ZiWYw+y;C1NUgm`Ad(E_Jb zX7!`du(-n~3@bDZD-<+xravA#8A6$ShdOIVb3a%r^^dCN>{G5GMVzXT1^B&Hes@0i zn%cWwbMv10Yv)_zV=Al@L_5e^{$K~0VgLTE>%&3k_H{=+x-2&l^V5G-KdaF#CpYEk zaCfNf@dov{%zySH$0TpZ=juANnSSszS~g`X4gIDf=U-!D*smzYBG=A z*=Ix>;a&&LUC7^obA3)qV>;nxoIKXug*cfA;)lNCPEcz__gQ}!svkggRZDQy_4+7~cMA-mZ5QG$*^vM@BzZuC{V|k0Hj} z$vCUAFHy=by+of`_VQRbMltRk*ax18|Q?wI0}d~_-};GSB9 zNA@X2!K7mRczH!Ns9LNahoGj2Cq=1+HynpwWBobc!W}p7NvT_+JfW0IO67um@g&mc z5Hy=RTtiOiBi8#yWi}&VzNH|zPr$P*`HkwIxj2l!#l~CSA6oO>f9NvaDzkQgI5^cZ zqBM+pl?ME*mxGMMf41E4k~vdE*zO`u38`5tF-`AE99@4DwQ)DX4o__0ho`Gs_4{V8 zpp4(7V}#J}pLU~h>|u5#S*1Ix%RY)|U;e}2P_xdj=G@;|1hY;xKE|2H0*2>EA(vYl z_;3m^gsM3W4+$pV)1R!!C~*>qb`ht}ip}aQ3c{g_bp}lrT)!bazl%!|_E{8-HW%B{ z$cRMFzC~SPftwY%=rMKXdz|Ad_s~hKRguBx_%g%eAijN(!*a^#AQqQ;ac}bc!dMv( zH=&}LM0@XXX6@<3;jR;d!cO48Bi4(%w9@8UIWcTy zprq0kcwf!?>8s)OmJA*N4!Oo;f9W;R~6-CI0P zt1dK((DNkYfALv&H}@HE#};-tdQKkx(kpwMOtsiSVO6G^33NcI*JW(`Gf`0mHQ|Yw4H5}ane1R2muNR~ zAe01+W*H(`yV*cQ{nRCe=9gpCNjo+@P?gG#^6JqXXu9HN)OBu+>^Sz=oPP&p}^3z1ThpbJY^?wCG33*Oz@P??kXNIakIisKbj4>nRynESX< z*Jn6SiHR&e=@h-Iqo*uIEzGjpEFi7jg&wXHVY-HdW}Ww=4@-042TxXe)tlT)xAEZL zrh2a)nVi=&4D}UqbMT43{HO~)5z85XIDp2eCY~-BMmAyHChCjf{T`7>E$4XvR7bwIQ<9gs=e6Tr zO{aS2>q#iBCoA>S-ke!Zx@|{6_MXfm)Y^3xZt3donrU`yw%xk$ExHvdc>cbgZsn+3 znI_T&8%b9$$A-P&bsC;rb@hb}*3$ybl$b-PS>V}0-}Ox25(|?M`haVAoPsjG3~nmT z;m6zrXfl#WsOB(p|3kY=9^d$fuCdC&0e|MGFAE}L%dp=2lIpF^$g1wg8>)7(8JQgl zWvqFUlQfz!yrdCsM!MP`J@f4F)osmXRd|Lh|8>unIncbt1s!_Eg?nBI<51hVH7XPc zBv3Mk&|4e3%kkwY$D(e3Ii6ZnPR5#jpI|K<5Va?dNcG^3>XbcVWs8OURgb^Tdw*p+ zfpMRox}lX#$4~vwI{gG&*~ORJ(7(+g+`f=HKt5P7FM9W%pq?`)axozvE9RTNpWXSd zUxf}FZF`|iC*qpYew?}iK87m723|JL?^FZRW{WGi)z*H!V`AWJGpCo%-)3sHq|nhr zCaEy2f7Acip=`4!6SP!vpIko_@{+9qkKf%0VK5%K>3Hk@_B)t_dBZ09uz}OW2A(i8 zm|W~!lr%8ssfn}P!M#@v=@Wc2iPFw1*FrXgU9R7S|o=70VsE}j$ijWVY+FJG4@_f*tT z@~*9eqtamW0Ou7l;o$LQ?y`O@FPYrcfKw;^qLXA(HKTa}LpFnNwUg>V#q+{ZPE_dp z;=x;BD4Ng2zP?k6T_CT(e(H4DqAp<3oe3A1Y~2ZOcPTGr4KydJM^%ewGZbX-Wf@hU zneqa|39E><=a)hZg;Tw1LB$qk5KAc@LzKOlUmjw^pV+pB2EypU`-9V=IfC|(rNrv2 zJI8ChKs0C_CpLO;je5}EyXr*Z!bN1koxDrC;g=}wl*9b)MmzCV*<+PKqP(-rnjw94 z>bg~{ttUM?cB3-hdUH1bY<8(j2dfzs60!3SDw5ZcVUwVqB4x%Zvg(6?{4~iRIAvt&^ z!pXreUfD|y9@V3z8BuunK6kj)OB~)>Gxo^DM_IFJYpk~=>vaJ)Oy-}QFJlj9WWmr%d0HZTy$=?jOAhp#7)Ar&zR&(YROu!n3sNm9`-IR zQU>`(IF5MgJ`KluZw%4Q+!x~1q2yB~mw@{k;qV?UZWol~8FLB=+cQQtygv~(dK_1O zveEfo)9qO8NaOWETDLSZf~ zwalE(dkAxOi*)knL(xO{Wt%XVJhoq=1atiT{O(7w$cMu>FW)gZr{W2=z?<3vdHWpY z9+@5GSQ=OU-5WwCrqznH5&B$X@#;7x)0VwCQ#Ry(GV;ERd}$6CV3xXCoO>nrp1s6m zUh;ZUw_PRb49;I=k>%&Azv7jqRw;mUJpU8(JEmLj`~(}K*|oSZ)J^w~rS7{`0D79u zFmz}t$LMA2{Hm9wml{$Wb>#n}Sn4{F|K!eo2@GJLF+X|(ng&8!G&5J+pO@2l1WO1H zJbS1kY*n+&gKRuKz7#MJ7?jx*#vEPOqa>a4mZj8hzSy%VjBbZgFL)bDZ4Cn6zrt<` z@98IN5HOuQI=9MldkjK_%%+er2v9#l%kBEirqJ|`ZK^*-TeyO!P2thiG&Pi_`mo_g zEZ?0CpLu_{DP%_It9iT^RcCFJdi&w|@B05&pipUSQn(B7RrcGV{^xp&Kh?n^9Tt#A zKWnzHTb5ftGPtJ^f^7gOIPYN}~}LIuv&0FZk;6iLWUCg}9WS@{#bZhf-Gq zJdN{T)7x{6*L1gVtmREm@iszkq33#yO~L?2LLGYSoFxjfcnJtJ_na_AC{k|EYNnfH zhh{tfSDT`PD^z!Hn<6NVJ@DP$K=qcV4m*;_z5tD!9d?@&K-NbO*6K5HAd|m3I@2oA z8ZJ9MCy|HxO8(=iFM-UDn`Wgx>Zj}*f5N=J{9m?$G@?Tkxxyhmg(i~^jKC+ zR7i0@obQl+g#K2Fu@ovHmpMOe5r`)#>L3Ce+#qws$t<7dgURQj2vVQ2cbG$P?r<3UWbc}ZX+THTj}dlPpO3j z0HR$BR>8NPE4;0|_)|H(Byp)o|1 zU(&WUGgMV(NxcowouW(a{HLZDxe|Rc#F1LZ(b(%*4W61P<0X3<>p)c7T2t`~p=hwi zyQNKPz=8BNk-vIEaqbD}e6)somtarf%Xi|ziO|kAj3AKA8cx?_5QM!%v@~i+B;Jcu?^Aq z4BnxlJXAz`uBZh=wKsX4;?xNFcE;0}GB#>?7FO9FaLDm&EU!H-1w+@6nD>__GF8_! zQE-hleaiS0Iv)1s(!zK;UlftC@Z3n$`^BmnoJPTMZ$r6S9;@08c>I_*VY^?o#rGz0 z-EQ(I3$)OuG(gY*&^9HfJHS(M)IOi)85st<^jxl|1iP z9Q-H`w8Xu?bi~?BKiSm4+I|E;+|GAyC22cI{N`nJS8%_>zG|uL#`5d(~C9K z#=!!fKAj)q>6LyB3n9Np!~xx52yX1#w`JzE{D(V{uVPfsWe&u{S{r#7#xw(7~b1w72wwMQ)j z8HheUR)(~fLw2iuxhC0_n}BWS*xh$^@qUlJUIm%GRWRRqb+|MNo^Q=s>CN_va`hrQ zlHrnV(K_5aZ;?}REUMTKkP~o;3OH=l(Xw0#W9xA-*;&gPqO)G3^@PY>tRYU%euY`dM`q#={3sQ)6h!G9_?U(j_xp>AH(@$)zcZUpu4 zru}-XY=d6~7Ej{!p;zk5!v(qBFZ;J&#of9IUrb34`4-9&JwdwV7Dk$TWl=m2ld3oT zypOv6po13UVH7ZH_4~!kK8xpVj_3VL2X=Ni;#qfI)9IY;w1gH&w%b4-bvCnu!qilQ ztfe$F2_XFRR49;jU@R|G-5s&K$Muc>`7WJ-6W`PW*EVm7eaDzGyi;7{w^NM-_gzC} zfm>qNcP%d?@-bV6WQ%`J@0~8rt)(nK5Jc-xq4($gO!izRTk_Bap**x?aX7mh{k#tI z@%!*H=@Z79P6FE|)M==S|Jz0_0&g5*Ul?g@eV@F%SM-u%ff^uV1TtS%vr zp9t)ax3Pnj@Ge(O54dft33k>n@yT(ec(>8zh$w6|TSif>{lk9M-OAdYyM^yW6(R4c z&5XlB2`~5rmvQ&#WetrTF`D#C`o&Tax7F6B=J0&_(WRY_kX#!~DXuM>QWQ(J<<=*9 z@G&M6wBTp39&7xnwcgb_%J@|$6nj^Rd5i}W?L&Fhme9twZ}H1MkC#2_=j|1%+QIJ8 zuH}3f1clvdqzi3jdO2~S>;kqVLWwK5%YyV+8E}3 zRTGO6{iF7j6x3iO_hoeOYMu!QQD$6eM#Eep0y9bkrrh$GPHeuaxsYy}{*4wh1X_3| z`$pFK#C{e^^?NMq$3u&NR%~@49HRZvJTZ0SN@KxKAu%Tw$ID*g^}gKt6M4$AXli=M zfOyrKOJ~*sin%B9%#~cv)*ln@Pe{I3>7A^yHnt|#Yljl|5ubxQ+F04TSkF+@7n%n(Biv3MWqL)JBfqm=zx7f11xI?+U(LD0GDGxlqGT^6ky z;FN_fvsum>V}9(r%zmnz7X&#i%>hs56xsJk*%&5jQqv<&rBX+E&B1F87Mhc3z~|M~ z^oXBXsR471VqgGic4cct^fZ+|p~Rf|uaL^0q<~xf2(z-8-1)k{8wqx}br_rF$-%5Ee)EBaA#7y72`xS(+*lfsQiL1JPv|ur zX2;OpVfTydE_0TI8RgIHmSI!V5<*oSOlDX5z7s;+T#^ zp&G!g$Pm;KH5X=S+St38i9fdf6|q&pCavdKA5tLZa>d;SjhCuThLMU9ZS=+-;215z zTaRkNZ_tnx0uA6L_RG6-CWbwYJY|qciG(3W$3DiJOQmextuvp z5oxTpgs5hd0)QaSdh)&`16zj9Z9csJQ z20svC>It4qWQ!?=pX9iNT1n*`4JqNx6Pr_*GK}{q9SAQLEmw^}BP{QUDEM^}&(o9JnuxoTMJXXRI8oNpdE`!TcuguM;(qYLg%rVauM-5<02V7C7}Z~pL-*+uwWI%}lY8a+uC z#@~BkQb@eA*f$v0WzOyaB?~qSEQ*GkX0Vqh*x#P;c>D=GhqfW$9~-5YUeLY2B!{ql zkWY?1+otqKci3&iXocC}doXe$DO!61D79LQ-P1W9VOjv@dAKsVm@>c4#O6uFz6Of9 zQJ zX#p+_UY}W7gw-lG(&~t-^%eQCq=^2|jOfXMtri6-Nc1jug8*|;|;!d81fC&(q>I_`|P*dkJ9%_Jt^fAJC&bc5N=5KM} za`OhmyOY9Y^Iz1TWJ87rR%5u=*N+$f~!8?U;PnYD0VR2Y(;<+zU7ZBF#=}*8lFmxDmkKW z&7EW(Xfv~aFcT+&?~vc9PLV7>&YtMC)^opUE;Vai-?wJsdT(NzH}SBU{h09c|B45+TYu&BFZ!^M9_9)IL zoB3Oe#eYJX=z57q^aHfE6k@c_be2q|Try1usYGjnO%6jE;t|X8|JJ%`-`w9{H#PAa zMh`{-VR8}HY4Jo}#Jr&|O)3D3La=*rX%FR61u#`Be9|u585WVnkUEe^4oPSZTft^R z-}Cp@Bd2m5%R(Yt4Rsy%lWf>g@|98b3HT|Z{0(`V%HKvcVvSb*vLKX=Y8rbvM9Mwn z6#jFOFxaA-lP>a4JY6XECNVviZk4sj#TVqfH8Uq_-$3Y>^djqW>WjJ17mdYk0EfiQLy@_8$)knk&Mjo-TQ5kTytbt}m?0BS~X7(mC zabv`+_puC)h*?im)b+4&(-kMyvUh=K=G#>N0H~dKz?--aig}a*F!Z%PL+f5fEvg zOb6n)fqoE+JM488pn&vfR0qz&bYHd_ljZPwneM41#AMX^hply#%o09AoV|GVFg1^z za9;3rf%}3Vla8lM{r0UK+`%C!9$cxeEANFn6uJfF&1@eDsYvAk-0Fn&^U@$u#yhYp>Juk4RksSduiBa3e+L@W?XWH7t96uo8d0C zhMJR^?@q=Y+PLc-RF%Cm?1xhzKF|KC zeGI?RD>p-#TTK^N^(aS|sB%ViH9oqQtl&1bwS>R1YR|PEp+vIQ)bBn{u9n7`wE97t zgK{+u&zJSo+ui=83+h@ER>m(0u>UMefYjsc)1Z3TY*V(h9N5xUA0N$?K=3$v)G)jh z%m@?=mC_%m!s!V&Mez8w`Mpz?CvS{N2|} zS^_u=T5Km(+Hxuyn`4INWtmtPgEP<@$3!ZqR$L*9RUg{ibl zAN;{etOws9RiN~y4ZyQH9@P%ofITx$XhgCnc)80H#Ew8P4zKBWEy0h8pJM!gk-eEn z7?GjBXTiCp0_Y$Wn{FeZ9qH|DPw*P(Q<$eb!3AZU$541qIcZ^f8b+{zaqi$f zj=l4(8OY&>y|>v7bOX}I)cvDQ37Fiiv1M+nv3)aWpTEPnUwZ?QD@f`R2_H^ zYIqf4atjVIe(WCz^|B06bV}6@xRjBB*2_3a6eiM4257O&1{5p893@@+DeJ5F(E0do zR32bb=K54?8vUKte2iG7#;W^SR6>@v1P*nbI}6>i?faDoR#v@iWv?*^R#w-(8msON8BvE;g!v(VGt8U zZWwftQFx9A)e+YcKj|H4*41qHn5{|pUEC?%@|4s6F<3ziplA2e?&_Q<==%TW|q|ZFr1*3_^7AQLNpy=$6C^};%2_>7=ZOEkgP)`BW zc7<{>C4ev>sGTxnSu*f>xc8X2C-C!E{P6up{N<0sQ+huAs8kxM$%dH`Zj8szaxPZ1 z?j~w&^-2Cy>J`F?*75F0(b0MbHY5e*s|PVkXvWpJ#n4m3pQGUySa*}h_2xgKeeOAa z*6nh~W-21F<52&>u5+isnGWLWv~MV`mR98UrMQBG(nz38%kc$4f#MBb*pG8(jG{oQwQ0Q(Tp6T7{)%eQN@hObn{=W$rDiaA1SX zHn_P!G~xUmh1S2}oq{b>9VM;emqh<)SYgW4*WeYDytLw7=1laT&D8aa_>!bP$7x(k zE=2PNXOu;lNK(x9BIp56vMXCJG(+!7pLVN&Immaq2A074&{0KDSCMLU1BC9vhTT{i zhe=?=s6$IuxcpOKL|KvH^A_YjNBQdOu_D!XnHjo=V2xD|6QAm>J{(TkS#c=I*QMf6 zS3fj@2^oP#7dA&iMGzX0ACN_mOmwgtmE4+)<08l(#O1nBeB(q>d{v3GHOl` z*o#EHt?V}|sJQ5qqjixg-hM6ubRlVM3biHuY8WMp{O7B22b|}Pvxpo zXlsY)n@Tf?9^VgNFC)XzzHjViYu}DMpW}RABYJHkC2q_4xQj;gnT%KyS}_`urMz0MZ9}U3lCD!#2WY|-aoF;nvm=c9BTKCYVGO{b9qBoh;+A=>|{B= z&~hQm!FJyn$RbNqs=Ht$FTx}MFd0fs1-g8_l!+zZtS8z(nV^kCyetSa{Xzv6SI zZ_$lPEUa(gwuSvpdqR`<#OQnA+T8t!Is|W;RCuXk`W_2cAa{;4{k;!LaUY^fK`_3x zs|1UV59GzyLbJnd83lC!hiF{|J?aq0NEXs|{;qgWZS5un+^KPg<4?}a=DGvE#bI@tBH^*+vXunc=?G)o(tYZ@E23?tp9>DKGYuIaR zf^bkkuR%Q-o?%apq$|jF?hMotaPGF$@*H<1FGCOt^~JRg2DN{*F{sWg!xvg=ci^wJ zhQ`4y7~LgoKK|jcn4=v3u&i<+x|;7;^m1Qs@WISdeJ3Tfai9k#;0+o_C$;>)Kui$B`fN*C3ACA=m7lZiF@o zj$3E8lUKnOn}Kd&{u(!qyBU`sA|Lwu;2noU1HZO&B5xUjIwgPta(aW$W-gJx5VH8o zbtv?!O^DCp4IfV-x1 zx~<-ab#rSbW&|_4QM^Joo0l+c_A&O^1!!ND^-O_of!Ma@fj#i|@N7}=h=I4_NuHbP z;@Z>ot$dc)W6u_g(`VDcGYEKwtoF_Z*Z+Dc2J2`B$#VBJo3Fm7ec|ypsTMaPB%P`ROrCRE zjikVWbX1Cy7Z!?W697%TGnR23wPZ3T2x~%y*l4!GM9>a_dGNQn7#^2rwQq&pRZ}`2 zvCBsMqE;_OKVT=*j`b=2u})A=cwC9wrrNgo2dUKG*FLJbQO(3ko_P2yZ3?DFNsGQm z@VqtCWsfC$^`@86&U)$DYowKddAw$4ryr&)>Y-RU2kMj6MVr{t(W&YW9^Bmu?#HrY zxb5*IxuWvQ*0(zuQATL!fJ~zUygz9_KcvRI0b*jnjUdc?zy#%Q$>0)1UC%1%dV$Yp zxHaaXlxw)Lj}yY{5#xm@aGeh|WOTB6aN`g57Cori(U2mATlZ=lW0zSgueN)ZJ|Jq% z>mc8JHvgc1Y+!zR9M64>PHq^U&f%@T6dE+EU$wst)adzMYo*!4>Z@%VMBAdU5)>_lRHI25GBp91Py zzDyll2@Q0!A=o=G z*?NG4EL!p?M7Mt)U%ws47cecl)J%lT=BXnPu#D@Ta&TZtlu~y8K>|DR z;r35)`cIATM$70>VhMZopY@7#*^5|_cH&P>?^RAoP46>q+UG>Qv-O{j-x;ai?hl+# zDT_?5NYnXebj-gGWQyjW8Jpj7`wnc5PTw8u=NS$Z`fI)P49vSU#kgxB=Hk@rFGr2M zJhbYyx_N{6JqG@kX1X;oHPo{YXHnA^*ppEIMdJCUk3Rg^ZsATHHpY$WwS@Eb9kBCp z?aEJjgN`@1`CxCgwh!8%Zb5iXe8>JUzTbxu;#HIOtVq>H_3xWG0He$n|Zj8yVPh=vr0#>LfCw<8N18ZA9h}*HmL-i%Ka$+S24}88O8qN z$m4z*SKz2Tuj(3wm0M1j7tzI8#BN^>D4}*wvd4osXyNpa?m(zsqIJ=FG(}9uL}$>4 zvNJylaa=Rv5pV_Z|Ksi|P~I1%(S^&==(mNuDW~9BjRTRO8-}~aC)~AB4r*^bIXX1pH4Dhlsd;kMQDc;KD4H?{JaGAj22nmxs9NSASD7^ZN0`tSn z?n;G0xLn9JJQ;Hfm@6_fpp#+W(>{K6#CX${HpX3^T^d;v(&S(b^I?}C$EB#H%h4^$ z%oR9OON%V#s8lobLIpB11LmEBX|u}P3K5WT7F%qFiuap|aFu%_lN+#NKVn86PHLxnSDd^!+c*&iGhC$ajd~u z9AFX@HHSMdZ>NxVm()L_qLjB#evq7h=5Pv<#xFj4tM= zahsuuaBlKu0@H%b+f)uhU4RV49gycs9+)I`j9Z|bb2;waM68F=m>ssOKEh5*#UjMD z{;&z?yUKNSz*1Zu^&r;%`)y-&zXs-&BTij+MDM{(SiRwyhY&smnK^S$o?#jb8lh(R z`)M^9NM)#HZRmx-I+O0U{6m>Qya#F}?r9CwU5hjNR4ZP645P}sTy)6jenQ9{d1iTA z|FiMoVIowjwaV`oa2*Gm42)xD`)=lr%-0#HLghH6sP7F&ZL4Wk))6+<+GiYnPy6Rb zl%H-`??CFo=$bZ-h$5P-^w@AN4fT0L9+{wTHP%yh%vrVV8*(7N#|{?YV^vlF2$-q{ z*Wo>cm~n^2vLmS^5aT4=k^cHg#@?Z&F=gv_(Y z#92$Lc4v=)U(S2&d*B673xz*!ZEZjK&48zCYtNM5+4B2r`F)Q3K39HI6J1+-mi(SA zzbD9VD)8E-x>h@{1W?N&{4B-K{rK62pGWcY4t}1*&$IY>5kD*OvkE_r`1u$=Yw)wl zevV6Z{>3{FsR;=Q_H$w+EvZY_Zpp`V@6k0WDalEUJfwC`>xnGUuPKoND4dkki(d?f ze`A~^BCe9l&{B2HgZT{t55IE@GPln0TCeV> zz6}$}c>gyL6$|mn&refeto2Sms7}7^8@MUpakomuqd9$Z;MYFB0fApn@f|-p)bmap z$vTyeABl})LOm~{sVeckR%1*k=`#4jjt(V3Qui!2^Y2xtX3j|WJ3RgNZZUo#*3nrH zpAh#SjPQq8mFMDl&@MIRDMUv?R9|Zw?IzbQZ^brXliWT^mFE0+zQc6dI&;u2RGq`? z`#4-Zi8xjKcxDA23$S>Fuq=m;SVldgH0sC3!>Pq?++a$P3+rH*^r4h>(cvwZLJC3< z-s?C7!Cx8bO5B2hB^d9Hq$dr@3r`vtbZ)_^>Dew`KNx#X8C@g)ob|&tuiX>(vF%vk zVf}ZGt-)8@wsI*UYAe_sm< zGz9p}e|i-Gi4Fw-bF{N`iLox}^+eR4S)r6#J<5^f_XZ+L(Pn%x?d~kH_Q)0tdi8sw zW)HtadzCtl?$!8u_Ub|OYA^S@>@fE`?9f)9>Xy!)TIBQ;KC=_t@350v$I~+X=1ZFT z_(a0G-yz{!FGk3c7>X`WiBdE}g2>8O)hOxy(e8J3fKCzax031%O_N5NtWJ~w_u(iZ09%B{Qwr8h}4#iXgWVFhsd;c)YQ#0 zl%F53;m4Ku$aCV=Tk+tv?kP_u`e{pZ5hn;*QdUvOU0j5HMYVM#6cDBusd2`cP!5e$ zGu1c~4qZ^UTw2pAQ=g1b5Sh4J_9N8DoY23@C1-DaB*V#EEr*0u&z|hl1p~PZ!nvhp zl?{R*p%2)~L%G|memRCI3olf4j{)ghfHX+m6LbNU{mVd}Ml=TGaZ7ww5g6>v`&WI} zZhcAsWVr~RA|Ki4)r}5RS z{FR#X9G5y1XaS>9KqV|k#X>2c-3zqN*J#-i>>HK=GQYmb&X3W%OzU3Zs{6%Yu^>5r ztx+9h^;;elxIX|Ej-&`vEh5~Vs4vP5T0S(ZKX-2J`{}9istVLO{!o z3h1iNfCN{^LeW8(V1{w#7vBh*ROokK@tUW|-VZ}&lRgGg;OveWFM_2CZt z#bqQ#e+PP(uNOp=%@R0NVqc=rQtPogabt@Z^CxzxzL#KRUMt|jK2ZTXkW zCG29gSac$HmYRwWu<-LGM@{QXl$Dw&e|LEE5J4j^kY0= z-B?)jjd9NiJkMC>ZeQ;X9LBL}-pgRD;@Tc#rWi8DZ5>uQv@mc!0Cykek_v+Ty<~Qi zQ9TC*a4&!i1g8IBr*o8g5hae}5+Pke#TbsRWc7jlS;AF>9_G%1TQ;azc9s3{RHP#O z-_Q;pAG1I5>Tu3gbP8ml`?_jGG^dW^!mrMRE_SA!p9g6ph-{1?GRpo;cX_v{$Tq}{ z>OA`sk3_gb3sW^fcm8h+ug2I`8B(F|Y)@bv?qF?4y0P>f?C6B4XOnEX>bDVF+hHup z1gdc@3{s=seh5OqgC`8RX-h>2l;MwB`*16K&^Q`eZx#epeHXUJmsXhlog@@eEWNLu zgAJhdaW$K8h67IPDs>~DFXi)#Y6_pJ%C|nL#^4#%9JDZSPDc_1P7>>yD0NW|JL8Z7 z=akpJ1NUrHJ2AnNo?GNE1g5$%k4y%YPF;;BY-NK>Avd|Iy_GTsJG_kq&pY27CC=XX z$NBzMQ96UG$K&486CFsXOh|y+}1fk91`2ckGW1`eVmo@s$1XMSP@5B7|g#k%>)k7_U>VFFv5?)N4h zM2L*!z;B7h(oM2|*`%I>2BYN?xIRa9E7%HtK{tAXxvf{I#`~qK&IgUdPmqeCl)HlH zs?86?c9k;`{P7jCVG~CU8WX2Rb%g!W@U1on+MS1!ExOgXlZ_ntaRC_2j@9BY;}Q1K z3_`AO*{rxz{rn|)zEPb-jZ~{ME|seBJ`T8s_n-jq{>KvFo!$ZO3|k-n;%`Sxj9Sax zSxcBlOeC|QIe<}P=8`bB*}$G}b2_!Rh%w>byo$wP<~ZsU=9*Yr!nV9|nQlv9HP<}g zfnR(PhjDB=;*NH$7*%Ih$Y%sgLs!A3fup$W?y^DO;1LTq%p*+1streX)W#)Wa}GY< z)=HYyJ7^`f^J0t<+0MlD&m}RpX8dj=(L`H zV%IjQah3Zy86p%{+A(<PF=1^o^_fB436D-F8*(< z^{wN=bhIU?xm@f67NZuz);k6!PTWbR!xx6xE;X?kasF|}cTC0+pWa6xYA|e@%XsP^ zTocp$*vUUb8!qhMvHEzbZY69Ba+b`KZVz%)pR5uQ%#;ygtA^!I;VEG@xz z5$*gw-Tt170&2bfZo(r3&Vb!(vDvR{01J$&0Ztmr;|V;1S#Mw_$aX?6F9tpPJpYXU z1pZYNBN~3!4)9;?dvy3a6Qj*{>`!2Qnv;1mZw4FmbDlJqry|gK9)t|}LAKIhfg^LO z3;#j^IyxOwlT|s1b13-)!w7y28+;d7@sM3z;PKtsKR}P*mx!2klMCiaqxwm_aEoQ; zO6YDVUvm)7B{l5}V8^cWUE7xE@=Zk~Yx_3BY%{~Gy%7Q6_|H7hV%8r@BAM&eWoF`L zh~E$3w0hzPp!5BYKq~&Wm{D>z5TQ_71yX2eOrM8q+-mCRbzu}7t^wk}69YzEVRGpa zBVzDQed-2&Th`&$oktljyK`gsSt2S{C;C%sp>qIa#O{x6G;UJ(QVMutu2J3nG@99n zRE#6|J~|UA961`*f|vC-8BdNhjU7(T{)ba@$1(cU^DqIyVD1?K`|z_2uKP%1Y01au`MOGWV9V{u%cVB zX-xu??edKV?%2@$lc0^FaDjqcT)`D=v=ul!6lulrY{hY2*uu#V(w0}-d4+|aV!4af zb%q2AK9MghW!6%PtNB5v_184(l`I}_C)UgxX^CJ|33eeYyMfpfK|ca zeCx3>D8vo1~{Qs1H?Dp*6_@B0?^6>v= zdmR2*KFDrGk@Ye+oMV5Pn@^W9&?}N*c`A1{bIGC=28%l2q0_#)@`CgfnHr{4t~0f zXp=68ju1-gGm$iGRLi-_m;S)L)gd$9wItV-o9EAT*Dfc7P}+ujxWw3`&R-~-hZqW6 zfaXkt1&eFs5?Xxd_HZ(+3KKQL{kCfcDb%BR3TYItblF zz4UtYdI(XttaBM5xX^=p8_mF-xyf<ldc=1hlr>4{4#)lJucJq?-KSrlqx%#h1%}ho#3hv0x|@?i}&wA|KdDeS{p2(a%jR`d$Fis zfTqSWCvI0D#Y1TyWCQP$fj8XDnI)7~FNHR#GntPZ!6B*FV-qx1?%HE?)`yXG^-2Qb z(oBJ|MH$1d9=(ITtu^*w#yiFi!}0U-c)06Mwh}Np#W(*JoC0?-?K(45ML&GxL2#C; z!Sblrg`uik{iY3kRia%6>P|$~a%62xW-0C3Bc+47SWvGTnaTd@U}xil(c#XZp9QKf z1fX0I;!?fraoTV~Ri=E<;BZ!&Z&no<8iBq~gzsLtC%B{nWI&h=l>Tp2FXIQ*kIPJ* zpheuT+0x;1a2vDpR{v1-=v`uhnoN7?8<;d;cRUTxYWV`1pz6YvKtMZhf4k7}h!l~9 zTcIJSOtl?_IMzNFgpooi`~{?uJ||&Rug8ZOf%4a0!Ud97)^c03D zfH?vHBvZC&eFS>8r&@3oSdgak0Zp(*q{XkYdC{NKh!n+ z2s4HHgHr?G4&lct&{czb9i1w0^m-c8i;Q<$zQ@GBJJX(I-02?cpcm#jtr5ABTXA~Y zO`-0)9IL-nE8PrTSTq~VV|@gD!x+FWgFPUj2p&J#b@sclDs`B#s%KpG7DOg$hegx> z4F=%=PkTM0StHu_FUhkgGeAm+zeucrqQniutZ|!?Yqpl-9Bl)3JvwG+Ijy=)i=Zg} zWbMd(CJ$xVt$+*H_ri9(ftb_FNypDQAubw!%Xn*edZE>8MY6A}yRAOickPNKc}nt4 z#Bp+_W%!ElJKl#WO%81N2J+-kHm-{0{?XCf=R%1DA9zK_<%W;wN2=;p(I!sgjl6-i znAqBf&Z_Ltbm?k6W5Ct1^`Det(mv<>#~F5`v|^93^cOS=2dZUKc}$)WR0H(Ag&Pf4 zeL?OqzJ6|us_2eYz3f=gNg2CV-w!CtZ=5r|EfFWnRTiul7%b!45)*j+D19&)p~FyT z-bTdzIHrZP_->IglEX=3+FF)Qt;jnE!>qj?x7qsonn9y;Ch0#kR!T&r^ZV~3BzhKO z$zN9p->$%q@d)QsXcX+M{6Zwd;L1a`WH=;}0XT^Gr(|%KTgkjFqpQ0|V=#HG`@rUc zf%OH!INS=6!1QjO9(A5z!def!qzBd&;VDATs6G=DPxZRfZNaRpLtq;FLJLmUi^w`{ zJWSmeH+&7v_H`S2jz77;I?P-BnmyK|G=+NdRxZb%Ia96SJqH%Xt?20+7HDtxpKe-- zMgJAQw5H|FPsUj?s|W0M-suf$)h)s+*>JH2uOKkpn}j{N$2G4YKwktV0lk0M3 z;#Sj2+Tsa<&kDA{q1`#iZG*MP-<>eZz=%XGaD(r1uXWNmR01aST4}@ZRD}M>mGOek zft&AtB_EE{@*%w_ziHM+-DqHkN<=x;*9UlmhED3xK&`(#oGEo%ylUf z0+6~?Vcu-{QOgf)@1CLan^ww$^FSPSy1^2#o0%|=<(*T5WMk1L0)wXJ;$B8E1x}8A zd4>4SV^J1Y6dG)}YtKhU49vknlH*nlLNNZMi!NtavN4{Z7o&jA93yw%WUF$(h^9;=;4+om5&a8YeI zYb7HvNp$O9toEq`9U-i0a=0|yjKvd{W@_K=Z}EodSUXi=16CD$xDP^R;bdj`Wb%Bx z60?_=^@mc*@vI*3WBxkuIl6T0=Y28fG|ByOCu|L@DOcO)aBf?gS%LiOEU>;vn%o%Q z1cuORR-bPp7CN6feTQm?B7~7J#a%m=HP!E-Q+jvS*2M>U2D>bpOKzz1J;S&u!IKus zE5b3p0p@^&4K9_f=-Bv*-q6r^x%atQ&D_q~*kIEtPE|jAE{*omUk7+VU%N-U!eNdG zr9B8`$z6Lr8?fQp{p}tEexOFji!R|ntWG25(-kt_f{Ym9?%Gm~a*~=n8=Mmy3Biv7 zKeP3_Ji*&jWiQ|nWE#|tJCy)q&qw><>NYnyOqXvioS~7atIkIsp@j(WR7a^g0viK! z`8|{tFEur)Ji#aFQY3x5kdec~)2k=W^=%sOO zmk4?(*Wp>+HJd#@90yanTs4(!F*@0E;8Xi*!Gg>T^2Z$Y7I>;<7dl%+V#{tkIXoPl z3OJm(Ka&ISYJ7%08GuR>N)%#k8fVt4c(!M}x?wT<7OU+#Tt!2Ppk80Z5x9dXnJ{w; zGzflC9vjtP{zCt7Y)?WlpIi-TfCYTB`g=8@h)i?o7-bQ;j$t|plB#2{I3Uf+u%5V; zTW}b3_K>s9Cr=F!tOtEFXct^);9CQ?8nNv|o-tUBC_;VkAm?i7S|{gfImJ4|LbWa; zPTJbE52T=gD^(xXCZ-s#453YGjhJILsxbc2%W^AnP$ZU8*FwBU%EL0wtmQ)YX*!%q=lM`> zY2vwYy{L-_J-Y`^I$8GsU6^pE6Z>JWhr7HjW}Xf+Z&ZKd^u%6yK;P_=2M}&69;8cT zhYcmT5H_mc=64WkISD!f4&so@zCUZ2#aXV(hJ z@(=dyay+8zoq{xOW~k?}kqS8|HOn?K$BWa+SZp<`e@a*S7SI}r@UGZ3LhLEzU~RuV zC=75NVd~PGfDF5Iz)kYSIf9ql%YS=rQ_cg~L84 zRFZw(SARwdYZvbWCzLTB6LXdkHxHbdf*q6*ku?ddwvB$IC&x%f- zB7r_Q!R91hPNx0Ysg}yfeUC(?l2R`ZSP0azwM==1DR7ah;9qAqn4~LFuU0vXqHwKgvK1D>hM0Nkw*0= zNZ>Tu9SslEb#vF!z4Nia7u^!qU}h|U6{Y@QqB*@$9aoA%s7ew0xo<*?zc4<_(tVX(U zN0a*7C!`nVQA&eKsc}1L)_*Q@m(Sw!dte994?t|>U}b{U}kAn58fs8eGtmQCiTdjdfW`SQM@X_hUVYFujbez@YCvy`qP4lR4CH;wV-NSG<3wTY*`e)pMVNFS>tzLzQ%8%jB6|~ zqBCl)pvu7>#W6a1kJw*`S&wh+ugARg7472Czf2NzI4?T6q}Q;s;w-EMF8s}XR@^A7 zXFd%q*9w#QdylfbgHlcFLscvZFyM*BB^@UqI5Ev?)^oj!n3=?k)nrsk$OWdy()Tg+t|K&> z>-$h>c&G1IQlWbr1oY&ttqSZ@j(`XOWp?QSe>Fcv+dLj8sym4N2zq#<&An%<%C z_s8l6V@Se$Zc-z*vJ70)LAnq;e)Vum#lnMn8rb!pUvh}aVm6ePEA?+un^8ZbYgt~< z8N({)xZfRC5tbnT!Xy##7mVIlx~8Aa!XE0i`$c}^JmywkcI^5K#>T<<7*agXvq>%C zx*mG>>0FHDe?DtZtz)=7dhb(bbuH1Jx2-#fl;C6A-&`6495dqV>f<-RC+%*I?8Z)BEeVq&38qdkz1CtAfZgKyWLl4~7WU(kuoMts`10T6 z8U8>`YPo*416hfM6pTD=tr~fQtd>kzI>X(dYhexj$t|NIX>}a1kE^1KFAT7v zr*!a+vez3EqP;M*uk=F2>KD2ruC(~<@AOYoKY!MSM*8~%-j3^mbZi8M-e!*0(JFks zAk#`@*3!!vv9Uh>kmTECbhKE@v4rD%4@JKE7g%BVLN|&1prrE7U%E1 zKEOKSgw!oNGz9XFOWmOH>1X3()V#p2c2U6$dyx+@E%3I2pCFX%#rPfWi(k6y$UXz! z5Mt*^Pch-iw{%Y!tB%zZEfarvT@w59IpweNtc|4DTK|vf()e%vX#A(T@mI`5^)GK4z&R8%MQQx)0|nkj+rsTO`SI7>a|6A&+o>oKTGw{7{7xt9FEf%FE0be zeSxvNmU~7mkApA8r!vaqKflIy8`anFlO7LEs%(``^u#7ku@eWAxkKZzG2Nt|7e+R! zGmwVhpx=e(Uz`_mSr8gD%^1BIg>N82A| zqm7rfg$mDrFFA<#rFs?ri&Obs)r&GbvB1xQlLp4Z?|zEp~2XS^M7k4L~L~dQV@|Bm&_C$ z(6=7)>mA*=u?^FZ{(1nwFnn?T>IGeEv$O#KOTBknb+X5nw6 z`f(%z%=U1cP4`813j`#+22hifs%}k#wZx6>c|b`pR3A+G>AIaK_O>18$!p#F>mQSraVLy={` z2G!-qxEC|%QKgmpf&)--SXroP^d#$4HkPjWbPiH#wJDn*g znZ|z(SCMIm3t(3M!eKGzG*vncZJK~Kp~ilYCXlGV-3ro|p{@3jvW0*=fE4`Xc~a0f z7`5#)^9u*~&jP8Yf%4xW@F2cVT_NB5@wr$(BZvV=MbX`U4Jw+3idr91Kk|7jpMz>2 zo>BLskPBfCl7PUSMBqChrq5tGvW*P8dOqiuO={*&y@H+$jhK;}ccT)y)=2jW&hRW} ziY<{;6)U`m74}zi_xAwT`eO+D62l z232RgX?N~|fY&uZS;X*M3GsMim>SdfTeKW&n z;ZIbs{vf#e3L_G4=1R?a)*~$7Se2U82Axq zsc3aXV5Ww9QUrnhuLy8#h(Z2IMgB)rHUU4Q*=GO*7TBlmCQxz^C=5z!B1Vos{Z`&d zH0ozoHMqdhI9LYY(cB4&E2s_4ji{+1;W2F7Ih9P47cQoQ-W7^lG8f7l>We(aJ z<(@Zm+L4Sf|0+TKeWE^E>$0gtJ%A>4M!i`T%{@wgIsUN~$%1cS^{wD13PO`f&)TQ$lDIe*9;0D)h-g+Qb#cEbc&m*UhCI9`juw&ttRa>1Gd_fo4Ak zava(0GQZR8SRgo4MAZy&MkZpS>xCsMg5Hl`F6)si)KFc+Mhx+AK-aM42Gme+)Easn zwFZDaxvjF4Ldc(H3)7Ab=|rl1GU*!CnNZOpf zbl?KEP|cnwq)K8#9@h;?W<%zqfk%?6(04RaRg~v|K|bOVX%d8@j(DSvS;9D9w8r}{ zRy}n!Phg0JC1z?U<>~8DO}`G+^uv0BQuVXhtfplrxP{Uz$AmHAkMm#*0<2-GZi1jZ zmatyZD32$sM${2SIn{=BKVY%^#dG%&W({G&`k&)o4VqxPppNWxmlln~`TX=#!de&6 zzCb7HU#4>l^(|DSCR_<~gkDAmU#dfSIdviiO$$-YTDT-qr)czE!#E4SrqNpk5bDeh z=s9b%A7^Rw{xpZSE?whdsqqcgxc3Sex$9b*fhw;KLo5Kwc=!m|zjA31R+m;B5Z%2S z>ZdTzg{j$psd{9*5Gsq9Y}J^YO-!BwhEYN}nA`+R+_fJdUyIfw5s5?-iBy@Q1?e?> zUihR?=nCRn3<_<(juiTo_zniX3@`y^L`Ry|1G#uo*MJqk?YFEC+<-CV_|plubYyt7 zcbuT?$7l`j)F{sbboDiwawN*DW)kI(na@VW%gnEgCDB@+P!DTpXAs)M8d^3pJb6@T zrGO@)1oKl=m}mtic79rx;LOiD7zHOMTTFS zU?T@fL@R0Ej1^`LUAMzN%mUF#AVs%y2;JGCiEw0ckY0oc%+}>TcC-*;XPkehUmbd5$cR{2*UukZ>Ewbw0sS~Ec+^ERF zp+{EOe0<(=Y1J@%$w8I)vfuuaroV^@^2|R2o9<9=@$J9h@avZ2#6B7J5{yele=Q`Hx*+`4V3v^Rhi&DpoVuIU5U2TwD2`KvuC{ zJN??M@^srA4ro@jn#nITRz1L)Y!eVT+|W7E@@6&gvESi<_r^%eheLja&xN$W0%O|M zX!%x9q;t!g)Z@20JbuJDpV3e$4dqr;#3~z0kCngK{K0UKT9tzi_Z(Vi4*}n4JX?&F zD{UTau3}s=v;KngZ<4JSz4--rNcMhJc`j`a83p!@K+&+nzYx9^DYGr(IlE z41uH!tbT({G^)?UOQ$~bpXl^gU)^6laV93S7{PV_jUr(n9z%fJWcD$tpXMhmouQ8l z+ZxqNkqE6amLdED9N_&maG1MfPkCE|5co2(dhWwybp+=6&MW=B9PAw4swq}|6)1L~ z=xF_2e^V!lMb-c|tE;Cpykf#zr{N7IyyuS!Z)RtBN1GB!Z@oV&ZUGbA_Ohq?knqxs zz-q2lY-Vyx+zF$4Kd8>huS}F!b+8|lZqT?a4hr$FqovJL!PjA*#&OwX;Fx$+95Hx8OB>@ml@hz`4Nuq00B!70y-yqDM{qxvjV8m1_Wj)!aXH1&rFD}1U4!_|?5 z_jQ7|$ok2&K8%*k)Fp4B-)TO)T8yeI#D}k0o%JBOLEqF%?l@lFedW@-Eia;e)i6qE zmW9z2UaDy}brNXS3|by37aqHjG;5u$mg!>Og9XA5=whd_*rP`+cAYLZR87{!!YuYj zUF_S5D0b~pi)HI#j!_J^M@QUcuqKrq|EbgDXoL*o*zxN+C_Dr$Qgn+BlGQ)Q8A!M3 zfi%P3*wTZrNqwN_i{X)Z`3kKlUSLk2=yK@Zq+Zt9WOA*$|97UC8$GaLO@p2cbLbR4 zPsB6Kp??PnFx~8*fYwh3RY2=b_N;Sebed>m+gEZxw=b}IKiTMa6{ub@Jl3QRJt|nv zj5KWCHLb;j9ciysoUB&sZ?Ks=Gi{Uli%t_VtiJDe$WUSmo6W;; z44@kGof#!@89D&u#`5q0f zlrv>5ZWZ%d!>~(Drv~gv7E9cH9G*%rs)}UfLnHPe7_G7DVq?{?cmzvNsNa{|4=Npb>_jltp{tQ`b+gXM?=nMdQm+l>@mmIK2nM7wOc7_P_eV&d z+=YG!OZ2r+%C$g2Z5s(TrFGrjpePoX5IrLV-_~G3Nt#EetW}EuCCm*QF!&vOUWaGk zayr0*!3{#J0z`K$_l?6B;1@caOEK?s_A)w#Yr_b@=!}g1b|JU&{p9qwZ2$7EAzT~P z4>}ijmz|{xB!YA|Zg5YQ$n4@QR*COonm{iYs}juiH3iuC%Ws@@3y(`BZbomgUvuc8 zo9myzKFvX&a^I}4-m*?m&laLp$3Q)79qF~K{ZLNLP=HlqQBI6J)>aSJ@@69-_4p;;VBs>OrWnaaYT*(+ zIu2(TRY^$khHl#J$^Y4?Isg)=%j;;>uXnA)YO1W$4?8~kmRwAm%5X^|iD%@x@ z;jpo4WPCjdjg2RxCY@7d;1HzDx9q4@BNHH}kY`L{{chx`{Do@-)Ou)`)OrGIJxAAi zZ!v03=uoSV#6Yd@Pm@~fC?^CsrzQYR@`mmp0$l_Fk4U!!etIiF?FA=R4nv;Rya}Yl z$!H>iFu=Yz7H7Nj0#vY#n#b_1h;dCPNRvh;dqQK{L4Ryw{gP}{xpe9=W@zj|Sa}w9 zH_;AnkAtYRhX>+UKlY41rbHimMIVjmW2*hA8?qZ{0#<^9V*YE&Yos?|0zsH!a`J0WZCy{1mZw5GY{C#+V^T4^VS$Ojg8jFJ< ztp^jrC6n0L#dqLUe72grmj6*tXhOUv@k4j}gEH74NG?{_xustM=!@`Gx?c`lj()ir z{l(d@BR34bM*1a-U?C~7zv!-gA0wIWTrTzSy_{kJG(`hFmq4%AK%XJdG#hACg0p)* zzgmEP0F3oyPhffyS6?n8NTd1&K#_Rcx~8eN_K2%9kSsMw8BVh56}!y&NVkK-hSCn* zlxAbOOt3VnanT5dSD=ogH1xo7{IPAl(w3_p(iMLVkT@Db2ahF~56MGR?_E(BkN1#- zjrtg*&$SU)i7(L>splWiAQ^%})H=HiUgOju8>`%oWoFxD#Mxo`m9E$#4R(<(D5A;o z;SycLj~KjIdEi!%YM$qmeTCSPAt~t_8iQPSsmOPxvu&W8BdAT&xp5ASojgpVmeq`mkmfJ*y zMc`qi*ycjwqJu!GS8N)+6Uo1Eu?F*EB;{>8h1+i$<_mVpB1sv)T&GUIn^|$D5Ls3w7DT5J7ECU5Lki<7(L(^E<>E_xM@BIN-G^a z0$(-ggSG$^|5(xXxI$*V}w)`v8J3Ieb+ zfR_n?LI6TOg8&{lDuAm6K+9OJ%>;>|gfUXXC?$+p8pbE10AtKiVH~GnqyUCW(IwAi z$O;gPuAm~C$v5oDh>Y$xQ2)6lBF!U3lO8?NSv@CIsT$cKE{%d z>5^fVM& zI>Rh^&QVJqm?Z6N#kn!g)hkx%xq8{ueeG&jzO?(_z&MOcqX?;tkiJ9>YDZK5RU!;&2- zc&09S5=)+>OJ2&7U+I#su_WnpWXbO)3LU=0MXioT?Zi{hY_av}JGe8r)0eN-Tr7kc zM9@2Qb0!k>d#KXU*tPynR(eoC(+)UgZD2J=WSlQkEzmH&0~x}fY8ZV9V-aC+f@=*s zDly6tCt1&OW#(*(SV|BRMRZg$*6y-cle03P0RVU5GCb`Y>~)p3!^>sWI($Kd4n(f- zW;b$qhBca(YqXc}LDpzSZ$vgRsu?NVHn#e&L?n6odth<3zSvn@siq;^Tb&1Y7fjJN8oLiZ*kU2G5aD9NTrYXmmpqI!J&O>|bcl7><#?K$T zuXBHI%@w4w(Ldn^-B&lVs*iy}l$BT;`}^7PWF_6-(#Yyc28Ho=uMDPB0+Sbo85~Bl z|5}ob&Qg7)*sQKW@qKX4=|$~-+UX$@jGW_8E%=vU4F`R_dMU`0y&_+Vy}fT#`%$qs zZ#6w4eCMdc7fGAi(T?z~dU!lgh&FXan_>vv@*TcKmO9wh)oUI4T5_F;S1xugh%9#g zmE2>$EOqXc9A7z09b_f5xr~}Q0uYdBmc-^ZzHr^6u8wO1>Mx~Yp2D7OI{)CV?+Uc7 z2saa*qlz@bmlNR%0uQBpQ2>O;qjSQyA)C8)3x2m;if=Ot^4_NI?Yn@^6GK#QlnvjF z4`9|506^L2SoS!U)!O8(ESo^Myn(~Rl}6w&RuDX(Xso&mZ{MP!h4E0gdmPlCmdmHY z1aIhuBx6-kBH}S=wYk9h-jm;ER4qgXPj;Ipbaqdk#45vUA+-FOFHo1gI+%eG(Hlx~ z%^swQe+oA=ux6)UCZ3RQdl3&AV*Ni*&+Nwo3}db2;FIjM<0Eq;8S&92lW)FbR27T& z{R70PIs;eWt>)54mUe|!JpfPu5cusGk_Fxe0qz6i%Z#9q5FdlJETrDK0nQY`fOymZ z>sxU;l}B4~x@Xq6ybx(YjvZLS(%mMlc?(mLaC6J+L~qH3Ao#POa?32vRrBzQF1Z7J z5#}~6`lZ)!^h@QCNWWZwX0_g?#^{{I%qfn)p_IMDkaHq)>WS!L<{Yk0V-<{7)?%=V z{(QY3udugt=krWFqt32?gzBCmkUjvSr+!CzlO&&vgzf;q;sX3C;Zv!J#jjD_A65)|hLsC+5Z4D%;RTvG zApgoeffn>E{*ThOP}&zT{EPB88CBKDRFu7m5GQ%zvk)w*@Fc=T3B@?kmP70%6a=5- zUK_>3%di=EMCM$Ef|#|t6@_k0z~3?P9!IiRJH7co8dWDFgE#v}>oUc}$c?(^_;8gB|mK8E58`=-g9Wz+3Dw ztT&0LM0E=Ki&CbTM@MTI3E!KyXg@_nH3bn)o^e@WEKb31cdcB<5=wj8>`M#4i|5i; z>0kJQ0T)6Vjv?@z)&&{+A?o#!dpF>_SF?+;nq4l(3^uht%nIDK`NAt{zg{ZAm=Q{2 zG5}V#GqbVy-#~>XlS<+0ahX$RKnPCli_PLqD*hziF{3NE2l-U$k8neVtSc5HSP7Ek zeiy8;Ainwr8Qm5_AIajxgQb*u7bTcD*h!R=80sxM`6MJ`J26zWTHAZa}7hh(3^J3 zpZv9Rk_PR<0oUvu9;=o}s^L8_@#G&wdL2U9!;oY(ng_O+2>$fmAM?$IdaUl+XW{P6 zC485f3v2coHCxeAj?rQ>uWAS2%JWUtGn?GI!%^!y+FV&OT^eFbL-BM)hqw!*l4M5VD-4_m?^dvfyH#Vz4hR`+kVUtbNbd%hlp`l_hn$Vn7wvuBM|Sb6ABCtitz`#M0?`N6 zwBax(g8ZfWc&kzU5-YVHu9ZJc%rcZ&fcf48UOuszTJg?A9(y%vz7%S(0T-bG5RXx5 z_#_){OQ{wHpi%425otdD5=iqiWrU_o&WALA|6EKdoQhliDwXQdvHr6ySpT6&cmnda zoE7uwC4QR2PowY&A|e4GF*o!ev;__{y{jT~8_qkAl4aT=&`S+v$xBeOb)CxL^AJAQ zs!TpF<@3ubozIK#+!Bw9h4nZ9=3nja?}J5|4GVnLR04%>Z@0({VMManJL} zAyX;~ABaBltygqzR)9{|u@SZ2U;|VWHH=D=y zjuaj{6;L{?(4gPsfTF73PSI~{rGOd)eC>HXxvaeZEJVj!%*wk_R^CB;f#~eK^7i|j zolyo9y^+Vk&zG&Y4cL97@_0w9ehW{LNmWUfXn zu%n<0YAojTb;k{F{Dve)r{q55;WK3ve-1i?FXo^Or95yEXwnt5it1#}ns)ODTQNjY zhbWG|m$XCZ0V;4WQMM7EWBpbF_9_6^jd)23Zpdutyq^3zqiPAhdJ)<$yWWiN&Qxv; zf@wVYjWDj<0L0{i6EOLnA=3GW@JHQt7Sgj&6d|q980oAgb&-P@j3ls_)7{TwZBQ<( z;X%!w>R3WEQL1%iLm(F{5K9M?QEgd5hqrl>OGnStI7CoS6OAM%n(Z!WZ&Z=RMckV)tBos*8Yr*n|FV zglxDY$1x(pda6oUb|IRc z$fjqb{?=2~cH{{c06uzSwjMgSGSBC%mqQ2r5H)qZP+*e>ZRL7M#>?Iho$&$|k>R3u zoHMYw1$kO3f|>D=ph}A{Ekr0n3RE{#SX_DShDt-r)n&-&v9@><8?g&5!HT#$3Cq># z_z)RGh>B`d_hZ&}aNMn#u8NB>`6A?$QA4kJZN7K~PsXY{;?2;_i7;Jg7Y3}$4x4ae zsJRbc18-|jTL?*9*?y;VQ%Co*RL3i(n|Q_er`YQ_UVO4FoiY#y`124S(OQ$6;OjeM z8L}bLNq4w7;C?j(I{8lSR?9!=?w^xwqQ2)cM;dE;OlgK6yCwrK9%h>hRqqJ zMxHKxmOL1JMki*l>nDTIA7j{0Wk}GSVULdEhD(1)APDhifQC8=D?^N8#J%B>+>Pq8 zz33i{mAebl3h7scPG)F#EM3?d_p71&Ycae4Y8A z^1p?T5taXs$RI8isHGo`fB}I* zcoBhbOaa|yEZn^m%t0#GPX2uLSf)^PE2}fWRp_mAS>4k>gE&X$GA?{?ajJt{a)bsR zW8|%jrw6w>7Xfjaf%qm`3tw28JM_~?jsf{3GzcH)dBZ~o?4C6JLAN8aqscF-?e zG3g&IV@P)VDKfIKI>lh}4Yr4ZKSOq}p(PO4%f6$@$KO+}8i>BS2yJV%4?+odsi&As z+YRJKUo;slAACMq{yZoW5g8&t2yLb-me6*C9r^L>mfvsh;{RTIrys4ommj^o7dh=6 z=+>P83#ByKD^@NvdF`oaTjmi>ewoRPB7i2xp{;1LI|ogE1Ee^z$qO#)+~gzd`!8*d zHoTL4zfs!VQOnTw{lDSMQSJK=;e)hTq|GMuE)J6o!+MVE#rZh@VZzdaQT@+cXH=`N zPL3JO8<7sXJ`}FmsbbKV{3v^tdhHZp^ViPbJr8R$x|?Pb0iSlr#C3*bFUHRL#X}1$aJg0E@;mAe zUZ-(6bRKZI>$tdFtZ{jsfgXqk;+^)~8&)>Nc4M8{a;1@4!%$8B2PFq8DN^D7(%=QG zw8?4~aFCjGbX-DuKqDYc zKNlS)l?T#($U%o)jgCB)Lo*Ad!yb^jVGlb2EYVFvc#aVMLG*F{kq~-X5Ml$Brh)G+ z6$nlX!k0Y&Vbv*u!*zt<(-3YVgmr*?EDq0V2px$10$uVnmYl6ip23n&9k=8yx@0{z zW7XBVb5Qcebu7i}@ay|h)KSlQ za>---sh9u3S4$JMJ@hDEL>twSy}@>}kA}EeXH$Lqh*~KOQMeDbuimzTW!LW_LhKM2g{tvn>HAKkCY_$ZJ!Jr@HuP&FfDJkI<%WhSU?uuxkOw?h&1d z(UEU}MRj~C6@SQDnr+AdF0+$%1WdIpvSW$SNn6BS{_&{@gKw+42YKKDU%1gWuZ#>M zLMLaV9AH2&F$$r~V)GJL;!rW?ad|O;g;futVAi|ps#>7@@t@n=rbZ!l9K}@FGlp@z zwBIwA##rTa$0a9R?c9@?w;8s082CGM4OJbOlc_D_=?Gm{Fe%lO8eUrX-K^&3&Mv=z zHunRZ_hV@rF4(wNU2>9iO9F@(%%Cg{qqK~r`^Q|8p938*&r71*z212*Mw^GhHgx3L13hh5=L5}3KLcFKvaL>t!f zeDzw#~zr_GFFMAma5O;OnR^1+-8Zq_3K$^Vyk?XH|fIH|Cp*d@+&2b`XEp z#gzO&Qg}ysE)F#SV zllpCvI9!d_zF%POsdq`jaF{Jh06lV6^l?^2=Kt_+`F-G3D@*k zQ6VwL;$nDMAmd_%u?_ogmo|)z5@5=mk}wPepnG>DY{%n-%>o~20uA8vhEmDfTRU|p zMm4}l>+kC=tshU#YY6NNV1QAS(sMwSV_R&8Ex~Nzez$K+Ds_XPM>ad`WKoe2yBS>7&l4Y zPO^}1&Qp)|k~Su>hyT!n;dV;BXVCUz+jwgp+h_{lK*nXr=%}VmRb6+Y3Y#l%ITJte zpGLn<5Yq2at^x-jej0<@(d~Uc=HTk-gfV`ZW&|_kyd!5@+oGjF307&4$bp!h5ORj> zR`O<37+CTh<_h1Z;z;O4{7(2Yok3y8FaR((QL913sfCo~{?D=|vJ!A&;sLm`BNx(76j z(Q31_zCq<*D6_T%^MbNfHIw7V{8W*VxH)(y-nAcW308A>QTbfG2Z~Cu>yn*-hGXxM zWoRQKdL=Z9G=w)I_M_ftyzf%AzNescD^WUEqx3pu)MlV+StO(8X!!pP6qG2;Xo?CE zQ|_yBdouS#M9^R1%!g6Q)5Ed{;cAg^jWd6sWe)-(c4G)}x-UyzgPMcfD8rKSColqt zaXxFlnKe@`G*`+-B2cCtiDMZcFiffJAnM0hto9;^p#ahRsp_&p?}Fyz6Am??JcCaM z#+7ngDD5u2;ki&qE4{ZtRigy+(iu#8P49xfZza>*Km$*pxKwRjww7#L4$`ZAYq?b* z`wJg3L40cHV1^9yWsqk~Jd=Dq7KkZdI)iz_e;KVDgJf1@I2MoNx3dz(ZX|`!o@?Cx z4;Qe8p(mnQ6ObdcoqJNbctz=2P9p|VL8uf!Qu%0ojB6rcCOYJDglV+T1lm~3Ik_;g zTR8hz1)?Vc;SRWXQ2S>%3l`=BvUda;{?yHnT$Fe8RC&uM>kpU9yc$cRmAcA50SI$%Bh>kb$GO;|0Q=bgOlnA9BjV_N;e=yI_$WWo?zOBdglp14Zdx z3!2^yxB$5*3m`+N$^uCbLkr}G=L^WA=m^921;HeEZpA3%Un|*aB-pzA1={{s6lXSB@kaIjz_=@WLbEUjz*;TJ2b}mP_T@ zxG-H@L66dN?MRW?OjvcR$l(G$kG@u(&#UuI23iD$SfhzA!YA%fOoCec+OB&012}09Dwes zMKRF!T0E-&D1#8zp=C&842n$5Bp4J~>PH79N!+vr-OVFrT*Mh-P1(pP!(ZL8;rF%? zw;I&Ndj1`4O=Jz~<1!({5N?KMN2YGd4lu_dO)$75HKqXOC(%681LdZQPSE5 z-x{T--MAz2*dNkq#~zVC+Fcs3kqyY^APuCgKMf7&hz7Km7)@$T&arw&JiZAUU(1D4 z&=p6{smDfkiw~vMpq{)@sN4o;pH4d#=aCxc{UCHuTonUp{fYCH=!|1=?spuV<4I2Z z)47I05G@1=M7(bb3w6ddsx3DVbz9~Apq|BDtJQcJ zgE$)#pLn^ISb{_{Ec6P?oCB_FQf1&;+b7S{VRItQGEV{xhxwgRfR*Zf*GA8W>V2}n zpp&(4BTCvn>Dfzte1Mi{4+a64So=2;Kb$zPMHcRIXG*@nL9tk0An}p`dCGh0k8Z*P zqe#zLmzzWq_j|?oAF1=ouYZ^(DuQgV=Qe?GC z*P`+UnbTo2+;l2u1XTO;C#x-gdx;d^p2^kLAa;O=viR{;uOU%wm5&hg zv96sn+l50qs>OetU}{pkb=tA0zu#F0- zViAB^^{dF|3dMV+;j7NZLjvOUYtJig@ebzYuMB9n1x|+?r`Q&#SN*@kOIbaYGzI{_IAef*S?K5bp6#yQP}T5;{k~6Ysu`hOg#Nxb2big4o@c zm5L>X+0yY7gqwy~`ZLsq$jY=y{aX+J(>T-Hxvs%dOy$E85M6{@%~bn3N&me>)@#vZ zT5%G{bSU+B{kJqrvtGL(n(_SC^0D-nZ*ej>bS1t?$qmt-lCVX+H*G-#A_5U{6#@-@we1=`gGBXjlNq5i~lx3YX(% zYpLsbw$3pBy^Zqsz3m7jW5N@o!6@8hR9`Uaj=Sf5vF!xIHe$7LaJ!MS%2*p>Warw@ zDOD?-@Bwn#oS2Uv5egMl3uGMslqpW`!nwShRX5qFX)l*gCsKU5+L!gwx*X37Mkm{H)HT@yhT{6QRRw2uvM}u zi@h4vTQYWRmFwdZ|79h9fkY>4q$NeXYG{^F;7MX18DF^FWS$<-k7XjS`dkagz7BH( z6%?Y@sH(@3Yi(77QC^wJ(nHsSCxcJu9xCmP9(p|Gcs+E(Y0^Vng@LyU-qSpCBVMgG z8|Gv5{^YQpz&^abFZ*yCL=d>4zpkqV91!&Ax_;}0y3T4-mo}p`sePGJS4RMk^!!lt zJdVh3OcI=5C!EbW6S|F9#X4<=GCbF9IX=rNR0BxgIDXs=|}`8cuqCS)>DNojjWvDe<)(hRw);8T6ks} zWn;6(GUKFJ#;#Vzb6G1Y{B)Ft-OqY|%X;s|cVLB6BaLdJg_Rs}!}|&=lkBtOeM9j+ z48%=F&h=T-i6Bn!h#2Bkq~MInE_i;z4F=)_Vb*Y>Xvfc8 zhcOeG50cwBnav`5O^N~gFQC{7r7bi%VxbBRt4zbP+n!i%E)Ttr%xXcRu+hz6_uzB7 z<8SMUj(@5{JR3!nv|COkJ2u}h5+2c$uGFOmur#9&K(L?1($^lhbe1kXSe>kk{R|Eh ze_#}AIT6JM9Jkmnr%0tbdXhWvY*slC|Gy6Y$b1)XZ$A94OnT<~;-M*vz~FfRg4MK^ zL=`&|fW3NIrZQV4*EtrWHK00UG_5Z|EBxp{Mhr(eaV%*#A;V=HevmQ>h$48}*3 zY*MF;5gIw7+Qze2Nqbwus-hsbrs=Xf@Y#aY(2ypmyI8F9r_3|Sesj{ zB~*ht<7C|z^n8@tTQ7W=)R3+#HNhMSikQi4m()^=#7};F9u@#lL^e! z0ywfF9xCvc;Fp?#tZF{{`&kl%^B5)cwLhixO&mCXrw|RK&9tHQ?}94rTaWwg`o;#147^BZwQKJSaOu_U zr3WRDVt*am&{(+31~tT*>z>zrIiQ7IhF{Q z^%j-w13}_RA|a7Su1r-WT>AsIU+{*N%G(=P0AhPDf~p5z5J1!82@XK%$S8;Z@kr?^ zEVoY7eJK!O^U@0L7A?%IE&jpkvqOUKtH3cR*E$B$9_WR>?~{Ng>eJ~j9I~NvV-RNVaSoA zm;Ar?E9h4Vy0)iZgBqPd zrUx7?EG-~$;vzD?W};bY@@o-!K`%|P!|~-suv;7r8dMjV;cS&OBt}I>m8Ec2FcD}1 z81BE);$d<$VT|UTn;a*`8=ksxMq2h1X;*j7A#PRI^`ob#*pCs9~>K%=Rm^5FX;Gnuuu&E+OC}cm-X9 zmo~9z8`X9}+g3>4UxIAWrI8BTM^@&qLA+?@3#`mF?0AH5LRGZF8+kBG zLS3hUIa75RPxr++e}@U&8O>&HRKH$>djw$5smB>t_3`dKygwZh=;8I!J71IACq$%i zqk6)s>SH0-Shy@<*)SKSxU_#kib(z8I`#C`yTXO7v;N72$05V2M6 z*TuBABAguG!aLlFU4D2smbK#M#v9dhze`Uwc1BM<#8w8!kcXx=6dQ7x6oW;(Q4Q43mPPwMTC~6WP0+Wo+> zlq7m~lq6<`y8knXUFw^9&LJN@IZSieE@YO$!Ne0p^VN5TM-z`&!{~G7#DA>K8r7yE zUHo+WH^~Suci}=-UdYJ}rV3)Xp->n2 z$O5p!dVhuJ!L(HM1Rf$FY*i27P2KXV@LqEYdg(;8483#-p4H#>Gd=Av)5!;p>a+M2 zsOH-A@Imt<0V}c*up-xG&P3pfALJb7^-EP}4frtvpf?Nvf0qovJpi~Zqc*>x7vFEI zulHf~(G{7o`nU!KtGnxhuc9Dh78b2h>i`yyK&=!R^vD*}9JZpGVq4KyIC3Behe|kH ze1HgG-H62jk|B^bfmoOT(-sRhZCrQZ!!uB_?Eta#y;wY?na^8t{138SCV5Wpzwc8u z*d(Os&~t`L(X}67?U$fiQTw+^s67p}ga1Q+=>EtQR*v0?11^q+4QdnrxJj*-$;wvw z97uT=?Yc@bUeyspcg9BzJSR`v!_Qbe&*=5nKJM0hY3GkwD1M|)Pfs`-{ z$V&79#&`XETAWr9!%v;of<7CC#^Hjw3_f$`6$fiO^O?T+Aph?mIs3l@`yVuEziZ#9 zHh0sr=l>Eji4#?H>o^l_SARSg?ErJ1`g0SVfaLO>UY3$3hA1uAGqJ}R?2b|q{S)o8 zW*nM*qjcbmoLW+9J~_@=^^o0B51jM zZ+kAL7nbi_6}KF>igQtg zfaN?IBXV@m*l{dID?C>|EB)kKEdH=^)9oQFK$?G%7A13J+}cq_Moj{$+YdB?_kh$G zoKR>;jG8Xe9L!se8{ve4vv0g$**ZV*8b_7c!R4z#Skw|6|44B8I(A?{(Iv(H;i+z% z0j`>dJaQ6$1|D2yN4OO;E{-eQ(TSU-kqf~WY*p7268hI3=SE~cFMdUZo9-K-3N*%7 zf|Y~!X^guQV+SxkR+4lD($ogQ&u$PXlj;T0C+C!4XJ7nx!PJJOZ}E7`GLRj@p2)#%)Pp*>3hTTGQ_;QHtW&G8M2EQsPF9cD#YUOo3!en;_5&~VLWr?% z{~;)5+=+DA&rsk@zud_Q2KDCUZANG`gBHS@e+I<^$c=I81%qLnUW`KO4z$;j{Is7m zUy6$1WJv}aZI|OiSEw^k7R4?jUp}g7^2bq>J&$FNaj`#`s`(PFrho$(K%chMF+bt9 zN8V<)|4Sl=ARu(qTCk+!$^h{m;|0s^EDZT=3p!6#NfM zd2Fhx8F;)5#6tZc(k*HM;c-CHvG_mu0F$-4;2ZXpY!?~+3aQX~D|ni3-d=|PY1XYS zBD^;VuZf%b`l4x+;YS&(p;I~ObVoMSau&x@6?6Z{4T;v>lG=l*laUHSuRa1|C4h)z zpcuW#^{YDV#SRNNN2_n-o#^O_#gF^#DN73K^YFeyi8$5%h_2`Gd z6~TUsilh!4|HMd=V+^)M`8s}-oT$gC`Eq^Pv&XJ3)W8b~e5?lEiNG&CF7RXkr+~10 zs9A$4p4+fO*wxOEP-HLUOz;{F>(f60YY*x;7SZaC?O<6h;po9#T*8B`?dow2@>zns zQG=XBkpFH2vYf*TOTydQgS5|=Yq71F4tR5&k7KdN&i_ve;UzFb`-3|4>yJMe+ZP^( zR@jaoQoG{lGJdz4|V|PF ziT(%6`3=58=jc#~;JL?|AlAibl13$R9lkn!i!ywzO zAZM&6Xx<}lo}a>=5aAZ5sl>5oG(Oqbe+ik~4AuabZ+-cxt%`Fgf?FY=6iS z9;{q-<;=w_>hHUxsVA|in{`u%|AD5?00|t`?7Pr%=5y(Mzd}?5SL%GHGT#-*XN^^6 z26f0uCnv{75tj8Bg`IWz4_N*OUH-d6DBmMi-kRrFesfZ+yl*k3S%T+OpT2_2lTlYe zM^a2sR`&^0s{ibimXxrDJ-UWjtl>S>Y=!i{AcofR9}yK{&zb*2qS}qtwz|YK0zWnE?e#WN$@VkxC~B#Qvd|vvR2@I5bbeP^Xz(P zDe@Y16^z}ZqQEQmT3f=eg+uAj2o08`KZmWu9xLjRTbp#0Ve|HB;C5 zGnhI!Q`cF-I(?`!ewMou+m9G2I-V$WwR&~;%W+OD_Bei5XK9#E5$0VQ=AQ=v^8#rH zJ2!G3ii74*;{P#dEay}2X|cGS7Tvtv4YPuiqHq3WvapQbrE&2)r@$Ylf;um+UG?PZ zD_1?)a^0%uG&uezCwNZ6k3GS|n~%yh3wgR(0Y5c7`&Hwhu-SpKdhs#g1jBrz20%q& zL(~EICA`K7*(WhQ6W~@N(=$XLPP|RMOr89r^yx#S#l@NzTMmF013;tp0;ECxl3;PW z-Erd$*WvgaT#uF zl)Y;A4ng}m*2qi9f%ccbqsCu>3DIs_<7eSL5d5Kq=n)j3S{tz)NXX6pUy1GD?MT2) z?T`uz!5LLD9Mu_2TCD)VinghGC+)HhjQc-idgu!LiBbim9jD{Spea`EP~t>$wHyyOTcTicJoVT&lKi7h;TNMbmUeAN+=j zegXm{hBiP%u1kIIs1W02Z~|Jy8r09v1DgLsKKsAmYMNehXuY}$=5jZDhad|sRrl#? zc46QLD|9uZSj`-(nzl4IbTxFR(UG~ZoB(r9mm5r6KAzsbQBXjCANTaOzK7#d2azqYD%4+r zIY%{~CPSCf;~l#h+*L=h1X)xF8h3OQG*-r6n%exiTJpWrpU3(i)b&64E9zebypA2- zPyC5kc@L<7E_4(_I5uuo%brTAS2CT889utYb@-*oK==lKI{FZtZ&w#uEOdLE*&p`JR{5z~Rww|y$XIdQicx6qu*G`su1(@r--#!?| z7)1!8bVaHs?$GMT=rxxwC=R~fd^JF0kcGAd2kAvkYYQ;QAqJLyBkTAE)%r(-%v=Us zo@z_)Le_?Ov?E}`T;2uDJ%CQAfMFBrF$uT>b{xo{1r-67ut=Y`8%Vj6YT>cUK_q9; z@@?5D%JH)r9}#(>HX3=MhQt09mpN|}8be84!cbrk)+n3J7&;Kzxp>ZWgdbyrP<_=l z0Le#ATjd5kLB32tUqb&l9j$Uy^Xhap^xIuw+aesZLvmM1b~Aa>H&i`*k{|U>#B(Xi zs)Vi5Nr~*FF`7hQ?*)lc*^x0$v^r_sp%|6a@-0&IT@1M3sk-PhEV>#XrGJ~RM%*)C zOZQ05JL=14y1<6O6kU7!TVyc=vB`0_hD-Sn9H!x3PPkse%|^T1M0Oe)hmyrNvL3Sb zPbdGwQWzp@Pzc7m(TOrf@~PNEf!ATLu$;s!FABV^igB+^9~~=k3hO-e&1PxqOT>v6 zF=42@y9YRJ2d>sonF7p0l&3hC4u2y#|3}+E3WTvY8~08?eBoK+<@?T8rj5Nr^m%z`c`c78;KS0T1A%^n{tz-!u2hb>NvCW$&?+Ak;G5w6h zPDV}(YqC72)Qwo|qiu+K=MxeXwoDmjn9MH@?D6^eU7fG<_vh4?D?Js92S}=0O;tNL z0k7U{!zh}KHu5!{uX_KFuSWn6t@$r_60Lcz8Le4^RywNbf*f03G^l5OXOE&j^k}S# zh?uwR7cqYoJDRK(Js>^nGD8li8E`V!OLTm*Rr&!x;<5!_W}(piFlzq@{J;RObAc7e z=thJt_!^V_FVHF%GSUB*eB)`$BEWD|hgc$zv3)lea81hbUlEf!Q;pX#(f=09sc$w4 z1HXxu27i_!Y44(l+72?r_{NsNi!mD1TfdQSLc~innKj&6U6ez+q^*h*f%0p3*AiZf zhIb?3J=z9dL2JjVGB?)lhVx3O{NOrN@7vMAQGEj8Zm-5b1vd;*XXzSR zG0cKtUBjOt(t{n>cJBLgKhbR-%*kxB2IMxoLxYo93^S4mD>Tf(*-Xe(Ll#4R{Zdt9 zxBU}tt=5qYb}IyZB$NiVsi;jbqObf|*-jSI9yoV{P<9O|F;~mDvBdL1;7`iN6a`ZD z3PDxdy&%H>H+!IXq`@9|x&}U#!1ZzKw1W!3&p9se-+m>RwzUU}=bP9AH+(IKo<&#z zjp$p1^&RRs7SY$+!?IlJU=Q5*ILIK<$frU6mwoh@204Ks%{Cy*$?RzRxb`4n4`hH# zOWMZlff0Ipd@*|a>AVI(hyXTey4D*pI)ZM^89R2N)(orGwleVheb$suwaAXsZJTUE92vP>oLH0#4T3d}G?173=u5FD=ok09Xt zEx>J2bH)L7R9`FkGkP=s-f|g5>CtsE8DhIO4Pjr>zSmk_H*%s?@dt+S?cYn57QsUc@>TMr|bhyhmLzwkc zT>~yLP6&RgYuNuIYB&=$9LuaNdt#L!u26O)wrPJ@r*MyYAAK*k_aww$1@%P`g|?I) z22psscZ?`8?uf0|ew=I3L+s7^{)9$QL$BAOx+rW)^)M#KbkR4F$GWPqP>oqDee@KY zbD~zIve=yQXr`q`c#t10BX^t@*Btf$hJd$g9RErbi@ITUZIx(Hk9HSs+N#tVsb(On z;Ux>0-#`BW)wH1cI4#MlCiqK?YU&+b^Z*88u(K{Yk3~ObQ7z8nl)breMWgyV_S3J{ z`rX1g)_B?{F_nD`l>3ATLl1%@FSJVWFbb**=UZf@xC4rZ)k&=YM7Vcet$nI~zL zA7l=3XO`I`anKpXP;Mnb48zDu;7k;S8gEx$Hwj5PvR%9f6(o6c2imn2?Ij~=15K&a z)TsXRvyg;-T;!4)q1i)0oRyWYe&HM~mJ@^@EZ0Ea$4Ct}YM|o?biM^t(@O#qLWj5# zKsiH+?_&(I-uQ3I+pM~)*`541#PpysXW!$zUdj~KwgS`BwT;eLwxVnZzg zw+e70+w=l0ULVx{UeWvAbHUQ;m07|IaP}%PTPkMQ6Cl@}FkWG&3cgFFp5bZ=V&%(f zvsG<8A2G+2!`uRLoT16FY~Zf&0einn@5Kk~nW^5~is|Q)?`NL{--oTUHj`r8b7T(- zA{JI59z^#o%hf(ZxG$U;YLJ18by$vY-z&|k@BZs|+!(io&Q{a!TPpF*XjesOdkL2& z_q|6gO14i1PoU+mbG2ASGvZ98jq0~WGWcwk-_-Oei$aqjZ)LbjEcfX6zo#pI5iv3-71Vsp@^tA0!ZtQL6i{-xDr2C(<|j@MX3H z+jv;STB)Do@KR9=!u&79{~G*Xga6yq?Ez@Un8$h|OwK}D`yQCBLrR{>*)@=HFuNy4 zSd$tWm#>=nzB6^cqp)EhUlZzLzTV8I&+}*F!jnzxHr!aMZkosJURDFEL8E%!D*mM| z{(@D_riWta0Oxu&&C179_Nc}_SEIAg%Ev20m9Ar(I*gd0wC3EY^LijZiBe@8%of5N zV8I-QeCo`vuq|-FD9H29_RU_H=$&Sm#r5V<=v21KQ;@+?{TgVGs*5R()ne|D(}IjM z1|kxQ@v_o$s-yaKo$X9!t3|fRMHP&+M+b)UI`Mibdg0qD$4F7wQtMT(&IbVzR;?RW zAp+%74JZg)vV_SHcW5Cl#}d~Q32d-l9Yje&n1^3=4vXBTi`c530|I8zLUd+lA1*+t z3g8_#r@#~AOJv6Rln;0}c##f{bh%(qTutPAu>Wr8iFjR~?@|EX9 zltbU01WQ0(n=hB@FNn%-76a@`&nb093&A%D%AOKwthX~is=4ng#KT?*d_M-#Xrir( z+pq_V7SgU^tD29O2yBEvT{sDjw%QDRYq7ZuV6i5p;{-{zUOqO7u08%XW<1(_nWLKX zDO&O>!S6I_@HI0TRgztt)ng)qr~L1cm;APpY?tC}@sBh4=}8r`92ZO4E3H zvx-ez2Yo9eBd%bF;Eo@)4+;mW@2ob}5mfW#>Uk7l$6ctZR*JMd3JDqH2{_CrCs6bU zR#95k1iK}sCM@P1tZH)ua4*Qif@=lBs0W_SDn}|#%64Tn~G;sC0 z?Pm4szU8QmwCHM7@{pjS`fia{Q2=JCEi2ILz0tnVg(#<1;}w0)^7BsPeNL^-DGJ_i z0|VLGxMrh)bLXITrSh49|R zs5u)K17PDll<4UJXBs^gG-;c=J3e~=Ih#8=KC5I2G=kk>1RfFyZV3Opb)ZVRgsfpj z+1lZEZjWDq@^Y}g_mW?May=-l&gqRrkk6owi&?Hj86EktL6x`cLw1c8+U13*>Des3 znA0_a*yjR}t_DTzQWx*$rT)VcwiG`g5^0I60>FQD9l>x|oQA!HmOpcoO2y4s6i`NB z$SFw&!_B8z?$1qXPk(+vdQ}IAjzHQRd^T)FhakSf81)u5ke|LnZ0^y$?5mwSBeAES z&8lHlzGahTR=>e<{Y^Jw#I*fKUTS}zS~;R#RhQF#b}IZIK`gTu46K;(%T-Xe}ot5UQLU5>H>4FIkT#b_Ua3^lPkgM z)qF|C>84HUof*ggzoagpXl;T}lqi&92t2lh`zW(OQFX9}`U!NtfOKx&S8CjS=y>qg z8WOBx8JXeuOoVuoP+Z3ufw!|V$u`@_HoN{FVVlq2jId1&n8R0)Ao3SMG(dJ33uF$) z1}wfgAqQ~PeP-TDH$qEpBAA5#D2zYdp}fr34G|#-nf18wv@Qt(31bh$beQss12ixU zsW3yv)@VkfLa0QACqpql&_(*SZMigFi0-;Q|fA)N*fA#|WK861; z;Q#9?Irmo}3ESnJON8$RZwBA-7E_G!)^EXgQMin|;YpgSoj`a`u)y^3qON9@!l(75FRtY;x zd?F=0#o!9&&c)(z8S2Jr2zFk+uTvjo5tn-P89-Q41~DG-P5m)IYu7fJ*BaH|1ekY` zhLx}G-;QaTIH)z?M_uN?GrKXxex>Fh=mDkfefZ^!aSwjorX~bLJax$HYYxZsZC5t~F*kxrax57Bj&u_Ua1=4J_ja2OQ+$uRdrG+kWX{6hEL2lpmzto%?d~1WwRE9?#4)y7?Yvf@Dx9z*{^~$LdbCLAE=w|j>w^lUbEy7 z1+H0{wGE%S)dF?~&tc7o{zX`EC*E__{QkS3lro6PAnI!aNpt_(Sqs<-$R=v#foaKP z_;Nft1GkH!FBN{(1TiRgbW z2ouPdw2}Ha;@dt3%?$B8kqY+`e3)i^=#LMuULud@bYxID5Mt&sWVi7kQk$Y)GsdgxK8X9yC4BfhKES2K1J_B$xb#$%#p1*5GBzCc!deFR_ihhpo)tM~x? z9o6Y*F)Hcf;BBD|grNGa#9|HcETe^TtwNIt&7794#$Oj=b7izkxQFVKr!@Vx@x^xo zSU$K8?kt8J!-_6viEFKLCoOT+s*_xI)FfPgW{Nwf-l(4T#q%d!ur;fr>)>{G&MMd1 z5H8q*RId$nR;Rp*YYn3r00hunXj0A+W34ihmKe3F^Bpx*QJgs~s%bJBZ`MnAKnOnd zHH0Hh`-Ar2K(&^15`_YE`Urs0Yh4ZPn&V|XNdD!T5ZijsIS~R~zd`p|4QT6o!mG|$^qZPUVmN6o>(MfNi?{GgO zAp4eOW4(uo;8dNzLgz20iDwB!yd#<^P9QMuzbp_aG{u1Lp*d*KzS)9hvsH$s?%S22=rUZ5L=CYFFs zEQr_5IoV~_;*!Vp$`?xZZL<}d$u-F4{mEr6S?cg@w~aA-*7G^920~mn?KMPgf!^(? z9;}hN3`i}`uP)@Lm7t2&Cqgl*AJQM0qG&}L4 z>oI-&oeG!Esy7PmO`V!-`0vHKa(th_gmBg7IR@$rbmNi;Y_gmL*#=F25Y86$1Y+yF zr$;Lr9j(mG%KRNU+XKr>!wprN=YHz@36pj5ZpJX~$>cspc#J+Z?)DGb-P{Xl-ee;@ z0&;oKZZRn$7D5fz!BAwP@duz?^RO}IvdH8Kz7cxHTM=5=9|LW$T?>iZ*oR&O#=Ckp2d;jiIT zfx||%dy|{pfy)#9PUOQ^JA2LSxt87?gACuU7Ncvc!%WT!*B7?T&Vczeyuzs6m5l0< z-#>OsX>)hb#nV~*`I;Qz4N(7Q__?^>M!I7}&iES|1KmECs$|77Ht_hmJ%^Fz$6gaH zew;`cfsA+8Vvvv(KmRDkiZbeS{++jJdLfG|oGBlyKWx%1)_AP3a?z2+m8wn~Ppj3a zbTxl0r;Aa2RCwdguy1B=f~OZ=u{y~30$Q@8TI@u_P_v`@JdMw5NT-ZDABk-TZDfCL ztgg#Zt>t}&2PGDlptRb(i0r&c*!cw}9J(5K_=lyc@^OMkDqhlwcX#p{EO*!&rd&{y znUM%@yvjm&bbEx;VhBI>VI0E0Ll-B)vyrGKEs`a`A?oV=q(Nz4Q&*q-iCzMvVJmBK z8d3YAdN(X<@h!B*znq87WR^Q`71BH9pvSL8g$B$5n`jxg6@Tlp6}OmIrK+>&AcrxM zZVd?@O+=G!f(R-Yo;v4zr&%AhUzuHt11=2WUg4Tz)7hi7IM3O+6+Sn`e*6{=Po1?D z4!(u8v-1%6(j7>q$45duzq7m@<$H%~T&6p<)#Z1mw@Q&&KlsPsLzBwiz#hQh)SGpf zRxEVDGt}oS_oqk8G!y#ru`EH8a1YEO?}_T#H4*1&j^&#$!O$>Sf|Y}-FzmS!x8C{$^56t=afADoR>{1J%B5Lvxlm zP>~}CbH;jiS*=-btlvFqV9zDs%@Y>e0gGRj8tb=>8t7codb#N~lU?Cjctd<-o>;MC zXhK5y>`viTzEw5hCCG=lY`roDc7k;2u1l|St^dJ2@Fv7x-(!2Em`hrSOuJFGf-3;S zRk*@d^%82a=w)`T*lxjG>04P7t{r72{|X(;wLZktH#38R(ONuqVO85uTXT}jTsF#| z{HtDZ6c6lT4oe31A8|iPx74hchSnbH=q~#P+s|y|4m9#xWa}MmWGzugN(x%~ZR+Kw z(}$MUfUkqHEa1Xy>w2!C5PV!%t8Mp-L^|&Lse>-$qLr%O&03OyJtsqwW6Z)y3xXYX zuDr1NVkOsx4eEgv{H3MBa}h3IcGDCiXI7qrj74Im8qw&C;846G-$@3{Goj4o6}Dg#krVqg!G6!m6ooaTQbDH^t^YmswwI zwz|#TdB3QWVBH4_{jKxp@NmNBT@)g$f;WV2Uj@+jSrR*9eTTiguoW9hSCwCB-_ntpY+uGuTlr&zh12gdiqTkK|?MaPBvFcr|-x+ zpjOrqFyh-UlgC<=*wua$1l~oXo*`Yqfd~78c|JlQjw1K-$%60_s8{2qYFrW;aOm)#@I} zZZX+Do&qQ#=1(pm=GP;Fs0?Zl=PrxMZiJCIP8*{9jaP|?9EgBFkKj;sw!wdw5Wr%z<_ZI%7;$=BM^_DC;0&_;!?;u=tWh^&V_9Hg`PsY}UN zwyIO)eFJ$dg%(ie>1^fs4SDbZn>)Ml6(t?!*T5x{s-9i#SZfv8fR%(%HX^my zge{~$BB;*JAgcztEyXn+T&HeJQlH$_CBX@07!b&l`9$Nz2!MYAfZH7Es-wq!g2Q(t zaZWEspa{`^++{Vcz?f74;Q3liq*TT>q;!ni%oYPm4Un#7`uR*vrB66fF;Fw!&)J{nA?D7kXhsMk7~gzndn`_ zxV(0`ngHS)U;tyZzuOO(NOt*t!3NRgGF3sRcT_ zN%tLmcDVV#{u7O;XOk1PS8PWcIH6a zvNZ_R%$?dCNPW0#I1>`~+wO(tzN7r8CfZ<}kh@Y$WvqEqL( z6I)z`>t?NT75;bjz+#A%K)3Y}Jpd1o#R$igvwozRj35b1jDd+RGr6;CU^2EET)6W6 zzq5Xd^+6%1iYvBZGyqxj6b~H+L8A=Lb=?j_yY}`K{5XYTapzvzijyX|7Y2&M&uV-v zXYn?@TDz-b;M`6eOGc=Gm&%~0^3|ROj7i>mz}Ly@g3n}#dMhvdr0)29XBfM;TtbPI!xHM^8!#3tmQftW zEuCm8Xs1u;OKC-uScmf{;vtC11Uk)1m#JWwRpysm7Hkzv#NvuTghGsPR2B_*uQfWj4X`;&1evriy$D4f8K6lk{sHEbVWpt|Z7|A`e_aEEU3 zCOoSX-)i6Dn?VCdwbGb;#1$UkYyk?KPC$Il5)<0!BbX|vpX0u<>Uz>_@A$~fuzW#u zW_Su;XvXHDI&7NhiD47zR8kDbLc9b0Ke&fl>MC60O~)*T`|H(I$oNnd#ws^CV0Eo# z1*2HTWxG0b6Azvia}9jy`1L zSl^KjQ^vsWTMtqU(3S4O#%U==;fksG=JGRuDaTa3ul{Jb#{4>Gjc;j(!f$6C!m=D4 z_jC@dO>0{zn`^fvyL{VP)h4cw%NmTbuW|li_DS*cJxGVV?SS1#U>Ew6-JINhRJ%T^pL;4~vAf(i2kzY99-A6UMmdYMyHYXx`no9I+5Ew@P#^H^$lo2d?=w20Ht;;45wG zIld1*Qu?krqod+1(iAfl28X(V(PtHEdn)f~PW zM3-5fP8@9M8eu)}l$0r$T3< zpPIf}=i9L|C}1VR%0U*cSRZY7qFYG>>Jqs{1=-$S>QP8Jy#n^(G8ibRJdsVe2%#PO z0_!j8ly)^-_{1r`0P-Wezf7>dT zBD>bNNsrkU3Ji!xarg_QwRRs<>pr%~@F>0ky?P&XiiaKotTs)13ttJB{<->tEC3r7 z;}Y`2mGVJ45Npc~;x|HD{7Zl6*7K}zZ0XZ&k526;smm;Bf8ZCL+7YRmQaDZZFoPa~ z>)|=o6kY-5$yME}rK`L9j$Wuq^3cy1Dx5EVgSi__Fi35FS!T#B4VW+Ts0;_vUdOZg z;T1h!{2dL{GvvV6uu057b{tyynhn6^(bf~}JNOK+e-~>c*1fo|O;GjRWMTd%rf6gs zsLFJ`x3bcwC-rY`Pln)ONR)TdYx%4j)(sl!u!DiJe>ZT@B~%?wUz3F*WS0$=R0oWilA zp}%Cxm$$R>fr2QUjv!LX^+!7%UA5aW<6(K^`V?;o7$+9Yay+>{!=D6@Qp)DLKIdyH zU!_{MOAA^gl@p5H`T#I|HLO>523||7S3uzF1nU*p^R?7^&D5`xtXI2!oov09>(}Yl zD^M`}dcugLbdVJ3AdMv&z&z~4NFE$KLyCxFD-Sm_E3;mR%abW7ug9g>C1qh;O1Y#E zLqNy%i2iid1xN()jrk4NTX z!|XWH>{es&$VIKOcDHN5Qm(bSuEDEoz%ryno|i|TjR9bqHgDN&-g4W#<+pjWw|O(# zyp^2 zu0?mFm(M_MyOCt|Hs3Payrs8!OKtO(+~%!Ao3}*k4Uo-RfGxL3hevxte9p`^Zx~c@ zxM0x5y6D2Z}rE%rBBeA`kjc1l|3+m;;n+yT$N?FfF5B<0t-Ai!Pzv5Dz6e+f*Q zR5K}_sb-1*$Gd@d9wRjyl4`h*Gy&FB(Q3r3c@DPN;|;&7RqA`3JD!y*R-kHDETd=^ z)$;iMn%Z6&x3ru#Qf4P=n6Un~h!5r=2b9WgYB1(?Q7W6q0Uz2pOH}HItgk_>k$S~y zeYV4dfuugXO3lQM6tzJyCPppR)O{*KP$v`%j(?W`019hPnh|66L+Zor?1%bXi{6oL zbP1yHyb+(IeQ?z3`X1}ERsM`#b(#}#n(606+x!~#vDv+d(S30p6IJ~QdICcSm%GZA zQ4cP4)c(-X9q6_kX%YL?NZ;Vj2~es~-VwNSD2&gzk;@Up76stdPFw(IiIG)3Q#~gZ zem^_SC|m0ApZfqRa$}2rDaIu%PNVp_H$L~^=fIe-!{3crP+YP|@sseqr+goI)RENi z7g>({b(TN>I?Lg|&hp1!XF2rOSz7-(%fY|Sa^SDC{QlQje*5bz`~N!2uYa9o-?3Q& zceglvyVHvTb6afwr7$u%e9d;3FQj?YPjeRa%2Kg^atxHf$)U5O;#)@t{}$Ci#eQUMc)l=PMhg$s(yMxvx)(bu5I?rcJ`fOi z@La_;M-`*Lae%gr;;_tE`7CTN*l~HB))*xB?agyg86Q~5vm`m~WxT4VuGU!=JwWAt zWr*h&SmZT| zmVcw-pT>2;*#e54ToUl6r6S$p-dWwwkwS?tQGk^23$keoS^ia&XO^W^uy)DL2+#-C zf_sB{7L<(x7L7mDv&ia;RoKl?JFzX{Q_1vbPxKC;Nc89$i>(@a>l!a(jd`e1BME%l zxf5Cb6^on}bXTq#*(ff&DQI0b46YgLM!_37rS+M!IV*5E_Tm>H$(wA3KV>VDvAXc~ zSPX(EsChcI4ylU|km)ksF6>4U0M1KD3}?J@X*YC?_Y5Q?!bQxV@-iU=(#8_#I9e1k%VY4R}B@(Bmgmr`)gRhI;f;Wqv0jOeE% zNf?!@PFTGsYW|3k$uLuTOg=jyTBWSd;VsG@4(Ux9=-6RgD$bS7l`E_^7hkb-pgQUF*LD6om~LbG-m9cD zrz1g#>`%!+-SBQ2STH0BPoA^QlpmOzed^nG*q`zZ-ohD81>F+NjMaGN+?`RA-z}kF z$U3}!%D%5lX}}{9a^RwB=z8;KF`5#Oq|)&%ke2>xPu$-S&PW9q0nK^%zsMQR@FK}_ z_{RRDFwj3sP!QXe_#4hBLRHLNgE!a!?T9@%<@~_qN8JHAMK#gk8*EQ24kX*XeF7uH z6P*Qb7qhsx%fupowz(GTYdhSUl4=Zzj%f4wfhH}N`4IsA&r(>xNu31-Ogf>Lz*dvZ z_T;Tw96G}MSoVZ7&H*a=U`HUpA`menoNM84CS?rU=(=^7*S4sI2)xOxx?DCy~f<6EA5 z`~3XoJi|O?GxlELPebFIQoHPT9vn=?u*(^YSX;Hny7HJhu|OnDiw z;AAaZ(l%!+81F=2Si#-hK*o*~xP$*QYk$~GnXB{7%14et1s6@itLI!Z#lWxdk3YqM z$8g3aprx5{5uSxFvoFSL%H?>4xSTZ+bJ2=_q67z_`i=OR8AVKxvG#*b=*a|W(>k57 z{|O|JnN_V$IEOi8SS{2E1x%3f#9-k;_K7B7{^e`KG~os)3TVNdMlk_}dm&^7)NP86 zxNV%JYA(bv{N(u8pY{O2M#axpI0aI$_@r2Z%-7UWwf|-8{@7fi`rT!yC}&&V0UY8s z_lOh6*c-{B$1cF+p==ST2=pP8O0E^wK3!_vV^ z;QT!QRQRXW?g>}SO#Tk7n$_1)Jr)~jn9 zRG6XnZ4WcnSYaJ&l5mWHjBvG}(n3fd0#f4CWD8$rD%I6}t1~q-)5EMTf{vV>N4#bI zy#YV!9(>O^3S1tcZIjVQbBe2e#7#8lYdPG=%gh(UWRxJh>@0*>KWeVAjeyy1wJKeu z!iZg(YV+|jPIFi)k*-6da4wAqO}5&vD|}4aKL&2yWusE7(I*Kx*k3*BD1=ALe{G{% zjf&ci7eGVQseyF=J;p37qFe+P}^5veeKWRv$N9O1n zH+~{?TEHfns4+@{x%&Gj`g=Wo)SdW_&JQ&JAvUTR2bj$jA)1+e;UwFKS}b+G~{6IpH&q zw9P*?+05fPRdUqymWYxpF~$>sfEUG?$BRETlPVw39f z1y8*5R0=mpAb8eA1kcKd;GqMh!M(CM3q|^L@bo4JK>C24VXMw@`tM9t`xfsBn@Q{l zTIdRgB~ymT>=wvK`B*x*7k<=Yw=jU2lcOAkdmXkhdy|%1n94w)?0OUsz&DK_NiiFX z%|kKvKKFkiZH%%bkT&pcP`AN|ka%aZJLw?Ci8JR2+6h9!N8=;02|KZ4_5#2aBzcpF zs#(^jsOrZeo9m|{3b9Yo1jiTDk7Pf{Y2Jy?aNl^ABee|UwwmI@nNwpxL4AvaJiOOQ&YiKp=h42zp+qnC zgr=r8zZZc6*v&n3eX~2WmPe`pSa+R_X0!tHxsXep@(FseZWwR_Qp5UG)lC1Jv!IZa zq)P+&9%oN&qYKHTWpH^~q3W<`Py9^Ap?_v_VWGDp_r$=5IbnCwfe3R(n3X%s3kXFx z5)3*ABMre6KKn)X9~=mr1Pj~Lu2_x313GrmCzqq4?g!hRwmZh( zh01y+eJCSN+gmP@ns6GT$lT`SLBxGuvWL}x3!@Tg`eL4|)WT>UK8hAJ0}qbC=&l>)+oFe32ahO8FrS#;2R}yAn(z+)%s${r9N3!)=aDA3bj$cztyHRI> zygvphmH6$GT5I5+MwOxnwU0rFOU=|!A~r4J6mEdMnqMQH7Yd$NYCJI{^iU@o&rDXE zkvTLAfJLPw^f%sAFh|GHsU8XV015CvcrxoE6O^5-h+y*L|tNmXCFl`Ux*$Mv_Gq31>~~@%YtTXyJ#Tc`H3*RQd%R z65{T4xnUZa>XDE3z$2?n*Z;vgtiMT(Wc^^F233km^wWKK(ly2GVp6@fGRCPC7)hbG z|F88+bmX3{vEzBSed@MLX}=?C-U+JO2iLg3DQiKH;O#^Xu7-h>5Ado!uhd=w_ab@m z8Tg~R!a(S#o-NtL8}}dObaic=(`=CJSpjX$Ynt=lbZSW4f>lzAZIgD$O;|n&%Z5~$d0X{=C(CxTUy(qP<4rjcc z-OVyt+T%u)HJEN%>hjOAOXoFjp})1>DS0I~jyJrQD5plPim1&eYJs#X@T}YxdL3{x zlt%9vP)2t)gY{eiTX2IR9!-2IG>e#AaS0>>3$OD zEVZLt+MY+0-BWf{d zhz^d8baj>nF`fBDNeHA3B8UrEFF6B!rT10EchZLByfnYu93((-}??;cxlfB&SVMwvntSHDSi zSna5;gTPkPXGTQ1H&2~BSGw^t9gqa5XLd4(cFG)j3rMAQYed z*@huZc{196LW0Xygi#DveTh(?sTN(u)7`-TdN|1(Gb!fAogi&TPirSO>Rz;kIL8L}=V%2YE1AgTsFt<6+1QM8 zi3hydj%ucgsW#(cmcHUHB28&$t77;nl)DN)0H`K1K(0)Fs68}WYQLYg|C1mBX$M|M z`F+5OwVwf}Ax*KNy7n|=a#+(17AmV%ZviK>!zlHP2A4x{&uDP}A-I>00oQ`&VT4Pb z3?K4vVBL8?r`OOM=q8D3beaM4SAzE>!-Ux@$sCai+X~LGV>vu59S4u0lpE%-T=HHz zl5>%qi(~lyVfoE{aR=ex>pdq7z5pjT^`}hvlns#f1m5Q}!jAlQa|HexBDGhm92m<$ zHAnTw8n5R6XuQ_X;Iy?!CvK3$CiMjp4GR40VWOtqPG!j+H{C;?(-rjU>gibVsit$R z0hRJ<8Em86o~XVBWj~uZ=}Rnptzqv_!OKfDN;Dd06Ac~EBJBpEG2s|AUPIHNn(N4N z@K~l-XTG!%C!jZ?W$0Zf7q<%;(d=FXzwdr&n>4EU8g*@vx(@dzWl`6gsFdPblZFvqNE5mt z>RxzAa{$cJ+bAU}mXTGsON>oof%JX^QYj%p^Qz3^K8HR3Gx_zLP!R?Z%cnwC)`W4t zn#jt7{kcBQaugn&mFx)2!P4n_Bk+V2z`}s8t*7R9Nw|DP@_>ZEh{VthU_H^F?c*$7 z9grR2)kCFic@?2#0%;ylDx7igsZqYXSiLwy=(!GGf;Ve=zWWO3`3C6RmTL}z!WiQL zJ(zAr0mJ`n)>A?jP;9a^c$3AHgVAqDP?u})QwjbK4So#4k30taQyTo4!jUi@Nal=_ zP>{>+*YO(T%w*OT$Vej&7H955qb$yZPjwi&Ts&uP##gmyx-@kc>JQG)xGa4cxO@Q| z+cveE#$_JaoJ?{IXTsASoVofGi(CF5bMFElWp(ZS2PT0)f-~GS3Q9c0HW;eWp&A5g zCYga5m|&Evw@Rg@-bF|hZ(K}hXM7mtw6;f2JuU6&Ikj&+wa0oWYA+cO2p|x|3uv_< zUY-yy!5aaQ{J+1wpP3}+>EXO@`}zF&FnRW~FYDQR?X}lld+oK?zDpl(YMYT+kBi(>czs;a20BW9+88dYA z@XtWgKY>1>scT+)6T}&zwe+ehIlrwH=h*&rr*Mc}hYQADS6qJ*f6MWxH1@j6x&dwd zqGKlq>hhe)MV)Ei<10Uia+-fSf zkP1CD$yToOr&R7Rl~(0$p;eZRMn!pA1!rp*ox7PUXjBN8Qh7i^YgPE&1v*?{WIcLczLtZHM8guUkLw?uyS{ThgSX;}cfw+C*)kvxjs7gqEhbiD3*9qsRJi=7xKlpvk9eDH~kqZmZqgCEZ}AuFRCi2@-XQl{N9{7+yWbH7{}02x?dnbIw!U?$Tg!yt?(M(CPnI^%?Zv z*QbW#Ir?z->ZCv-LvB_jjiN>FPSgna&53CXkq-2+a|aR)K~On*VJrhlG+=wVr$Ryq zM^@=_^1bCB%s2_jlI~?)^??`|a=%q)i1EdrK#XRQk{YmpuzO>@CC0Ax^wW^7rjsiq zY;clC!p7Ivh1R?%q*<--(DNy3om&j3lYk<6v~FMMPFNd+i)2*ax1zdNT|wLM@n_c; z5oFVsb573YXmSJxIkWaT^uNFHNtHOeu5A1Tb^Va3MnVg1Z{U}+{&M<0d0U`tgV~!U zIw@x`SCzN@9$TKF&N>qfDJX#zCkg*bSS1IUlI32>hldJ!^(!FA9~prD7#gNo#w7Df2Whd36!=mAaN7)^Mf>|*AUy~p2d|3KHIrD zq#acBtA~f)wm3R>EfAZ3{#d88;-24JF8%E0&b{E^--H96SKi9p!Se^~t?9XyJvcqz zWHVaN32W{11vX#xo_g9o&q&+n-QKhQ8TG-D6I_h(Kx^9{I1~lO@5Ca z^SgofI(~QYYvi|r-?4f0{s6y+`Td+zZfDB|<)E{2llkb)duPtFipJ zQc|<>SIP=-nEv#am%L$7=M>x@$+chJ{<={)ZT)TdMZylZeKo8N(EE5Od!=rnd%Eh4 zYb6M7mit6N*ZGK1)J={$TP5|dK5B8}rf~#caL>sEUvf`WFqQZIi%?o>XaeXl0Hp?e zZ55+))8Kq-#?&?tAnqMY$g4X9v;sYLr3BxM3Srq5G*BPvGwk!@<$+VXjhE`ZcD(!v zFRPjH;w!b|<)h1T=jH^e?K z7dCjGrue^3PxFWWH}v!izojQFsxAHK%=!O7Kc_Tw(N7=3$I#ClckkU<`U$&rCuHg8 z{Oqakw|6=Lo{;zbSmoHm-*)lduFSFmb^m0wNY|#;xUVd(~iC!-CJj8{(Y@(Oby=-@z zY(41ZJTLnoWzW#dx5_f~;y_0KGrbt!aE70x^7_%eZ1Y&PK|hO;(}pL4uy!pA@_+l?k$ z4}5&d%iderGx(TsW(FU3a+k&bj1P^P%+Tqb89IB;v_oZ$5C9?q-4S8QBdcEzpX}jd zxBJkg(m*%6=bY1xe?N1_|IEL0EDSuJ7|y+)y-k?+W=ckrICaBaT~s>6ZSJIV1$mdE zzO3u&;Hw`gmx3?4yA@33-9zoOg8>WZE$*sv6&s=8$Fpr6QzmMQdj@6TrW=_UG)44O zKXqrSX8!TCkhMjo&DMFA`?v*(fo<&)7Wm8yg@ni3a9zHHdKA^j00It+wwp-Z;pOyz zcXbwCaW{B{#{*9bBQ7~Cr!DTUPVy?b+g^p|_6Kg<8TzBeqY$%q$>aTLxLA@6n$v8K za2uU0f;p8ezbe*=rSH&rWbToZgU`nCQKb>#Gdb0^N=-_c()K<9gjD+$|i6&3ouGmRYVk_k#pV$kc z$q8x2o)aB+LED*9bQ61SH2KxvDt1_O+_`PIfP7c6XGW7>`8UPl$KlNSGAASN_?Mr; zV#3o`YcH~!|tP(iaKa6e2O=={W&$JSPP{UCQn@&H4b_>AD~Rx3s(fp?IRK)HL14OFUh{zz{9 zqDOkym0+)@jJfI#o9|(pFSowtk>2&ME3Yup(A+!eb1MODZZF}!Kee3%==Bb)Eom^Td5!Iok&{H0P3Yi9jli%Y^ zJl7JP6(V@wbjeOtL*DD$e;tUwZ0$Bw6ryX=^$g?zU*QP#r^(Vvvz+6LVfB_ z=KBMh3A;b~rd_+sBH+oh_~W*IE2I1KXR8A*h|4p?qgd&1&9S6dYS@8Xm$Je;3Xw_KiQ=Br$#4iHqhzo2?% z&)fe42%zm8cl8|8)#o!(o>IH%>NbQ;_mAH=mbeS)d@a`=~?A}PfBQR^qhj-T1xD7reTJ7G_j0-iD4Kn3E zb1$REvvkE&8bz!jJHf&eq~g zYQgtf*C-eJ(+w55xKVk)mS0~&`5CtSCc^H+Uim*}%kQUMlyAE;>Qo}b&4=?}lP^HR zl{3m5?9ovR?2DH8%&5HBJI0e^U#mFeES>*9_BN?OxqluNcI1Lo(U%>46GB*WQfQ5_ z)k+NwQ4cY$UYEb!*_LN4G-N_LN5@#N(Wp%vP!H0VJ-gRTk=7sN%r%wN-|Z~0vaGkMxL5sCx1?TD)D zzi&Ay48uj5`_8iLXU&9wy2fzw1xg~=kkF`!YV*HApplyEvu&(el z3;y(dwau1ur+=V|Zg&e0xyIRD7dDN=nnA7jlN>TH0+O54-*i7GE8Q1v&)5h08*kS9 zNc(n7c)QcgM8e+k>xktl5;N~GHGwZMu6c=BYu^k2sB(J|dza`hFiGqJqT-?)|}jl(Bbyd^VE44dGB?+j4)H`;5) z^eNdiWbXlZtyp3|9<|lk-^S*kcok1LR;PXZ)-+dbe%i?z6=4^T`BKU zG~$=_YDC@C8VC1LMnBQliZEQ3Lx$~c^pE)bTVYU_IVE^ebJ9k%VV`b4(>8#y+y<9= zugw>A+En@NpQv?P-AOlSsS>B=%-CmAXPt-n9u*eZop}cqcfU8#>1Uj>8NMS9G+);6 zt0;x(=X8`Z7-OAE;it?POAXjN0{(f$WtRU+_O`RY|GC@~9~a^Lo6&Db&MYgoyE;n8 z{PJZt&w|?qh^Mrd6-^EJp1=+8!2N-^w#OKk4bOOBGWsHOzt!~6tSA?b&L)@JzprI_ zT&r`)`7oNivNG)KbDzI8GXUxb0Ax0kvX1$)>5EYr3xHbpxeIiwjG=dX38)C4`y|;| z#Cc=8`}j&r|Ib6?ai=DVaMR9ABmaMr963@_+|i0^%)K-QiHzmiQggW_a#1ans0Gs> zZM72VqPs1X-lNfiiYqcOa4YM8w@)t~0h z8}<&cTUZDfSd)Hd3&;~&+X-&8Q8t~G{^iSf*IlS!ScrBXyU(KdTH?iWM%=GXYZg*| z9&947Fdi`TnT)n}nrk#WyGt*z9HEplp*7Y9W9X;D#4et7=aOjl z*v+8q8c*K1%se!Gu;I*ko$UR|8ZGWOeP)!hXuM#&w`jBzXl4%bZbadX7xPfq`84c& z$T)UBbnjhZiR1;UsKrjX1m|zjs}t06M7t-a8`g9k6@6-+kdYcNk+7SdVOf)Wv{}~V zM^$L7oU)|-6|X}j6S=F3EER*O<)%;k_n9-X$QXRI3Q02HUq01TLpLZOIqTfKQdDUI z(flwMn{IOZf@Hd`j@^5vkX$B11kY9h|YILyywnVTXb1}L5INhfK_%7AOEF)m1 zsHk&MzUx+I<_v%1b~TW$A9MOUofCTJ^kUW&>&;BuFeo!=Y;}VdS*9b^V9dB<-Rf>1 zVQKwd17xc^f?;b%)C<2gG+3tzp&y|6!ad>ac>BeFn$($gtqIMa>((t*omh;;=k9U) z_RSFeR(6m=PwMob&S1V@DWcbStUHBC*UWd@-G?kRWa|&TxnaBeUmxgW+uRzO8d{?| zLz+vq9Yv&jk@b1&iBzoHCg$NjJH@)qeCYDZm~%aBd!IcF!)@6aYRuf1@>Y(azApX9 zdOSB|^&^kqkKA2&iJ_u6-^|=MnM2sUbDE{1yZMx*qD|175yIqnqlf&x#C`@{h&z3L zX8!Rv%5++NR?F7C`hU63Qi<*xt+zFX4C2=VB}>Vd=|~}?{9a4>PZOUJD2)*Ie~8eu zZzNdBVOlX+Q4T^}B)Teh?>QipDpWyFPI!=S)=#h!d}keEd|ur`v4klD)ORdrc5xrF zf_Q7jxeNstq1JbYv!h`Jq9Fz_#6 zuZO^@PtFcz1iHFM-LmESs9BBv#1n`fyuk=WD`kZvDTmF5)}8=S#zB(Z`5Hf zD#(ZQOb2DN0 z9vZ2}NEB&33z(TTdRF!pm2uyuxuZbh$Q*LIcOLC$Tns??d$~9`n!LNxoph6kL2Wb| zf;qC*V_biT}H!J@G*zgZup3S=r%sCYL8g9;U92AlX@j z8sUwDf9?^}${slw}*G-D!z4z~rSU&`bUlT*q`~LB9 zaNbscaIxmMg#2OLId!5lypME`BX4nkwbp{s7>SO(yQO}(n?E=sU6>CYYO*}$I~QhLVKL-nyHZ+&Yxg~4gc_W z5W2Q&`H*`eOt9U(`#o`TD^a{n_l78V?S-w}gxkmc#=D4U8Hr*uiAJK>?)LMzta0!x zA>t9gl_+9*DKMoL=ZgxrxHo!jaVQ>lA}*CheBCCA;p&-c&SF^d45FHp`+iVL)Hh5Y zLs(*tPg6rlbBm|AE8kF`nxXVHTgpfYt#5LOu%^QF?tgqO+q;ZzZ%Ml5T|m&Ek`cDJ zFYl9#Ku3E5!qpf)gx07#+^Z(b+wL~EF9oFgo^4LKX31IO!7Lxq7q3}Gi`<(&HG_d! zKRjks#=u@on~giOUNmO`nLE+uIVe9-k+L>?u+es;u`!1cRcgjx2-ekoFPdIQTk14x zh8*wS`v=>ve;@(&Rxv?TR-fzaYzs^ABOsDl8G%4XM%edG7iHN= zeVaRihG*ngF@0G>!pl)2dNRU&R4XGJpwB}#c(y_u&59LPq0#IXo$Sd5^|xoH6C)Rx zwdJr$hPhs4Zu%7coFtiG@kN?&{0uZB4?y`l4W1UCFAY448Rij>E;mmzbjkjQ5!9N* z(LXUOgvI?$4_X}qyn88i6S?f#FLIIOmKr)_kf>>wh+|+>(}2a4(c-pGHk`O0S)O`K z=t~WFb0D0!W6H6D+{7BI#kFUzrO6%h8I*=5SCG-jqx@>az_kN5akr z&ZiBB5G0qWrqD$^HQ=N_ao7o;5+KMZOTxw*wzLSea`E zy&w{*bnm=Y1i!_tHgLAP*DpBG*><-31fYQOZxpxAB~t@_DNw%$0X;$W2b84bF52lh z40U8^=9u$~M*okx!75mP{E49>v+c+s#sluVAG}Fzwt*|R>JIVEJ?wG%8@E9~$YqHQ zA|(0r5OXrve1d>fo$F&m;5M!lT(Lay9c}w>#bDF1 z`;;fgQ&Yh3ws@+VAvrDD*`f|Q50G3GS6%D>aZ_hg(%1HMoD*0fYXIz+qpvgBE)s`d zvMPJ(Ja~g9i*4>Wo(NO80XZrY4r(F&4bue+UP>)9z7 zR~_)*zrj!kN5E2^9vEGj_z)^Er@A=!T-cdX0^t_B|4Oms^}{#%;LbU%LnWiloi>Dt zIO?0mvZ=br{pme&4?v6Vn?>rV>Y}>*Ue!Zd_anV*tR6zCoQ*y?erVlA%(&}^8g!Hod8ekKdxYm2`bhEdMX4W$&T1bYg|8M+7oP`&Q zOwH`Gd;OX|>|VbGw;f;ouAASzegsMRoSZB#Kj`2L<{`!3lAIoPh&=7$j z58o=nbVI3$A5XZS9`k>9m{R7#+VYOx_E@JpPE1c4j}v2f7>^UWB~W|$+NI&Zp^?;E zrSml-2jedtNyOmYCK!W$Q=-BvVb6QF8cn2Aji{s&C}T1uRtZ${PJ3%z>-Q|+*<`Z- zmnni>lRFy5^{&Ur3vC@gT*_y z{t$EvI~~z5u1|d$%}&JG=sDE*i9qPO(t1}M%@{gmJw$jLfmf|t`A`C%5d6;O7v=X2 zes}Oo@EZ&YbNc@Ous>R^0-` zRrN?}=y#M!tiyc+LwiU=>c6@P!6sZMDH;sRuY|Kb>DyRcBIe@$=Ef>O z$kC4LEbvhP*Gv?{)Ze5V9+7b@;Wa|_FV2kr_H6yNl_HyQ_tAH{M!-)^jVhtk8(;`b z4A$gFRUT+*iSy7I&Tk0xze&ND0DkBCNzxLny1$_Dp*8m0|Ih`*tJ>cql5Gy8w?~+X zZRn4L#QE)bkciI+QDvYlW0@nHB!XQnRb7jWst+$%Yl`)?+ut|Mbnd8)IIT74Jk53Y zcwafmL*)(Ta=T)=#*tE<11ncZ(79}S)OVS=P-V9J@d7ac^uA0YD*BcxLWAcp^L5qlSb8k#(r;!P-T0nqbP#<+D^vMw+RZig zGK%S}=Qvo}`AcQR*t)?hmwn3TytAR}Dl01NIeN3OcHVam>Q?5^pG1iM5li`-!~8vW zTiJ^e?`C5H=)N=YLu9K(QxU#rFGFq(yRUWJhj*>lS+&&A zAN8fX=W;ZE5zyRMv7h%elglMXGP`L#?0bcrx0swChSr=)Lh5=7PwnkKY6>)o1}~y= zXQ!sAUv}*8T+5Y3VU3`T2szOm9o#a;(YTGhtNg2T%M&5?fAK7}cV5-TTx5?Lm}71z zc_V>0>T+w6xh2MUb7Skj@~N1b?Z0m^!Femy-tA?^JEm#xkqC}1X)E2xtPM66hr&O!Vq>CfOTop$pRedKO0Ka{BK6-sa&QB&_wBG5J< zy+1#3qJK3Q4JC3yzC$st_!{@=U5)c(=zeTf$5Yjf-g@{7-B_dz&i{@&?D{v585Ex1 zhHKujwDAekzA+s6#CgvctQl{FPA`z_Z|n`1|0Jh(U4J{Jl<=yfXkquVX;S30TVcO` zjJp|S1(NRIK1jMzaE8$QejbwUb6>NPt}h4o009!*;!4!rKh)%DFDfe@WqhNJaldOp zv{3A+i!6vo1>$}W#I3|x5U*Yc2ze>Meh}XI9N(X83x8jQXV}6wtMDaWVRKH?e1DY+ zpGaY2V!OqyoviOK8RPb~AZi8TQVU|VK=?fn*JrkU)+x`_AseAM9b9Cw@r-ze~^H{CHu;Hy3GcVxl;qrhkEJJ7FaX?!YL zdYF=%HxUlE0ZXQL#+llFRp2IAa03PIBR-?rnj^XfyH$X*vp#IivMd~5^iY1oxQ>;_ zEQ@}z)l%fUV$Hi9920KBRmXh$?PWf`eTx0|4+y)9KKJbnG!ARUTV;<7s@xMVRx>)J zZzLw@e#t^QUyx3>kfsRISstV=8okRx8m*(JVEOKWcDrivK#XPvu84Fo(_Pn8t3iST(yaW-y;usTSoiz{^&=NkGAX6bKLFwcp)@DJ{C9s zb!(Mj9C%}USjKit-Sh;zxf{weNd`zKIn`{yj>Saw9dSL<<4$$oWNrf&y}<}w|! z=UW!yQPIVA_ZwM&f61``|5RWBzG{Zh&|O3$?x%*nLO@uxma*Hk+v*0-_|%Zvf0EZ= zoP1A{kKw6w5lX|^45tO={x%9SoeZQzPp>Yrf{DTQN1c=Rz`oj}$ zBP=aJEt93cv5hwIyzdpjhQ_@oO~6uuPZjeg?4SBE?R6)8+)wS!>~g6#k0Y)@&G-L; zY_c}N2AmP?ww-4f;27%llu_oT1|&K;#rWa*h54zrWex-n8=4 z!MCI7{l4h+oBh#ohw3M=%+1|kcf$K~51Ej+jN%4)?m(9Lk<>&EEo{qR&b{*%gSPv) zUbb^0vewy?Q-x=9LnIdd+oIzz4@RGOd4CRtrSfL~D{4i5n1%Du(1n9?-1+@1Io(Im zXy?YR%0_VJ)CVj9sE+HskV4L!@|GDJPK2W^Ox{uvF1$D%$X*hM&A zq)X(};=y7nk9|(v!G5h4D{EW;yDhJ0W`%zrmYo%TLy9>;w#WDbI6&RdfkF#VN;wZ1 zx3`eT=9n80^SxUh4;<#r2)U*`4#q8o>$1?tde$tLfIa(}i z{GXfEp-ZYXmwWJd8hE$`5xk6&T*RvCiCDvLQTJufs2#?zXhi)HBhy5vt^MUBNzxeN| zC!8g_&VRRLcfa{U$p4cK_{nX36>_n{v2=IdqRc!0u-EitoNK#MmE)rg>GAP#ExMJt zdEp7sRF!!u8oD(Z+so`XIS+ijy^Hx?lxxGWaoa$t5ubNb4u5nP?7IQWuRFt;f zKj}!PE@0Lz&HedZ5}4;dYOZIl3=qFWag&RQYf-MsV#Qq&P{#6X#&ShwGcCD<+aE3P za{=Ccy=^YJ&l>60D*up{Jb_OE^}y7To=!`l34!OOUhZ8$u90h^oah5gLMT6oU?8mzN3wycTa(auuLgB$m)P0q}$-iey|!^o7=8Fn)$G#5Z_|hVB>*LAB7c^! zajsNNDP5_RCIL+*a)tYLW0b9(*b7jz~@EOt+3WxHJ9! zh>;u9Z6ii*>1;l@dt`bf3#d4<9OA^sul8dT_KK4EG3Rm|R$X5la~76jQI}m;XLO)e zjrhaenfd7kmI6^70c3unmi_qkp69>$)F$YE*Y4%%KsrdsS%0H8(fd16#53p}e&+{+b zmG7U~oC)We@ZwFWQ0KuN-~|MYOds5pMaBL<(>Bb`tr4O03Fv!SwsOv5W;As}Ny{3E zf$sNrWl(VKKo13xVidm8cGDGe+z;4bklJ>R49-$Dd)$GAq86;JP4doTlRqfvp8N#K z5)tQ6bd~$>=559*TlB$fQ4d-%_T)BuvCVO7kJ8ulD1F)IOW)R2ntF~U!_%zWQ9)Ny z2UVCVjLZTd%ZYkvXzNwK2Lmk9CCZvseZ_+`N3X^1dxI?w5OggL24ouv$QDw8zslxa z%iGhCula0tym+w5{j(mqAJCg>@S1SU*Db2{xWDRefbOwCZxql$0CheEWLQb2uE4vb z2fVO>_kI_=Og~+2;2G#@(QxwhxF0wRuXhi4AMw#1^~=EHh?c3u99zL>$@ld6rqxUN z+pT_&3qlqh!8F^{ErsXp$LeL!X#`BGuAyP)2};=KPR|=VvXtl4fIm>Pl7RTz&2*pQ zH2m}arTrAK4F)meoGpgK1Aave;Q`YC2>XbvG9^srr8a3T(KSs<`;AGf6`;nhv`CA) zVVpPxo&E>3ZG8JT{+TsR{95>}$}ooGViGmQVwW2j{IWk5CxBxj(=WtX-agc?Jr0k; zG%dfJ_k4y~j33}J3xk$k{y>aVvS7d;2o>wNY&kKO|0%jsQMGPgYR!_mn41y1F{+R_ zP?k}74BGH3mbf$RLgL+>nGdfk?Ec~2!B5~-{=npbAw%aiQwis&KTwsa#^myDHCT?s zg%M?q^CI`qHhh}Du&Ci^VBugiSz8*`4R@wD7PHcb798%}P@Ndxr(qpa=KJ}7HD4%k zN3Y|{cYE*S$yc4YtB>z^Gv*#|#=PUrcwB8(9s3?H#QfvU*zb5V_CMZ?1;?AQ@OU#0 z@G{04nofu{tmT*H*HPhLAstP2?!D;+$Q2dbDsjSwXXby=enz|`pEKEA7rRxE*Uj5u z=sfPUx-Xx91kK(IbhI`3(PjI!8pXD4S=z{Q>BGaLSSYQeOvs7kMs)xc36H%8tAh)Z zRe5}y0Z|xsHbs}|)KhyjeTYaF)inVRRM>g0E9a0f2X2qeS>)wpt)2xroVzcM%~wo5 zEocxNviVAqE6i;+pUYgD0lL2{^XM>Q$FWe$v-LdOm2*tk+1oRx6RmL2m{|0LIfP@Y zfWe|#Y|+CuE2cH}d_vUM?5Pf@VM1tJw%0EB4-I$HA#e3B<$Gw^K9c}ug>L(HhUtz? zwhR|%NLAoVFMG=bypAz>lD*X>Di}?>**=?`%Eh~>jgR|Khcv}VwFRnjN$p2$+}W(t z5)Ya_ZZe6=m8{D5>Po(WpDSHR?8%>tg*|>j>=0tdelBkjvFF&>xYrcvN{hYf4d*WF zGf-Ul<37y0y4uETh-=`5fn-(jvNsiorCWysZIse1a>x?0`0^UmN`p#5%id6Ss(wz$ zmbB1R-h)_5B6Za_y2Xwr_VmximJ@5f*(2{5sdGyWIQpuqP}pZFz@$Wc&B>|?55eK& zWR!5vRCH6HXUrFauT>Fe4q zmi|qGLw~bSkTT9F=(HS}9LUBQ1zk)EIw|B>e<-6-(8Y=sDXBt{OJa(=xfiCe2nVOS z-_`=;=AkY2xdm~=+Y=q^G_E|sBQY3q4G>HhRj z^R=}Xu=+oo)7$Ihnjl~ItY;0L{G*;QieLYz-y7B#_K&jkL%=%}<>bAdJ1Vmho3D@7 zvKsk7C)60bJG8bd$0nsFRhy5+Bx-qO=@Tp45^PdvYB5UFfXT)6SSnTI3~+(ywe{wl zKeEK~^v7luSDqYGVoIuQEecHpv8>Q)6|PmRrzw>@S{~zi=2+RzIGiD01^@e{|r+?Nb{#oxXdmYhe>D_%h zH#clP(z!O|U?D|I%Qi>+vznrFck{rj@n&|HCA#o^Qfq6<{+wN3%nSK%DjY;+1*Q)| z0lYqUp8x6{(bOgF^ZYlo%=1@#^->wP1QbFdz3Gx6SAXzXe&v>VVgK`x$4#QIt9J;3 zvWLiZMS*KG8<1nVvZwWt_@MIc1q2_A2)4;y=w5(Q$456)(fH$p3t+{s8ah_4EACA2F+H0xl67>`TdN>)E@Drr33gmht2`cC74ZO)B@%SlLJM zvR7+TL+`z1oQIr2HK|LEME#F32gl2gM13!Zoaq;^8>xsWb2EK^Nr_(LzRfW$ z4A+bK<~w6bqGhOZ?(P-$ZHWY5fu!>NsdkanQE2OJ1tuKrH7W=CdYKpJMN3|y9}&+n z1SYG4qBv{5@+Y-a6*)!AHb;4cpIIu#aJ8+L)s(#$OHD1RDSIuZ{~uYx z&G?h2-})Kax#?nPZzHr<@zpD#w;SmTQDF>pS}GLnO&=J@^v8p3B;1y@l z9bLIE&XAru&wo>_yj#{QA(n1AyC{iXyD666J!iKXR8fVKKgw3&5P9PTvgelL^%DP0?bFZ%fR_UY#caX+{nu zQ>XjAaRXeI-lTXvkl>u$@15 z?uqzIt6TUpr73f~_Lv({HGg4qjekbjP2u$iO<9;avAT@Nr03{=?Io2BxFU6SP8ch zIG2=Cb~C@{zy^-7GXhYE19~xEi^Tf(l=B9Kg3LYQ&XmzA>4}mE{zjZ3Q=uo6 z2^6oRooNAq2NRm46u|bZR%$ffh2~q5(-fB@E+nop5OHn^#C`3$E65aR=T6c92}2^zH->QQaV(Rt zhf89IkWdtIE-vDuF?`2lK2Kcb@Q@Q@_Mx5(aPRxPCZ~{4SsZb$r8ce_5%4O`E4TF) zlUfpSZZC=ZI1blIgyu+ApyF=Z&Q>wrx0V-b%GgNmcD7icmGV}nh(RXe-$rl zVgr|BC+W6g7Jk%u6Z^+8sP(B|&A(s)YmCk)+pQor?%SaaZ1S*Xyl=wyUyJ$-ZBU>0U z5Q{m^gQ71<=dU~CdutbsgbR_0WDas@>qYduE;=&(^Bo!+5$=EAXpD{ptqy-G(GT@YfPf=#b8LcBX-<1I;JX4<}#7)*%DmM_q zT9D;p60%OWD#m2MHDL1MVzY?9Fq1Qru_Tibq#9VInH(XIVPK8UWFdp75n$z99tW$s zMtM!CG4Kj|pQ&eYm02Ho_V#O5iAxOolVL5GavnHXY8O0qEo828!{_sE8Ks`<59Iax z^C@^3M8!J5Z$fBV19?MCFoZ+GKwc4H9217hTf=l_bj|lmV&Bt$b0ZhuiXMDR3_?2@ z(XzGC9ng*VOJ4N)Ca#4O9f}US9@D6 zv&}2z_P&gn+zs|q%LN0O!zQ{jO)SQd-C!<|1c6uIlvMP^Ph|k3iIx_6pij}2{((~h zpbCM=Kd@R}9C2oPdsBV8cBsPp`lQitM#7JAP3x#D7&^kNkDwa)I_5> zIfXfDn&u}DVsaXjQ}yte2-a|%~;dq(h>l3QRZH_yMIr5Aq=j9?!&n%+y= zk^<7JrIwjCB$NCB$t@&JQ}h&RXR1p`+o7WEDoWavV!rHDkJ;fCFyW%6I<_;Rpp1X$ zH=gp?#%?zT1uHRk|DOA;P zn6akVsToZ;!b$!axWuSIF!)xvB5Iw;75ZYi6Pd2(HBO{f&pA#cuIFqg64jG@VLhWx z1bDCqCo*2o3P&|&4vZ(SY{$<;M>JWFHDrB$Jb7avx-qg;gSIW->?b=Brp4Yd=M@J< z?NQ{`4n$u4+6nIpIW4RbSVYZ8@TdL}1e6HoZvMxv{Ea3`BMo&R0s1;ZNGtxwYF1`N z9%7zBnu?(0@;~+_(|!c2aQ|aBu2LyvJzJM@G>{M( zX;OtsQ!7>JLFk&)N{wjhfKXRyYosYH+EpfMzvvQMi@4{>cL<+E%;u4mI(HE<2Vu<< z$`G%JfHaz^QcV+oA}s6>IW_Chgk~o)o4<%<_PvSRNPR5LnzS-3HBMbO;OV-|H7@2Yk)yXRvt}fiEIeBF%`6YwM zVA^YYjaFx*?n>6c6gJrM5@Nc_BT;ze%jp_+L43_?jHp6C8~oMm$-)>zQz{G)`)${z zbR@5=AO(y=Quqo_-aVc{9yLd`;|}!Ye5=TO@Y?5))h>hDw`N|FK#4@$ zur^ojdNj!B)hi6(h8vgmQp9{S{R*9D?w zt{E_jc9Yiz$UP+NEEp13b|boB8S>lKz%w--?YX8~6akC*K+ zODZL>uKR{rqJ^F7isNN;4(mE4usXYz0bhw$0o5LZ&8`w8#ZEShGqf9}@v`Pc|k=7t#A5%u!$qBJpH?k$P6wbCOP(czjeT7KB`bLfo`L0MXWWR^56 z5o6<;78Y^*qL5n{n?gJHvU`#rD%cTiI8?k~UvhAS9L^TacglE@th1F76AAK#3ormD z-y322fo#hB*KzOd(B&h+o)pA$;$dPe-TyZ)<^k?w|mV_0e2`FY7IjeKrxWt;dB6XaIH z+Fl4WVgKq&d^OH)Rx*A3t1DRiumGwn>BZl>N^;YC@(ViBd-MIPt9qsP1PVGr>9_RK zJH4l&rXJ0J;$OL~V3HW7%I^rdXwj@DYd)II4N9h^z5D?!hESIOvb@RKm}d2uB8S=`}bUsIV4tWU#< zV4}Jg%rxx=64kv;peRx8gOBR)dUc)&kkrQnNa`C-+{rf4x_tBlILx!mxyW&ibfjNcNp)9o1A8B>8M3{ zBz433oN&^5(PoT^OtSmKw5FG-qknZhXTG5HyW!b%eSSeZX@+PNFhtYtYzPI{BYBBJ zL_HdGmTnY)=q&0Q~geJ4Y&(atSG zK~cizg#y`7z6m7;lYp?D$zy^xlL?Y5oT#QEmbHN6NMa0v24fJ(aeKNILFXl0MsR!; zrBYGE*%l%$;=CAAWMVqZHxV!C9ZA5(A_=%~h`78EaeYF>^_4x_QV@!GQQ6HPX5GA% zzusD#^D}-wmkK4^34reZA?tCs*c9^r7-SlaQG#4cL zWBt%8#x17x?T9YZ1E~8Zd?-KAhNrl0`C-r6pzr15v>9{8kPAYIA-xfG$a>el zaB525HJ#Kdls@D;_)4T~cf&piYD>-Bbvrwzq=p0>cu4Q)NdD%IP4td%4Qgr4+)aj0 zk7%S^P7dFX_vibUbVh?-HdC>;W2x^Q5#%+rj{VWKcU%1_^{HaOUeZKha&PDrjy<_j zv7P+s??sGP#AnKK)QhQ5%%_Su;>CgSqqSs`nMc#Ax6QSIoPLdC!X}QcRm--tl6FD5h00`@I;=sa0<) z#`R(f74xQI-tuBJzfOKb(fhoZT-2j`ghT)G?d-1`t(mp~14gTr(U(2(3nPs9SBEgT z+l?T=b)YZ%S6}Q&5X6i1AxjXlv$sNd{?)O*d`HZJJvhuH_;R(!2xNr=za1lfP!2)X z^>t6fsHiQ^{Y4%V7m*SL$`d~8yQPc8DSAwzIv3UkQzIjaMLR(nCTODj!tsnAubXZz zGd|dr_T()&Bddg7+!y}cG%st1=d{wq`4CRD7lGS-G}g*a0bw9q@PUgmnPF&y+9DH7 zwMC>=Y9x`py!|(lNZuJ|W+aiN<9ACEY5F@#B0^G(wR9F2@@4V2!UYCGrP0)+!_n0I z+}2T9+PK9IoQ}BeMDeelDES<6kSoYV(bPmPh?c)aO`(z0HINJQt;j`^OX6)1hgvh1 zeh1O;nS;Bs0*l4-qXy63dPLK;>0r&-c-)Y@Vy12qjuBRfu`-HmHh3}eZBIFcd8Ned zM7;H7}t5-!*yExa1ztr+$iWQ4(biCbxw z3ADtMH%cPGZ3r6@IeH2jc|ZJeM$lM#MTQ)YSI~epSz!c)vk)S=!~~6?F@<&?j5tSR zNLDJ37mMV=V16_eIgGF&Nuw1JV`F+h5(a{Xi`apn(OMMUxDhY)oyZ&{ycSBgv3eMD zb~sTKPSo}aC!)Q>iABgUeG)T!C8lCK-Bad}E8KZ2D_*S48O^Vl---MN@ax6z;}>dk z+W7r(S8dL3`8~q#U-&)9?>>Hw{O;hF;it$OH?SuU*%X;g58aQ(#Ru`!8jSfTTB z#Q!^#o`+~b;7_bT=)d2!gr0`yehBPRYT5m>DLFK)#p&&n*{|f>$UV- zC!nN1*n+aAjQ6#nkxjJU7bdV zgF8Ya(KhWUJCJ_eZ*%QvEjxgsCRBE7zCJ~+4Iu$h>SoH9&CDfka|jDU|MIKAI18=1 z2*wZ%oxMZ;2i7(0y)e|UF0U-t;j_H>f7cW)8^X&bUTE}lVUkP=FC-N@JU3Z?r>N(O z#16~TZv{fZ1L-&UyFZjXUuHL@q2$fr?FNXG>*Km6F{3Z|oP8CZSum`+I2Zk&Fbr<^ zi^-KiwM8@M5G6;kbCJ3=aPT>`4@7SE?eNWP$EJria=HOCk94b_MY%5+?U_1TqAN3nq*jSvqFYn znNS`R(0rmU%1TU1?-G_?AiYlPYrgvlhh$*EOdP2FLy5lPEV#9sWLJR%dMl-lfP|z_ z;vz{&b(vgw3TR`9P$*S?V1)oqVEchA8+DH}`Iz`x?j8a#Epj;9#*K{2JI z^TGTqkp?}7iTdi3_+5l9{z@&c5vHLRC3>QDI1y=MiJF-4z7Ymmlw@Z))NC`%E7THB z%y5~GYu+%d~*lK|R*` zbw0Lp8`nQ`GuaCqmuzttf2w(!Pho`MP@ajk1&Q=|sITW2%nDq!6=CuT6X4_TnLtq@ z@_wju1MP7WGv1+lSk&dY=lU_k_%e=&I@i%T+$7yVh4$c!y6!^07fJN>XxuhLL~g+o zVzv-T!*PlRl$gcp8~RROiQHM!t}U-&KcSzP4y6^NlWV&#!I{vEAI8Bf zL)H|jSO4(OwpY2iwegVg2FdtuC!i^>G~_%YRbwjh(JwU_`EkkW~IOa zC?7OonFXU^&kWhdzxo`rdC9sW7kzB7J^gmR77`)#cKY>v6DPSaK&&sGI-_XBW1U`P zU5T>h)uiS#SCj{lb3%P6ZUTKXfqtRjqTH~S$D#fLYJ&w`!2ywAlvuhhbb`KUgMKdv zE+fI(Lh$Kd4f&`CnOCqdDXMlJZfyGO`kYYkVGdv1KnumV*i~JtR1=$bgHjut`tdXb zH_0nwU_lm$h#QqV(gVg?^Gac!rm)m4fl1B^^Sy?5;b-hqnbI3<=@pq8RQgwMoa8)U zGDd<#8z>Z{3M^%UTWmw5RYiCBK(}HjIzJ3^1R!|8zs;nWl0c$lB)B6Id^r-_5^`Qi zw=tt0!Y&yzL&z&1Jj>cRr<0~4(d-L^3SJ3*8hkB`X6|4pcxz!uB~NtLg@O}vL&2}p zJ+X!j0r^Dx6|!?YIld&Ey1O&9gF&#-!ISzruZlczVPC`FW488?L_|70Xi(y-|+Ihkh6Q&J@ zO*ueOPMjdP)s_tf8Sp^fO*2BlCo)xsMAk;wCF3lhL13Gqav5o#;I(7Dx;sOd zzwg%T>oXK7&qqO045BGZV=c*MN!8*ZOY0eORDaxU2AAS*y zZtvE0UqqvS`J2<|a6_Xr1BOV4K%_;6NQ+};BjMZ3!*ORkT2IXx{?$#WSWWwA4cu%O z@t7V+Cq=XvY4gXwcc(Q4w0o|-#pPK(gpqm^S z5*<@*B9T7HZ?KZVh$PbrcF81Y6f0e0Q$l}vwiQ|gBD8{?i7Z~$$*vwKMG2-nQCOmi zZ6eh=QALcIY^x9()LDrtCPVc@qKa)In0HZvgRTmY)X%c>L{)!J0bT|4CX>WcivXog z2q!LM<#-8uOOp%2i7MQ;G1W0u7HJiNKCoO9SU5sKqhgiIP7*D4`6QWb# zOv~l;0=!=K1A8!qTqhYBlaxC#y2T3Dm&{Mr=7y7%`Qc=gTe4ZLjO*|>d_-Z*td*K5 zqn>QdVrX7=|0U#{#F=c6R^;I58LDIUX%7cK2syKIoy{TG2CCNFFtHN1@O5bGD;kPg{mN0vms6HSUtGnQ4QbWOJmIPdc72F0}3I}&M zJJ^Y270w#c*=%M7Ez0vXmm7u$6W<)%9A=K$>UbM92@qp3@weoW*R{65JU|8vKHo zYqA!CO44IET#e=$5BcR> z0*^|s2!&%{#!P!%?d2=YKuX<{qk*&kA^i9?ygtIWHr1Txw;}rT*3Z9)d};$NTz)Jf{}=*7{e&4?FtA=kAK8^2TzX z61@x4luTL+Dok%{v_)g-eYu#lN&18-P3#rKb_p-61hsdfHzUAKL@fR4vLT42HWHOC zS@O{D1jk9NoY)8GL238l!4vzM7um?c1}FBzQ$i#Nf6(UXLeZSD()e{FoHa7+MGjB*J z{U+VxhSG1S4+eR!1P6BohlYcDh&fT2Z15y6xa_kgATFLt>dAn+wTM59hifM#9BdDn zbk6J4)|zJGY06}SBfMbSKo-z#{rD4c-ikQyMVzBDaLZ>;^8JC2but%( zgL@;v)=2QMwrHJK!@){=^ZDRx`e%DM7#$uC)?yr}X|Ib!(2LPi&ZGaNz6d^gzbAGx zAtU?X{9=ERBB83_qu;f1uV&P+^ROw9r5uku!oi2TLBM#g(PVSrwj=x2g5-|Sn$0dnzi8iAg7*# z7;S=fjlA6C=n@#ie2_ajcui?=elZqB+UyA~DsjA8>Y0xkffh1ASL*8w#6)IXS{y^$ zi1x&4Ky>9=>J8Hjsx(G3L#qpfS&2SiAUiKIFfEke!=|(+ZrTB*N+4!uKF(+~rR>Ne z+6>1BY&jX@u=W6Zj$J{n$P^!WTugEpfMSEF0=eWHb7j> z$Og~Z5xGScK}A6mwFddea>5!xv%o~Fp!Ck?_!4JewqmFgM zg7IY99W3*Ny$EveYMyQlqw_g7WerfjaZ z-yH;>!N66E(q0wI6>A&0mXE5|Sl4dyqSi@4KvV_dn~ADUNkLR{<+yMrgt5Uf) z(qfuyOb3KTV#!8(Hzg;Bg)ha(oN02@n23xZ^*mZ>F^F@=$vWN#0?9_?e6Xj;+6dZUn!MN0mQV~3M@^@9$i0Hx8Dt5N z#VjO9nlyQayt47n8c+`?-e6s@GT7Z_$~>4!Gr-UT&_X~xtZ2rNAZ;+{ymytNAT`@A zVm*?)_vcE0LQvo<`$i%5-bZX?77J?)cJFY|o zK~Ha(D0*YFqVi2oC@Q#x!M^O%rGWC^_e=f?gC6oIw8N#T>Xxl47RYu9MQxWntN0-1mJ$`)I-7o=m_fIxtr__yrU_X2a4VSh4wsV3WsnSmz%(7Q z6lY1l0jFhQ|1NB50BA<$L2w~%BkYT)NN}$eZw71an7o>O<~ovD6_K{JB8sxgp3Q#48kR|1m}J8QGhykc4cBL~WxrMXTizX8LmSO1!nuqS zot${U(T{UrL+-exa;M(#X~nHy4jDU_XsU2rylfr!L~mgI?)g`DxHEBE@D=xBbd#9_ zrI~C$Q8whTfX$njRKeXriWo?l#H6tb4oXbAK+nO6N#pb!l9(iWs)<7rlP2hSVq((Q z^gJms=|cA%7A#Lys&_U{oXvv{+wPwKofcfQoxl+YdaqoVs7#!|9G{rjiw;C_!RiBB zpQ)HTOwCmcDQsdTrWzBVseMfrv>j9P6)AN~Vq!n?sZ4=Mw6`dQoSCf25Rej62k03{ z%q&C{NX+ynrkTG`7a;lA>7CY z1W3lmX55Dv+rWvOn=wy}%?3^c$A95$e;@85ts4dyfa%sDbN5qvU!R!s5v3@ggUIY7 zZ%lrT9J-$<$>-EbnG4}=&;lN(HtMt426^mOhpy;CpuxvO@)4S3Xib~t`M+FduoiYs z=B%1?X|8iS$_(w1+@MoTbtTtm0Xj!Z&)HgWnhn-y7}781R|4Ue%9w1l?nmux#*8nT zY%E!&h8sh7Oz2o=OwQ%a?z8e%h2UFd_gV>fyJnb|i%}3ETbHmbn5(Q^7UO>8CZTF?#QBz%&7-!023>m=@M)5t}<~NY~btypL8pp+B}5PuT31&r2j-7>kWAr z(ikO-xD-57%~GIDZE>Y`UkiEb;E_6Zox#Od$n6{e59#+#E;vBLM;jn#sjOOYw#te$ zWi5_<CBNEMUNZkMALiwheE&w^qz!_CVEGImo+_f`#qP1e9!oVJ6v-zjf>911sveotTq>H z8Ob%OTME+4HY%&H(eMT6ob{ZuE3|B#V$*F?;F?- z^FLp`P%3v+o`*WDzxMP?zjsnWyR*&NV6j$^cDB*?)W>nbR{Gt+46F?WTSsmmx!$(C z0M*M@1A=Wra8_h63tBTh33em@A8YRdA6HfNeXyn$)Me-NHJ_?VOCV)yYB@OL(7(hY6D|pxE@q(8JOHoMZEl_C7or0GXq&*=* z3n)zslK1;t`%E&E1m*v}uODg7oXgsKulrhi?Y)m(p9H)91IKO^CCPHo6N<9P218GX zbtJ2wlE*#>MPccvL4)DQABmI!q(r~MSe<;_CA+pgZ3PFvY zu`R}!_v<}WS;%iIJN4V}Yr14emEgMPxhX=S7Cj5uNV5(C_5duHkn9ac0Q47bA17Wqx6NE>%_>8&^mo_ z4bY}*5R6tNA|yCyZ7ac&9z7db^&40|qxrguHzh9uB(@_Iw0RYTj8-#5MJ! z^hG)+g12I1GQUMkX8pmT!Hy;)4mAN3Gjm_-5m+g#6_ZH6^^X-XXFaHbPG($wZ(*%F<^5_SEyxhX`gXX>N*^74f%=1+%*BmT(+1-6yciC#^ z8-H`ZH`rTzcK054<2T&McjigG*+d)a@;7*(F7LhQ-*x#r2D|0b#M&x@u8jo&wA$T1 zJ7K%q40ZUI)^f{-5^Fg>!3NT71G&}RKW?3a!frc3_2;$^taEQ^P-FRNs}}pl=$x<+ z|3t}GsmAGppNDoUH;9fO8%AG?AH&$Cr!^!on^f%&4tY^%C^~-L%L$2#s$5t`Xmnxk zn+(Nqiu*UeKIE1>tR2BDBT;L({6Yg#U~pdVM<7H#o+5q?J8_=-kcnhF4fmDcT=#j{ zD9ma>*@YZscKZ1`(M+L=MTz+*y-Ni+{GY~GP>Ni%|4Q51HYghOw&XeczMtnS^_CHb zup`l+%GctY}}TK^eRzWpNTz@ zj%)=YOBSd!>EW=U7pqd0?7)I{(DsDiESc@1Y^jXEAFhdzqZ-Rd%y~ zN{U+`f2Mc$v`arcN4~aMaXGi-qI+E&o`P6hJfRk(KiET?_&8ODR=)dKy%U}Di zQ-&i0_Gc%#zOWNp*a$BZ*~u;-wVBAaOziPAHlulDAL6Op)RNTNS3Ypmg|q*$=!YKi z!NBkS+|kz@dicXV=XrEXSa}(&s56~H%Wyn^gABz#?3owRA_Xn{74_-J{tW+*J;ece zhX1ae4VlUZ7v-i_r2S+=Cb9`U11%F_Yu(Fc>Og9>wTHI)GciQwkyO`CBY39r5zu)k z+>we==+aHYc#xKQg?bWm_nq5-pvJQ zUF4edhBpZwJfm@Ep0*kPRX4z043JW9_DNf)z1#gvlW1R+j%>4WvIh6!CBvErpKR-6 z3x{oFJ+&}lElg-TBi`@D$n}7gfb-LO>eH2-nMjW#EY#d!quI;TDz9r-12lr1Cql%O zlzIU}3;YroL0m9X{X_d$!*K|OF$bd~+GPWs;Vt(=PXr=HXbGw_kO_xW-RUo#8CRtO-LD%YmJyl4RMg+H}*=JBZJV24z*7r_EmhzXBRZ3 z9SUGVQ#WG2qCx5002(#Lxx}^y?F|QoYPLbeh4!jpBNJI;FepV6d%|X)T9i9p&3fs` zWhL5sN1V3THD>}tO9zW`O<{AeDZ7ylHU|OUt-bSTts-n1MODa5V({22>;{j@wnj6R zj8!s!alP@Vqns4Haq%v=c<;j8)Pq54aa(GnmNIkp&t>1s3AP&$x}nC%RAl*tgR;7` z;f*$T!n)l;86TsWk`*?Vvbv0az^SF(sHFo7bElN2W7|xvz9@8#hvE5i1bgR*?>$lM z&*%|^vk&3?bB4*iOl-T;-qsA-TY8j38)9sAK-VPjR5wfMrYfm2G~WruAsLDx8VDt4 z9)$QDkF?O<42`p%t~SvyieYa$Mod5?V>o2X9eMx`X?=Dk_H>5LY_Mt?WTtY9TEOP^ zO6!}-*Z5rPXn$=_Nt0DWtv6@Fnm@W{NuE5fid$?RSmM7MT`T^mq^FsEzlm4 zV8fJ-UvG{Ia2?P+s8|*lN=w`nrOzodT|v6*7I( z8|oW2C$G%JUd{NF#EW-(Kru4i^LDz1`GYc27P*K@b)8V`IQagat>~Ohk5nrtm8o52Noe zwJ@6XM#Wf@4t0aZbJ5!vPv;&~m>o#ti9Y{h={MgbM6w1>(Vi;ZZH9cnHIV z6_s11)JNCO2vyA%WTjlEbj`9^WYbN$uNGa(FXr zwLkTsJSD+6&b5z%MN37{n6hP1Qr(yt($>7RA&Zvh9r<$Rj+nIIjzI5b(c34|h;%wi zo1kS;zX)mGv8fs%t}+ONem<=NB6Lm>S78?ZE|AmFW#?^0b>`cOYB<2rm^Yo5QAFgj zcsNjLTTZDm?Ev}l!2A_^Gm!vwgqXdZ>?(o)1K=EQ&9!*abFX2E|nR&R(RPA815JqkaBW>XQ5(F3T zA@n#aOkQJqv6;1){xG7_;GGgHim8TMSB$f12#}(`&CZb$D2op10B)|c+;XSl8BR@c zzN{<|HVeC@D7{-6GK`)G*U}OEnAmoW#|Bp=J}-{fD==}Tf{9GZT`UqDnFHB~v`%-B zW|ZghbcRzgT;q=3g^e#AY`odL7`aK0$bSza}ejLuvsNX>_#` zAaQQ78%U#5Xf&@K2S@1QVQ62AgA@`Tq?#k>b?wGYJAi7KC43@vjZD8g6*b5~YO^V9 zv$nWRN7Ix=F~t;Z7(^;&M-h`qTE>Pz92fP%Wy|C${| z!+cMkof<~4(~`l)O9ma^!gw4(J;AZgFbM_sB0Js;c9EcAH~gd42pTNe5O%VNcbme- zTyRn$6*c677D`C{h#^WSaeHAN*<+M^7$V8xRj|hVdsBJL zrqq5gr3Ezk+fW@#rZ|-)!=@o5shJqzM%j4dSFjov)1tl=unaldgVI`CjNZ3%v^}FM z#6&VP4btIQPV<*-*aaqB{Dcu0=KKyBRH$9(E1({ft1*)!gK8-YjMWgQ4uy*U()YD- z3_1;fUE;u^KIM+p;0>GqE3_WSXiceNM!CS7hqS&!M&5W!4By_chJs^h!wM%~qTyf{ z2CeG|4>+{qQf!4GV%dorwhSnQU0TGuC=E5oAY`hW^Wq9!i-(=}9AyP0v0<7c6M4}Y zZpBkF6#ZdPoMD|6jN=VulSpDAi6dgwkWI}KA7)cQwZ(st2hu*uHMWB!O%pm66=?0F zS+(PPNYOOzo(+0;1B{8vji!|_e1^9zweL;T3|~is+bTF*pAP3nk~Yx9|-C6+lF zmamdqFmog`K}}}TLQ1A9F`U65GojmXY4Pg7mg2s_oE-|7lmtnG!Yon-B`=pjI*5p^ za{D5*ANyVtG%-NS>pSJQEfax-c33BeE8DnJd}5}W;zWbQ!?@Fv%1xQfS9rM8BeK_0 zy0N`!m%?{V;@J-BTeBpR2aFeV8W-j@P^_isz(TN16BRNYozV9j9N z63d$J<@940dn>)MRnC=?E88`os^Y9NNE|0%*jAjI;cc69TS4P^t;)7~qQHou+N8;F zXn)Nj3h=?w7**0f3 z-#$|`RRI`QQ>T-W9F^&m$%G7$()3T0ngyj~ZLRHQ%}$5eBHUSq${X6`o~!0Qd4jmK@;FV#xiu3_ z2w`WUM@A&&6r+iV2z4g*C#M4oyx1|8_Tz9gOAV9u>t{eUUWDU)G&B-$kC?`N4pHnw z6rahE`5Pe-o}<{c1q~=^&0}SJo5ykqN94FbBE8Wi z5{?`pk=Pr?8)v-a1>jtp0U1poN2EB1XbIE4Ly1Jc=@6HHA(4!4Dcp|%mZB-l;>t@e zB&FF*A+~BrC^3yi^kiuD46S)kC}+M?p~Q#|Nu@WcR91viSsO}a!*KDC0+LjYkVq_K z#b{Egbf8T{77L>W>EXhbWJJIHvhbQ&Vm5{8a|D&_>=w(#5a{`rHE52Dw3&-5@mk4W zJO9oSvuOUj6@FwyJ{K#QPn5{3&)fe0JO33%l3Tn){%h#F4$s|!IVl(T%(^z%knv(Z zBV++3Js*tt!Xb_3%N6>pn$9pcs9qFAnwp$PnV*$(-p%MvtP9cQW)`$dbShyaV#GaN zQDuHEmQOj=0+kR(CL>~N=9VcbD4T&F6labZ3gH118f!(G>L&VNM7SBcubz~E^pe~V zQ?#lqcrX>)F?=$e$^tjZTpUPGot4i?B$4%Q#k@m!<$y$DFAKB=ZPmYsEK7A;PU?>vP3Z;=u&b3sMpt3VGu1ZW`xOf9ycLoUO0!FOqcH} zQtEgeFpknp7I#T#6p7fZmGtgl^F)o%BJcj7G)J~yYbFz}LF0_Dn>KYCAD#+Q@SxU- z$qHL8QbT`yXEt-`pw0CDU)W4YTBvA6N;GI^h)Fq=FmaPK5UPYC2uzhCDNDUqcs?3N z*2@ODWQ6r<#%42LHq3T9)aD6VBp&9=$`*$0FBzueEe?t2OF;`ZhSPe>5Q1! zuVI-Od|0NvpdBF_k!F2Ct0t@rJEWJnv{j*w9aOIQ$!nC+#b{|5tstr;$WmywH?NA` zNoZ;Dt~KdYOksT4|1YCTvs1|uXGRn_zm4XtdHW!fb`{q;`Gq?R8?T`8k>Mnk{$FW% zBJq-3^^g- zR2o6yJSuo7ny(n$?EKp(vsvDoSKPGn79&H=QA62L>J!X$CnZUpMW<1ASn^5r39t`F z2_pi-U_6p>OO2>-2r7~aA`AF0sxFokkQBi!tQfEcEt@TWvk0N$rqsx~2rE!%qCmkT zBP@2T$U6&rowG1%p#s6mD9yf&(2S^1fc!`_B`H~B+Tm6^U!5Q(l4E@c7=lh1Q;as; z6Dp{1tZ^}L&9JemEMizXCTw)6_|tKVDg*zmkn-8FdF|s8S%m?^5hFZkizrXe)s3eF z1J_`bzz>s?SG#ZH+F}zQ#=CvzZY_#Rr9;k;N>yjDP!`AIt>RwZkGG0^Hz))p{3MhV z7UHHu%$ivQy3~AKr9^gX&WEI}l>riM0)FXezO4p}lnzEIa_0-D_UQA@x@fiopv1&58I@;G< znqxHvhSy6&HszR?N~?#k%$X9wsThTOY{BIUA<`;@z>aLPn5~#aPnzv!H~mdv0|2y| zh+%3bpW_~u?**atVAHfXdfJfpv}K4ugwoO)jZH{ zXRn&6^_=x~AYNDv&3OC^NWtu+n)oJ;#Ot9zdd-N!=#GHH{AggAO54w0INnaIQziF1 zIk7HWFMTOoP6c*NSaiPYw1Uca&fto(LD*WY#SeaQ4o~hKtmOT}U?pw#$_DT5d@-|^ z{me{n-C+4~Hd;{E%SKqKyYmIP>FoTf)Vh3`bUM4JCUsA~AUT~~T$|2bSRcJB;$>F{ zDmeEeVy7%;gv>+1lH{5X#pWrr;t~}#uSQIhosY8S>{ffBmP>N399cNOo=f-Eh_@vkqemuLUu#cP*o^jnkp()b@-Q(xl6Hc0-OJ^$Js z^)AO&TAu$6^^_IHI*)4e+$LL|?!Wr_`trouHoX;X+@GG&>E#wQ$2e@Ud)TTD{cH-n zv!SAYU60<+okIg#gL&H_a&BHZ(i2Hv)X8}y)v?Y?Zu0zU{HyBCc7#ah)qA};|7@AY zrw(1VYWW{$jdx4RwUqpC^I0dcS8##(7k=0(334 zIWOFVUub)3G?eHbb^9zmh)!Bn*GBjB_a^=ZlNq1qf2W6Qog!O%UmI8K8lRr=T6)gD z<)79f?n_u5L9rYe!7aADB$_$ZounkC?)IAiYjo9nM@<0`o5 zv_afo@6ZkUzcZg#S8kA`B|Zl3&sz^?Z!V3Ol$Ltg1c%;sUXo2zarQ004#_5t;Mco2 zkh?ZIDb?WUv}RHP{Np2ZE4LU@Bl+8c0}l-hh_stm_W(fu zOU?MRH|Nh+96d;3t-FXO_j)t_tStl{-~%tx=TSWB-`KI$!c>YW8<6L=P54*qZys2?9U#GfCPFlhJ@xaU)Fj^{S2UZ!lSjQJx7{7*MV8-i z{BgaO-*8gq)FexAlKv8oL}nkaWo%+{RTT#%S10|e*xjqSHsLR+Ys_ADAD-CBS1 zAFRJQR_kw$&M%*tl-wPzzu6bAzbR*Z&6K`r`4^^#FHFz47p5o6SwF*;m6yHN7}=T3 zs-GJeSY!6$Eq-!0%VxA>dV3g2R*$Y^;yn$1g`%c!Xnq=fTHrw9gcWe1`IUU(hjKWpz5V2Ujghl&WRd#GS{Gvj$%EG#7~5EY0j!7=4Z=^p!9kpKY=EzC)vgDT zv)i-D8>I_Oj9A z**DU2@eM2PM~J$gj5vCY3(hY>xdCuJrMF7R_QcorQS9#S$Jz4qcJf=*vnTB2t88=r(I~SL;KMA`# zYRj12A?&B-QzT~;Dg?b#+A8)qLqs49awUp5IfXGzNJcht@(Q~QK-0^1vl}4_q0xVm z_33mLAiJt)wXyO^8xRWXw6KA)y22KElN_l{_*>B!NxqScJ>fPTu=d!tIc$0X%b33{Ztc!3UOCzhiANXZZ zrWDFJVma~ilQ)}E*sj)S{8p%5NQj!khK_U%0iW@(`GNHw@X5;iAw{V{&P0(6*q)A? zwwl%$!>#GgiwL}hnqWZP^K)ew8l%x3%=vSe^QYtiM0Wa_*OIY^B3q$WBJ!{ihCMzj zyTr5@N1CV8);gOP9qz_f1Bxd|`eZWLmqxoA`N@IPtP%E+e6s81u#qQq=yeyH?HY}B zL=(W#x=HpiL7AZq%Ryy?W*Yqq+LICXTHy>||FV6&j|T>$9L~7Q)ogM#t+M4LN|lXH zXx5n6=Nu4eDfX>MRwiSwB-st zI+u*0@3w&gnc7648#73)v?Bwq?5s7^^CeVQP?L_k);ByUZg1HpPlDH+* z!`OND4;gRd@{7rZ31ACj3~g+a4aJRv>4Omifds2z3TsA~Dt7hIR@Q`{xmnG?`w5?8 z#1MUUt9!8=*t^-Hi7_3p7LaR@CE5(f7c3!eFio-(m6SZxJQn01`ihb8p)Q*y3P|T7 z&&fTfnb(xAlO}+lV~+`W{;OVGHQ1V@CZ0`@i=|%e;AcA(x(8=;0&3KhsO^&6 z7-Lf^bPVcvo5_mlQo%J1g!sd6KfQC!%iq!)LZaUpIGC;c5*2-Drq zqq}!NCUo`=qZCKjAIQT-&tYvqz;4vt0q_P3zpXt06IgdnyfHk<%qR?44SDa?gC+l) zBi+bk&G2?ap8h2JRE9br$>w({1n*2f7iKX>c+$ub{uiNy4jb&*oyO znZSKqx*}r=S?rL8%Z_aHA4C61GO-1XW^6>6JXv0GMA-#VUcoCEXNruICS-DULIZaE z>S7$pDQGU#R)K$3c7*nI=alBIUJTjuaNrL1h7yOd%emA$-d!Eq0a1 z+12MHpCI;p_D9CYWT&HK6kiL&pQl^(uVY z=ugEbKss>tlZVm_QcG-t6*Z}q=0|EJTA{qYjR;K$MF>&v&dS$>PS!HCLXcj|Gvew3 z0dkhX+)x+>vFWXny~qntUR+Ip*ewhqnHC*JDD9mkOjm&E|8Md$$kLocV(O+qfNsp= zZG^*CWiiC2Q8`)`&J^4*;VGaH9!G)fU~i7Kuy8^h(alW9EjR-OY^_+m8b+yrxACPE zGYpypj=o~={LM~&df>aw$j>waOB^4UEE%+JL%|II^1;jnKCD0=`UsoITWm2eLG;7H zr6Ct4Pcbv90R9aq<|3;Yr&z8x0x6rdUY1VDrs3voFz*mHD3>pv1L$+p zU$T;fFC(ZL=L+P*seh9QP|{;?XmBHmfXwOwY!PMZq%bxLw4mIAejG687_OJX9B$^q z=-);DNX;~h{=={kbwm;iV`{mK0ylav){$c}80#nj0Em6alMBzFHHePVC@fJB#fj87 z)1~Ga$eT8#Dw=Ycx*LbP?O}VU+QH^fYmkJX&H2oss62zIIb10;`mwuFiLwo{*|=da zL|&`xrWK3eoc(z$9~{bK7z}KOqEGaOGdctn7@aP9kDAxJF0O^6(1OsC&EK}>^8n&t zr_e|(l+7B#VGxEo+x_HgPQkzgPP4%lrl6e>z~*2CMz=Y#X~{}wbVB#FutCg$XeLS7 zGt9AdW~ZDSO)|zdld?OI=uoF@%r49~VDrCNnz| zU~^O(F(Z4%%A3UuIJ=W{c1M1r0?MM1ObhM66iBS23?)M{Cq-sR_6+8SHPkhNVGA5N z3^lgdOj0*yr+_7lLM~%e)E3dHxosRVXEuj6BId3+_dtO*w3oqwHEslpG?2X6+%*|K z49<(?1Rz6p2kx)LJ&@McF;185j%+HntV0$8s}pw`SSpvA(Pm&Fh_XZ}(Ar;6Gew{hI!~D09M0*^G zOolA=D+j@B_KeJ5@C08TO{!(bS5R&(?$0fCRw z*fe!$grtjFDyx$aIVC!nl9-@?D-0HCu05YG!1m|!1*#_~;DgA;UDzhDJrQ7Fh{%$)YI;j#k@&OC7pt_|x;*ji)hkak-1D zHJL^_7u#bQd;0%=L@Xi?dJpb3hXpB~BEwb?PlXva**XLj4M)sVoMMQbJ-C65m3w{S!s?MNU}YS+$kM<#B?AIpfYQCM#)Mx$<6yAMXsA1 zG(`$U?cjG?GX=99N_&rR&Rm}U-#KUWDk`r_cpb$z1CNd;ojayT%Cf%Y@@SaHA&;Rz zV8ldCQkhBSX;v4y1!@BKtbGVSjo~DKj6~ryK!`U{7c!7wAmf7IK}hB9@iPWRggHjd z)W};-e$)D)2^7W8@@Oy?MdIjY&|n2x6A^H3n1Hfq75@l&D--&Vd7+T%&{0IPOfxH* zZ*XLRTm_kFb#2H^7w(jkDjwJyhCozrBtzmsWQPZ~k##1MJUB4T35A3&nhz>$z@g53 znxYAm5%V)Rw8`A@z~ej-3^KH)?Mldo)QBWWf2iA<^uZuZQQtTDt`o>$wgfJeU1$2+EyN0@yo{8EM2us#uVx%DYF=0n-lE=(0nUKg*I+3MxB70R0#Sph9vz&=WaYQybtS*9w_e48-|4e;| zpLvh!r~Y)TE5B1A$2%#%dx(-ASHP2K;BTD&*pW2R%zw)^m#T(gbD~U@2AdNVstDMe zn4o$3&50@%Kx|G_t1@A8qDJKdn-fztsUN+Iy^a!B`D0Gj=h;$!ZrRC#kWG~Nb1P03 z6iSKwxf4znq-Kex)tDX1*+=T=*-LpD+E&#kedglwY5pF7pc1+s~$l+lsMS3O*l zN>%QCSH4u`mc@hhaBJT#A!MpiDN{nugg}i4y)f@RrT9c zzP*YnG9B1);JuW-)$*DOe_pAj^sUW)Yz_X(kHONnxJY-*F^q-MH+!RfLz63gbM9prx$oQ#8yEhJCs+_Et?e*mstC+2sJ%<|#i`fp<7rDg^VTIFe&kA8y%r;;B za+PeaanMbCsEqAYcTkGI&C1wbqc%8b)_0MY{ntEy(J~baQxax1Y}cr*T_O0@fU+lxS+KRF7i3=IlB$zu2*D!`=|E_(+xXoUkT#Q)wS!trRoYyS{` z-qemqTg%(~$F$Ds8>>I%n;MTOYa6(qDA^IEy{oKo21|1(5)5wn4~^{1vhWexde@dp z@a9c$1-sR*jF6JuyacJt)?0T*_`l7(T~dM)(_i-r+qHY^jtl?iWj|8Q`{*B5n2$=2 z2G@VT)HcgjJ)p4ffuyo~jfZCJ)jEqge_lR5=$X#RLDTx!F?&<+IM?aim1UdD)dQUq z1Q{YP=LfPaSj_FdGL9xH^AL{qUHwER&_#?C!suw?d5HIe;UU7)|z&1QdFx0lOQ zZs~ybmha~}&Ao2@&3$%)*Y#2)&HYY(djlKOxzAN!lGm3iwWWvAyXHjiT3+Vm&a3F^ zsnCHZ^DKtZg^nGuJ33{ga<3{dxvMiq><9JO^9B-a@BT}o?LGG;+76B<+M>1kWG@Be zI_C2DVE)*C6wp~(nz}W3@vVIW;YnJ$B@I#^9JgiP09#ZBM-9EXE$AHiW^M4sgZl<> zE^Bds>S9k(J6|8$$|0M_`#SJEvF5Z=T~u)~#@E4DT#VI)?u;Y9r#tgYb-|qvvq~W0 zPQ^;BnWB?6*DQ`C)?CAXr#itw3n8)PV@%u zhxaSI_5)>?zohbh&WYlbgGMOYOg!4?&XA(6I@$5p)`{AAxLM~3d=fy3KS*Gb{u9an zcw9#^G$&{5iB4+-a>qb))pNW-9y*A?^7Y9%Z$?-BjkghWrocwbq3&E@g-^>lJiMQUKv$~Q_l2jTKs zFL!o4fBf3R!S5zKf&Zso#5y^1o7M$s83$0tAKjH$(^4PLe!Gnx^nMIi1K1y+L7u!_ zbCJKeW?H8fH`7$m3@4P*L}ft}v4OCW{c-{ZXpUAM>vviram={nj13GZp<}Q*8HqQe zKWv4=%?bzeV&xM9v1c7D!{#l3>Ug&Gy`I01t5F`*37;f zNk?8^xm&~UJNI7U$!ACO-pCA8QdeBBv**^Fw=S>uX1rx@quTb@NhudJ4~Sr?^`Nhc zGIjQ;js%bMQsgiB@u^Q8mx4$;5<^O>Ns4T#DJ?s_?81UI0fqMC&gxKivDd&ez8!37 zhbM6g5Zzpmt3IfGXL(DdErWQhzhX>ZWYd_o#6VY~eShs0ALQTo@*{L)(Aqc{(U_7m zSobB{GSh}}<{(RK34UziG%1<#Tt%7 zj51fM!=&~H+qxUDuZOM$CoKMKwBL(cy^=TO6} zR^h0Hpq?-zZ$;g@i~L7+e)>6yv)6ih?_KUYZ!JIcKS+*Qd?h`nGkWbwFp#L%JovOt zb~P>+howlLKznxur)VV-Ym%;JMLhap=}rE3+JMvOKkt=ZCyYuTd9&-qKWdoY18?1et`CguIdsjv(g#=pUD}2&)X}naw&0W4OYWoIGm)5#{|_g~|71NHf;=EW-fV)r zHF&jLg1j3+rqm*`Z{-V4knbxHPj|6#-z0DGnhNC6O1!Dk2a62Gl&q|Ot znjly5QshthE(vv^AhT@7yBiGaf4IFYq3iY9FRfczIz)SscUiP!s+l*D8^LE0I&7e| zdpIv*4fH@W9!txir-w%`rQ`!`4L-1cC@%667Ze;2>G=0+RmQ)ZntjUZ0M}shWIE#I zCXV~z5eWK*`riM2;uUv9N&L*jS?-&UyKhh>!Ba=QK!m+rN5!17^=~Mq9vr*YS;3zF z(>nmn+kS9F80FzyAfDtaF17xJj^)p#M;C(#97C)*TP1*h?NOE7JZ|tUMzwn#RD` zn^OnFu*n`OymzM!ZCR-|=P#`l-0PLekKQ|O6{ht#0Q%kQD%7Q~eEd}!X_*|o_w-V4 zV3WJK!fv+qMemKe8<)Dbj>^kJ^xj3hz1clnH2C(y0dHW7yE)r^QHRSXdp+kb(N({& z9u&NDnEUWK9eX%3APNQ5%|_Zkd=JvT+u%>^;yRcL$wk`#;hrPyryzE$v+rk413KP$ z_rZC7o9g;@vIFrXAdKIVkA?i^-hX|fy>E|RoblS=X*oLY$;?&oc=!g~m^yNh8-KCo zZ+PIWRSkJyc5wcM*6$Cg-$%JdQ|k987*8=v{+f}%k}Ef~@tSz@XSSTpBU=D7UM03% zCN;cNPFr&!gEj>}y3aA__tD`ns08C?cs6k#N@!rSTVn@Q9Jt-FXJe3Z8Qi+G3X%yn zD*jUPETv}$cvS(cd#Gsn@zcYf(N%ZJG~j#u@`ntMrD5_4zi0QlV=o2M!^V4{l@9$fRbiG%nmgct2NfB*q=Tx($MPNQADa8P~#6M0zP`nP)+W=Y=( z!s_HL&mK}zuzrizIYf7$b9Q30<7Qg2;sV>gGntb&gVv%jZS$rh(FcX0nJC^a=Wd4Hu5>be zB7G4*ht-ZVc_a9^`-Uhlcfg~0YFho6Ax!-)Z;wy)>JfJV_nJ#`#N8A`b^I z*No@Xx7JT7z;70*_kZVa#?tSBg<{Xr$ys;_(c9Mtd&|^qX7D@x$AX*3_wiQiCxY3o z(>H&cPRCrQZ{<3e8+KalyH4K(n$heYefTzSsPWIb#=j_3eA&RCiB_WtW?nmk3!`eI z<$VloU~{l?9ems$z5e|WWiSha)!_hiF`ry-!$$Cvj~W_F)uUOiM`P8aJL##=IMk!J z=p#M4D`<6}Z2cCWe8_$BDA&OXJ|X2*g~B;^o>*;R#X6l5Zv&F0s14Q`jNZ>UR&n;{ zigM!*zPglXh4F5*KNHW zXch6$6EL6kRlc3S1V-BuETW$&^hFi;_{TRv1j;^#Ulna61BwsNV;G7Z7+&h#3+wUL zCm*e*&8@*g_h{l~rW1Hl(!1R~zfVwjKEpk~m@&0nI;kYMa=EDH?A!-<6?OvJBXbUg zfTU@=gV$H~8jBz99zv%je8)Sj`h)s=B7C8r*1(SX!js;V+U*!rBi}V;?KM_Wp6LefA^8`s3W>2^vB)`6mRx>=)Yq;86QVJANyi z%jB?EraK@ropVQcr~Ugq|CTyMY{9H9$!kj!CuE9K+WVC%Vhk(kQ}6eq#i`{os`Y!9fo2u_F48N0!cHjM+ z@A{TAu2Xm}(%eK20jkoW$JD2no_}SaZ<4v7Pl_ts`c#`}(emZc+Te3<3BGohtPQgM zbF7Wxt|PFmCI+pjhi$=+Xi%g2I8u?gmvI`=Zd%hjz#OT}ktZY6#(f!~cp)!0&O&mc ztETZ~5Je%GCh6M}EI-?DbNtsCqt28EH#4~ozQEX1)H+B7>{~ynBtwyp^GOh|dK z1V^1^SUv4FuqqY=tIypER*wVQ(mKkOInG%Qo)I)0h;YXavNvI`f+hoLue_6!0KjELq!Nwt4s}D zN@@8yc9v`K-=&7j*h?9IZ~Nz_qG5N$qql7ILXIqHO$Hx4(?+>WqnzeOc{SI;sf=Eu zjFe6&xm$;s206xU2g#~$FzKb(uJ#QLOdiN=$AfRr78|tJR*9nxhqHnmXBhND1ij8d zf0v-|q}5{R@6SW;U4={pcWfo<%eK!HFsLyrxY0rCh63wmI7p9+8n=#s^a4G12j0#G zl2nbPPnuBtvw?81^NX{BQysjk1h2utTP1k&N5K2)AUr#)7J+GID{0VNZ!q)bWt&I* zVS>~W7s=`Kvmde1SF4RvT^qG(<9XUH9{mY}ZCt1KP!ku%#pvC^{E3eL*_J4;J~0Dk zsD8HfO>queBIoDv0EJsQ>f)7t{WJI>?m8b2hwR z8Ive_denYz4VInCaHSTz>_&I6Xo92?Pj9QoR!cBc@Q1_t2GSSx=%sJ*QmXyU(&(!H zLw#&?yKPRExiAt{9huGhrU?LlOXc}6NAqMkDqS%%h39|U-AU}9nN*|$3+f*P2B6% zT$XUMK>gJKyc|1wvmsinGt==vD)*5JoUQFCzdUpeSGhqUa1HY7>g9*lLc*#b880vC zy&7<)K6@|c$}z3@wN_gK<@t2Gx_RB3#-~`c^G_a}%fFA_a1rjKcTsoZ-UdFwf@^=iC6ILAE9n`piXWfme#*a593On?6erXt z9x47B!wjB1d?@%bOrxZnQb`I3mWV9NEMfaZ&{+w)>bK3?Z-bB@{|e~)DYsMp>Jk8_ zB5!&A1?*)=8nN+|WzoBmWosHwA)00Pz@UQ5&#gKCl+Op1G{32_&MQp|8rmhDV;edT z^}#=O=1?4ws8W)L?We%31VmWlA9)CVqFk=a4WEHLy^HuK*x#+>+|wqxALT~$u1s0V ze|!zKBV3MOb1?r_tRbn0Cd+h6^+s=)&U;c>ODtiFzkRQ}KZ*MqVQzB!m*s$M3GNA> z>=SBjmOqudRpZse?}QH(IVYT@ggu+|s6BjipFM&=d|-*GPlXTUgTY%UfGx00eQ-v2 z{|4@xQn$C$zgNeuxtak9YVJP!d0AoK54R7m`Kasn8#lRrN8H_c_GZ}hov?-$CO z!+U-A*u5hK{WF_(*(~%^zejyM0G__u<#+XQ-r$ztEFQ^vU2hsi^+99af@l42)zyQt za3Xh5vkdK$mG|F%u-`a5_H0H$*CVsOUIPkS4r-e{#n9Fg5uf(<%9ISWktEQY7nt&p(hCRTDCd~D&6_Ho)< zxUShO?cMt&z~?&xL%;#4?5V;3e8phS3dT5?5y8CH!TcSx4-Og?=7vpz**A;i=chL_ zouzm`H{pw3#BCm?SfXoZS%!%dPO^|3Qk6hooXo9$=1n9_Q)9T7OD$8SAmJ)V5qC5n zba6v^KlHiDx z;coqnjPp35BsXyz*TFH2UzV?ldx^E<$bQLc{&C}*Lk=S9#dq4cxkHIqS~g@1CXrt4K@JL)21dJ!CwEPggSb|*ZEedq3f2) z-*;V8mp?$4iKJbGton!gjQ_Bd4wto_a@bpf6U}tC94A|N6L8iY!at2+kgONJ`)=9R z=#Ae%EP|H#hPvMa5d9>qRAc&ncl;E|dyhvMi`gqK+0T|!$o-=~!1 z|G-=Pov?EMKlvN&xI^vJxj)7n@KuNO4fx$Ex;sa=la=y(KGzVu*=T+Kp--Q~w8O8s z4$7?0dF^lrZS?->4Qn&HsNcJYjn1!YK=Pe?wp~w{ML7Jv3N<_I>92WehM&2FXyb^F zZ|3n(KoURuOr;?gd%2(0)iTJnJ!CF}AH8aN5C87!8TD?T%#Kd1=Whxw|GhC!ZRFGS&@y*OwT!!VkD=9oNA`NLZ|^6vu7u zG=rjY1kcY(VFasAv{9mtsix!!@CAdnZ13i5CVvP``NWc^svVd?A->Yf%ZA3TAc zgDpJM9(>zX@|XO@)wwSjPwg+gqGsg* zwlsF>ZK2ikbH*~bUaS8OJFVOx*24T=aN&6utPfsGSW}O$p{X4-)pv8y<8F27maIr$ zn+E;)fBsdfS181kKOKC&!AaP22lowl7wv_;>VqG-FIVf!Uf1vQxDI~7coJ(B-S*BS zE=;xmv-FA&OOKx%0{J;W&@@vnuMa*BjCFtKRT6(6=3~Y_MKC^PFuo><96*yNV}Awj z(N)iigL3~n9ZyD_(+nLdtR4o>kZ5K>w(*6kVC(x`Ur%$*OpdZ4A{pB+R)HO6U(&hD z_qBW|`nyF9(cg8g$xQ;*C5k;dG(YOE9s^fqI}YJG`n%QR_;E?L<1l_9b9y0_9oIL_ zbHNPc;AgP)S59wTVkUj3}-pFxHdok?fGM*ZoHb za=rt3Snw`4ih)&(qFIpd!Pr<@<_vzB_#GDP|BxZz8NH-jQSaAyNg*^*UQ@4u;Gg=6zK`g^ zn+NGZdC>zB&To~roQO~i8TOjcuvdkKy}}uGzAL@5mjCKg)32;j)PI%n)I=I*JM=bO zm!Txbnq=eQm~#p1e)mw?O^Q|VXc`QxX>g6z&ffbKxO>-^}#ynMX4YbE(Vzy zP62IX_7*)?y?h)?Ca3jPil(neZ|US6%YvN@FvIFefcCK6OEnl%bjNVVqNj}i_e*Zl z{K_(=?7j~~VZ6#~k5K(PuR{L>{e4)iZNYW+uzbYBg@*a$JHkAwZC>Eb*%ZC@HmcLS z(wH}6lUKUD!kfN)ub1VKxFK!zFJ;;~UasQ!Ozs+Lws!T6OU9(Bg@KyyTklH9>fUE*n^jY3TVqBcY^pLeP?YEt2RGAm zR;-C$8=yIyW45=y(6^G8rTDTif9B;bt=r+23M6{-PS%uTLMJOZ#q;mB0LkB>Vzx#F zwQQ--i+|PV?+X4r(VU)gqV0!7sN2lnp7opGB8{!@32whjnI#v7eOJ$w^X?A1e{Rv$ zsvGRqTaB|Ws%m{- zXw=MVy^DKm7a}hTN!YNdX>w{a|JxUkOf`!mzwR-c02kp%oL-_>hqvQYE<{!o- zeh+Rz_SWFPf3u5O%nhtSoT4<fdE#-A3)N?+RJzw=UtGn|u}+ zk&Ec}gmYMlwMV-K$zE*AP)2b7Nj6*5364#reG`?ISnDztlP3$Ibne^krSAMoSD4bh z#a2n!gq&~Rvsh(2(1Qi9tMmC>^yJ7-4=xElkM!Y2tdc!g`TM$B{wTE&?NBmQvuaAn z;W5Pdlh(Naxykb$Q?{Wp*z?F6kV4a9(N*L0Hq3lvYOf}_>eZu=)%$E<+k&b`_4@S& zFjzi8nTJlGb^P#P3$Sx6(+eIF`E_L+LR!(W2x=nF0;}rUfEOJ4S5gqX*sL6rl9~Ao z{u-V-wsCVyL*M;G-+go8UJbY7c={V0{6SONyRSiM&*CXQGz;LZ;i^sJztz^i?C1R0zTup_|<=MxVLvhKBy z>$L>0^>5S5Q_^4ZO;!R%jKFnanpUZgIjUmy5C6^0e!?d2zv9ukl=PXqh; zQzuj}wa)Wq{ZyPvOc`;F{{3niz1%f=C)YvcsEuyKkw?jQ5l^*$zHD;qv1xxUy5`hc*g4(N zYY!;+oAIERU4$bxP_cYwQ0|)R{{qc@+l~FVTnEunn|t8SHwSwE7a%D2e&#zA1itc0 zMG2*+jqmSA5Kp|Wgf{1`X?zdM-L5Ou^@)w|->)0{xgo=(8wYgb05>AxjW=}T4Q`Z$ zH{R5ZH@Pu3yzx)n_$N1H(gfoz-FS-|<>8G1-53Zi{(%d$4yKzROckPS3@ou09KlZ< ztrdJ_r4rv1Z=>wIix#t1kGpQtB{;0zJ^PA1!!xl*=+=_pjceVbEA=SEt{!`l#TALQ z_R7=YD`)8y=5J=Y27h&p`|?A&O*AxfY_#J9dqv&(%?kH)n!&xn^k>Hd(N)q@6a}}; zMXiRDQcuwjoj5P8*B$)Fd{by{g-19(H(J@V3E|W4^G(|9A+tl+!@r;AzEX}W?cX3K zkyVTfQ#Wx>-!?buot94>-lYfZJp1%?!Bwrd!OLF;A*llaQyIse1I z&_u$P-~$fAG1k(ugq82T$E|sVP{7VSG;hh^k#JiF+&MaS4Yf3d8k@Lj(2U3%tw{dWuo<1KqSflyk z%-~7#CQxbfm7w)iUh{Gj+qq`GWi+(j_A}7BO0R`X0*9Dd>}v_GcaT;HlFEI7G)s_f z85Pp$K7!q}kIMAXg`<9SI3GC)j9#?|tu61ZL6~OduE%=itcDV0d-ulFY4!c!^pw^w)bdVH{U=_S(Zg&Wvw!XVQ(6|c_gA-$Z@=3U z8e6e!ll5!6{hAYe^?ioGuU)|i#)wq8iF3IQzRk$Qmk04tVy%kC#omkmN7jb$N7F30bQ304SpUUWq&Nf$K;ln=P#&`&HD`4p}M)fx3qw(JO*ayw?!cLFX+00>UP#qn9!OAbT?#tjw`>&FsZyDbI0)E~>+7=vcBkGl7l=4rnAXAsho?hYq zG7T?!a~_Vi@8Ah}%9%KY-i%%pXG?YTuETT`j(=kfHwU(3dHOE!{M(!L2}|%Mdb4g{ z%60TQxeF{pKIO31@jTbPzDcLR>iHjKr%Y+RnonA;WK}0h`LWiq-m#DL*;Hefx9>G_ zeynqt@r+(MOM5er?9^Tiu}3xHgTG)S{$Ox1c@h-PkCroH3zKsb58*nv3`~q1@q2zE z8upD(m3}PT41vk!xF&Bs%{Z&XuU7NS?2q8;Gtb#!w4g7uf&>3yO|=WTZgtbdTZP;) z)>L8fU=!$e#e?k>W#$Eh}Fq%E+}#kYz=!AkZsa!)K^r zg$wZMyVP)&u@4{PmSEnG3;4AC^A+Qx?eCKTo#UWP5R{W0l&6KMc_9?17Vgj!xci?! z8iEq`gnTfJw{oXS+)28jY@(|k=T7@Qk3(I4#3Vz^hBDzlxeL&qf6v~i7!F^HAKQYh zL4{QV|B2suk8ASn_9iR(!iSxO55E*Xyd``nZzaF5cyep_@XGL^stE_dHir+-wTG?o zV9s#%XbROlu79~CFXRuEYy}?oj)xhE8p5+!AD(in(N0D_2bj7AJ2al(5DNu zM%?#(vP0Q<)xlJC;&NVbQKl?G+$=%Ffq0zH)?MvD+#wK^0x_gr!`^Mgzs~nA&-hkF z<0q;=Wc*w{=l+&RtaClPir5RG$<*@!R_3<6&GR_RF5Hrf`=1CUX6$VT*Sm0$GB0g9 z;=k&r@SIZq4)TNzxrAnZqt~f8zkPos+96K^dt0^qJ;D3lL+3ByHz@yaMgz**HK-2= z3*$;FN_w?ol<0^}vL3+WvQ&p+U_$KLpv{8g7W}J4B)=z?@}+rWdDo1|=-^xSjw^f! z?t3?2FQd8MyZ9%#lbZ)u5XvE=3|^|1 z2iDT$x}-Wmf2+D9$+S=-sC1ov9}6AoX0EB!pIK`bmg?{9H4De+@0>LYBl>&FnuTRX zo4yl*GBy7(pwQvAWrnGb@+X*}zN#1othkctbHN=LB6I7>Gg>Ic)~@ZGtcZ&F>Y-vp zB~#a|y$Tcue}61b%?Brh)YOT6#r z-rU(eTzaG>RU|q+*~YJUEvKa2q}vVLxkGn&l)3X&-D+|NOY(pq;4&rvCS|q-!iz-RX^ZK^scklq;ttO>ouJXZfA;6pV|7+d|(+5mMpWrd>}a0_2udRqA#Cw zeYu6}U_O0;e%2jF#IHhb@)z4^CF5^Ekw3(H(%lm#DgA1d?k5F%jr!H~C5*YTcV2dc!XFbx zhV5!oUAVzhKgErnl`<|fv&_Vl{piLS9?Lr6b>i}}AeELofwjAyFUtfR5 zeO;@s9|LMJ&mGOzDgU1-pWU^#1+NiHsvq}M@K>T3{OZy@3hqAd03IrUN<<@2+dd7z zYXN9F8>o)yw$njsCw>Yf{#*_q)hhd$X6dEhNa3v9AmuYuMoM8rVX&h7WEj7lwJp?7 z93`Bp3XW5+{Dn2%=6OO}Nzkb7Zk|`8AB9nX{CiIIZO}Vac^G4Nm4`acCWy{uj+brS zJfBq!bBTG*s0b!AZG|2`EqYh9vp4ekBW*jY+8&4=!&@gt*F@WPM%ubcSw4e|A055x z0dK}*`T1?uqaD$JzBrGqfQXp(n*NqDuGgUNeUwq)>)Ij~J7l-8f6KOpV;pD)m?t&&z%oj z|Jmn~P192>!EBO07_l$>C;W4asFj;|BiF&)TE~zRF#C>w zZi3T#wdByteW>~`RV97cgb&^P9WKhb_|mtlh{wl4_Ut?rDxl8(@-72iE70{0^hpA} znRBukW<6a_q zT4#}pXryqtwh?7sbLQEDYb-?j;Fv8o;)0@YW@ogz_QgiL7XQ-~)T*@{;`tE@e-v=@ zUlsq=$hT}ZzNX%yJbLXt^ad;_H$pPneh>ryzOIc%qIVHwCD?l;!d_TiI}e6`Dq>M$ zvp7j%{tgUu4rHV8Rj)_aSi-OUpEx_80!IAxO>w65xS|m%htJ_^Q~B6j^*?}G;+IF) z1<}~{mCq@m72B@*k&2bi4RmcA=zE{PiIZ7+%G-BFV)qYp9jEPcx^^BEyPqT5o||;s z!1{rK%I?@Bd|J{_5_=>&MI4@6Ou)Y-_{(Sh4u?ip1*q*#gozehV&*2vVqW_x8I96PZ$?*dgDzEZ#};kTYd>PYSo3>7a|#Ju z;^|#4R8$6BkI7#|sFoM9&7X=#YwB81_9?;vne7=_WHUtAXMFN-w*Cz z23w7AYlx7o|K$+!6t@Ry{Gk1a;P=&r#MS4C_BZ+d7d)eumhaysgx(N-e=E2BRo2v+ z`4xtTPaf6rQtN_E<>T19EcU^byGq)?`3JKfEW}23zV{OXqK^t_?4^~v2HL1u>dbyH zk3mt#U*z?6F>9xKs*d;brem1n)FK=7m9_UoT8^BtvE>l{8$V}b>mi%U%lam6Dv$J4 zY%13lW96lOxv_bL>QqO#?ul(sGl@m?Cec8B~JKVr##>J2KoddEHXf;Z=h=(Sn_ zkwO)o)56Sf=?t`@QE|pbVFXbgh_3#Y^s~r%vo8Dy1F=)&A@FpBCc7xEq~iEK4ncWI zT%ob~(KhxOY2fYXx>pY7xQEy)c@6)3?T>n66#bu~!5H=6^-lHs)fGR+%{JAhiHQgZ%9BaFBb82Kg48`8x?E zOA8?Bc#oZYR{w|_2#sWiUXD?BYV9lbbBSJSEC-`{?VTn$Ui+pu`;HwZJ!f1h-fOuK znL?nDCvOIIIaji4Rm(V#)YQkC<^sXlbot3)4G*Z7>YA3xUQDaz8G7(nfFRex%EbnY_wz>?2=ii_4cVt+KEED~o_U{??Zv$)ZOD#np zVMH2AQ(Zk3>B>&YkUeJ<^}5gz2`Y@QP>L&-~~Y-!-^rS3EVNi#sbOq#_U4Xj9qB^;9$- z!p&&dZ(#*RwPzds>ap$&*x#9UQ&(Fdj$m>TK_4PVxJU zmAX=t;l^mkKapehY(urYCW&U}PSTH^aKiw_?~&;0Kk4oxQn$_+qz1aZ=(-ojCsy{@ z^jp`9<2-74?0*sO*zM@Hu3q+73#!|9mLeHm<#sRfs8{(2{r9?_8-QB1M~?}SZ&_3KKkSgq9- zTU)WB@@uOG&x9)hL;|RQRs~U+uqxoeA;|tepEvW}YyfN9Kf3vTzjM5K^JeDFn>TOX zsExnSHMxrzgvWOhNoc)#?Z(~F)_aPBH8;>{i=VvhxqZR$_q|*|H zCL~;%fQidB`(nghngA)mHTNvM7T8JPBMt0PL-{9%f6nBeljQ_r8bRHa3>%gsDCqB1 zUR;s_I=RYwM|%8yNu=}N7e+e%y)M#0Vyf171ehs0AiQG`9Jg0(cXi$nfyYF6R)?qc zUX87t2PGk}VOxhIt2;M#ZgIT~e}U{-T+i%4z1VOMAi$=VULYNo5&`J1G_4trd33#c^ac&fqqFpH%W{v&a=OXVA6YO{tXDUhEZoGgq4imfNmWP* zZ=ygXcrY<=7RIlKX}1_4+Ytea9)=+*S7jO)s}XLWz|h8Bor9mctfcXl`m@QjnAJju z%_O9cr)oqWMIagl$ehpF8&NWWub0kTaW|k_RP?(6BVBQ#bXh5{w}x=B z6l_TLhc5CZ;mqlXG}RsOF>hMd5OAL)5dt%#UQ&c_ov?BHW0e z{Wd*YYO|R*iBj)3e9<7Bpuq0nw>PqDVhFRD3SBR6@dgj21TKanbCG6h*OJ~MTQuU$ zYh)vD*n`zO#xI*A>`lZy3uL~_T`!MItNH<>X#o=hNdKYun2Qg8cvPxCso5JIZid+D zx?@9_#5jYbjQ(179iA9vl~W5{Br_^u*{ZFkh!K{ap%ccj@P#V*SvdllZ$N(;%t8WRlmAdHwnF z*GtMwsejw6&%7R!|4b6G59yy~H8pT5WX}7M+_F8{d>;|_-obenILvzd{G)WbEavYR z_rB~(fosMH^Pk2)*fDHtf1NyyS=Z|1@;=vjZ>s{c9@Br;Bc1JWGy4N=uRinImKkr9 zXWH9GFCJ7!bH5pwW7UAy^|-ZDzWeSBz3Q(m#SCQGFSX!NXm7XdH@4snez9=qtcr%+ zGr;vB)nvh z;g4~NwDz`i2rTRJiCdwP-0#QGqD}w>%BgSOkjs zYGH#Iq7E)LPMzE!^IetG1H+#Z}5sKgB5pyV&*X*CYyzG z{`5$a4=Y`exc;ACLNWZds(SIuDD2`&A@0sWYIKo`u4s5^-24L-eZ)28LdH0Odt9}3 z_@>=I+EG%X#}oq@v=ZEGlR#E#+&UqpR!|Ku z;qobv$7T5bx!PGlxRel~gqj5R?KC55MlXeunPb6O_wZU1jgLV;6M6hVKV8s&o(1U6 z?9bm5D0R>^VQuId^ocUfA%OC`L@d{!4!uyyjETo!9iFTe4kN&%C|!A_R=WOh^v%;$ znT|C1j(Q8*lpG6mt4L~lS=n8vEGphL=fhT-05E*;(NM79bD*qOdHpn$jbsZXeJkG# z7t!O6Q*TyOS{6hhYVc4-tKMioGyiIAMNdr_0NCC?jaL)8^NPRGkU8^K2-Lg}; zOot^?bB73)=G%Vcy&Ukbg>=WkLgV=!A`PC}U*9p<+FO1O$7;YJj@M~K;&Gg5a2#&@KA&ui@f@2&#+o+}SENa8^f57mQ2^zz;I~`j*Y)4ycN(hszvK7f_Zq+Z zpg=gP7rs4S)9*$d6;HoR!S54|-z@;~Z|L_3!2Iv{xeR`vEI$Unp$5MdIw~GNTuy;e z@|MQ$0;KzC{8CaJ{Hk&Cngbp#9HXnMauWhy+aI?ZAQNJPc$71Uw~W)>&!cfTKW-2| zk0>_pX*vqWyJQfjLI>8qHNmWZ{;pG3H%e+OS6cs!$Tt4i=H%}=lT-ut!3P;n z*% z@jj|#_D%a~7{NQHqh4@(L&Fzbk|NchjhK3kH4djgFoym-ZHB8w8L6-)qrwzAPuNByP{hx=uXhxvL6*VCe>V>sxB_o1g0|48H{G* zJBwdj1f^G)Q=Oe$;UcEc~WpI`H$uHjMR@9f@0){MVB_|Wv2WKzeCzod5K z?Jsoo(^}dhe zqc|wp6N(qZ*Rbj4$-M-8Z7c52h(_;2mqJE3vt_!0n5OKe_LE>kx(AUcQ)|y*3-VvA0AW)tIY&|(hkbr6X-TiZX|HkoGQyQ zT(kv`Va=*HZVf4fkKI(}ygPI-{>xyy)O831R4h=w8;$lEvPnJt z?pLz89hHKUwp#fI$~Fb>%|;uhOrHtIzwkH6hpVuenf*g{AoE+u3^3{@WcJqQ=@!2e zflCkIU6n?OBe)bqRysX!xjVXg!1Bf&8tHbuJfhdUk-h8@*`bkOdLtm(kHjDwfgw23 zgC{%!jI=Bp+ppLy8tAPr)WG`#xU#+iuAz~L0^k}(*kdx8uCKS=r_+5u3h6R+Iz%<# zjzD`%mPu2j(`;ax!(R$4L^V_lMVexhW}Hs*JkxB^X%N*=fzcu(pI6WDxQB}Ly5aO+o5-6OX_#HUQ}!?^TZlFi=?wcBDsY5o=SirrpVM_HiPl#_ zh5fj7D2bLLp~8NCI2dC;;Mw8yT^5?~a|D8={=8T~$7gBd;~g|wY>ChRW~P@bOX;EI zQy$t|h=|+{6+L338%vt1OA-RR?H1Ro-wgC6VF3txS6_X?>3TBs*@x>a++d@7WcXdt7gRO>4>oNJztf+{jf5%~ge=^`V>(|}_pQW7bltN=S zDh-CE#c>T(qC<3c>#Z6?|#ry)i_toU%7L|`{!3M8ie`a$A&-U(~AwX$a0 z@gtI+CrQnH6UT#Fzz1^+>Uo6tkcdvHBmm3v7=54rw!McKE${1vX6lgJ@$ zRb+q3>!YF#{vit9W(JY3V52oL&>bBz&@+dpdUL*tT!qDks|NN02~c8i?)s_%2OmF) z&7YQiNJg0J&3*$Q)_inOMnNCEF4M2ws}4=YfuKWYyJ{D*2snqbKe6B&Y9-yZ1iE=y zTvzxDL1BI}+IzI)sa#EuVE}xY9>Z(ZeHvss2eC*8g*rC?PJ;s zKOmfmcGy{ddmWb-&_iXaVP880gd&WnwO$V&FgSM8!?N{BPc43it}KP1X2t2p+PrOQ z{c@U-xm*4cFSFqW#@KpfW>-!yn<=@;qDB@Z)!m%+3{9#WC+PN3K-#U3&H`G$HC_*=J!mhZvZV+vdL?weDKj3S~FfVXbED1OZVU}YO~i>E9ygZ z$Sn-O>GW;#e0`AOGrW{sl#`^z+QY}=Fg;=al7)xU|873-)UnT*&(S(|vH6^W*qjfo zylXQmE<~oK6Y1It{t2W4lQQ_8M*x(J!s*?S%Ue(Hb}H#x%(0Vj@Nqf1kzACz=fr{l|SH_K)WD4NUq@iT) zYRH`7bni4Q8d*QUH|U!6cg?&br+i>t6jjn>Fyn#BDC8Ii@#Gw60Q&qVV)C`nAO z#M#(wEEr>+y|!r%do%hALfYr2BLK%lZ zl!6EhdIVtZ2unz;W4X|qy4>q+0B}JB`*FL_*D-4E%ERVs(BiI#c70i zwLrA;x6bTs4cTf~*1$Nh@(Ni`-(IXyX!)L}q6RKBQf$iiTh4HpTrpy3q-Sp}v_JRfJu3wo_aQXq^XENP3}8?^ zV3!~&hbQQkksdO{LXbBuFT@AkKQ5Bz>w`-1)bp9&{s#g2?C)hMEt@N0-gFf_l36~+ z7amap2V~oP;d?WE^q)+(dm~j*=v*}(>z3LEpR3?UU+BI)NPyMZdUa_ml+$16z5~8s zQ&y3E5r794%c`A5-0Gse7-6uN+<*^% z-YtVX6?d!FubEl4p1x-TU2g&5Vj~aw?S~bh@>aL=pr_h{cW*syZvD1P-@TBHy($~F z9w=H$r4t8Ez}~2w3hCRG4M&{lHnJ%T=7VK_^CxYSO&E16%7){udQdv*DqKlpWzv-P z#iTKPLXz-23Qrny5VQTb$5r+x8N_U8>N#Q0r*JLI zqt+L#L+#BRdiY7xMGlG*3_eiUe4Mazxiao=*?qvEMqJ9t;{j68{*nv7wLE3K1Lu!t zFf|emGW62vd$*;(x_$a3Opp7Fh7uqbwdxWi$obGYKLP)-;jwNt5iB~oJa{O%>}#zd z4W}84gOwxurUu6b5hV}^On z`sxWLNA(>UOR$u~=q;P#t)E3ggwvnNhYBVE!Rn*0KoRs|58OXlA*i%zf@|>!gT#3< zKBxf<%_EWs&BSYoe$B#bDPKWQG2WMFeF@v>{oe~c;K;p=g_gj&N}D{Bm2w-Gj1;c^ zJu(oW+^HE4I0^bK=QyiRwNVwE<+wZF;NIbG$E@PM-ltDr^MS;|xH{I=;^H;q2 z?0X>uPS4~brK^R7_&^W2S)e!ffMJ}C^Y{_~JAS12HA8>pt;g;!U_v%ur#^XB_J1bg zL^zA^*|%kaB@HF8^=jkg4zRBXY@KS900N6voI;4u0%>3(j{HUgb5xnVS?xD*EUD1` zv7P@>DGD%LwORm7*aif~&Y~OcqYxm(q510PXxmM^kM*8!fV}YDfb;;3S74ziDR{*w< zhzSZ#d6EH&HDTcz79BHzy>*miiEs$aFN`NLc`r0MHh~FNEPzz?hn}mK$FJe<&}J?D zd1Eumu`AuA&k6D_JP!04UOGh0?F3APXjmx0P>~EV(@2C}hULalR-YTzV)CI{w zzGhW`;!*7_03}4KqrviRXYOa%v2OUF{p}o2AvBIaAg1tMXYF$ zZ3#lTUhaWoDZw8)%uIn5K>$5qt!nw+7*s^wicKIM@)r05ub%;M>JPs)vujGl`ZKfu z20qDIy&2KeEG`x?U0jR&z_!p*Hr_7q+8abhy)zYDQZ)a8i`0)h6%Wt7e<2na`_O%? zaJRqVU8S@tIpNlJbJqA>3KeU;59m{j?zI)#fV{$zvWwJG4dXY2fs_7(F^w=9m4;y> zOdr5=X&Cu{A>O_Hb|9OQ*F`FzVT>V+&N6blokAG*7#MAMtOaJk;~OO67XIDO?}8C# z+C3Mk^EHSr1VPWuK;x4O0OFEkK+Mx1USuNd)J?|`@n2atY_Y;PK@VFG)r)I+bbtoX z6lbxs{rxW1=D@szH@l$UMI3jHG#^6E5@0&(M&N}yNd{hVjGe~AnRvzaC111filaAt z&Bp6_6e@FJI-3jF@Fa+Sj5$mty!V+oqC4#iX_m`@|Eqm**Jz2wj4ezS=F>r6xaOVInJz>F}cBPy8 zF>ordp~*N6v8)`&TL)TLrQ+fbUl`|Nk~ZY5_S3|9EN)-f;v2FxFb(=L9NEdh5iH0H zBi+bV_7OpuW6s3xgH3`Rj=%$Xb<5{c^Fm^HxyG;$F{}XQ?Q6aaIHTsDunCeHXtqee zfLN5&b-6WoY9@AHaS)gM$nFgUN_9r{cPeD{15n5_$3gm;y6 zOo3NOfsxyBe})DwomRB4L>7}YFDs>}I^Fog_G?!R4de#F6*Hh}!!@D$s591^ z^Dw;VmO3PBKP&H{Is|YQf`xfVmW_V6X2OM3oP|2z#vH%hnP1>R!*FVmvry$T!>*REW+T1}`e3fHStE4Y-?AjX)iWn+ujm@Gyn#{r10amyeGk+d?K zBo>D1rH6#k`RrC|3+xa4aF@iHqXhlY7d(jheGJ~XtibS@k>=0q5a{T$5zxWPyE83- z$-{%nqB^*pQ07~SO!V3EWujTrQEpVz3mE{nbCp1B0W*9y8*7QBeE?q#*hX=oS!P$8 zRl%K@HM99bCvZGZg&7uS8YvND9iC`4dD9S!R=ErKhq^@iTOnQ*j#E6C>pQlk4t2Nk z%F_bK3V}L0-0jPIlXHC_)fak@FIMQ68C4gKA&X#f$&*(&GrJ;__Z7CsV3x)p z)oL0Uk3l{$sMHw1s>Q(osY91J7~CZoL|{Q1S@5=;f91Xt8bx-pj9c41sFfO6C#^!q zX&#UVhIzclhUS;()`j*~$n)4{N*)wDA#jOTFYzHa!{vDaRG!R-ndr52!rPrd^=t#k z0x7q81Fjt=LHw9p1{q@=&b5NWZ9nXqywNpz^|!8jJ$t}!lBczhw7TTJ69Pj&D?Bep@2#fEzgbMy;I-aY=LPFC0lQCuZrFASi-nm78Arfg0d4fp(3 z(iRT7S>ii|2SDNOx#BEec$Cq|dEw<|QnVwwctaqCy9_6Az8}=W^ODM+6p1AH?TG9q zVi@5{Dfrha0%rLGZcwfZWPcgv~>fNnO#bp`L?O)a7_$F{wjzetK<;1@=61H-z5B(&dWmM-=)D zh2IN(cI_U#^D-24G)ymk9AUO=4?K;clM49cMwbFz5UaCQV*D|km*nx71P zgKM^S-^a6{2{_1!L*6)c#V-rzI=5#Rb!qm8u%1dp0M1-(&+>Iadw2HPaB5VXgu{oK zIELs8oge{LGE!t%UDn`uCISG&lLDA-f0xFh5Dd?mrz7&D;lN@nPpS~qWD0hGd)#4Z z2VgoCH$#yyV3uwBO?_>{1AScy}4>|Z1>s_IHvsH-*3vt>}AS7kXYvN+nt>9JDKvE z%Fe{$SGDUR9z%xXg=Q9wQu5!yMFamS3OI`9cFh%Qk#Ksn`wLOK=Kja_UICG7S#%pL zM*cbEBsmxKx_PcJ&v(u9GxPk;Jd@AT>3f;yAoCn*o)gUTM)SPIJnuHo2h8&c^IXg) zTC5vzO2@B1ey8DgHhvf4cPV~__!Z$d5x;Bj`vrct;upa09{g(Y3**N=>bHHA@+lAD zO2foDE8kt#(aJAQo9V(?3LIB-7v(pWwW37nJ5c){9N?NuKWo?n{hVe{-dSeM(+JO`QQS>}0xc@8yC zzj;nD&nZVeZ#3Vxm}iA~-ff;W=J^1hpw8Lffd2ShieDjqMfgp`?;8Amf#2u&eTCom z`2C1q3TW01zjXZi<992<0{Gp7UoC!N{2syYN&NnT-}Csrgx?$Zt;BB~e(&M;5q=J@ z;gjPBx*e?IDbvI&>axMLRMA`+O(JAMeDQ(>z|Lg$xnSlP(V}Q`M)Vhc-hGjd7>?=% zIT%%g%fZSNkI@IW;qp?t2H=ILd&&KSKLE~7yulNK11F6m^9w8gefTj$j1RhXe+?aE?F@Gb-F|c26E2jelr$V zSYbhwd-JWDUk~e~?jAD->fDUiU`RDxCG_iuzh)bN<{e_th};9M0a|BXMc{CBCye`j&1=``Jld20rY`}kw*zx;oM~#%Bd0vtyD@^m%YKPo+2#(5oiRKD+R+m#VLvmuhi6`;8yBCDxL2JfesA)EM|zc?=&kR? z^1@?#6bxBqCB1{YaQ*W^u<+#59Is<*RhG`7rGL%2exi5B)1M3f3`ake4ks;eV1O+> z_5!in!3Z6zD>K=TI>@r12(yQX1H&*0 zGl>jAvXwYQxlDj6NVpYLK{61`pZM`G<6c41w8}|rmFK^0Hy&>L=9rH8GvhHHzPla# zzJy=Y9)9=#8onOyFW)1W>oqpV`}Y8J%y_TUsZ9O2XR6jA9mYG@p7glO^e1+t^t5n} z{?5d=%mV}Pebm7GZ|dK!{612DU4X-H-rW2D1Aocg@$Cn@j0E)+0HS`Fqosb=>QB~3 zroA!$TX}Kwu{Y+g{(N0JW}pK52hDD<*2P*A-Mf=BEz4Syf@ztTyyo7lqo(BpU^i)j z(+s*V?h)*JV=_+0p0GjscM=*nRZqsA=A*T`Z>%L`)aKdMr_i|m{Un3`CZ?2-VGx| z%+wQ^$j`J{SL;<2!e|PCNnOV28rSRsh9#b$2kH>0fJUdao{zA${u-}8_z)hr>H5U} zft-VCP|oe?8dGQfn6q05<*?~Wd>++J|L6JRYF+++K7ah}^rPpG-m&@PW7q`$cZgwC z<+rg*gq4=PY;?F6WZRA9-FUDQT4?AdmX&=AO@(jB8VJngNmxkwhv52x#3F7>*p0N; z5PeKcUTW*Fv+oeXasiL+PwgvInDD)p(aqTV+g4KVl-)!&yzVqqPF6t5SxDt?F7rd& zGb@HoI9Rc{ET59zQ<4@lD3vKj%sYjg8b(bJn3~D2(e}PjD)uxh;06+x&nRsHfda=4e7up}AyI19jP_{K#;W3E?^E#?6SWy3@)EKvYVWz9& z*MCA=t1hmL(V)oYd}}mruGueY8VDz{k2tGz>fPWw&`{y)Q#BP}iLY1J`D9hI{B{@n zYvd5_^|?QPW!W#ty&5F-&7j!ld{UMGXPt0`KN8CyY zJ}|K$J>F``HJiILY~CHG9MjX$AzZVy()M^NiryyYv$34$;^0tVo#Lr+ABZfT8o3OY zKGH}xoRHMS+I2HS!`{v8Bd)ot`4#-Jo2ypb%sD*wKG0S>wE|3dI{XuoVe2y3csK-K zp}aI>_Iw1vu@!ecj-aQNrNC=oK^N?>9CVk>bh(G&Fi+ziR}YAfNM3d$lJ>U3zhu%o zPn4uEYxqD6j~!fK8v&N?vi@)cpL~II zWPr|?RuH-vzxz5s?#%j++VLm)_vE&ojic9`2ny}uY;2?z*|Xph^iwR|*K~kg8@ye> z6{D->60|`PT=#hG)mDAwn}0m%+>Zuz^e#ncf^Yve@o?CkS2trD$i6)dTQ1O`e{2ou z14T=tqHj6herykH1Q$>nd+SY(oooq;|LfITCg@!W5-Y65Ca94mgePONgMGURdtQ=l zVoighP-bLxgg65?&_dC z0;J+fgUxyRa}>^;TEUgD75JpA(YtR%c47tgc=geR?m7wd>XR~w#*uW^sp3R@XYu=M z{oP)&|0&knHa|P(Kg!M6`T*p&d!jY0;F^BwcN!?rgf;+$g!H#>Us572)V0Usw_a7~ zK$w1v7tC(d-`GeT&Tj-~s3j)|w%U{Cf3*5F{J`M1uq5|&k+m8UyG4yh`!$;QJw`v3&`S;rYx_ra`+V)$XyWOdD z{CYb@m*dDw`!r=8d1)s;kE-_nEH4ew<-1;9mh)jvgS`n@&=t)i*qB|`Q{AHrhK;5S zsN3Yz^~iE;&HZP4JW^lNz6~>+s)Dz_D(k8K8a@`U+|@T?^z@#`RPQHSRk!Cf4Y_SS zwGtqXX-`kP@?|~sB+}{il=QC$^=G!zzwXuF>~9zaYt?P|K8o-^iJ9h*?dt#Q`1;Fw zs=qG4=}+DNzu=d)gD>l;H+r-iKQHLd*!cMazW?|7?72EcYAXoT-3sYkQWP=BHSb9qvzZ;iTj3UcJR3hT2w!c(J0QZU^CtHCj?ONB1m9j zX?wsel{g)lAXkv`R-Kf`Tw{}`b$g>N=Y99(bLy5jqXS-)fhYt>gL#kn0iiONI} z#0w4yb-OO`I_%67)$8@=IC4K2_jgt2b%AqFZd1UQJF<+#=pu91dS9aEzdoO8#8%S{u*Ry)LkAemCD6p(lYKewzWOqobwG8HwziDg$qjq zh@hVGG%YqoF0?L{!tgD`I}P8&58pz(bZaRV@BD{r0vam^nwr97t`J5*&`hSQxqqY< zU<)l|xwVHkit;5gN-ZbTBlB&mSGBB0sb6u2`56t=T=D45g3l3(%6gqlyQ=m zn@`Sd9n8B81_Ak7U9%gQBIzIw!|8NOej?ptN2Qym)3w8;Y$oxT)%y_b91R*^XlSsp z&Uqf-QdWE{9%Vz@a45?@PJBNdk4<<#lmzuwc$DslP@2XKu46Q#q_5i;6_)-|h)NBC z46kpf9w>L^;1mWselA-FhIMuiHbwT)dfW0|`r^2wQ?F6;(m{79ZgHhyPY(a-Pf{yF zTfqg5A=q64m{tz86%y+LPJdl7-QY$&#|PB2mD5&3`U^19D-ms4vdy30P_~2G3zrm} zq=D&qrz4{FjaW_DWZp|ru@v$iB7Y|y*XYGfsnjg^V3v&O=Yg3^~gnec= z*wu3Ob`V>F0L(?%y#TTGL8b=poneLTg-irIO06WAJ$W}MSePZMUw**jY=KOGx)wPd zC|8+!4J4G|fP$$|gHW5d;MTw!TF{M|X>eSoLX&AQGo64;u9qJsqPQ)BzS-wqhIbgE zuZlbm!0l^R%SWM1eSehX;lj6C)-eh zANTi`aPs->4~L_Ku|FthaT5}fUA3D~S$`fZg)5T*dK7o$r~7Q|TuPqNvoL!#K$*+2iHR=$U46WBJ_8brQjV+bfDw*+>(mO8m zFub!hW+Jl|IKwIDbfSAC`1qFM*Xz#GqnVur_ZR?a54I+C9a+6IP(8uY=h#ib4o~`n zaXr9(r2EtB+P1BFMHQ+qyUtUVkLWcnfxp{9)pkFI;Wza-KW9MVyz69`yiTt70G>MM z7lsWVEP`v>>P9w#jG$dAX&-Uq98i%?Esrw@JwRg0R2Z<@bM=YKb&aYI<%}8oTdWPO zp7U4o{OBxu1JAowp%5%H{r2}h+%>TX9!^Gp)V1h%7i?adiNbv0Nr~P!s_+I`u@y|! zpR}8vYyYx2)`vHOwoV`Z8tH)5;G?Xcncq89b$0MHoRjEhE$eRtMACD)tLgu_j^|PWiZ_ zQwu>Afxz3K`szwSbs={KKJ@4RbG;h~m(4@2MF5DJIhD`TmH!Q3k!Bw|mDjul^&v!c zvw&+I19^ab1os`Xe@x^d{+4?&KrvAkC{Q z{2=ix=%+GqvsEDkihex#*A0JwMk$&)A<~_1tW_i#NMNx3Qw%k&@P(hHU?kx)6S4mA zg|}j)T6tHc1mdFvCe0!C7*>w64w*D}+P4Pe689NoN3zC~RW(Gr_kS zV3vc?3Rj`m%wwiv{wb-zoQZ0M3UNgtI1k;@h9_33C&ZG%RWr9IdNR&sC%9^^$E#&u zQ)NMt$CP*~O_h1@7yL*r0yzvV9{%tJ!Z z`K@Nqf;_8)Sc9}Bs9=JsmOXn{&0wS=0wpo2*(F-Z9t_W$Pd8oVIIOch*}HTs@>v+V z+$`Qc$3Y1o)c5&OhLW!M&_1v+Bf%UfN{gCunkD|{5rzUv(j9_^CWY)il~Jr z)^qMFMS?}l|6s13V@Dv(wCLP5paaV(Zp}^8G^)VK)Wf(#WF8WruOh#ohxS&#`IHyu zA_wXKvA|W6C3Kot&&%yf)TSRa?ytKw%o1K5^DA%eA30gLYE}WDWlwDOh378>2(Pe5 z_4P(FB3Dg0i`!`8)T|9=dUe&Fi3s?vZgwU-yRZRsn40;`VH{P&^=2{+s?GZkL%3!= z%CPm$wsz|A52qe`Og)O#-=rQOms5|3Mr{x4QLMU{dVB}xcp|fi^7if+^n--g<3J>6bNijToA5a&6j|=a2!cIqbyE{NAHn?WKC;dwk>I_XjoOXaKhS}X=ou9`=>F~K#DiA2k}wdytYE{v2ilODirLG2DSq(P0s z(5~-*(>&yeuWL0kv8E(xG2jUMTW@Ono`9rJ(xr^TKQ}jO<~}Lm@P^si6W(&L_;J-< z&!GId+W>ziOe^ID)S#dbtC@5w`f@zF*PxW6 zn`W72ezh+mE&9-h9U#?X)M1LzvdJaixuqlZAS`XxBi)D{qV+Op(Bca?T?+WAHmcml zHqv|T!pcq-LdXK82>vY0M6kCDO1BFaLZ*X{bQGKWm@u>2t{TM>qKM8BL-E%LU|;tL ziUPk4#oq&3Jc@A1eVitn0@PUCM#k2&sqoKj<)QIhbGSqTX>wUEh#f0;q$&40luNDC zF~jQ=r|eu{kWvv74yHkRAi^i27EVSj9K;PyHS2YDK$!|u*AL9H&{bne1t!+3MX#WV zd0IHWdJuwu>%=7-tD6nWy8~hSMGOY=P(~~x8w;m2C!#*!hOzo=MgYs?B8aYKHGaAUSCGUAZmM)5Ps^}jOA2G2!O5*Z3 z6gJL_SWWa)OgHl|nDSI?QE)?RE z-pBT&6t-|U5XODIs--ce^zr&ZufP z+dYhoj(mP9+K?GhJmc$VGX3p%nf|0REkmXlnSSRW zgA%X%8b0X=Uf6x>OeA-RGbXAVq4u~{F3^BmkH+Qz0!Y&DQAkYM5d2TnIbMlJXD)t3I>0Y9H!P%x_LlNkukQPtE)gS>f{U0-7I@h&Bg|?XTbB@Xs$5UHHUJs`P zkko;zOT5cuJ`0)AXo81-4x~;7(v!!9^ddt|nb9A0nSVcSN@-M+@_UoA@wh3k zMkr{I$-Wv-gEI_@e+Ba}=Dcm}Zb%#=FB|ah92fjyGMI46c9J;0SMM=R!LvD9q-z}p zj)SBxJ9J_S34!EeGT;}YJ&??wE!msFFjWF!%{wRhzRQ=_= zt!}>pL=3JdoqBED7Okf5=|w_cyPAp^pg?oG4f|Ub!Q8?Zp2uAQq4wpUW8nm{$L;Xy z{9pKgB7dM={wUNk$Ibs&oga5(UeY0OD(n{qTG3^^2`SpBu9}htPk&fYENBewO~h6v z5y)BH_U<&jJfRNl!!EzUZYhFtwm3u zHUx@gqARk^*y4g@FoT4(k|7RS)R1B77c;H^K1y96y4+}(F=-t6u9rvla`nIvp9iOA>R)Vn z?32DC8DV|%j=B<}8G?`S?```oHP;{}}uNm1X3za;Gp zG{xTUoW6e_dZMG0pvgKSyV4&$?z6)~?LnVG<71W^@hI-u2&W9h0}hHZ_(dj%^k?B+ zox#Gy?z*7@Y_J?%4;^5vTMhVHx}RJ90s{e5I)UUyubaVrw^`@|rLaK4Sq7DYKI^UD zsBtI;f*dG&eoOR_nxi?EKGs#_1@;S6KdNM`$YpG7l}jto{dB{r{5`sXu<~C~d^JjI zA?UxLezKC+p=~hR=BK~Ik&!@5nOD`;@8qODeL+a(B=pZdfl4wrQ?=;Vl#W4inHtV!cI@sw>&aK(NF4EZAP{)k54&*Dr71O7-tCR5C_(?`4iN6*aO2QPX6)Geg zDA?!<{u#MEX_oSI_uH4Aw`u2B9~%|5<{v3&bX^5F%I;Q`u1TNQg7pdA+?fU zW=L(tyLy*Q+zhEJ0OUIj%{9$L70%L)DC62&i&L;7-i*W|xol4C&knk3?8z+H0(*v$f&r!3`Y;(fDOZIx3`{m*P~)RQL&{SBvJF&nYHNzeE!UUGeC5c z(2NZ51{}LU4_Z%$Y-&5yFMyEmtv_2sXxXhvQ}P>48bA0LnD$aK3NY;k7GJ%h3J-^G z!!DC&)wD3sL+DMz`1Pt#!@kj5e-U6u{zUtH`hP@sUCG{;Y2_7X!0SYqE+=$NzJe{G z^oUR&>W4GoSPK@ZlmY4$JCz3fB}8Q)-Njo^)1Owkmld3t!pM0T*v8x0ndHt4yGLim z`+D_%lGRmnGPsV`a`wR+Fay5;X0#f}tOH)|nHX5jxQ|=tD_G&+nMTT&-%95Z8XT@Gd$>q4H1^(2@o7-mLT%1L*{dO<0B0Y@CBx9kddx^GQlORy7gK;Fh2nhR^@k{ZnzK+cnc8W;3;SSSmHG`@U!9Oox z-lYPLT*x`szc(kU&UtnxJ*c{B{peoa`briVPJb-o%nscjaySpX+#rb`qdlBn2N%f{ z924&S3=M?Y51TVh$g1YgLO_U49`!|++%$6LS0STx>1H+e0TRtsqy3q!SE&RPPH*7p za!x%r-G^D@lrKQQ@P${0G1G`pcRSxOQZh0B6!IPSeB+_6WJfkr&u|3x5q(*=apOOW zs*j|XG@ESQ&Ov(QPSv?HnY*vfEpra+YL-XFpwGG0Ak)vB&i5{Aul~5rbiR%f+eHni zjO%>Y?b4mE^#KxXy*lrn&!8=#jf;ovIE)+v?I;Za*-p#JvG$um0)Bm|(|#*v#@bJ~ zipX)T0i2ierNNsI8I150)rDlzlf#KzALQMYQE_$|&N+N**;ixid>HsrBO&AO4X;LG zAxgslnITS=!9>6scvuUJ1iaxy)m!Q}5h3f;C?rLveHAWjz#67}4Ay{f99j&G&};|x zav%6=4!MxD4O1Z6*!6{)!oA)H={L}+-TKecto z{jx$O5NAE?jw=I4=D^~{e8$ET<~&lEGiEt7z^Uqy?To;*^si5ZGXCILp-gNAj7e*g zTK})TW>pX$4x@wR73q$A0Vd8RL?c+a!>vM^5|Pm~ZIo&EmmXG|pfgLI*jn z@Bs77XiH8*)9?vsR1Q6m3XA5@aF1@eCa;b-y6s4B+w0FC<}W*`vZw8XuEfe;Q`tSI zA%BhQH;#w6dIF8fYz{u}USjaUE%zT*T*m>O&2exyRaij4zAJ-Bo`my*Kyo~g91SEP zJp#9K;8yPO){{p$NQcwce4+d3B_X!wC*Z?@mpF$49$@R59DP0}BknxvFbAW&!knnM zc14@+_1e1$2_ik5GKG5hLwCZP+MVuJYIQl0%0#{18-2gF;SLN^s0i-^;?clP{yD?4 zF{VPp-Tn}&6S@{nH(VGKJXe)u`13Bq73`q_ks^v|C(}DE)>%l3X=DIW3IUkX?a#Y6 zEpoCIy4el$3;;qDpP-LK!%z{|R^66Hce*S?P7B3}%$Xt%>L#%AdO#^*4+E4$Xtm%f z7#}Fmi*pv^AZU%s@Z-mwZ3tPzppIDW3n8&R3dOCadA}rvN(@d3VE^UD2^2g44X^AE zGCtzgFKy?2KZVVmjPNTnL14_yURxFLYzJ-}{xEQbQO<W|3+r6yCr@JO#BtQ_=&JN@Tkr zo;QS8knK~pa;eJ>U~iJf#Z)qrDjKbd07^g6R3sVQcW~o>%Ob^(lK%;G4E%L@W=Yv{!nUds^L3 zr4!`v#~IJb8n=DJieb&Y)gCngr#LDm0GLb6teyj2KHwXHE7G;964m!{l%v;JO~Wz} zncxdu4PxTk^y&;>@UF~+Kr+J7eC7t@bD*IB%nJZBaJ3hH#wwBvLc=UVR;LKiwIKL( zq*5n<9$*dxttlYMPcO(HX2I_qkbp^LR5HW)ANH@P`yi!SKXBfpKL_ue06V`Rp!maR zU5p;>XIl>*bdO0TVHL59}12K;MfwNAKTg<$o|U!=Oea zUBenjhiW8MWIT_<9Tj#t*R}gM@N%{uCR;51sYoCDzy%@e)a|`FYA*7JXO{Y!XsUs# z;li!2T(u)Pjf1OYMv}Om2MsMstaTitW*LW8E9SHyJ9@@-4wQ()}qvkZPF&r^7Kn-?^pANv*BRg(e5PSOi1HRk#sart>Ahoo2N3RURp_- z8SCRAuq|(b7o5sue>geKuFXIrFLfh*ej``gb$IiN_XpRoW|$Xm+76g-@cXJWenIX_Yw+U5vDCs4+9}Mg5`9GXjP=$K9tvbP z8j8W#)LP8~?f3oR+FWFUJdQz7nouzX7ncMss(PatfeCcrP)l<+^oR13am9nKX)=&# zB<0f0%SMH28Z&^N-wqYyLD#;Rfj1WBE=XGk{QY)y34(pDLTt9k zPKz(PJvOKv{ys=U6sId8r<-@O^6V0TD2L=DFPuHPA+VIh3RZzQh~Nho+a35>yE}C)I*Q%?*=KUs0C!{hQ>!^n< zrnZ~C(Uy8-H(cL=60!v*l0r2nN!ul3*Oz}l5Es@XyP0#($`(piEoG+x_^koxQG6Nd z-FG3-Hw~Am%L?1YN+FsI@V|X@2 z6ZMp2c{`S6mio2MGRtIXiOW)oEHl(?$l|Tfq2)n1efx)c($Bj$Zqm2H)m-Yv7_@c6 zvZ~jm-4iP4UJ7!MbPgxM(ej(#%7c48d$69NAxoLnNJWitI7;1X%35c%L+mpeB_M#N6ZdMKQ#^2k?s7 z6rM*KF;jXlZ8ik3C67a6P-5bi0xP9Xazl{}k*g-MC=IXx-sR74nb|q`V~0SBe@F{` z^l$Ws;FO`Mk&KBc8<tA_L>!1@LU>t(W;vMEvRSWyaoL^LafjO z5e^{qB@Q4K*E?57p(k?iSgyh~_Ip^L$hr+2?5NFOr*}b~y8)xDrZLlL6W(v500kmMK$z*}ZYHQ+LGT8@mRtS0_l9-D49kma;Q;0&l2CoHJx ze$X8ERfpqZ;%%UNF;EUkn=x6y`J4T)W@Rnqr$Air?QcPJW`V;Q zeaQK-(6-vP$%mhvrR#G87FCT^uXiC`69Sm>pV%s-4IYY?V|5W?cZ$}rFY&&L*b(5k z5giZRk%#6O=+%vN$mzSGfxXX40x!QJqwEB=AHxLwNMFh+tWA^>6kBA!tp%CXFayI* z$@qn#xH$&QCX=mAt%JtlM4U_cBH2oUdT~sKx{{;RXn#4iSvHnwxDN+Lv|tHf5~T=C zaP`plfWb*cvSpW-;tDt$N|(WN>p%%CZud2&;S4B(g4^LNw`M(gBKU?UIvzy&n2O<* zIMtNE(C~sm3jtAO4rS}lO+y$*-F!D*qQ&~o9J)nrKqAijk8$)?uV5|P*>;eUa?8mi zx#VQB-b=e=h_qA@y^Zbi)~9IZQnMKnW+}QlHbqp^-CUUM!9c+Mjo4g+Ab}e*adaHQ z-*o5*A->J9!X*$V@5V{bRvIK_+XwyOC2VS6)0{MHCh;Gb))Y_)(hI-jzMbJ)C>Re{ z@G^G53Uz_<@cw6^_xpSUY0RzQ{5z;AOzDLO#P)9dGW%91<6ZUJgoL5t#LZRnC)7B) z68A^=f~&COzdU;>0?>7%Nv;P1QvQSNFRF6BNS)yamoPf?!XgGSiFCn5a_F zLe7|rlkVAHAe!?8sPQS#Rp-Q6djAdIcyUL(ebAe`xI1UdiXLc6AW(v0abpio1xp9< z@IIMh_+ATioc9oC{ITJoGkuLall@6pgFu)5A;f3&7)#~{Gh8e^Hq3Cbl5S4ZV*`_r zjEyZ+@>o9HyrnK#w>Mf8x+z1ZV_ZyyNhbx94jUTVYN-}(tAdYpl@sQ41x&QQ1P)BF zQ!Zo4f@@7@FqMu8J-t7djc2dtYcXC6LeFI2;ayd88LOGdaE?~h0;6H6utPah>v)dE zA(9#IhJPFIQmf9;+|f>XqBO>P*z+K27u~Rpqx&FK#F=C4n(drAa88m2FL0B9%?7MK ze6Eqrqe2hJG9~v>5;f2h2N@CrT?^(t!%w<7z^XS@RzCIz7VZ^X2=MTr%+(JLRU7#V z+kN51Ittcg**M&gq3&4EeLETLJ&NprIRCrVIWOz8uLT9)FXA3!;aoLt%>J!%ctVrF%Igw0$I$93pX#!NGX62a{k)bbPiH2#u2-dhIJ4Ee0kW->%vl z!LB{IugD4^wIK8gM<6LZ1rC?>a05#gUX2OwnJf$^EShqesCvaV)IEN!3$loqq16Gw zx8@-k(l=M~03biA-Xa^sD4XoYexE(pNJN%vcnf4A@0*BA@DD-N@}T6?lX8)3@QAf6YZ$^3I89tl$?kC#86S?#?arFIYhEUN;iIE(?0gJ~Z=6XZ%77?s8*y z2WB?eax)mV&I75u8muYe;-dk6WuUczh<(8DtasF8oDUO_m54nAQdvd{$ggbn}MXB7BjgGfxLS&*b{Y57yz&_hbZMB3g5% zP}nYpzu#qWqH!e+R!y(1yo3WNWlZB5J9DSrP1{dmNv^+E_kO4 zY$k#|7pO}hIzWTJ+(hhy!{kUFW~jleT;izE69U$6Kf(nJwfp^8HAW(b->xk|Ff;)F z!U`M5&Aj#`x4jISGkXolr0->hi-w+P!oX>;CA6Y7?{V1_$>6Y$)~w^&D$>P#aB*dZ zq*)rAc$yxMqy>Zlt$3OuS|_Y`N)f77WIBW5@hfA4q6Dj>V&KU6Pxh>!aHuXpKzR?u z@I~9&qwg}NlriwGu#_(e;u?&d(luul%SCUi!c$!(j(!e^fcSd^P@saiF^gR;t8U*& zvggXuic#W`e{ir4u;6yvi02~>dtUn(f{_AamOb9K-Ta0iig$Qo#Y;9;i|kXvmB)Ib zlMy5K9Zw>40#hR=#24CkOwQ(ceo4-%3NSAudFngvf{SVPVd4&@pId-% zWaL(hz?BxQ-h%JQfT%3`x%NZ-Ro0gPaRbzW(VF1D5FZU@Ff)J;0PuRe3wm z%8^A_a>W&F>OrgxaSv(S;l)&{*%it-Ok;`Fj|m0w3IQ{54m*k3Kr-8N*ywuqU8MeK zvBywYcAs%lgW@pFPVr3vITj+5h822rl8=V*SM4F5x&EbL9iIzcuUj5nPOz`(B*sCtgsPiE&; zy_~XP@4CRSaUWJBoCKuP4PVIyX+e7gpz~S5Ot5~`MP@i4QJEbDWz@p<$Y$D2|80j z-%)Qw!}uRZF&=F&6y<(ceuuIFmA%DIz#a!1sWoLTGz|8zkxB@&yloF#^n(wfTxb2$ zPqzWYdQct;0ol(UEgRW-e^_*bSm|EX%;ni@^N<%RJ7Qk$#bI1uJj;APxNECam5w_|E;I zv|HQuhp_uJ9UD<;&egS{E2hW#8tEwG-`G@SYN&9>2CE)2)IU#QB-g%C)5_iJS5#pBF_H?K+n?yHs28#j>kt3$82{co*5{i2Vw2UP8XF=%Ip z7ABB1yU-g0GpH6%eGAe`LIh`dx{}$!8^Su%k*)pP>k~xw`@w45jXSj{03Gn>H@Rx6 z1&DMpXhZg|WEb1)UHUwOA(*IZrlT!TrzudXbF^5Y*B0t5f=KvbK3wiXWYh7G;axqF z6J72i(A}TtwxS(m2n6j#NQto|{k;8kQD7oF4XUyI;O{b|#`~&GwyhQC;c8}<1|7~^ zZd%(RB7{Rc>Klmb!9O!0E|Jq{phMGEzFKgW4~G^u90W#TTyHv5(`>dJ|A~dhaXM8n zFM++Phwj$S`kTw#Y#4&pw}7x}gvJ(U!TMccIB9JII2H`R!A|8|k0@So^I8K|9TXL2 zsi$7h-WG02L*dX0Wx=Bp960UZx+mOi(-Y$IZqd!gp*U*Q;H>)5X|;3m_b48pss{vKT;GKbImW44O`Ge_O26 zUoC-!P(WfQE)xWzzn>W*nuzV*t4SmpGHji%Gn}alI3-rVo>eGdG_kDz@Pd~&&;GdY zaF)FPMwnwY?&`^-XyNojO!P7m?Soz{v0|v)jEeBx2xnb>k0^!}BW#BWn`^>K5VqNb zm9w01`rGF7=^J1>xX;SN>^QT;J`&rHrF^tj%|R@IX3OUoJ2_S5Fh7G3o(e%ZIklp* zPTWUg)~ae`67541c5VT;>>mVmGeTRR?#MQqIFICb{4q_Az6b-oM{9(D+IEaCH54c3 zaj0e(71AXns)t1ij;F|teiVo&bz;km9tew z^;&gBTRHEWpvwq!rvNCNr)6Q(V9{@M4X)IXO@81qkdW_egWP1m?AJl8L01#>8G>MB zREIm+Tjv~IK$9WS0dOpkXr9i4+;6Tx0e#vE7+|3Nwyl8iCMc+bNTSD(i%l^DLAX?Y z5rQs7DlhysGC1%0XcVDv>X_lxa!NS;Yy+jgiA$t@aN=WDQg18NyY)S;`7VcuDMNjtW1QpX7v=bwj!)`c07!m9@YfroyVwuz0Qm* zZ!Ty3k=}xhVixYjj)Yipu$xnGiCqGpuA0A8I zy6pdv_9pOAR@eW3LJ|ldP7u+!5^GcxYNMf=7?%tr+S^yb9Gn>vEh%J0|En5UT8rMT9DtgpYp{3f&5Dq{jG}nieRWumECOdmSph_G~g9l z;H3zfeHa$^_U9@8(eHW0mea~Rq4!-BeeEQJ1#kG-L~EJA23|bMi-`Z?o3nsocpoTS zpNcmy9D&A5l2A+)6pvnPP$(#l^qV|P{dkqqiGG~sQ)+^(VXVr~W0~d4@|*Uf>;hyTzWiKmf?FBp@qW0VKb-nnV4X|7aemhWPM*`c#aoYHc}5 z0C$F8t1K33ea)8Ri_i$C#iC-G?@hZAEdvh3Ae^!53H%iGQm1+MEb-Y#9W zd0Wn~EloBp;#p4*C^mMsJ&%%@71<`5s696fD+@hIwTr>1?%pToEM`=DlO+J)TT zOPTfjrE{;pX8sbkpnenfR&xOa_TJgmScS%>`T`uB|J+`9Sv@8bULg`e+G`K*fL7Q+ z?-at{C^LaF;7N+Zd#*->21HxG`4e7NPA3bS4Gc%yBHqs~G}1OXI~=bV$fwI8x+wPG z={-FSCuBu(5yi>-@p6A&{>6X!D_(NQZfV-Aa-P=uPwztxxxLW8y^Y(jJGqocgVu{T zDsbJb@+Rq9ejiY2e*fVA-foJUbbiC9`c>SL3VWb&HzNkakLSHsNUK!AqrXr)wiOY=EJTjg5+Mq^^9Ut0vKv$_*~^Z^B*Hzdpe_d z1vdV(R@lHByUKg~0*i#Z%$KT7QC(Jyh^lFd0JhWfq7z+#5Bame^+E2n0#~h%KRnHL z94xR}$#klq&wdJjzwx47eD2AbRi5;B@WIz8hm-zBHWj3RX#ir&9K(HBw0~24#Fe)@ zl+uLsGDHL)Uq3!MxiVM!OW~ER zfxvtjTUhy@E;Lqt)fGsE5?fQ>)TM^$_$-IdyusAf`6i`#Rgrx4RST~+pU11`?5ncm ztGD#(HNCpmz6vH^J+D_SdbPm5IzIX8KE1kIuP(K(N|LW)dex{`Q|+swO#Fb z+P?aE^3{oYHA%1bwXcpzzADnILcLN7NBVU_^3{HNm8(}Di^Jks$0lF()2nY;$LU;U zUmcNr^$Azr`+Bw5zB)Yl>SeuZ*DD3FP~oWLtH<@~5ngp&oAUfFJ-=1Y&r5keSI@7} z^U6@XAx+$PTo0Ab{uyz*+G1EhanYeAkEh=Hf=?M&j3ZYnew@BOUSG@X@bTn5%LDX6 zE{~3+0D1w}z$)()9_#nHQ}yd__#Ga`RDX`>r)Ai?dHlmjTsS&GJu}MejEO!ee3=56x*jw=2v?RB%~?MB46L*?J@ph z15{VG0V;@ZES|M1Km`ev@ny9AL109zMAwJ8W|iB!7?d%3Uq%?fFWF%iIZXfTtABRo z=!CjS6nzGWx`=ztdQcZX<>Gc-Z5b?s-k{$=0o3VR2#r&=lJhVGjOi*f_$!OE<9Pk-$MWcZ(skD zA;S3s3=v3H0V=+LQRUJQ5#)fvAM*gn!9U^TrX^3?{(x41-<(WS6K1ADag;g!Z|3=q zLL}H(K@HyCTP;}YPfCDA%!ebtcGY&cOxOePNAmniYZt8O-v_?p9pHQ8egof~oJ499 zmSHc9`?2s{MFv`-?Z_VmU(d?uwyfFOcDS31YQN!EW9p>ZUm_$no^>irw7k#n>HdA< z_iX?EV*mH1+MG0UC4C*4nl^}P`tkP@{&wSUZ~hEE{QWqNVjdIwad2^JKh6X^;wuv# z@u0&4JmU5N9`PU^@le7eo)kmh|C?p-*|78grmbmpIV10ws2y(nAE&|{YrLOuUHCTpoZ~l{hW;AqpVqTH`rphMU3O6PhF>4Uv|F0PItl6>u}unNi!e?eQ)sGB3={{ zZdZmWM*Kjpx@Qd+s2s53>x16)yVEUbPJF4buKibQ`r_vdsNiouJ)@z%reEFAq@VEd z`u^G#TFz$Y(G6GEWYis;e04itB{$gnKH}uMmcO=S9LLFrrjVSe$tS{IZ@lP}{VWBw z2L?0F@*F#2^3oz#2Ai7!9TBZS&X$&f1n=i3CDt4wzhHG}q!U}PP-WbS1zgJQ%q0k4 zu*h5vc4FB)KoKdwf!(RaGDR*F$qg(SSGwT(JZj}h9#8U20#`YtjcD*|K{fv(50Nq_ zdSOj^9S1&4DKI2Y4@-X8vC)9+C$5xBx6RMq_v9$?)?g^$W zz|dsz+yww_(@!PigKtf@_;KGvXlG6jEQuP3t*mxu`bI?bot zQZ>ss2aA1Cw~zNFdO#Mqnf64VGcN;;nGA)FdtE)-B?FCH1e#j`cEl{P@%0)gik#x- z8hD`voC-AKJl&9PqDnh!p`MCsNf52#KcEn+L_If}ZpSNXwZb32do7=FmMNuKfm}sY zPlxI;%fEJE+U(|=Zx`|_*tSzqBlF1{^uWhGg`YOHI)So(Y5|$5{$(Io<$Zqt#|$ku z`o`n=EH$$P01K9#c2`O!B^oh0i>?C&Tey|G!=XK8Ann0ls& zTCmV@uLQU43bSLGjv#(`w|HVU`qO0qifI9yC+-wO4VYP!n3AA`N{A8&SbX?z9^Xr` z7QyBqYYQXG0Z`E;+G2jlX zV>d0}_A|@OuntR{23i($CSyl3Bb&!=Me~p<^lXAQfyDDDp4FUL|1rRct z_4`D+2IR4CFlW6OX4|(um|1I%Is`Y(3lhB=iQYzDv)PT*VQDZ3aae&RkK~(p0V}tf z9fcW^5M86|$<y`FBwOd$s1+jJj-hc2)G_^sWq|4&9DuXL{ri{j;mK7#?m#Hlynx z$t&W^VLyehIipxg&FC6R>_!!*)HsE&I2n^+mBCK*I|8eYqi!fWq_X5eIoJbHn;Q6% zEd`2sa(CZEii_;P4SP_`Id=oGUKg2Yd?s+yoQ&6%RO&<=1!V=(_dPY_zQ6`R4z62S zP`2+W=w=?E97ob8)W(iPHG8^0_k`SGsjFAY(6o8pzJ8H?^m>dIxL)z@EHRZv4hi)b`bmw1;W!+G zzn54?U#RaB{-||TGXGmubuP1BCpM^Sbwa6=VzH-#qq}AwgvbUOWCCBmdxLKRn{Sqz zobJnYK1-U;A3T3yfcF{veLH7rnyv@(r(e}6Rh}a?&L9E&=Io%}+8_axX3oxr*!lAjwFOzH6ONUs)0b*HGda?G~Ys!DFdIAXqh-wWjfh0ZLu21 z+{DU}-Z~0hUgH$cHn!O1*kTp6A@}S&V$-oH*tz{e&}MdZzY5}Pes!Z0zaifklm~7w z?$XeK6(hCHfPOU4Z6{t{8|1Wm-CHeh4D>wjp3$0z-kP6l_$JUe5k9SSi4ncQX5A&4 zW^W=fh(k=bXJ=tQ29`9*gS#NOyCIU!res%8D9^?t!S*367zqrgkorMm2u&l}e==z)j$%Qq?r?kLHVvWED+E z(ni>AY6hfmae4Jj++LHb9rrcwW6kS)^k2+Zejz9Rdy&dT=!n?<|TE5(b7^MVgVD4IP2TXU*68Oe-ljT5_%RhBB@9 zp>X2!^fctMEq|VAC2qQ5#X`fr7s#=~>aHtS3#-^?)8E_ed`yz)Gx-g)U?KB!=n=SQ zj!h7&)lRumUSm1rvPaGM6~*yNEK&rZ!XGNzd>BTAlk(xIfLB%=I; zgh*93eHdt*hA@b|Lhj3azg9dI8nvrp;Msy5xc(Qs=B!U=R9mN{Pc6E`aC{wd<)e7t zmf3-Czo2_VL(#9MriC&(dfp2*F9Q6oU4zYnxvPKBFV!vr2q?ft-6#PHHm4gwxxr|^AVOdUB+d?T=gU;rEqz6? zniD`A_YbXV4WdXE!tOj(!lAX{j2FYjK!0pj-9(K~^;m8Lj|^53B!Gec2Lo8#_>hc0!s|TGOkqb*v!+f?>~5Kn zYJf@09hyPGqCIRrLg9c;@tYJ7k-SC+F`&~ui{9AyK1yhw9i~hsnR(``*k6P@uY>BP zMJ=5mf9G)FQ0$a!2EMKXx5t+eE+i|I-d|!mUVC*Jhq8svxi7TRY|>U>Njh!*)U?g> zSaaqtajoC*f~jd`{7vC6ajoBkJ!+?y>qNcl9JfP?p`=6lEgQ!PQ_-rCi;u2Fpu5kl}@d(An;)t7I9C2fD#EmUw zUI-35XuWQMoidB@DVX$M_vSb1x7nCG=6FCwz!ZyKSCdwE zZYaJ`q8(fs=U>!Y-aT4>SnX9EFC$642{bJbEQ}Pa^`gOpl2^X!uC(TqS#;i|6A~oPRa4QYeRDK1OkIOL@SMkN!bxUn+ zTe}tuj_DnPf2n18 zBxw0$T9%6l*03!Didu$ywwjNX?c9EUINuwV3oHrxnrj4_YuA}ZbLGSn6Cf!rKPH^1 z59@b>c=g>4Q`0i}OxorDp@^aFIJ%YVJ1(5vgqy(sb0i$eOGzQ2_jX=TTWp7 zs}tgxM>62e0)uTKcYSEo3cl6uH5rfbihkup0+;Ggi1Dq1LK!ZjAv>@H4P}Ak&+0k3`7s`kC`Q73n_?u74Li{MU(R{wz@$C2VEjVJjY;$Sz^`-H`jA zpz)fA-Rb#x?C(-M0$5XQ0~S<)n7k^oTV=_r8ABX*On#;NrY&FI^r_D8&5aDJELlE- zi1A219ot}EvJN?o-OoBgfJWX5x!v};=882M!LTw5g6^^~No%7o6a~*pdXp0Q43>0R zK8bGYsSAK#x3Sd>b5XNcu`c8~wchpDFoQ!eWoi9^IelrZxkxac|C{jdO}90Y8;8>m zGb5vQ`eEH^AvaX(72Ap;6Qc_>ywLr%%v4ny*|08&>g3T75aXZoc2!Y?2PtI|N*OHJ zx31GOUBBTSPw7-oUZVXEH~Bqk`_{m)ZJ)sWP2yqBRloev9&(vC4xE1TREEME=9cms z%2?U+UesLxKw7rK>27cO^}WgLdy{Q%++XV(_obj)n%CBusr_s34ZxP<{MtfxBh?nZ z6f?Ej(gNHoy4qkea;8N9x}^^SF23G&&)dx}Mb048aZ^F(=(+yue_v%Q$+eZhp?xYj zSe2Mw{BhybwB707SNvYZ-x~hr^LHzMm-2TE@6yuyWhej3$js`WyvZJrz~`-<7N6|p z)?Pq*F~bJ{w^|?g9ua(|Wy=tDz3&wJwbuKN^0po0DV4(d-?5;5Jqpf=4>_J|Fa21L zj83gB{aI>Q%Az6p503Mc0ag4)0;EU<+V!aDCSo<4H}IM(r=5i}{%e+cGUb|O?d;^S zVdou6<4@4U^FMs!=ihn6^ZUs@BV{mfLkxWEit#>}D09c%sOlLNIb|=p*<80qa@b3^ zRoCvXJ${ti(uvzWcd_V3y@*Bif*)`r1tVim_heBRjQ{o$ySfLWG0z)#*!*BKQar4=nJSX~DdL14-pr*hFztLb!R}!mu ztGop&=+j8{Mc@2sPW+Ml9JWg|Z>D9=clNQ770Elqx|W+Z8hhVtED5`v{>GBRo}dL) z?qUWMp-c|M0wJ%or>X!$D0b~ed`)ce$K=?k@t~^A?TI~WZ2o8SV1(GSrPS>ev$RR6 z8gKE_Jv~NS+G8Dyi)N23uFsr(Am&4~HQV~*csQ)5EtIi>$?J?SnUt{|B~Z2&FpecR zSJpyYkH-?)v0Ho`D9vm&`3c<98SmxY zU{qyDJ`j9HuQ{%sQFJ;z50r03Js(@^WRx9O<7CJ!cwBYQ^5-Ep^YV8B*N25LueV*n)KnGCw`^85lBpugNw9!V=@KX7(;`K|rR17%y>lNTZ^;|#Ln zVKnK7)JMtD8WRfezicH)7=|n_^trt3+VHUVO=boK=e+45v>D_lhn~s{|&m`j-0lLR<{wY zj14k%&MYhbTLWbyKMfa~4TuqD_ndDskdJ_GPd|%8`3<(Q~;GF~-|sH`Bj9l#jg0c=%TtZ<^oq}8!Ovur=FoBSS}F5tG~ zKjG^xj#AZc&~1(Ni+-10muH%Z3rvw}2|)cNnRu(LeI zkG-89Ef}m2Hm_Iy#J`#92hbBO<48)p0>~M?c4J!PsP6I&>5-v0_7-^$U-MZ{^t=t< zg=5#=$&|x0VY+4LR?R(z@ZB`eSqY6}*LqPSvT2dZ?j{XSjGIn8-pZACzACw252@$b zW`6ORW&;6t4cwJkwX7<0OqE}CsLE$mBx>Z#)S3$y*;+U9!pkwW>Y-}gz$0oM$t`z_ zxTDrQ@m;dkyKSv15;eL%Ei%5lsR+)qwWf(sD3Z-sW)2T`PCgDmhZE z0}ZSpYL(UN;I@XCrFNrUe4`4nMD_KDjA=LyO68TD`+~-~zK({);Q-5by1ZshY( z8?_>A6H#by>ZB@+(f6!AJHl;81onuMz+e#nn0Ujkfo2@zJQkeklWM$=7XzZQfsO^l z4L$AAp3E6b@#jdua78WRhcQ#u_Q>s@7A3!9l+nPR+~2KBby?3D-EG7YX&q;<+1A7! zE!QNgv8@JD^j(jyr8V!#cl~bRN5CS|_I{#Yst_7w-r#Z?mrvTuYA*j|FKf8`gT0i1 z{n}pEb198XYJ>(V=k6+dCu!#H5__jHguAo23pN{e@pC3%wv4`oP@7uF>%CMUTSs#M z8_N;MVf+7r27Nk|4U;UO59R2xoN;-9p{1N#HIaS0P679&+?R4+#eEg`)!d_~%win3 zwSl3*&4HZkz|esKlK_O(j~s_eLR3;pB|$22s3b%sglo0WZvamH$1Ai%w3SO@$~acgiJSIctXB5H(+Ynse@8ds$ZA=ZVo${x>p{Y(1*-+{cHoJT-f8DnSa0poMl zbic$%J;?K3J3~Ikx`Tqvp9pvCo4Z+N-5T$i(ZbF%`3*p0Oyydwx8jVjk6;8Ic=LW{ zOzzJYVsdM|!{lDYwRdBIHM#qx@8X->|2V=ox%o_@FgGSb5@O@CjFWz|6kw8n%G|js zfWm9@nEWvFS=+WTGg#Of-B`=NG4nQA`eh8J-tq$y0TgbQqEj6jg#*a!%8D=n4`RvKQ+Xg3a~8` zDFpF$<=R_!`1Zsb0XhBo5<7|E)8Rzv#exR~mT;g{@KDnu;32Fs`JbJiEqS|7d#Bm< z2C2PMZF_&Xk@n8np}ogotG?}}o90+WRCBxUthq#dVA7vy_p2Y}w@)TrMcRa_%yAW) zyvvS$()&dS=eF^Z=^!@sN31u z6PtBs^e4U+l&NB-lA6?_40<`1UegS{xUVu7K1MA*&wtC}Guk$qaj7og9n|n~pZs=) zIO$&oawg=kDj3Qk@qH}l=fp@QkIxT#`1Q?gle!TJJ7@l)znp&C$%h{$`|+EmefJ9H zAzO)7VM%V2lW_!6qUpkf?%p6ax*A1#8$Jt?C9D8K?D6sA66SU_Lm2g`Fp|O zHuWK7I5x&7qQ{OVz|`= zPB8WL2QvbV1iIsGW{)f+AkX0qrU;frUGB4)e9hrdqRG2gSad+nnYfOf=*F$=cFHDT zCAzJDUDmV8yBBtc`5x1WMP=@b?n@!}dP$raD7lsx>UyNs4;uui2Fzg3E$TzESLqyw zpcWs6@FIYffAsUT#Obu!1;_XSUHVcbnP1}8*A4YWcgRoDXz|zxAAWNF6w$H-Gc{Vb zH@^5J`#nsXD~>0xmOY}+ejK_Zbaj-tmXbb2eGW}=e$2889VxxO7&&YV_eVxyU zPqD#$9G?y;plz4Txeuz$2SxJNH&n)nUy}c(agup$ubD29gyN1}53$aj@)2e3FAFLe z1l=zQ93()eyy?S8u58)C{1bd`1CX&e{oGpp_76VSVIaMJ0Y6&mIu-3u{-8Lad<}9E zE5AE1wEQvtp6BnTz|hJb{sP_O$0TKVe4gWShRCS1kl_su(fEPu&w+<0y1N`tWXt?; zyz*T`&~Uu`G14{8hp57b*Esnbcq`C2hQf|}?ei?eJWYEy8Gaq1hw;jr_}~u8lP`k3 zkWTE{JB$%&H<7WiPMV?o4rZXV@`h=}d-h|oY@qR;Yn zlb222!RWFKqWvXyA#xAy!HTgyTW_FS?e+h!#w{MS=|TMDn^*?({>Yvimc@{WYrXp^ zp!a{|J%}p4cK)mbocNjVE>uvoay+@iyaN=%(bG;ER`N$&JAcN90CE$RQAzn7(ew8F z&go%2rjiV!`XL`d<%7ZYat#n^(MevmiAM6Rw)Ij&-zgE|`C7tYC_}n(i9z)f3_29`>R8-+6cWMEQqfLhLO$;0L zdSuY3*MrZ9Y+c#JS#^;GZ=?J0?i46OOLyryz)i0PQ-J&VhbAzn|z#*_;*9?HAY?iaHomjoN1Uck4J zb4wd%j$sg4sX;`i_kwa|R~*G&)fF?!<*F#(<`eMm@_j(OlQ21quOgBc99VZvncE$E zGQWXlytgPSQvR8e180uu%JMoOZlL9(fq|&zPQh-IM3tAb~~)@1C?!n($o;3z8h(GVBL_4 zD_Al~$e})1@4>7VtRAojTU8U9>=oW6ByToSJzX3)qJgGpYqeMZ(gzZ3HT$T4sR7Ar zeOb@f$lD$596*M@Xy;Hi*qD%IM!0^F!Enjo8M8Xyqas)|FVOe~SMAYU>gWHouG32! zKMll|>9UFfrS@37t}?#cB@q2H&k1;vz@*g`lrjFP_ws4w5po%#w`b86249tEYBeGs zDDQ~b2iOqO!aE8Gwrv{Vw!gaZz_s1W2G|G;AuR)> z8D_bKPozQKKht9IdULW8ev!kv z`=v(?0?0gEz9kDnW<5Z zLCq$|eQKn+Vn1p50tuoC!i9CYUt|s#YAR5HSoxj7=2m@6jASj=>m;4P!y9d@+NBEX~rO`%b{@Cx~aBu4sQP9jMNX z4x@CNif9x0R9RNz!iv@*?opAbzX@1XH3+-L1f-M|*A)dCwI{*p;e^Wn_M7qNO84WS zd-52z8-!!$H85r77%jIPt#Brm&Ib0Dghrj26D8eMe3w$0vt`ARA=L4RY>McA$%%Rg zS4_hLO?PuakjfCYp|Qk}u}#Yjg#3OO31c2F48cImhseuTxsmGBk$5IG(ix0W@(c^8 zWEcEU6nIXWN9R=-p-`jl>>NhQ=mmNB_-E&6vg_!dYVUVwP4=+m7>P_5RLYY}AG$3-Om0ROY&GWXsWu@&^Mt-vnps|eAo^|2kr*Z+PZtoCyzHj^E)1G*C#WdWq5wp=MT zY+MJEetlN%zC(5F{phaSjW+~*y4_&H;JfSCq;r8tDpj|c>WC;yRM(xX4prS985B-@ z$Qs*GtOP>i907pV{$JVpgC}#$!ty>W4 zd7;xWbCdFV?8i{NvebM2?9Y0F(evuRtBlnZS+VQF?LqXQ53~_K`9XlAyxAFb?P4b; zL3Z5uBJiQP?ckLR0gNkY8dB%Jpkbl!+%|PTrHm!-PcQI=R`{~!e0#7RH0-RD7*G>! zJEc~EFX*3Xb}<;AH3Nlr^HjMAlubxm$UdPx;x$b%?{!`&?xV&vVL@(Gfp2r)LxgUM zZu~B09>&bW#PK~3WkxR1eC3%<5Y0u3Ao*%_p^wYWVYV#8iD#v{mi6yleUjyev8M1E zuLP>=`!64L5hg#b zMjdFNZL?>r1YoMyRd_yw%FEYcaHkyyvcgQ zd`KTTqdJJmt#0_6tjqMe*%kr)&`xo85$~COHgLQGoXYfajW?!K&{!0V=L^4-H~KuS zzn#dr2CV}6QEU$Z`GQ*DvuF8C_rrRDPJx!S5?6?G+vH=@dT}HxO4j>J8#=QZwx!QH z&!>w92dd=UfR+3kHmDp|jd0_CH8?OsrqL2+LD8&qV9IH@X5;;I+@spsA>Hy)w?K+q z70#GhO>jb0THVhb4AIy)?2P@B{M$6{TMVv)x+@Sy&QP3O>K%CouG0(fUAI>h36$QC z?4Su=HNB9`-SHw4H1`EbQy*Dl9r7>GunXx-G>1Lce*VIDU=jVj^z_uUkGA{cd`MhP z_GnL4?+tECNauUolG16VfMB#Q-3hoazbIU<-r7)Nw~cU%;lWWaMh5dw!WC8Sek51a z`(f8)1_;K6I-K{oQ60doFG&*WMmoi()coM)i^ z#q0Q|I(G@Qe6cIP0ht49dSZj{DGYDx929*oK=S!)cl<#nN6h#`7mVE(X9Umo<7AoG z*q^fJZTAIZk1%FE(}CDnhkj%FyYn_uv-TEo>2XO^U+f|x%3kCdZqNzY-Wy+k*Avze zaBSSg^#;#Zc^M;g(V=aZqcp)S*6yBrcxlZ=_Wi&TQ z0rJA}Nd?{`*c|Kv6lrcSu#RVTaa|s!SKaXL0sZQ7#J3C!TC9vJAD${i!M!3uDP6oOKbZ}%edk1JFG-_?Aq?I6< zbTIJO9Nk(EEf2{9qUOoxNXgfkT0J3-CQY(~&pOSiy1K-@vFekSx zLq+#4Bw8sO+xhM)R#kSxzLvYDfGNdFMzyRf7e(|0E*ODlIT`G` z$$I%>Wb)W%v{B9s^2vM}XdJ_T+@0g*eu8_~uBS)*bAj~(-RiEni3&sDS@gV4~afcba;<@ylmXV?6LFXB~ z_lp^j_i%?%9FMbko$zqJ%DaIxPwCF+SBC(bQIIjL?NBqME$Pn448{MVmg@e@Td7{& z-fQ39*9GKj*2va^oK`qn3dX6{A;C0sI* zLBV2B+8E-w`z|?EA`!Y;t*(AMN62N?-WsX4{w~~y&f{=p)Ef{voPUQ#4&>i-B4}Ci zieztDE;oJG-rdXdx`sEt9a!)mWWQ@@_wv%N{=CbP3H%<9s9Z}g&nD}yyFbwKLH5ki zLEkS*Es+uIFlEj`Ks(V1-WM#og^Mr2_r^9ce{2$iw9;{nQO)M}cln}RuLqJ7%lnQ< ziTyB*OMJ0bykulRd$6dA&%FJzO{~^)aAV6D!=OXnzlhkWg9h4It@LD|-ONEAQ~bBJ zc{C%s;2}QNP{~k~nvh6ho$W{UUsxCnJfU6Tz54 z2pPZ>+EE4nO1W0c-CE{O1T$5i5?G{j%?={335a!7=^EN|61TLc~VwTN8gQ;?& zH4MzP7MRln%q$Dc&jn2VfbD@<0eHQ1>dX^m-HAEg@jq2UuEu;vjbYZ5nnuSF=Q)!V zoDpJ;{oQn!KNsfK#$oo^az>O(rZ&!-1|y!9a=+%Q=cGBbiZ)*V9HTX~!xM0JWErGA zUIUyuLClE{iF56(1%89E&MT?md1fTNt1867yN~2=Fr|l2uG$NWyPH9wtk^D$Ap$GB zjjwgWIgtUzi^6us`0zNn*U$(o-?-Bh-Jeb7n z9v0%B3xN2q7UH+L_Vx$jR5D)zuKQqCa|0rN!a(Y-z{+sfgxs+VYx07F=S8ZE8*Fyp zCBqHDK2wVrFq_m;0%x~o8k}7#2ozla&Q22qzXP&V2sQx|5YP=@485AUAN6#dkf>>b zt*KBoJ#1?VsHUmZ)Uu5SRA;xVEwVKYTr6o0wGZH5umhXyIjWB!tv}xiQonQ^o7-hw z&Wb)_REL?ZNh;&iy4<2KAOO8GD}BIuT!w$s{_4wY*6W-6sjw#hY&<27Qda0mDY|KH zhC#{owBPczK#LC<&$Sn$gQ+NagRX6-o0`$NJ)K((y+b)ItU;uC8xQ%IdC-Yu4Q1zf zj3?T-U6)0|bnM+dN$Eh&ig11rdAqV&GGQ5>6fdO)_M}u~vK@^}9v#EoP<~7J&EfZ0 zCb1?^dZcEnd8~4P$J+8*J9}uII!mvI$2!hX)#~xn<*N zWI}?N*Y-0wxb8e~ps^JkjOE&E0yn8RconSlGN$dms0~309fluaQ{uge8d*m+F+9x^ za^S;kq#)T?j%{qH8av)L_S$qB%iW=|^LNsiHUBvEG($7(5{?F~ol%kK$5ZL1A4{mN zrNUNym8yQ0CR6({c*m-{i1B3CeeA^C7B<=t+&qBOo%0Asovd)wi3Q#r$C84uwQC5@ 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 z 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%0D` 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%0Auuw;A?%cDvjCIBBIX5F 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

B5#(`6pCcc~Ng0H`zX;g<&?*Qu-* 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¥nG_K&L4nj z=ZvLasA#||%3fVmT*K|rVD_VLCo$@0as0TxP8guBu#FuW@*ocqUc**j32oV1qQdyK z0`%>+1d;{0a<>~ydR1CgrGlwoe-0?pY@LS%=c7d2?vPck3&0~y7Mtv#webW z+S%$x8^fGmsx!B#3okr1@>=lAN6y}*f~=wy_v84A@dF}3rhCa?5WxuqRj

QLjb? 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%0AuTG0U#Jr4ujZyniFcm%_Ry18XbHO7$1H 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

ZBB$?zB?$Ic)anE1oSot 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

Ds@1}D0|6{;@GUaTPuSI6xpS=#b2<6bc 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 z zpxR#r%HD7OI#B%@=I=27FQ9CcY3BE}FXQ*Tf9yYnr2m6lcpvNB=J&i;(&kNjZZ&?% zDtzARS95>IL%N;?%3f@~e_wBW;xjod@wGXBChN6!;+$~*fCinn^7yMnf_V-3m5#S{ zjnh%WZ<&V4)G0UTg6h8rl)cUR`eX5s;8CKsFO{yK56Is>{j$7?i>7hVFvZyl&=UsJg--;B_B5TILfkP zKxs(hTH1zdO~AD-g{$;oUi-7Te{Ed*P#1IA`}2n+_QMWwCH8}lEBl?naZSJvb6Q$O zE^Y36LD|ndSIRYWE_0;iaB0A`3_r|a;UjRtr#S8eW#2VF?RV?9iYqv-Lf`M7Jqdp8 zpPmPXe&(}qo#W1dWwc1gZJ=y4b=pUhtdIY`vLo7@+iT4Kul`;v#im&L;qs#-9z{{rQ&flAFhP9{hOQl^b0*F0R}fhXup3Va>1w z)+}##Rb8(7LFMEPpKv)UgYr+l!ChaT4a)x%@|FtnE;YX5wh!Pc`4~hs?fXGqtX+fo)z3DZbM)AoNc(ikG<;$gJbW-pDl5iU(dqfPG22VT?Lfxe2#UUOiT*t@q{oZXQV%6U5 z{^8KQo1NlxM4)t3z#?&~{2fqs#u3iH5tJQtq>JfNP%$k5)t~Fe;O~Ed&tEsBNqv`5 z=S_QL1^x`HP`l;@m&7t>xrK8;Z zHLz-2KQ``Dsgt%dtxx#BVY8SojB_D%-W2Cz{P22_KYDDYX>;xYWlx|kj&tm&b9f$k zgTHgq^T@5@d5rtru-)&4JqbO^!T28y{l2EkF-}7SD&7WIHa&NP@%n(u>3+1uI)neE zEiiAGizU=~Q!dK*VJ?bO;pU+^k5(Bw$^39%ZAx%>E{{Imoe56e2X_5<=st^{Y-zHjy8=p&nGW5c~rfG4R%W0z3pEsh_5Nc_igZsDQ$PX%RXnjdm( zOmHN*q<4FQOY6KVg{x)r=Z+&@YuOINHO?@9xW-ed3)grib$*R!AS4HOxSr*dx=c!4(druHUb>%E54_L(*8p8HLupVZ2n46aeT)7i3g89=irgq8+h%YG`hHdeZR)O z&5^AU1+D9D;v2y)r>4EvptX^n+|LJJr>5fbR$y#?Z5k(Ye|VD9`!33|Dk%N_9`^X) zx#v>m*_(S-lJ0-LK8YgogO|!LJ7zs;6;33BNcFrY!3PV@~`1w?k6io15`Vcpt~_ze%Gs6`9AI z_VE(@wN~NtRD6chl{Ku-9GhvF##5f|j*oHFN%Lz!jr)Z86A#`03jH?i5h=e-o83ta z6ZXp-c*fX#PiahXbklyD;BnL`F0JoyLnnSshgW z4?WMt_-!zbA=qb=9~r#QP&1;cY8)$zPSZyKXVAN?a=3AWIy=l>N@ zS{?*t51X&{cx?Rs{g|fzM1EsNR*YNgZ zi*XU-yw%X4JQhsTb)f7<^VJ?}w0pO4!98vX{_x)Iv3$j%Yso6cdei-U4ZmU);#AJE z=Q=$l!w77cw!fIx_sw$}Zv-1R-}JlFOLP`Rjr%FSQRKYqUJH}jRQ z-}k`S;LsX98C3sMLG8EM#{DudncL^L$?fhxT5kUhKGXH@{+YDl>et@W?-=EF59~et zw$xhu@cLR=JT z=)JfCzOZWbE$Y=T0%K#II{RCD?oXiXORsjE2cK`R5nbRok67S1-w1ZoZyTuJZ#wTn zH|JL`cHyc{j)xwbDq84+j`b|Z+q|AvuES*ccpd(W&6vZI6dL?C7^2VeTJpa1@$z<24`AK zMuVCwb3o0V&rUEqB`}8mE*Q=kcTKuqjcd~K+sv05zl;4*($vrDn}&B<`&n^mMqeIT zW3pc{&IwRil`or~2TSy=_;;RYY1e`(^G{HIZ!yXAi$KX))Ct#lF!bB)A-hNRkh$nH=nklY{=bxf8>au z^^S_}FQwjvyPn-UxAh&H;wB1{$@8K(WYMF)?Vh@bdl$~ekr$fn3&7B(d#0^d)xFF4 zvR8HQ(%V`Vv9$tyx-%{=YAR-HMZ z;#dOP!*M?4lhpH)g+q8Q=Qy1PO3rn<@@sMI{a#y+(mluF9IMV~P#hQ1YPDl^=4fbG}&?pUAz!b?Nd&^RMCk*7>NlB90zC-%Lz1J~@YJu;RFF zKGpJW0mbnlr>_P#Kp#w5jSYW4IQlHh6zqjutKP{dZffg>DsJL>D|&PfPG82l z=v-|BPPRHq^T9s@seW8fO=*AyrzU+vO=X#H6LHLg;i=4}A#oHBi_o!{lD zr*W2^ZCG)b2PFl{Wf^Z86Zg1rcg}aL?Po!?eeqS+_A{W`9y!y_?H&cycKmzR*5vP- z{wSz(!)<5Tx#HfS%KZ}5J3YJqz6ecV0$zZ{m`Vs{c5hzaSg2gR;o5eHzbJGKK^;hm^ zrk5SYD6f7ilCL$hNcjT&q_$SLetY~zE3@rQR;Js{*2bH+*n0WxTdmI?1~tB)24!nA zY-nBn94LOLfoj+9>BrXD({o+u%m327ypPMS`m6z;=i!SN-9K1`Ueo|be4iEPTRaoL zvN)ygw|Evn#WD6vi)+zg1`LJ#JpHGbj=tT}`rct}ECMC7@3cN!J=fBw-eqm)K-F9N z3oDns+id+DR3DyukM;TJ`>YQq!BKp#0+oLssQS@s&e>htu7lG3I=FyiwXqmf8>_+g ztb@xbpQN6b3h~lfoddh$j3I<_=U`lBb6fe2);@F3^l-u44# z`=_At{5-68-?v12AJ=?nZ#(i)Ysac8AGHR?H=el0HC1Yn`Biz`VwnRq#p0miQh1E( zKjI*t^AB0saSN>Mm7vN#4XW%uzqaxHFeurZx*7-jgJH}f=Xf*VQ@!s?_hV@;$Lfa# zpxRggwr4CYp?s2hUMlz*j(1R8UUYuH>U8DHUUa-CdbH&z-E$nvvFfyd;+O;5!*L4b zlhpH)dBTl%P#li~C8s!D`L#GMrk(0IO7|RBbF4budm6`~V0$>mDW4PxClxq^adezs z1SPLJUHKu$I&(5hd-dkz9P+$`V^6l#7$Q(i5+&{>Kk51qlx*{q8(;36ZB&hW0E1)L z{TR;KM$?WL?fv7?hiMWZo@3!T77$}Zuuss|{ zQ$9&OFR3Lij*ipb=#m4$FpglzajS0Awpz>i`IIJjZP_K*vZZ6tcyZ*Js{iqH#a?^U z4fyD#iGvmB(ev~2%NDn+!^EpLcS^6={*wjOZwXNQU%qUZq`dS1%(&R!`+}wY>P735 zl8UfLY?Fv?0qZ z?Rik;2K>SFiIh=(bWa@7r|le)?m0~0SZz!J)fYE_?U_S4$|tGkCBNL}P!bfEOF_w1 zPFH?7ha%4lEvKF8wMe?>7~8}+#zAo$1vZDRw#G@S?78F+WJiBPDW42e>|X^A+7pY8 z({rHY6{jmdtlQ8rXy`CF=#XCXJYyDZR`Zqa`4%}={Sqj?tHAcebQ$H7)bo=0i}8(t z^6@xOa*ETHFMIwuwjFESGnlj~ob-%s^nSqyOp|Y#C$nkCi{{;2^t6m40`V)nX8bCk z($Zkgas9?$E$>QDWzSw=WiAAz??c%QsB<(ZJvx?SecQ%ex{v!Xj%9BYsJLgq_QZW0 z<&)I&lKs2IJr0V?8K7jM)0HoK{@P+toooC>;#KDwU)obry#UmUkLogR=c zdvM&H=cJGF&q*((o$5Ix-E-`;nQ`n5isNvwJ#)U9^2vV_{ zj#=8N<|y5BoWrr|%mc--47P{kBFZPJ=OqsZtxFll>6@VBN~bH|acpxxcJStHIZF2& zM{}$?<3Mqo3bv=OGn7wK&r5uzaV$DcUC|{?pyI0hkmEYusWrAgyz0z}|5mzUrae9j zH!t$8fS!|6h(La(-ZsBW@0hVCXTp{y63lgvOL z74bd$FXIyjh=ZYZxU?J`mzJ%lhpH)hL_ft1Sl>Cfs&(~u6)^x?j=)c zr<$X5&+%4{RcAgZj?2LItS=?XC#mP93;~0R7<0hbFpwpEfa;!7vmx<2@9P`}=v>o&P(UWQ%5y*bA zv)QkJDaYX=$7xT}RPM8&%KGb;=(8Rzv|H;sMRjvk95m)Q|2^>AtU4b1a+P2U=eZ1=};8;*?KP-%GxS^;HrSmrFs(RZdsF>_x}? zWZJ3bDBW|+bF4ZAP~&L{*dC4xD4(RBmv~R(Sah7aqDz`UaaMjUj{OI<7L^Zj#XziD2@xj_N*NR$|tGkCF3|I9j9YJ z$;nPveyun*Y~7Zlbk8xtvFap2aclwG)7N7tpQN6b3LLZs=NzXOLCLF5SAH#yx6)2^ z9Ho1Xi#b-EWuQ2A-==*WS5YrXJujI?;}`?gx5t5!Q=G1R+3T=T$6%vZ^wwMExcgq} zUNx?dv+((#*T>$cIn?o@^Tv7TMHxi|;#JzjcxAzKFXP&=$ zu3xL8&mRxo7Oo%g9@A*bdQsfRq30tUbDNp{A{g7$Y~KV*&LU04F9kzR?wcV_n#yW#KpY%Z2Q)}*$h36UfDoVZta`WP>W{$G$~52kRewr7B9V=ma9y(CZhB=x*xc6aME zC@#l=l2e?n{4hrDy8~MW_D0F@T^Qn0tLZGq;EDjO_v4f&qM9)aT+F?-*a7w>~bJp6MRN?xn8&eHkBeOlY2O!uTA|nDf$~ zgWidWL4zhdqWP$?n>*0Pa{6GKqvbTge8$%3(?P9WKL#aZ@IiaW7eM)T5hxzt2E{je zecE@IwrjX_zlJ9{R{x|xb?IcVJ!|-Q$|tGkC3l#u;R#S&E&wIpaJusAt>N>(}u0``4#?@7DUzmvNnDta{PfAKzomwLklLn**_NHn&SB+S;E2HP;Kr z+q#zkr6XX-}fl9^&L=c z-ver|tbp1}-T}p--{)+t-vQMAG!B$c*MZt2x{R^4{3TFp_@6;l{BSRD_gc29aj&KKFgSg{=MFw-Ir%I9hSRne?YHC5D=tC2 z#5sS8aZZEb*aekd2J_C3H@;xweXEnLFLni0WiYLK4y(F;St&oY_Z`)c z(XWSnlw4Q$SxEu&7E~kTv|F@hz{4|>bCxFU-&FObzQ+lo66I+fi)iILK;t<=r z)o1PBebJ-)cVPm)@Taz?fRdR`_xI7Fzo#~icA}rdc$-rrHfNw`!&Y149Gu-FC_5WN z&N7?tgW}Mk+eSgRb2}$C%50bpe;1<8`wh$Dp!XR@eoIGwH|*2a?-+V??q(-(FKVLM zJO-5PMw&SEt7CHv?bNe5C1P`G4V&>VbFSoUyah_0IdAPY7p}!-C1P_`4V%TUaQ_s$ z^6Pt`WFl#ypEZ7qV(4&utnphE<4E_Su{0jN*(Lb-le&QR`BU3F>loSF>9u}~B45YW z!icR>4O=DJ&APU4bGGhXi>>ba*7j>C>0ad5aP&3&`YOKg7y6}RWN)W)ebMH(L?+8l zo!=74SJ~2>FQAVNTWg&gu_@Mmnf9~J#x>67!=PgQluPdy`OS@rY*)Q2-p|Rt`?d8k zj-HIz&0{ykpM3n5<8YTF%%^bS26TGx)E`wg1N`~8RS-`bZxmg7nDqVX~TeV9w|^PrH$ zE`RbV14_e#V(9U=AN6RaA8t-}9@}g_@B6`v#_}FEp8mA<-06&+HlM#Ptzo0n z0d4*0j=qMCOK6wB(1wm>!|72QN$N-aXo=X!)UZ*Q&c4H+Y)k|tQ=DFFoE2qf9pg-K z)L2*^vA+sEDFcWfN3l!IX7O8$8)tVU$I3q(6t7zEh0h(xH9_Rv{~tB2`d#qV$MUBE zKJ&1tF}Dg7>!~B?GZ96+KZ$RfKWR|hmFH1)CG}7-)1S`kg+pmy^HG1Ahq5t_G_5WA z3-7mq8Pde>-|zmP(PNEwhF5B&Woo3ATw3Zf<9{hA`57oq?||YlhH{cLDF4)!IOah4 zcQYv7_d8wfif_f~9f^;8=nJa6@8dy1>+d^E8o6d4f7G<Cry!OZ(A2PLdYw<1yj^AN8@Go6cQcqmOH(Woo3ATw2bJ z(Z@i^v!M8OAzv3%S4 zO4a#L^|4<|hEm3hY>hxqx&(0*uVl{nrNM&ho5w-Pn_yVZrAImBJ^+V#5e^G$aEM)L z91>vmJ9c~xD0v7Jho@Y+zix|uPk6*A;<1kB=hl_4xG7E(Xv>Sn&kXdWNFY9Lh3^{A zGN`n~_e{@%@vH3kVNmjd)4xB{`tn9lu|J8r8aqD(B|md*`p^A8SLd_A!XfyoXBz&8 z-$&`!jwSfwMW2cEJ(TCF6%@qBy8J_nPieNrI(DtaEkU~GMg~;B4VJZPreWQS}_Y-UKHbb z=vk3Q{P@lO%={^WVb;$Lljv#UEuU_8alhSu<>V(7b z`y>2j!UwG@-!(U8V$X{_oR6LrX~g@Uns2@bpwh|?a}JC5@r*F>kZ*T_l3}-5*{cfX z`x~I@{`Pi@$qsjz{?e?*%oN_qf;eA}IYk*iijDz%Yi58%}HN*rh{b(0bmK zI)d0$og2$X)tOAX7rB;2&$$Z;6bZ^vgCL%D*#A{N6A|y2f9zZ1Xw;Y7fqmFRZ*| z=}GG8nkD7*9JNmz12I=5Cz!+Y0SV%IJFbq<}8WdtRh#JxVyeyS3A$Vr#}8ubW;5 z6}!|Mw#KXk726-ZY3CTrK&^SluXN=>oe#YZYOS0156j=+E$gGZK&^%Q|I^mIU02z; z&)uNpEl{!UM&IbXX^`uqUBPe-bJqv;-tXM~`1W5PXv```L#gXUK94|8M#gNqla1Lt zm|^^iQ@0H)ZG(H8IUp?3VxcpMzYGlPuXzg3g zgDiSpn-@{J?u4} z|2dH{8@Z3Hm@6Jr$@8K(%|Oq(1RwKa-=0B*xX4C|W64QiXtPh}X?;3w{6_S%+p=ta zz;`h;oYcxeeb*O#a%iQs-&32Jy)0O9whsx!=~pkh|Iq1V#!zJK2zIcsTNq;FB?D^fNPsF@ z+1}=H0n|LpfZ;ib!^(D+UT~NJHC_`AE6vtU!C?jr+jm&m*6bD>WRl|S90XL!$IpDpeDs0?avzG$nS;eCs3;ypP7VqFBa z)*d&&){b{UtwG-%Xlu;AgKUj@9@IKGZ?LU($*pb8IT_R(`m@ z*kN*GFnD^e@czuUbf zU;Ul{>Efz3S92`8-A}X6{fC0h=n~IW$Fe8%qCEY@DWCL-U~2E4;qy;%P<3_#CHp&F z`KlM?IGJ{;IZF2&^Bk*A0aSdKfbHS9fbvP|c}ebL9Alt34gw`RJ6-v;IQIWyTaMB_ z#}OQ>&KOV}r-1F@*h2Xv^}M8pyEuYj96`zcPFKF;*yi12McS#3qjb-4ImfEA3KYk9 z%bL$O%ZBU;y{L}G*s6T!s3#{jBb;leAVk0zSDPb_?sKGeh0cpyC2s(edVjx$K|x? zMKKS~tnqH@(Z)CBuyR<>An&;U{9wy_@=)v3mqGdXIA!G1a!|T|j%K&OKM`xsk=}~E z=-&Bp>+)5;Eyk7?U9)wXu;x4UVkz^de57FkUHnrHvkuE(g?K3M^M{-7?||}oqa&Wi0LdUUL2;aNRarTeiuhhy2C2g*hnY|mI-MENB3 zyktJ>_5n~Fj{_yAI9>U&*P%2!AMcv1g!M_&j^lrMYv zqk9G=XX{_i_SvKBc+4&r=qFz1U~hv$p+ zo(0H%2_8Fty3Q9rE8SPb)^(qbpse9oU}m_b*19KGb? zy!Gd;&efpudLCzGdxIJ)FMyIx$J^Xr0ZRTg&enkb*wXkP0#=We=E(S*lBqvdZXnN# zY~G4KOk@y&+D(0ddu8y~aZ}nj{2r9<*W`YYHMt_2b*^Xnp3~N59K9%mh__idxo42W zu58=~O7>gMil%58gz?joZ7g~D3l@DlF<&t2^&51r|n2o2;v^jF< z1S|7pP;=(Evy98-p!Chow)<*-0mbd_pwi#`lGQu)99z3jKi6V(9;p0blgzitpnRJL zhFs3G`*SlurT-CZ0Q-E|Vs!|p&mJEIResa2Sd52(k`$=>qwj&bPk72?i_w=rmA?cG zpKAhjzwiy0zWMofAMq+sG5Zmy@^^#!>~G)&cK>lFQ0Zg8YW|)!#r*j@D4vHAWA*9h zLHYSBP;q(O>3)AWB(gtbzOv^25Zy06Ze3$sTxVh1i#}f|p(i8zdF(QaZ~0Qg61x1$ zf!ZfiP7h{SdIAhCvEwg-YA!b2`l>%DX$IBC>EE(8_N8sjkF!BtTU-E!Hk{2Lf|^Ia z0A=$fmmfV>9yht|9wXiFF;h9#9GwZOt@&Vk_Lw=8Pg2iI5kH5aq@CY4%WvdPrz=0) zVtsY^Vt5N&qvoP=Y3j8J6<$S%jo)UlEnLz{jTw@fJ!TZIs6jG z2SG`3rsX{j%D0TGb0HY|;`(E+NPi6f>YDu_Zn86;JTHp>1oUErTV)pU{hrzQ0w`Hd zTD8rlh|R)UY%Yn|ETiYbR$Jqdxw>Z%|AE;!43vx~t=i_Gh|Qr>*7P$v?);n?M_DhL zGZWCGeUiD>?B>C|v;8C}x$GM2i?hiKIn{V?VL^PVKHu>%Us7dTF)yPp4_mGB|3{oN zySBUj&}@E@v}#-Zt3FHWe_?G~BS`lmTcgpJL~JFm!#~&dW1wVD(n4GAS(SRveSd6uC*5HB z%b>j`1zb#h$YK)Q9RX(ZBPG6rGOa~?2 zr`XM03&US+zXM7(AT6}z)`A@)b7U;-MCU|Q_j4pmSucun4m~a7i06>J)$C@#q_cev zD4BbU#bHNRW;hsf3!ih|GQ2Ocf1S_KhGyaU2|UXz-xSYrwC_dx%p~;a^=rOhek5-< zKPu=&=R=2|S>BO9x4er$_4UKIS^Bpqs~F7&tNY`mNPn!7pC59sj9=91gU%U;p-10` zo11HVg1Zch=z07Rr^TRT&K;)z8I-*4^t10YpDqIB%U4}nmx7^BZf!WVP9H9YPkYw} zKlWBv`KGbg{bJ^{Iu7w;FME&k2~6UjIGykOTrtl$_jhG31V0Gp>Dc+OoD0pAl+3^1IIjRDhf^lx?B??6k+C!M8y|k`jH9lX1`Z~nCnNDKKVUpk z4;se6vg6eISC$qOjpIB}p3{9= ztB<eruw z%KtqW`t0r@rX%+d7s4xgUwvJ=Uz00UKB^xZzRB|mGLFc$?3SK1{^_R-V_*UQl=nwa za_=J3-vuQdpD_Jfpql#?sP^u4Z9NLAzJIp0-g-74KJ8r_>Xr9CFQZK_x;9vi9$n{B z&zc{B!@^?LaQqhM7hD{Uf5v>7N0m-wn!_ z$3XeAz_s-ZsC<9#C%Uc-|5p2bs<*C;r!6n)$EoPibtU<#@y>(dS$3EKbNDN+4>Evz zlo|LMUPshRcyG`VPF>`Fkzv%QsXuO0gEKkK6hJz7On${onm2HX~$T&hGjr)6Gig14#mOndU=S`K{Zv} zE*5@9mIAf5=9^>TXM9Oe&yke3jfKzV$3T4!mER^7e%6-TIu>Xp$__Hj4~~VO-30>; z6D~bYx<0E*lCJLxWCz5;&j4efJ`+sFW8r6qg{@-Y_Zw57K1+;&%C9&qQcm_Nw5QMd zinJ#zVMq5Plkn2HRSup5!4&*-%^l#E{7h}{`~@>^?L71?TT5RC6`PiS+P%lkSJ`J> z8BnpC0&1<@?H&6ZaSAB?uz&RowjqDWdzQW&+!}q!`=&49Q%!L=yF)A(3?99K#rR!N zT$go@1p~m24c3SEfJ&d%#eC?~)${?N>>b_BeEtzsdTpV{g+t^|3zu8mP~4 zPu#@%Xva-sfxgQ-s;{Mg8&sd)28zel{jA)Po5jNKobJ21=`Vx&?r8;-e}`{jW8flC zW8el*-*x@7)4OhI?T-V+^N*l-{}WU@9T_WXhtD3{yf3vhlf5tUUd>gcc?nOazPskT zJ4?)|EPrbAPEaz>>HZz$L#wX!=Fvv=wO+rf>pne$r~HqSwyw6T^RM!^6He zG3@gdU`~q%;@1$fhUy12;Ciqkv6L$T-w zs=Rn92LAcV1A?~CbWWeecw5KyTE9rX#-ZwcT{!~tBbHyK#uKi4D3rSO38t)A>mc)Z2*SS@wBcq>MJ?XCX(x_MW-wby;OPvb>g%A|Wy zA1y~;?h=$QUcp}Mi^GiLp`hePPOtUb34^bxZEG~?USw+w`ec{jZI!T<;7@js10}aO zy_T(99b5Avwu&`u<@ct2{$y)=9V1_Ly1(xgeU72iY<#P8zhnpUyr}<&q8H^D;{A&a zj|C-XqtiZS^Egl(ra674(`)r(R`zRrRy(K4wqjdAkM?7H-&l~uzHFTVO73;K_c!`H zL#J!k^tYD(8pk_O){DmdaP;W&45j_eZjg-eogU-zHBhq8el|ZAP(~a*r!9iE-xZl& zgOeZ6g(?n;!#wn|8b^E|BtJtOurC{Df|5l}_cnKl*j&99n}dJY>bGp}fS!%^`+=NS zV^=n>10`1<;QVy=#RpcMD@~$}>hqQAWBICh&#tnoc;AX1egAQGq}eKfNjS*=0{!^X5U9MEL#dV)*+k?lbLr@8Qj1V{M=5$nLxduOfO*g9h=uf>bOh z9AP{%M;fomU>J)!zt_^~I*rTPzY((od0sRRhN8E)1aC7thP~u-X5$!8vKwik&5buY zkKamo#m{z~i%pWf$UE$P>}N;p-GH8n*b9z{1x45X>!4)r(av5|m-CuB_ISE$qD!VB z-MP47xnre6%D#_Qk=;#s2gR;!-$CJR_r1QYuW|IKuf@+Z=ddr|E(9fCA#H8(9Zx&; z;yWW^b7l>j>0_}o)@sNR5sIAQDj2Zr9>qt;?oYQOB8buqm{K`aZO|D_9Lc4j_ z_EXN*i{4h`-qu{%sq@=WORH?@{9y(9u#kD{9+^DXe2IaXbIiX7LB;qbP_ojcb6>5^ zZ^<;zX|?V5jTC8KG)BgvM`Kx@7z=XzDV7CLGT-S@8#8Lym>aP%zlM$Y`Pi6jHXa8h z&pVyp`q`b|`Z=ogZi?-Rv77KGviC=I`Ki;4VU+cv*p5P9!_N!MZWheINB+J8Dz?L2 z-e*9iNBy2EE|Ghdw^rFyZ0FXnS^6sb27hYv5>PV3>Gk|>xcP(oouI52`JF_M`dzxv z>?XcOA51YWcY^Z!QkQoHSj+Dz;u7)uhANx#`_>vZi`dHWr#3GGB{`?p`t6|AvJ*M) zit4_v2j9}_bGYuICqoOZ^C*3h*^Ggitnqjhl+RB(o%kQkxVths?xxdTbeu(X&!rII zQbZpW;ZpgAamj%Bi;c@aKylfbv@q^9&P9{At~(~t7_CbeZ`~_sp=~d^my|=#xCHSM zr{Xln8&p~X)Lct^(=Y%NQ;q*mL5;sBLG{UVpvt}ms_t80=x2@hCMAEy{m-iRCh0u< zqo!5AhpGBl{x`s<1)JhI2h=^Rp+9Hdh$!OyPfmCKgW|3{kE$!Fhw@#2x?d+;h8@L6 ze~Q0sbjPM*AKuGzX>roT&;MRi{*u@lztpO%Xq# zi^q6Sk^xmtZHc37%fEX;@qWVTYFB=Vhw__1`Oxh2dcWOP#<%D^it2t|HQd%ZK4rTv zdUUT$f6Msh!92Fb{Si1NwuFM5s$gBH?BfEF(e6GEr&Gy|%v|a05X?7I{ z#oz|?XrIKcWKH;vwfzfF@`Tfg!S2N1Q+%=!o72&wJ_g^#=1jBs5-9nD)A{YF-Me+%=w!VWPGhSm zTUEbb<$YTou~R``!#C`t_>*tn1trfpz24q6;*K?atL3ZKqzRPuqB%ANJ-T<5zGrsh z-)C%dEH3A}v^zn?>^_$cr&HNa?}+ZFOWMIHxU(&%82VU8($BNREaQ{~)9@0fe}Z8? zDCta|(&2O-oSu$ynnAnKxU^SSUn*{Mt2k>NT#Vi#5{Tzq`hoGvfl5nVZ5Rg&j_Y>h zX`Fu+RR0|7%ANyC&Id!E`g9%1T6S@*wQTrY{94CaHl8wGWN!j`(j|Dlxoa5XaFe}f zLCGJS9v$O1$YzZ(z9?d6F?u3mCvz>IPs}zuyKpSo0}TCaY&g2HWB2PD5}h*}rCZnQ z{&!)oPv@ZVfQ=K~Gu_hmwpPA>?$+uN8RX7q%NA)aG?4tvl~jOQ*l8i!{=#dQ#Ql0(7jI1i}9X+G`Oi(7lseVms?d{`}A zK5Zy0+p5nD~|Jon=S3{pj_XdJjoHRj*oM{IzFZ6)%VHAc{*jhXl&%ri!R~c z9B<<}rdy5Y+dnrBO{9f!b)PZyt@?~x4y&jW`WARY~!k!m>{y!Uil}qq_ zU7TnB6z?_+oSt=<6wWo@b_6A#yUX^oYe4Oj4ZpDT&a|Oed=pgr-*;`!0afm9FpQCV zp1)s>=lL`D5u^X$Z@|sMo)=yF6w!+=!N(|jzxfvjm6n}vSVGqvDx(MYm~V%GDznpl z7Kj@#J5$=c*E*^f>kOJXVo34xDw*u7f?)1TD@fZNgkA4F;o;I*$_waoUXVp26iv60kXF#`L0BcX5 zi)*~^e0|>fxMX^!dsKf{W&N1=SjU9c?Ktyx7<18!zI!n2;WfXToqgBVrX;8}tNf0w zEoo4C?4P!#<=?V$8FZ}$r41UxH75gVov!eUx>|$spw5?5pw{a&E11@h-21lnW#6`S zyG%P;zmu!n^R(~TT9*d324((b>3Q;1KSsUqxm?=O+7_dnzI#)ooYt=NYFoEU_@VVX z`47V~sQd!?!W4d}UYvS~rRK|hpg50u$!y#L%EsBhv+tB_`Fs2B$djPf&-==@uI~4; z?I}w^Md!9x%!eVbnhz&~+G}=NX8P@**5@V5ZQtni7hBIKfs*ZBGj5yy-M$mkzhdj~ z0k7L0bkv)+Hy#J751w0T`^ZN4DCr3*-M`Ch#~RlN6CQEb2q(9_PZVA^e1J5KE$u-w zX~&DscW*^cYrr4^@hErJ?nSH{8bdt=#yZ;ZEuiGhPK{ytFK=k+6E@82&5I-W%0Dsza#Y*F<2v;xqep&3%HJ_18_cDJI4g-g!MoZ5-%s5Osj5#dFtXu)i zyLb=nVlf^8DweB2^-V)p(=(uAopt(hP<^|~>FI9#LKb=kRQ)BO&Rdo{J=MK2P~WFP zHeM!-#CbT0%f1MEL}t&f5P8mT;Kls?+>>* z7x|QpH@}y6dZKj=*?~FM=}Dd?)?`KedYeetd{6Fd^Sr#1&F>5-sSL4sR~c&axBzPY zra;Zx@@}T*L4CI+4Ql<0gW7w`yV_o!!H&KIQ`x~G_OwUm$=6Pl24yi0hI{#rhDC?v zT^j9sw$#%;n;>0#a+>zEKIX{R9$rA#ew&B4^c3m3E{TD1 z?K8mIzjC0h#Sad$HG1%1>(i6Bu{HKeP-}B(TdV)`?QIO)3x+}r#j6n%->pFTcM>Rj zXMy7LRZx7ta5kVr`=jzt5UXcBMCyZ&zz(c)i1z^rEpJ zdxqz^B5P*$ARFJs!)?B#L7fv-Mw?y$bq-WH%;s0_5L+X2qioGA9c=S2aF}wK1GT2c zT)7mewYKCiMmw6XMeGYxhZ@E_Vy{W%(X~$HK&_1_SFhx-K)LW7%_DphyPHpoLFJ9y z!{&73o~9oSYAx$O%xumCb!~LzUbePe4QkHsy|?901GQGP>|^?!pw@~*h8xE{_cf01 zf#SH+ezwNF14{qF{SX${{+()f@co5UABp#oeS;%g8iIo+cMg)%HwyNe zwPCR9oKDp57;L?8gP?DRrXczWz<;dH=)S>dxQ&3@{%{-iY2x;2j@x=}ssGp*T_5Z9 zv99oW{Q7aQJ_hT@L0NYDOf2z)> zId1>gxcy(7KOe@Q4^c@tm!5C+f(5Q!>gn1uk1kANNA`11*gdq$ zW6sVac26($OVeYI+C9A3gN6lA^$MixUSIBE*UqmDOVn3>AtWF?>I1{;%Taox$vL+!qBa_$w?w+ZgOBT-+G!1}4Cv zVC?C}V0SR{o5o-dF!y+4uqT)zZo|M5{ipO|sWI3ajD!0qoqkjO>@$tQzF?Vl_5(9d zVQ+%P;JmZ!KIk$~pSg5B+wOyI0;;bPU$R&n3o5qX%velr1f{<`(eC4pm}D{e3D``% z4(C}c&H)vRoxftSmX4(c7iuRHy$ti|rvpklZ1qQ+n^@S$%s2D^Yyf%2=zC5^#$ z=$nDkdtGYxqb~yW`QVM9?jzp`>R$M-K$ZK-44Wf)Q2Oj|+kKMTK=)DE;j3*&ICR`!>eE59%J;pjp;0JA>+%eL$6u|G@5_TmtG|)w`hXi=T6~@w*Td zzaN5{qi=({7yPwr>^}CRpzg!;nr-*rj|0Q|-k{oFeXZS3&;HQv+no0!TMI4%mH#%V z&oaB`O~2qe`%LjwP@jvw1*+VEKem22cTQvY4&DnurH{O+G1!)Jmx6kC?)N~Yzy5QZ zvk%>F>0R$Io?}6MZgLT*azDM(K0CZ{uJz}|pwg!?*EQC!0`=M9b)fwE8K`{T8*yIO zjvGGRA!W~XEck2NchgJPTB?|?;8<(sYEZEnykgDo4U!zTEA*l|`ioIM=@Y@^%f_`_ zHZI$vOLharP5G+l-#K?=;J=A$Tr;#6K38 zF-SXGFE2COe+HFzI(d@Gp!8^*R?uGcZ(d9Haq9gyi_>6GaT*P_gsrvOma2ALatPw3 zzhRV5hAH+h_D73T!Et%VaqCFB+EBjg`QNCcYCUs$&spX-)e?Cu3s=03`({FAP1G?v3&Ya2`Hog54T{^%pyW=cD_{2fJ97^WTEB^_??(^)JACvVt?KVNeayOi*D1<4 zY6JMaFR9b@LwmL4Wl;V7 z#A}w{{qI(OM^G_)wqk2gf;MIA3{Ww<5|qtbL6!6Kwohc<4yzEubvz&V@BXK(7xjMz zJ?|1^Q+DDjjeq4$&Rh6X+QFdY5m4n`aOoYki3QuVY#78RZxn1cy>rmif%P!aE!8#C zCEZZms1&|maI1}{ZPn5vyvM#(oqOzw*YP2IHhP1g&&km{>CBH0nJ2#$!?CpQCG;D8 ziA(T)SKc)r6RXX~9GG8a{@?z#rCmW8_0cR)ajEzH^7-;7^6nyWk*#G_T*Pe!dUOsZ z-(!4XUp6-Rm+^WNT$|0r8})6DCC`g&rqQD|%kMV^S?tQjFF?svq=hzXyen+B?8W#k zt;oAN7es6=LT?URYhC9u8}#D$N_&NEj{zkoIX(LBrQl8MMBYKvOqv(@lR%H!DAQiv zwfV5K@wm59!=DMVQ^TK`5nHos*h+Wo#q+-A&;Fp~P^a^K`H{R6;$r_!h{du~)~9Uj{uIzjP;j;!pj*KPWlW>9yXm zc55A5izBv{*05Ek-JENCfwT3Lw-t$B?|;;e-*D2s$fptLNtfW`m);Pc_>)hEfRdw} z?)RuYB74*v*=y&X}EXjU5!@^J+YA;-vvtc>}>jtpk!b} zuW%i@2~<1Jb+Pn6g7VG#vRTBJ@o%l$7r$4`psW||pR>^yx&+TX-^1)>z#_b5d#~=s z<)@(Z&KsLP5mdb_sPua8FlqQF93yLIRQDVcl=Y%MPoj@<37%s-X7-Cc>3jZUy9HE! z#_7>DXOcKX)|}Z9TQ{Ib=S&`3Y5rs*>+DW*IyTz;4o11kMzt;NFAb|&{nCEb7hUIB z67N^8(fmq-MQ5WI$I9Q)qtcH#5@3i%!H+s~MbZ^Eqf8sp|lgiLY*c73@7lc6*_hvIqs^F}UuDDF^61yZWmPt{uB=9nj_q=*Z0Xu<7J4*p`OS=f1}r-pyKpSo z+vObzirZMHN8`D=1_#e2isB|NgWqe7Lj(F~^eDG%f8$o!+%WF+g2NP8a-24#jOIWJ z)EJowO0IQv?*Qe)0#I|~373v9M{}?1N`J3w9z3F)y)N!*yIjRxzN|tY1xkD$X0|Z< zC5HvD?0CNmitF~Isr(+U%>H0FmfU+X2GzOFobf&!Kc@F&c%SCdrkBASEJBZo9O8Y- zZEgHw4pU&oalUa&^Y>m*K5Vj;`L+!x{c=z~KkU+C>FP$mvU+K4_qo-wjmx3!Hwb(|_aimz~}`$jT1`RsQQvzryLyIsK1L z-*K>&-xpN*i=6%)r}wMzuFwe`Cav+V(2tr{{eIBuWA(4**Gz0`>@Eg%-zdGoq&40Z zTG-b5unfu19xUsx5JpZTYu7DBk-zUG0jmcqsoH zpnRC+bpIP5&4ImvQuhQ?9qD7eC(7Md_&Cz4f5WEw*!S^dY>MAJP<=e2)1)9FhY>;8 zr`uah5@4wNbg*9E`gFxmf4+}bVkg?i{jnMC;{<8GkB31dz1^C9SFDkitdW*?X|ZPG ze*`Ew78JMpK>4}_6Z8kJF(1`6?*xmpfhU%CDl+7lZQS&rYv3kB4r!<~;tW zY1MPL`q=mJIBbglY*2mNywRHTIJtxQo&i5~!D9g96>0J!>9y|t%^}XU?lYE1_oB9zpf|e& z-yap)&G9F0cY%`oo$f#L*gx`_#|~Yv_W?ii7(-bv@^d_TMaGeTbJ)e~7Qyt+#^n}J za!|tJc@!v)wch1CUwk6(F^=jQqw1e!5k4!?nKGexKBe8vb{;J6YIcXXv_n9ZI}WVJ zXLQ#O&Sw&3y(m6Y(W87SLtT7?{K@WNF70$sd@gY5wc@k19eg@Uq1`&~g)Yl(kta_ z(Yi2#x?Z$zk3(;93I85D2N|#ONaGbluZ*zc1Zk4XL6!fh)1L;le?1R|F|6}$^(7yH zd(haHdtdY@_u|3EFAa)E4$Qz`#|7tC57NYa8&~!yP~1Ncs+|9hWxe-nFT(D}b)TqS zJL)g(w=1iB)_GU&UXy}6DDnN3I>LCzK&2HAH$9JDaXy`SsKw<)P`(~C+RopO1m*LO zsiV4gxVrxNrd?}1-_*Sc|3BdKP5-TOieCf%427!~o%2sZpDsrcAHUKu7Ml#HwAAMf z(??s3;-srD^5|M`%b?1|C|7Z@KlVsVTL7w`j!2ol`)93g!%RqrG)9HZ{N);mQ$!)@tJ-?a0+*6l5?*osRITVCXI5xqrD zAOi6$A8UNe$C-b5P~$HHYCTFiJqG59r|O(f9m#Z8_lKa`yB$>g9|aYg-@AN2U-ztY zUw8N>Zobxf52x<;u1^o20o1dq|0iy%6ow@zy`dQ>|a(r&*ttot_8PkEJhK ze-+8sc*&rL*N&j(X1c}nIH-QjQZ9bH)!liV*|-l>-aaQ-8}ER6_V>Y)%=YKfHWoJi zg0*!osAr9vPc|EqL5;VUK)plb+*54a^}z?Fe-jj!M?tl_1Qd5arbgBoQ+@k>@_RtN zy`jD7ny-p`63$-q-jm7baRmYK^EE%o;$8ujmORhmlmj(i%b@07{L6-EFi-y}W;dN~ zb?*YDZ+ND)@s$Zy_xE79rk!nlIun#W8&vtJ87sF6RCZddoqzdkDU1JWd6re-~6e&iiWqrbwnAeZ7`zuUX`KQH=8F z(QB{lWIn4URN&~ErX;akFIAdl@(`yGJOH-p26e9rMdUC-|i1vq=r+$y8b z(d0z@cuQw376njgsf(i&c~ImcxfD#vE@bMGw44l$hZU_ zulP63_Y$bI*d?at(32ONKa)VoPe7GZspzrwFW(Mh3SiOR_-_7v3jdO@oBu$@&|#EF`&}#bLq=Kje*rp?*=E? zi#`*u3O}pA3n5)Gkw5V*=`Sz=s;#kLGbr&f(XrYUdQqPKMo>QK6T#efd)f2*pz2Hn zC0}>C@>S38mD|=EN8=utW4F3Lr2GCTa4fq;Q2ntSY*Ax~=cr?~ zEA*l~{Vk?^(kFt+tF1rEj?4DwlHEYHp?uZz?>+u}jrSf;j>Ad6jS{)1^HI{|ul&o? zwim5mMf8FOCgT0g{>Zo%9R_*F9ZWkv^4FLjhtIY?cny@Ce67vNCqVhje!V;Q&rXis zKZ_5*$F=O&qsaH7xtl^yxCGBL_;Ifw>$u$wO6EI#-u0%x1BN`@I=Xe8@xNMpH2$xz z@!f@wDc$$M;DN2a>3nwtdR$H+-nZNwD>II= zl1Z*k4%B?S)urDDhB1lElL9=d=ZSPbPnK{j9?L zN0;mdN>{$@Md$Ju>{fG>?m153SaoKA;y4#<&s@$^K1n??l!cgzR0>Z{b!bkM?SNxC`S6svg-Sl>(b?$Z1vryt#1kRbi}t{uJK7bOn~v* zE$wkoQn|x?7;vZQyMXHFlbrr6C|_T8>HP+DpEiKsUh3Dek&pGeCc0!A(w&Q~zr{Fs zTYQRl4dVO;O4GTK??V2U(iJo9O{3xFMVBN~(4)WQlfBpC5Wmm-&VrI6>55BizF~>< zoQvzIyR6JDzp!y|-raV7_$a8@{S8#tJaLbe+nu&FK28ReevWIq*6-o=ZYG}V_#8Q! z@5jh+>Uz<=fHCONJum+&%h1^>G3{`hA+>L(WIA zS*7JukTh zwiYKqak&7Le8cI=m%Zqo*1UaNj?z8H6vwKQ2E}nY*dC6PD4(RBm)Nh3W5IEH$8qdP zx;QJp7RN=jQyoX?p5sc6Rj1PsXD_z^-#MZGn8V@|mL7LlTx5C%z2IUu z`(cY^>Jf|K^PrkPaiOh+PlK{?-D7tD?P*Z@5s%v%_;*lyb)L30e{Xd*>g}0>ccc$K zHtvbpLK0V@0ASRx$U|l-LEU7Iab@_K(#d$Yz8HM zF6dZXg<{ksu$(BkanuqKIxugg=5tT62`F^Y!AoY zluxR@lh|+F`Ml%wrsMd&)0OWywzKS64# zajd{aaoihSav&&O`L*I0+odf>>7L^-j#XzAD2^GhJ#idI`6TtcP1yPxS)=x^;byK-)>qz`%TW<>F zYyXK=m1|I8q#k-ZYEOHt_EphX1TWox(Vx~zjfa(_>2nbMshrXp;4P7^cPuHbKWVb3 z?{4&^u_F8?qbK?ElEkNITCPU9SdDUJmsWJ+wew3BpTVH!#}%OB@GdByF8!V9e*~3( z-0yAOodAk&7viY>VT6n4bWkyP5>$M|OEHii^6L+v;#$4*XJ9DYVeNBxQaU4#*7xmQ$^yvNK>>v5Q>@wp#;}14go(1KD|1QclLF;!> zva(tAT@-I?PQ+FLy;&p>f!ZtlneR$tPsbHdvg>kZD{`H&g8J3#uXMk*_1?qQw!xtK zW;ECmw$^G(s_eMr5X3`&!ziB&Q|w>zFK&;3pXzLnF4+x~u6)(A>$~4_eYZ5czPqo^ z=a#b$gkST*+Ce?Dscb+!Ld z^FJ!Tb=6;2{wK}hL4i4t?DP7ni{<<)6zMf_ag6!m`Ff9OeEuH#GQ+UrJ|K+CeIxl}- zQ+SP@p41dxdzU6Qh1cqZbDP3z=v<~LyvEL+-4tGX$IogCuc^~#Hig&Pr87)VwKRp_ zcTXMF6n-Zl+re=9JcJ>4(LoYoY+6FhxLQ}}-L1oelIo;so_d_Q{S zFw+aix^nPVe)MKB}?q7I@BqK1Tq27{>3 zw4D+f8ts&%O-b89N~*z>w7*nSostryHT9ZmT2r*YAZ^D>Nl9x;v?*!(|9sEd+nsyv zjF^VLJpbQ%o_oK0?Y-At-?jEW`qUWH8XBt+{FwCB4 zSfF0xR42J{PH2ms4PKaFdW!M%%;*3=;#oZ1<~s@Yklmqe!C_zqJRGc$-?3o!eQm)J z(hqJ6-Vdh14}j${ZPE9{)8LWlrGwgnqrmFuw%}+m3u@eeJRSoinb!}3`LS)mv0wt! zXP5Jbw*|+60eC!+J*+L50A}z%5zHOZ7MuW9;c+6E!q10Pe?VLGUCe~@Q<~NmoJ@V1 z_4Z*fAYZ~P`4;BM%c)@XxVGRlA@eLfkFNGId5rKhXXh|So>iY=JdK}ZJ~Hke#ZN%h zWnLZ-oDI$e)%UD51A>o$4};R50Vjjw{x~3-qeY(>8z@C^DDLACGy=K;|(@(%~4&jLr%e&}BYL~Cg# zsAuXATQ?wjp8P~m`qx1{dw(%_2)N~6ZTx|t#+mSjjdK$y{opqT1f%Hp7^t=Ph4llX z=b}&e`+#VT{RG^O`iuTy<2(u~=cn{&3-$r`>)954HsCN&<9!#@I3s$sMZ6vc_5Auz zx3oo{4}BI4(O=%AExJ!PzPIf?_q5txG^nrbRlfjrk7``Mw%}0kZcuBnqrV-imx6k3 z{wi=d_|#^$7WUk{EqV_BH$m~(b_+Yk*Mobbml9ULcuU)JzA(V-)`O?hKBKKIdanLS z?Y72t*~<2e+rXV@A287DF9tK{t%GdO`YO1C^ue}AJPc}$P9D-`zhS+#?K!W2`aOY_ z+q4CzfQyIP9{P!GZ7+NR)b}k$ZD)EK)SkKR_HEJo5C7E#>4Ah@L4;>ya-=jbYFitdby|7h^yjAUTSe9E;B4TJp(2#wpiDIl7nX2wa>&$ zOrHsAj9Z-k@y}QeCVkf0_sCiQ#OLf>+K(}NG0q@RcJcWfe@xf;lV5WKI(_H@<1_)(aep5e?FZM}_30@$7?*E> zdhPkB)3?6SxLyk?KYs$1qpyC$u3JBKvt8%^4pc1XeAE2=9#qWhLG_P+)@jMauKR&> zzaOlkEMIFtjnRHWbDz-(=pSO|r9j@bA7nvsxeSzi#p!B~_Ji1SeDEW%h)h@eu9@jHYEl25|K6H!=5uZ z>_hmg^BIlt)O(SyiRd$2h4+=bqbH~$b&s;PlL*9!qu1WvAwDQN~%A!J*x!j8YJm3 z4{8s~fNAot+~+{aS$CSvCqdc#094<*LG459T>UmnZNJ;*F6;LjP@Mh&iqn+4ZJ(-u zo1h;AC+&;Jg7VjB?M^?b@!DNLn-}?5h+c3NK3BzixxYZZuK&g~!W%6WuzIY&I_(gVgT`JnNt zq9^Y+P9Fp%J5i_p9iZ0A7%<{oXHS`dk9vDbp@xg{xd=VJr&QsP=1*f^3`!n$dcFPY z`3AoFPwtFczS_~_zDf@fCx7yF6ezjY>G5mzH1@UU6zR&baxsIlax(|iIi(0r1tmVm zD$9@1i`w+Jfc~Mc1k(?@bBg0~AiCsmP-CcFcJ-bmx>j83{0`I5Q@ZA37bLpl?v6f{nA~sIc=Yp}q0NitIg38YRrq<%{lsEPgH`9FpR3ys zRKG*OMsY5Y|2p%xs>Zk0^YiHO`Aa;~7L?rBuQ;D?yp6BqY4v@Lquz^rrP1TQ^7sn* zQyiZFB^NtA{+nHM%!D;SqQ@lL9Y?CkXT_3M&4K32tiJYUC0 zf$=hK%$)NvyMd3vr+1Gdq|S@xeH?l`SHaJi4>xAT`S_jp(cpOlOXaK1^9J5&oj;dV zYWV4TWF30^JDTO6TaGi2+Od`dLpUq%b3yIb3qc)+MOXhVY$S`FZI8CT{1)Tsy2|MK zTa0ynQ=xPQ=Y;{if_{hf&geC@zGtwpb+ymtt}QDMt?*opPt8xqnf5I9C1CvX)1_Zq zOjS^^D?X31l{7--NPl|GES$pl+VAwI>mKE9E_Ip%{i&bo3e<_8|4oG?zQ>`5{CUaM zsSE1VWn5k5Y2&~3DvRe}P@FCY<@0%0Uj?K1uoFl5R(uD7;(d(MHLm#PoPIT^7#2C* ze~!KOHx(96VUBBFBWaE|wyyU2sJ86qcm+PizyDb_$MeDX=kXI2%Sj0Ox%Jp+ZhgIS zs6Ri);}}0a$64y)b3BVWKgaq_h3s#;&vCgNYd#hfXz1sQ8)Q;OxA7P)0JsVQ< zAr6yi^CF*9(Bs#W*=M-UaC7mZo13*x_wPS7dS>cU`PTbShjst_zvob?;h^|eqZb@0 zaa3+9FSZ2*c&L2OvzE7&p!BPLXZn5gk39jw>Ex#4A%UJzJ>t0){%HQQ!ao?NaiHWXQ2lOq^?uDZdcD1#@&8-yrE48& zPZ)M?XO47FWHNdMl=vK#UbVc0Yb_rEsQi{*v;8J;7=o(Lfo1Zk`JMNQ**plUt!LG2 zhJ#u!9|tuz4}p@;;Y)kdS3t=kQ0;d*4t6aP?HXOLMTYd9t>=pH&9QFJ6>0w{^P=Yu zUln_%NS!+6MLfq~<3;|*qt9^_J}hff7*`kKW!h~ zvpKZEJ)3pn8ht*wh0lNUKJ882_rDFcN1rcj;rl`L?G>Z;s^qlJ7Q4iuUyZHCcod9s@-}m}94^hy zS+yn}%~|lV?sHb?Y4N0mJ=!B4M}U%#fZ}(rtM|wIz76JTF5|yj$Ge}CC5-7sbFu=x zAX11xc}w;-z8O$;l}+1&3h}7zd%djgk`~i{4l4JrIsLj#to`?(>VJ%_#wdfb_urWu zS?4>Gi$6ZyzB75)W~u(!ewn@%d*3Yj9_4#nr~GJNcmfVybdR~^6WyQVSlq&5Njc1d zinY9Xdvtvig3|L~i5x2KomwrY`+?Fg?PGblwXgNvv!B&30kv;E?DSH9YyUl{W9p*K z+N1sIGEn2}fDg$&&gc6;)eqUeAHQSY9(~t)_d2py zz36iitI>50E%9r;Fv#+iahM-ydf8!eu;sf#yXGYcYMrEM7bd`}TL-5mY%W%T8vg@Z z+T0aE&D{Y5Y^;@_#`=7ljqyuR=gNKB+oNm!!$8g9`mIdw=eRVwj-L5R=CZ+c^g`Oa zh{Iy^_;qw}&7KN>{$M-V^7+15AS2AIE_mpv31}W%&_$ zQJelo(LeN+U`$(mlr|lx6b(D2{8ush~vlo}0(0 z!L_M$4QiBGZhraJlG7!ne-2_^HOpg zGmg_OpyYO^tGyn_Cm5$TkJ3HI^^|4Pf4XrT1vbMmN&iqJoP^uBJUULp(Iq3nD34&o z(S2U#@CN71663zx=gifN>7`YL3%WnsA-jX|tt1Uo=o!a-^iZo?0QSUuF{qe^ZD%oU zzP;(+$5t`^0#tjwdm~fu9lw7eo4M3`(Q!TxJ-*kacH(>lFO8kp(Kzh}M!sBL8+~3g zbK$#}*LQP%vyQ3IO{Z9f_@2~e7<@kY5{paR*4cxpa$3tgy z&v9;~#a$j{IS%(VJ%g?sCqYSxemU~5oE*G|)xB>|%W=QGEN9n%vgw<$9DfIt{$o&f zYxXg_a~VrHz6ex({9G^}p0#_Jbiap{DC^iMgPO;6U^9Ez^Yjm~drS7SJ*+_7;_{M< z|23xvv`2eb?0FhvF6zory5~5BvTUY<;h>0DBW{xz1TPo2E{Q1o8dTu{-H>C+27^SaeB#de9h?r?e#d$Vw_rz(mlt;lx4FF z6vyYmW;mAVA9{XZ&T&kFQ653b`A%1R#L?}mhcq}}&G|IE{zadIFXGFKKBrPfuZkoh zP#od=ERN#Ah5@?jGw1~u+XbVo?N?w=>|OxX=j;P5=5K@2`yOP+?4F?XkTK-vL{R#W zZTrsNmS=KuZ@9?Ao(n>M+a6A3(_;54XMe!XvD21=KO~srQ@y`w!T>Z#&NHz6478F_viT z0KStPFZs-WZjM*M&5Lr}|Fhk5oIc*-E=;f-CnlPnLRXHfu00Q?kG7m_Hs1PdevIY# znh#pv;8@G?wV-VK9A~*a7*sC53rcopEamu6Fv@Z4TrvlqwR@m+zXuj6YkibJ&F5;c znLY3c`iIzgDHE?=pJhRDx!c8mzthz&fAMqjh+J2W(mltClw~so6vw$>GaP5oKg7;U z`UK-xb({`FmmCg?v)b!%EHh4R9;JJZ>nO{n^>fBC1vbNRDE&i`a8h<0vyRi5Nm$QM%{2fU;~BgUaI*U^5&`^bfJ~l0MPp(Q!HuU2-@Wn%CEbIzjg{G$)n1i-z{xxRn8CLf?sQaKtPO-jsg01Ki z&ocd7Q2oDmw&TG4V$s|G9J?<$;#~9nDNy$-uY;@B^Y1~~*?UfVv!?cm*3{Vz*3|ef z{NI||*fs0d)O2#`MQiE-^w!v#N_^JlvzoItmATC3t$L}=Z}oFFuNkN3(Isj6YfV+1 zeHJXT9yQl9&bK~iO}91m$OYDS^o6#jehJFnMrfHp5vtZb6k$Hj-x!NbKFv}nRDDi`iIzgNzJlzT$y}{%hu?U zoj~bokIr#1j_Vnx_B<@za~$?X<2VWw$H`zb9LLc=6bUCq$1&|VT?a~TcDmZ@aV#)S zEl25|<8sQfSqX|`aCvha*I*aQ&Pi&v%cJA8HM(RcFv=rXkK8nilqWH!7adbs^n%DB z0_8A$h4C$2ZaEFm%Z~ewU$C~vK=pqClucjy_C(Kvk{>zSx4SM`6~)*1x};@pCs%Rr zkKXONrjibD4O_gh6(Rs3po>jsSpZDZE z<6i<*m;9>f8T61i71OJrWXP44?;}Cg&%Da&$6sxGp=6;Dh z=<_7;->rFvb$)FvVH_`7TPx7x&&*I3<+*IN9`K-Kq|Z}nwR zF>Qy9>f^8dI=&CI-I1NXaj0DA5?e#cj8K;(`bkA`< zW!WqOqje27!*Ln?Ly>S&avU>`(=DLncBiYo9>?~4SB}y>$1#*;GaeMj>0mP)GxQI! z^Aav_d32nHqf17DQ69mFqub9%HrQ`gi&vBTxsNGv<#bOc(0#fjdR|Tufq0Y)#wQ16 z9JiCMH?CXWU~SWGG_EJpM{=>VdAogj7QWu@_2c3iZrZ1ppvU*=+`{%C`3>W++fC;C z5Kz4RKHcbkY3{1#^VV#ho9(Z8(L5}In-|^7TZvxjX*GUcir=w#gYR1Iv!Eou*!Jo) zD5=t~M4ptBOTKA!pZS(u+xA*yeLn_jO}Bj8^z(1AJ@J1)*$r~Oz6y%QcRMn z38);m-)8z{w_7gn1ZB6Y^Yu+oIlc!}eSEK93eVboRJuQ&E0nc9s-S!g{%Ui3R15t> z?7W0`x;=`x#br3UWF#nE?eZ7rIGJ&3IZF2&XHk~TJWw2$fz9lri|8L>=cVL0W*nzm zK*{Y+S9?8E zqHDl1dO>6mfpVDnk?}46&~lnUuQ=}e+-+^og6jWgP&QlBwFS;zTJ6i%zVYy z`1QpU+P#STH1zm+D{&w9Dc~m#cin5g9|fgvXipvewRbPSjq=`Xz2-^htqk0}Xiv?f z$M@9YPb}Wxr#2T^P?CSd_QfX5%tnSM{vmAf;=a#bQ zWzzj#HlMPN%SE8(u?#k|-!7wnh@F@6uWY}~g5q*FD7oM1YL`FGTl@0e?&JM;yW1CZ z1wZ+$KZyp9Ho1X9h7A=8WhJ2*vxtyPyY}* zFWHsGF#&3BPX;AtI$iA%M|W;*^j(-yH^QsQ*Lv@Df8|Vj|75s%(S828(tBEsVpH6O zXWiO<&f+aUW0<5~eKHO!VBY2C`%hZiaZlO4lz-ZCH*{5dG&g(y+S-eteC+#M)2|1$ zPwi5%ed!=j?YBCg_ky~=|8r3FL!#$J4vyXzYV=xrUSWFhuYO*n@*5o|`1aCDVHNre zB@OZU&A(u|sepz56V|megl~d4LW9vNlqw!6F;#+W-1q+Vr zF)vx4<6pLUmKxG3mEZKHV{vmc=O7M*Cn>yaNfRfvtu6FtJYj}^?8t%UkK3c;yza1~GvKd927acES&@-`o z6<%YlI$u}5YQFCPqr432Gh4r3xmTUu3)R)PV%Gut?eTz-b5OGbj?rgr&@U!yK%oZ90`y60G-ESoAQj)VWFxofKy z`iIzg$vci2$LT^)a;ekRUXSBs#;N5f-E*8pSvK=Paa;y=Xi!A;id|)oDofO+zeV&9 zK`#|J=-4khP7i^S$DFQq*~RBNSk#rHbkDJavTR0!;+O%Ond|ZN53%!-`I~VJK*e8@oSUZ zCR;^a%Aj(P+SD)yW_ntA(_Yr+hoFw@_qS{n?_x1R-$y(*) z9bTIZy`?h-U7L(RFUbiah&ZH)S`(AzW$<@+9$_q|e1GE~I;{3Hy#Okn67_i(!$Y8?M_-$VV?YhJ4Slr^ z4hA)Eb3oN!>Bbo1o{O|o&@a%v<|nJI1Gg3G~6= z<$5lbBdR0GZ*Fl_z_=)WNh4G))vo;LPxGO*yad0REB$HR$@MDgbRO29`l+r;okY5> zJ5|>ruArW$lu!k{#sMLJUNUv+Dw}oh7uM-la&=YKfcWo zp!&;?ViGUqWouAz?&@^;jQBhKd{8l8>U8;6tV6cnjPHi`j$WH>+2DQ9)U6yx(KG8C zJlpd!HN=yIv1T_v>ObWsNxw)Nz~z z%~o(+UIHbrIbH3ti*sDUIJMW6(mlsjlx4F96vy`4 zn&TMIKg7;U&T&kFitAiZa=z2mUXSA>#;N5f-E+)QmQ5ZM$E9F1d0a^U5WBZzTbD=2 z=_SYUHKzx(JC0rM&#q^jT8`2^$6>b{$5Eg-P6j)iLyhI<*rVs7HvNsGf9Na0wBwir zWwReBIn3#5cN{O^Ui@SJ`k=r#wH&2;j>{>_W+f<&!II|kxCXmWc23H0&})dS<8(JD zx!>t(ug7sLerq{O_Z+8Dmd#9192bJk%ypjrA$DHU+Zo5I<8&apQ2K@Epi^d;!=&-s;yxmb6$SQB6sUgEIhj>hRlP@Ko?WP9#cK9^&7_+(vNXt z`Mvj~*xsAIqkF#M`P-1L*fjSu7|V;UXXc}qdRmRoZ)UjhD>zK;Zv3n0dEylJr+2X! zXYOh-oZ4Z1?f{j;{=1pJC8+)nI{gu+`&bW-#X927=418qHW6Ddnzw1_6$OR(*s6ON zpX8o~X)sHC;@D?|#q=1cl=Pynq|MnJ>GXR+jrUVm?_-It^VL30Lu6v(!aTJxQlw~^u z)cP-g&Fp=1=^tX}r3!!T&v_TmQ=sHIr>kB5dhEYtu>aIVu;+{|`OT&Q!8ZKv%a%Pl zy5CRPY}0)=>(zROePcQkEYbex6g@#hMs4%sS5z0Y#^H&C)8 zb;>Ev(L93ZXr9;ku7o{Lbe4QJc#h`cn9n8X%7H|jG;ZQhjw9#e`p|q9!N_OqetZ@C z+H;k3KVO6IvH9u%HD774nfV$+{}4MbMRC z%O(qo<9x80JkFwjh@F?5yf_827?B<531fc#G3?Q1Xb=Uw8UP=_B(`Ih!GEeJ*J0wfSRRpR8%e zboNK&d=Rg@kB`0Xu2bjN&3tUU=>EYX^!U0-9&Ow*!Xu6EZJ^{)r=NI~@jM4qJWqj= zZLyI)WUm3U_nNv%Fnq?Q?2o;J?dP`!jh+!Y>R#d)l?X=Bf6o~$LC2h4!4C6#25paZ zjY}~oHA$L1WTYe30OPFK6**yVNAI>xExDBW{R z{@6ICKyjP|#@C^IG~zgx{-HWJNlkKltmCvbx@0F%W2imi*kJ#9Kz#nm{c8=jURo)% z+}C~o%1$;O1&3uYIN9pH21?HOuKY%lzIwtd@jz!hTJK5pNg|B+JZI0e7(%e(e7xy=?n0gV?FB~h_1|RQ z{=Ir1U;F!MQy8;eF6Y+xRxanE$8#A>*(xZyv40CnUV0l}t`Mk&Ahl%4q=<^=$l#6ny`xYVmz39ENIp_r?0rB%&I^Xh>J5qF(t+(Jws5$`65(!_GDRK2T#E_ECG^=nhb0>^jZfH@Y1Zoku|Nc^uT3 zBR^*PO`!V!`Q!FJ)PY&kZw9r`z67ej-@}iNt-~qHn_q|Z)}=ViWh^iH9QR`MbS#&t zPh0%?i;aH<6u*G_+yz$daiR73JgBzUoId?imaEmEa&g)W%guS9`uzixTziqV-wdkS zzfZk&(D`aruaCo@5c>u`L+7s}rqb7o@;U>3xvTK&B>ar|OMn^Tk?$v6yzifB`u~8E zzLyxsL14tO!9KB89RJCEV(25CoV8DkLa)d<;yEWjXMED2coe{_i{p2oYEpLXZw=XxS-UbL>KpvTW+Ww=NRV91~1KiTp7xNE!E z)%O_C8Vq>VJwLCsX*v2W)E)ZG-l5l!9{qaqi%Z$QnLef7Db?whbo~l0_fLV6zk%X(6gKj?5R?=_#iy~vQN9)5 z8=!b^#(2^-uK0#dp8zU`sZRIrW7Ph(Q|{-?am{CHwHIz|UG3*`Ys-F)7vWQUSA+U2 z{q#q>|F%>4s}_53&Bo91FvgG1@p$UubDW{h&#|7>oPtAqKJ#_z5_RgbuC7AvHFw+1 zvv_s`HOE(gn&UsX`oDter?H~XCVU52(InFzMA*eX+a=QO4(yl@0XOX6q zyC26JTUUGR)|UMo&&8+suK+d2lYY_tI8MyBoP?lar`ua7TS+5S4)y1c<5tFx&vA;n z_#BU=&d+g*3CLdCeU8g@>e6-Uimoo@*66vQCb_RW1Z9M9mnGy>wX+>Y+dcKTU+*XoW-a37eUSOn8&*x$C>La_B^<; zbG!yW@i}hCXMB!3sPl8ILs44K|wN8K7hq zs5mv2=2*U!qo+Y}|D)42uI5-g)IJJS97j6cAIG(yDID=c_v3hD>uQhP+OnVHiTD)1 zd7yrOCHYHotAG)KVofiw*mIzCwRx1Sq!GrC<1&8YbG(ka_#F2a561PM4J>`V`+O$h z(`}CH^b1_S61f-eH$cfI^cBAYK*f4AD4F8)8$k6hfYDroit{N@F}&z>jUUZ5sP>Vd z;yKLe{`- zf8T)WC?28WkTgQYt#-|2`3IH<>0Y!BHSZ(vV*mtu%TXKi!U_I(4Z!b-6lqi96hQON{?*pky^D9)EQ88c*EiU%aM(is_S1*Vu}q z;PfJ>*zR?@{3s6Y0iVEq%d2YdTMm7a^;zd0@L1}-$oF`3#V!#Cbyb+Lm{6IdmFae8pM)s;c5 z!A%~qzK4S9dmI>Xcdw(j3_7pB=in=TZk0{3hLbohMbCi}&ndft&qu*SV`nH!J^@C) zwrbVyxc1mCmZveR-u<__^9zqI!uGgSx_{><`s z1gJh2fy%{=Ket$a0!n`g)EXJ!#`MQo{61dmuQ$9#6pMVN=<7vehv>^)g^#8B3*(V| zjPp*};yM?U+zhJUi}b0TkB-=UEc2X>kFDD#ba58P=W94?9k=|3dt@Skc+SaR8NW2B zx*Vu@i(r~K#Pzbrt#?h z`FakNyyEnLcE_>H`{RQvT{%kk97D>oNrU1z1DvWs5!L(rd5judo7$()KlGJg;c4R- zg0i_Blw9p}waczYqLuIW>=C$c*q5SvqOs3Mlo`7=x6(cDb(A$u>oact!De_5rGJRM zm-26ncNP@yyFtnQPFK79`S-#;Q0Kj{5@Sa1KgE8}d1LAnujXYnV|$6N+n(jv>S;CN zCC=gRjC%=GUEnYWR$UxhR;=wHP{-(%poaMls4)gUXK|knO6Rwl_uaBr|1(-sy|bHU z><+cArL*{dyS-pO?Ox=kAil1`^G&~quNTbE{wgC!fp6z`^#=SVp6lv&FnZi?c{SH^ z&d;5cC69oS-`Ef&g7Iz9%ywyLsa~Y_$lm(UV z1ekNVx#T6YxfPU61=O(ZFPqJwplt2{bxpGdRE|f#V)ZjY)z1Z0&pzK}51stGuIoU$ zUk9@&%jY~$K9+&atb;}L53%!-c+J*9k@&^s4VRBz)Js=;?K(KJ!8%yf=71Z$C zzixd`1~rG*gUax7?S3KMuam`;<#QP*AJ2o$tdlbR zL+sv?^|nq5E*~#}lGmIb&>pRm*jnyb-Ib$s&oNC|HW^SH=YY*{oKF7`J1;rMF$pSP z=Yo>+ovwDrvCHSCS1?X(9;JJZYbnd7<@d&M1lSRc`p&Ld2Gc)u{RL$>=-RK~INbqC z?sK}@>v7C7PAy02p5uJVvRMQwk7ckKj?3sDV&^6KH{)1!oHj+53;>l^wL6Yo_O14p zx^k56IgX($oAIDHP6wNr>kR!v?7S2l$Ix-Q9F$z`bhSqu8+_KGBtEgvI*6-mR@3H1 z$9NUJ;wsdx98~|#eGBL7W#{`d)Tzy%GaLN|;1e(7`#2`Ig_myLM~D1Glu21fhw=RoPNfRazZNqP>{IrmCX>#qRT&Q+t|BUtx`|DL&O zG>7kXz1B#7a+>^O=Nf5O&e5})ctmrcd}oK)yd*%?r3TwPRtDMp6+q2T!eMTpt%ned z_BMwZ+GU@`UQz^0tTXwT*2n64_p?4zKs~GC@1V}}PxiO^zFSzod7x@7(p@$-a2*e{l)Z$J%On^n^Lv6ov^YfXlEru6C$urwnj{4A7 z{rn!+=vj-IKmFfv)3?%E-84blU2GmRprpLB zVS#q7)eNY8F$rqDbA1gQ6!%DdWH4jtw|NtyPtTN8b@bz_4X`)8oWego9l zW4E)hZwEE@I#6Tpy}gZ{-CSr7)Ea&RRJcbDv*UGV7t=ye z?}0rEMr+jFzy4tSe(9^MO}$rgUg!N%VW z`-&?Vr}o-Xy63o-vTRz`y6a!CLxUo!*SIQsR9T`n{SBsn2ztpljw#3K{h;J{r>k9d z@z*+8#;N5f-E*8zSvHG6<*^JllgDNB53%!7frGBmbB@y^pyY9uBt`d54!UH{VNCA$7aueb`0 zDSx?r1_o*8>(kEn=G3XppBoxoQ?2?!@2RZ&@1MhF*N)~~=cU%acFuY9 z*+cZQCKB<-NAf7kbK(Pr1&8G$E$?|y`#>60-b>?b{&JwMB||V0+LNKp$w{E33TiI5 zJ=DfH3e*@^f;!IzhuIioL9NlJ$C}*jeEn?7ZY1$Bg52At<@j>1vNSx?^v*z&~BXeJ`Hz z8XbG{Y4f7>RgiBDh6t2{@(G+voUe-~n(v1|wfXb)F%9xwFI~&yATsg_voof28!DtQ!Y&v^Duf9EY2|Axgp7{rPuleqTViV6f)O(SyJbL^) zEY&mNdb(TxpyX3$IA5`2bp`gd$ANTz9IT}*pDn=!Q7%S+%^a(P=^x6uliU7L_!%CcDmYQD-~GaQ%EKg8}WIm+8xI(d)`vUsm-Hw&#^*T zHdRm@2lu?7sn;ki^bfJ~QienHUWem!2PnDE>1waXaWdo7a+K~l&Y~=vd7wBh13NS* zqI%^*WsfRL)TX~h^bbKV$&VPvs^hdNx?}*TG1M-*Av^S+y~EV)qUU|Qug>#A7xjWy zvTu-_+N=9-w*H6JE0@|M%W(Fh*DkFs7X&L6EFw_+`Hxv1QVuI0w|y=LYVS#bnuiMQ z+UF|M1_q(a-R!fiZl|f1(=w=Wk2=T3I0clCTS4uO4?2DPM{UdxgYt0!*b|%ss{ft% zkgrEU+5HAoy?>tT`|HflDq@SC=Nf;mjGpoO4(gOMacSA4GiUNW6um>_5TCQ~0*kjg z-Qq2RsZSWkXFy3IYi*B!GXK~ot$t7XMzOf-fJ0-~0rT+}f8D+z-E%L+xUZBRngl$3$yaj?z8H(UfH~4iv{M*bK+X^bfJ~QvHl^EP=}7uRzJOPFK6**ya7{ zC5%&>N9ms9D$2511BzpNpXTN|pnr&+mu${BCO~mK8I+vqbhSqu-EUUy+~C@K4&yd^ z?d@YKV(Uf6PzgOLr-(qj60?j~`LcmQ-f=tcb5{2`PJ-gHI2Ayj4FM+ zXzyQ#9^ZosU*L7S^Zj|slCOgG@-(Vn*E~u0d74aFKBs}o(>$=5Jmu&gV&^5CV|l7L zF2m6!BSCRfdz7cxGXz&MPVF%w-E$238^;7Fj-$b5j^knU4@JUB-f_%0P8WibOP#Lv zdK_mmPAy02p5sEwvRML(<0`Njj?3vEV&|m-hiE@^oE`!tk2ziKj$@a-Cb?Nxj?z8H zag=2<2^7a1*vwo{qko8gBZhfX)VST;|s?Rq;jqx%VU8l~q z_A)3r`b*ZY2&(-Ye97+>p!CI{#<>U7IR5(}!-KBRbs4+`In#TIZ}XiYKkp%Zy=Xs9 zqmPTl9A0JoDp#_v5T|^%<*jZesP4aY`UUifxHY(Ev_#xu@A1TS&uvu=H_gu)^f1P) zdbROL&LjWul<)65zGu^>?wm8BcHKryM@@-?i12|yelY+?d)YkthuC>3IF4Dz>GPoEDyOU6aqO~}tzn#6j?z8H z_5sFm7#JPbU^9E!X!?f{3;UP+y5+ItIQ2!Bw1LW_+Uw;p&p5RlrF)J`Da&R#D2`RI znLMtde~6uztm7CuPA7trGn}sWh~ozK+j(v9`WNlD%kbr;mBK3Yibx^?<+fO`IMUxR z{v}XSr9SUs+iQW+4vQNH<|CfLCK4ty7!>1mQ(zFu2r5>o%gv~w(8`dJ*q!?{C%#>qJcrx z`MBVl=KCfv@)_Hs#$jK(Zl(Kmo24wiIWRi@!Df!FdGrsl_maNF?NQ%0E(fAZ4hO|e z?eZ7jqt-A^El25|WBWkkI1G%Af3TTjYc%~sk#JIQ9J7woWuW9MPFH)x(Vg?QkKJQf zDLxxmPtCR~R-LbdL7j82`)3{KRn;Tnq#P!0HNKVa8qXrA<01!Yy`;h1cP!ucfRYa^ zw!Wu>>ict0ef!W?IUNdW?D*@hS@^G=7wLXp7E>0dWuV4*9&Bdql<6O0=cPbgx(*IO zak<>Zd9~Bk?$%D1b4SPEt{kO%j%muW$$;WG2W*Dpboz(bc}afHojV+-zUY!RP@L87 zIPP2fyX7kwr#6q$J;$|_Wz#amIF10DS>J={ABu#Ntm7CuPA7trGn}sWdK|NiQ_E4h z=Qy9TY!-paV;O9Q<1+e(*m)_zL35pVoW2E0ZgaZYBaXXmF?+YELxLS=Y#sf!*!~S( zSB%*je!FZI>@u|_*kMMmV5^(9OeL~gWHvALFZ<6(Di)17g}z>N3`|3xqXtBvdCcC< zwXx%JdC|B{z0DmX?tQeuHSeP>k$=69R_C`=d=6LD_|<*gHRwy_6!CtOKQLcuumT5h z>AS@0t_9U^nbTi(dK-P!uScJjpihsTgXo#TZtvPTws%e32AYR6G(&j~ZnT4K= z}B-N7bmFZcD&QD$Tn7R0^JvOBKd0j$T zV?6-s*jNKLv&XEYe~6uzOv#Ro02H^QK*wh&^o0DrB9e5fcX`y_i#!07j0aVJ*Dvha?P*Z@#g5w* zpspPkfZ8*^3o5>4p!)TQe#3ar?!UPcoon~zT)R(nu06ZKT3NjVYb83@@_Q3A_$|*% zyPs<}wq0`;?f*M=&Y3=U-+|t$2E@-h-&)(g70dOfpE3QcXDuh6 z29=W&U$pu!gVGl}{TZk4#hCIJIv>Y^>er*CN6_*tdvTp>f+yfw``MaCWgqMMnppK( zpna$A*9Vzb%x~Z@1y=rGbwmDWF+2fkUA+K`^PfR+t@k?u8GO`xhSVJDy(ryz^!WLr zg73WJaW5!&#Od+p(Jz;un&+4Ky}larvkpCsM(Uj3OqI_W@uwJ$1SQ8iJ$?=xF$_Pk z-z%6%ofpM289g3Lnep;&%zK=VlJ`;PHv{I&Pn~CPm14e@*YTD96EX0oc=l2mIne3# z=BQ=o`o2a`??rKqLhlwAPNAQffOW%6Nocz?8qj@nu3(!O7*dK%G zSBZl^#dLtm$YD|70MocW+^eGKvYI8Xc&F~nn3Ogas%uZc;YCmgzK_xnEiH;rfY z4dYh=#V-QH>vG;Og``F|2 zSAt(WmJR8C{#Ik_Me`SQbf3TM-;GZJ6u&B1B2Ja}U2knSfzo^Z&Ghp?#q>C+7*>L6 z_c6rhZytU(#Cb#Qe*Ts*mKV+6D)jh$yTm4gqAuB%K@qV zep9W+Cj&|^f;r+>`E{3vGd4B-eo(ScZ;R<_P~*%8)!)YupTCL2n~xzrpML&kGL{$3 z-vadb{8cwIp6ShvUlJ6*5?FC@p5Dj$ya1|gLSNH=3hMYfq@UT|0cx%O1XRBh`&+-i zf{OiP_)9FafqE~m(}=GoDZPK&qpPEWu4!-pyURp$9=4;<0CnuGcNh)K#%*#4j2?9 z_*3ro10{z!9UonO(_tF+K8`s0wLY)Lr>>_LqwBS=#IN;CoB1n)sw;p+=krNWbMhxp zZGUt1Bk3bKz}a?-zlNK*$Tqo0EPnKO{9!xe@~8L*sf_I8^tg{H*w>3&eCO8q(i&P& z$5*hGjbCQ`tn+a>D6Thvl3QGTbdTkX&U-99+WH1d$a@`8=x%ku?TLDJj!P<*lD=h^5gGsQ>#_`d{7HlwdNoeaw75>QeC6`#fuNBLHKeHll*w*{qZT=5kT>1Tq9VTRNF zv%zbh@joureU3M_u6AB)%YKft_!Pe)s5u_9clS9i?`pC0X3Sf2`_Int8vMlPxE-JI zIqsmYc8*hUh|g!fPFZh?Z$0<-bItmo` zQ=G1GHOG0U-wY~_rB3&+iEDqWW5hnpan1KlbR2JN-P^scRt_iPQ~c+Fn&ae1&V>pX z5h&K=aLY*=l&&_9vXwMK)2Ne)8OVsF)l!_xQe&d#h#Y045+#SsC-sHMZ2kf&XFuD+a{fzDV{9mgllFb@bEuqX-gEf!q8t^_E3U%lBt6pN z%Y&*bgNiRPifdrS7sgmz6;O4_gDtKMm^#2>dip@?bKF7JXF90ZuLTwRjbIdeoqK9)6mOk-YVD&t zxoM1{=pCTMa|_-#C@8=~V?PK=e&%%l+L0qQRr~z>EaSvq3&wRF7vev^hJ*IYMd(F2 zK|F`jVdg&%Ru8fAq|msW1&Z6RLG^#h>0FO@d7p7DM0S zWxooH&W8s!$G3`IC_+y1?>D|FP<$^2C6_r}?eZ6YjWZ6vwH&2;j#?e#dWVVqiy(mluagN)-aP#njD&2St| z|4<~Hq&{FA15j}d1SLBplLA=D%xv99NFByyd~37^Ok&`vItV zD<5l^N!zt(5|k9cBstfbUI9wZILgMlAJkZVkG8S?0=A;xI^M=Q^B5cJ7Et^9{`ipm z0MxbNa!}*?b4gq5T(W|A;^&SH>B^1bT8ph0oqPKqazT)H6HsBNZ+?ZzrZj7z+pt#q=!Hb@OGa~H%S)b%d z7Jp@u#aj80VZ!MdhXpWAt`zrdm$ThZu)Y_8n&XE-t*5>x+M3uERQ|pMO7386Mc9r*L2<139mN#v2Y*32muM;26FVX|S$ z)mPCo+*W=$=2rzP;2u^P&xRstAF2zZOp0IDtCD>%8lFOj%e_ifr*E^J?=l~ zb>MsbI`h9}e66W1nai2Xk(VJ9TE_DEKTSU3*HrmqHvgG(Y@H-PNp7k`+O;l1Q0p`R zb!=y+**=;AweMCxIw*P`RQ_DU3aIy7s*EQLU47{K6+lT5``}bta|eKuKY{Xj@M-4r zi=ccy1#0geo-v;pP{&HknS-L=0h|CzzXa5Mp$9?jJ1brNPE(A_d0^BJ)MrqB~YPl1x>oUV5Hi(kLDzP~F+>7HYXvTQ<79H)XEphWfZqq0YpC2G^(MEZxImvFjq zOn|c41(fXLbhXPa&T%p0)aFsT=lBF=**p)5WB(5{$8jBYA$DH!a8MpIj?;yp7L`zamH~3D2@}sX68Dif9MH<8OJf@IK3Z~9Pf0sM;zU`t3%5f{P z=ZM9NuG35Cxt>;|Txjm9Im=T5R9)$_w%;W{?Ma!>7?wauntDl|{yCT1Q5Ra>H$G*1 z^q*(gSf^ZMV=V+V)~7DEy?lpHn|{Dd+v86LwFaKK#Ku_*id&=W^JzyhNB`t!jpkwN zMdy{p=<)0G?4`z|?63f)K4+Xh3`$-Gm9Ji|&lpgtodQPO-{$%V%-amV2QU0=Ry@3&}A&~?!~a_dFU0&n>sdTdW9&9iySTxs)|044dnVV3r2 zK0#dIs@t&EeUG>NnT}G!CrdS>19w-pkIOYrZw@TTYp#1v9W&*YV6&e#O5 zPwD>rIhnHJnFeYu=7Ce4Z`Er&sUBTZWTW;R{X<^~X1;Fc_yCm6QJ`dk)737!__fkX z#;HBWOZOauv~f&;;y4=Y(4fdW;W&)`Vbt6H72%+3rJUn*4Jf(E>1waXaVF!`a+K~l zE~G4*C7?L20-NEuoc+>!a98WCUvw-vKbD^aC@Fr+_LwXf>EARA=&$)G(NB9?iuNjbSI+k+Sf47W zKBK>3eSQpT-+B$yI^Fap8~ehWt^W@e+P=3pKD2M10E*^RP~&pkc6t4fCyv@hM9hV98P=g@?`73^B zP>}b&Z!zEB0@da}Q_$$Q^wu)of6HqY&7ao%&=WgzqU(~0=%E%k;&W13YI(?jsw>}N zn7Gq0>#*!FNxv$2RL-tjY_=1=Yke>Jp37;`^k=?r$5-oZcD(EdY8`zP)Er#}%I7w> zo81M@_X1Ef9|ohji5*v~iKF(olJ3`R;zPD(lc0Q!1Djd1qv#)E=OuHOt=Rw+m!m+* z1gEQAF~pCnxr|fGQM%{2gtBZN0L5_)*bK*&^bfJ~Qh`Hs&UTz00VR(+UG4QarY3de zDBW|ML|HadL2=B3&2XGa{}4MbnY&#c!6=WQWP;Px?l|sS`y7BL7^gOm(mluZlx5TZ zB;z;=?1)C~T;EdV&s9et8~r8eA4WCyuLy@IkB-wdpyVc}t3Be_V1LRnUZeeK5p7jM3 z<9b?+kFWBe#gqqCmwmvn=rC~gX)yIei~BdAx-H)n^rU!qPkAF=x$e!WejuNQHciC%RTKE~q1=Dz|aA2Q!Z zgOU$Yr+%|spD%$Cx7hx+oN;T{ymZf@N?BtBlWom+fXy72?eq_2?ngl$HXaJ zIZF2&M^l!~I8Yq3U^5&i(?7({OZF!&k6@HXP;#cz)$TZU+258hPHi5gdycCp%VrHI zj_s#5x4#AS53%!7fod1&+I|Hw&<~Yo%g--jJdJDXRhOQDdTw2 zu~bG+%Q+%Y4vRmtTvi>1Kj-t3j_ZP-8uy7SjPt`_Pwf5|l&ya@;8C&HN>fgQzn%?P z`#NbObC%O(qo<9x6Kl&D_gs_ao^iQ4oxi~b?#CGR+99H$FG$)!$LyX@lo?JCBp&7*YB zvE>Zo*ba*07_gZ&P(Dc%VY6L%i9~Eq!;yyQ|%E)caOQzbCQeV)#Up;KBkq} zdeO198a*keh(J7wzcyZ3Fy*)nc-rcA0M%y#ZR&ppC_SFnQD=6|t8~v{GG*CL1C`f# zU^98m(Lcn_OXfG0*8mikqd>_7r>kB5{Ai6 zJjIUV;H<73rF)JYlw~s-6vqtM%yB%P{-Gxbrk-_k2`XO$LCKCzS9?8<3mK<2kJ3HI z6_jPO3KYkdk2J@zid~4EmmD0F$F$@05m0iT)79=ccDZjg4!^Y=rF)K9%CgCU;nGgdrhO~B#%EEUJZ_kcdK4GP|VZub?UQ zacb~6qYUFV`kc|68V;I+JbJg!8NJ5mjQ(goe({R=?p1X@W7kb9u&+G^rTci-QWoEq zbL|)$0XB0C4yJ#Iy_YJyqSpnW;(rR1Jm++^%U}GuDa$yu9Ho1X^C`<_5vVySgU#%> z%jh3s=OtWg921~8?gC2oak|p)8y6V02uA&Ezpd|4<~Hl;EH| zW*nzmK*{Y+S9`rYE@hnBJWBT*E0kqZ1;ugjwC3{ILjMpuFNxQUW5sd$v*Xy4dgWE^ z5yuVew@)zcyS1-2JC1UrJ!m}~yy%|Ou#a`$hr>5)4l1DHO227)V;+>`s86}r9|a|Q z{>5Hrja_FpS+FPdi=6%_sAIh4uhxDaDES4b9Qx1H#NWRt!Y{su#dSaLE9vV+^S&BA zQ~-!TelmYEzj?4qJgVDaz196MsD6I}Rqr{)-)kTBamT67d+n2H^P+gCpvT{9Pxl-g zRPZk!XZ^!`Uj;@!V|!Ww``UFb-N(C}viPn96>so~=8nZR*oCrl61EJEj>Sr^!4a3? z=#r73xT#(K;(OXy{MK@m?m14QESs62I4%U6+0*j$53%!-cN{a0(}keqQm3okaqP0E zt!A8Bj?z8HMAkSaL2(=hHnXRVqJJn7P7<419*dh;-rfKuy{K0n)gEzl_l)+abI<4j zaT?5Tv7WJ6s(-d$#y?;7oz^Kft-m#l<3;apwS1EA#K<`!5TA5k z1b=bqi!NycrK?^3HuPF?=y~vJa9u3!8e=SdyeQA((c`ZbgUtsAq4WL1X2#`pP+a19 znuC39o}~Lc6)DSS2~?g|gU#gW3HpcFc`3kaG)f4H%jKZtYNx9`%2VvM;)wIRa+K~l zPNXcGDWEvc1)Dj}XV5>y&Px>z;#hK=eg#UNb-LQ?aV#@VZ62k2j_WAPrgge;Oo7dC z97_LCB%Fj>7{>&tJnjNY_Hnw}9mg(v(R9YCWCqLqE1y1E$WDBMSjXn)0bNc7t}!=~6R*jAev4<9hXcoEH+1_NGwD>S2>jDW#USBx9MJV? z&4Hn&{C$_Y*>`7QnS2|8j5h)7$o#Sp?Zen^pA3CZlvY(YQX z`GvaSxEIUZru<6bSO@IL{NkW}7~30(YmN?(9KQynT&{Jl*Br-tpHXN+KVFX14aX#w zxlIblJZ=ScB##@>K8)>*WWZq}l%(dg4oG=Y>s;@0EKu`x-v*y{)_eon8X@<2(8KH; zLST-f!^D3AnA04F3>Di;K(>1i$hL*9LDm~iKGz^w4+qx#PUx+!L25D2!9RUmI6{2i z3Uqy1*C4~M${#E0W~`=TnS2|7jCTpJBlQ$R`!KdM5 zkWJ{vTTj#t#~hZqjan!iYk?i9r#@&O=Jr|=nqyRRS^%Uh);ia_9QFH}hpU#~Lf?tL zyzi2--&4i)U&Pi{-`@4i55ET){Ep!#|Jb{NuF>bC`v#1js+Oqir{A6zaT(L0btpvkl)@R|$gy zp3|U*@niP#ZQisrP#>1eyLa*cc zK4>2{Mqm(l&MpzfCpf_oQ z;g~ucHHJU>Jc!GX0c_;r z_de~J=)>R;DO z^*+4E!=<^BEZ)wUHvOV|wwp3|em}J9i*|if@8f#>-fqWQuA0>2j4|_T)bKfj)S$X* z)$jC*KtI*el04V2-QnG*jF?}kYUcD;1M2#ze#ce3-_lCrX+pce;l=9kIbGEuhzU7} zRfW1w8M>vvssWb);L;CVdL7sOeQ#Xc4m>U#Gk+*}3;_>E^}C=VFu>xmiTz7%V?(-h zQeEI9{mJ3#ulSp@_zE=y>%F?fR*gOfqR#nF-~?WoTf+l?-pG7$Suf9yZKvH$cu zuJrfbT6Ft5xci8~8@sEv_Faj=v+tm0{r8==$Fv#T%ot@*D<664#j97{TdeM#Q;s>K zyTa@cS*c=2$lUf0kn&HhS9LF*(!F!ZV)OPkzB9CX8GKgYcdR_mxf*}RW1SKDn+`p! z4g35ZRjJ~W#ou3ml=rn>D9#o1xt};USbjG`x8uwnjbjyja}NJHmML3-)cxWty0&nf zW3kQ%)2HjMkl_+$UK11a}u-P~)%wD!^-zvq8+r8 zKXVpCpGHcs&Ey`1-_WZlnS$fRV*7ZpjWO(J9xYnl!jrivhaR?g#?P-* z$*A~!4@mh)>n4X)1#*}`f9||?0P2XjCCBw1&YTA`(9=N5zTD0czUo5BTMBv_aWHpJ zULdx2fgGzNv5qoBZzJ!@uV6%ZGlse5@V;Za?ncDtbIs8JyAg7p1D)@krWlUtZ)5+w zQaG)}GUZVqIhuF2e^c?7C}d8^(3j`E{2g6p{+O>l9&YY6_)U242uVT=x7;G(<9t_g z6#;)%(0r+ax7Es1{U(Qvw*MQ&H276q1(RFS7Xd{-)pf~q{w>GMG#>D_dl-19jEeezz=Q9w7( z`u_AW%ABRj{;>sdwfFw?UbHpBxXN$A{7jM%latg6F z?OR|TOrie+H4hf*hvUsyw!)VY=5Y`7q&Aq?BDV|gAdt573el6$bBLEQef<`R^%Wp% zVd`x%Zl?md{(fx3Z6DU#nq$E+1#&R&*1|a;clNacZH%x+RzbJNC3_b*YW}~tQ#gKW zrEm)LC{{h*?;0wOb%_>bJLLjBx~d+y^Uz|!_`1J^;(qfWZ+;JSEmB=C>l||5y{tcD zi(B6Tns^&xXy0!=NB$95O%CH3kelA~CE2QdVpIB&=imGl0yy`Mk7K zApczsTROmPuf?r=1@0U3q#RJkaH)=fK{o<+wEq zeS7CLGuCm~jW8|E&=cBVIHcEOtaaZ({QW4J)hff=xZ!)kw-Bv{9L$v6G-_`>*o0F z%|jG@_~oJ5@|A#Y=OM8H$2j<7+@As|U16i{Uf(=vpRJfWzq<8#OnH|n<(*OKv6eB~ zOKv{zgCBlJejdg`pXhpTsguyFIJbI5xb(&{r5Z?XxF_7oS0noH^OdlCt@QDgLBBEG z_fNI24aS##j_fF(=g7+Mgiqf4w(gs>*T%YFjn<(bBiyH_L(j5UA*NKO7q|Bu1HiNVZ0D`7LjzLL;eUE70O6o2e^XBm7*v8Tu?1CWN)m8cYr1TZ}P3R9CsS zP^IWO=xz-UsFLf4{$h^}ta9goOjVUTHz*(_+po$!*N*}@CuRFqsf@1clRlJv>AgKN ze~kG^=9SBVoX^I8EbIRcB(E#~Df{5bd&Pb=kX#-Ga$i016Pb_hE)w650(qVBE|9(l zcByjTk9;nWG2))_c-$L5-&}R@KIz2oW4|eIZ#;^1M(DQ@dPo~g-kiZzDqSP}_rNlx z2B`hDx);A@KmIb7zpc>2ZjUYF6m_apME8FJmMLcfU4Qzzs;_ljRecX)w4VDhbJg)! zXM{0Mho0016Ju^@6`nZ~AD3d8vIyw)X}{w?>*Ler@I`gTz~fNKy)7}g*T>N9^RLJ# z;hPvK=s@TEo`cS~)7l<~Jv~g?j2kZd!o5JYtsGJ19{)}Sa+`C3%)zgKl()2gJo;k$ z8M^OzK-O!s_Tg8DX>cx7hdVv-(QgjAU5DY?DwWkS9)e}c(Li^MtaFgMBzzS(2VqX> zqXBJCgLi>kw_DqXp$%oU-q!x@^Nr-@`Mp+C zr@K6S$Zt3FFgt?m<8w%riXSZgcLFK9wGKZc@Vl_b@{HO29lihX_cIfABW@m`JKA9U zBoD1pDfnV;E3r%&4%E5MJ3mO!kM-NH>n$H?==S+RB7}bN$3ACs8S*u)qkhi7Ja(UV z9t++FKh`{E>&zv`WFp!c;W^bb=r|O*Vq%OOF1~}n_+hf#bDWIjSRi>l2BiE&>wcff zt?2--jKwPpJ?wgD8RO7+@f`)mHOCQk!s{>~d1ZiYQjs0-D$0zs0z0mFbFgaQL1&@+EWTEGb zPb_oH4+C;+kJ4=~2U4!odVtRe%m_sNu6t*#22am@Ym<*S>_(W6X6W|3>r8{66NKL` zAf*p%%uiL%u2Xt;F1@VfE{NO*+)RJH-RCbt?r+^@izCM8Zp-Ih=ypD`C*c^0Kl=D3 zkn)n&F%O)9dEh=X57hktzI^7J2H1@-wmH!4eI)+nDiy*X{ag&BT&8u~&#D4`Hd=l* z`}k4QG2iHZ|Em4GZ~XYwaQOp>$(jpH{_3#K2;-Uz-LByT{DttxxE28^H)$O;oHxJ4 z=*yblR$4xi(Czsxax!XE_gRKzN`D}I+WX0Nj}PO^Amd^j^w8@D0NuR!jP)M+FEG~5nwFe!p09;&k99VJvBn?!zW_+NRO`Rc`iojO&u|a1 z&M%jv-?q->Oimv0aAHn2QfIIb0(m562#+){c$#qgE|BZr0#a_mI<~dXABuhmE)v+;-w!4dd%&qrN*M?;+jVH`?~gX8f=B)ilYxZuC{bDH?8sT;oOvlZpR&( zEwN_4BDq%3lbX*)AZ1j8tjhs8zg(-^{v7D?)}MJ)Tc3H%TMO=OeCF{**E3eewgSG4 z@LFsm^sU;kuh!0&cw#`>a_31rj<%=8{`0`+j5#9r_2}D32^O-@OGp;7FGm;Z z8~|wx0~x0RGLF<7%vH!2m#Z(3wkHDF_5~o@)}syOWFTW+0p$L$8p!SZ&ex|s)Vj{e zoBhVH&ItQyf*#fe!#gz>yfnvKuuMq;U4K2ho`LhdTU(v)(WiC3XY1s^n08wnivA1V z6Lh__XXXnG#t9kDf)HckpwzlljUY0!v2`IX(Dr+jVN!vEe=u+Zy3#C=uvewPD}*TqfMJ8k0chQn7tI zCY-MYGJg}lsq=|8+8I~yq4FV}sT>mM9V^L`tB zA^h6cKW2PZdpNR2)Rj!r^Ej*Byg(l;eTaO&;xIL~vc(c{Kad zM6@-+ z;4H;}gr=_YVWI7LL+wF9%Yt)OuC0Xlbx> z3BD61pDV=SCs5Ie-yhs}&vK223-g_VZXdg1H)B7$N%+jbGUa@rn{WMG%wV7AV&?w> z{=UF-F$a2E&M};eSAmxiz9(}F^qe-BeJggm#2LIpFbSRUW;I4&rykds1}1e*x+P?r z@j#B#kFn-*+91YhUQ?8Z}d?; zzB5|Jw;47g>?;A?9$&Q*wWZ^D=1$@M9*|tjJ@>wfM`3*roPK=nx$m}o?S-Dy&iCb2 zHK^EU=em8Lg=NZIpzEuR=UgKj;FsTH@_F9h9MhW6#t3s3ho001!y$POj%kRQKCik< z;#~=(K4X>Wi_wO<-!o>L$;0Q~+HQ|;=6Wx5d%up}i(Kozzl3GV89?pp3_Lq`tM}Qt ziI4gFio$M$z8az1=T^xiYEt*T6w8zqK-ZVvZ!3KE+ehfrXTLQ$-e&pU4n6Mn-g4fM zybs3(?dvKmQ?3WPzJ2ECy2s(unxk#qoFg=#tr4Ch%!6*v*|Gb@R|=TXevid6WfqXN z(5TxKx{h2=P6dvQSq}&1eVwrLguuu+af7v<*Cg%~XGeUo}pxZG! zKfraQ?!O0?DK)@C{#NeCU)u7w$;V#`eMfZv_h^3)8Gi+?7pl{(bLy94&YQ=htr5mN z6S`gFxd$Z91Td$4pNM6S?`c3ccI$JYW^%AT7fM;a)Fnw<@zv? zTr)uC@oSyr7;r0)dIrdt%5)!o&l5zREWDpIVVx1?I1U|$EmzDqMt)qSVn33+9)xAe zNkIC?TyP=o13qlu2i#7d)?Cn*ZgN-ts}@c?CJl!^%k|olyWoF{|2U95BfzZY^iR#P zVXch!LLl4R0CYL)$GOvgWcs7fi1gbb#Eyqgqd4J9ZgIJ3bQ_1acgM=r^tFVB~-4I!H+$ z+kmW{y+9uGW;`s<5uO9&w$(qC=Lt^&QXlz<)b(jV>g$2rZ{Guw<0smF!_P$D1tiC1 z>*SfqH-Kz+B)HPwTpeqnW94?_$=x43_ez*k=B)gwmYlNBKG5xXF8mnIRdin8+#uY0 z!ba}qy)%7%&Q+r5Yk%h|%^u&(Ujlj(NHOD?eH`ufF1&7=NY zp~kw`nIi8t?sJhF``YT^#(eICz7t3>++t5i?$W^EFJ<}2wB+?uAh|SP9ov6hw=Gmx zlQ-$Q^4y0tbu|a;jIgfeLAUEF`YW9OJt;nq!7}A6pqme$_fc%1zjmI7Zuj`-IPHcW zrUQt{MRKF~i~&>H?-ANI9msZ3pv$MU^M!bS-^1oQYvMEDV%cG?9uAy4_Cjx!|EHw?43Pekz?Am+ zGLSX(p62q2wjYi*lu3G9``l{UZ(8zVY9R)j5!ONzbel`$Y4nRf#ygD5kkMMN8rps7 z(8dAo_ZTJ=IF1hA48KFl)DS%1(D!_w?=Ad$-2u%D$CmG!osPU4;W7CU=qp)F5CX?D z(Ou@abcM`)F^$O{4eoI)+_S+wj^&_pe+gneYc>&VP`oD;2J-lrLp$mTAnPcC_VkfM z|EgGgJP)Ki(N)$pl{C1|@(<}IeIHjUeXj=c`2JHM`~EGEeRnBqaK8g^8jyYe7RdeJ z=5pEYmq2pd2IL%@1M(e$gTR69Cji-g4v^<8R|A;?bN}73=J({YsM`)cV`293P1x25 zxn!a5(FT+IU?0gxwzpss==2g!4*)4&f{pDL0m=P#pqI0KOq%)J=jXf-+ZrL~70~Tt zRJfn;3Tn*sZBP-!K~7tAJeR^o&TE0>{41c#xuWDkybt3^?->A}+pc{coPFMh@kMML z2i8IaeH&pd#h^#E!Hh$^N@5CYOjQcs959YJ$^8d9-hur^ABAv3dDfo=c(@~9?O*nq&IlC{G$?+i| z+dl~uu7%${v;sZ~y?cnf*v|$JFXm<=^caw0c&Xpwd=;GNujx0!F%ES77x=8DHKw z4y2r|bv$=D9-kYZZ$39(2tR(G8?W~8Vb0e=w{sAD9-r4gC!Br*r2IkaRg-%!o!nTb z>YB!@gPZHs@RbwX^M`L1IDcq-5j@6MsPTNV^OZ>atzw?3{ zSM9EsaTRJD>gr(BRW0gj=)ddA{SGbXo_^S86WI5R48cP9CCokC=^Q4fJ3~5q<+(6*ya#H>mxu3KH$lQEs2j1cZtbJ4NQ_TSKd$2bEnVUO-4*Oa3@|Q8!TF-Y>Svt?z*zQ2tZGX02$Pa6j=l6Bs!kCu>SqI@) zd_Jp%of7&;#yRt$pn`4-^F=9y%pcd&C;wQpJicy&e;%Lt$6Ry&C;}Ib6aUzbwo2G2 z)H#;4jn(b=j_?HZ=$4z~zfy~R5K*qUD>-5QZ(prB3$e6clo&FiCxz-wMoeyt$6?NCa^WnDI zS6ThBZt3Sh*=_%Y`nB8HI_sEqyBFLz1`D@c;m$FkKjJeO76*i2>W+HiGpXOuCxVU| zK(j0CBZu`Ir_iS|PBEZc&l*$M&IreaQV5M-Q|p|2Sv#A-llve4I7gA=ZrC{Q@Q>|i zD?uC->YVFntA>sB!FrzsLl(Rmp@;EjB!U=hTiT~x$fsRK+v2D@@-O{Z>itk4^Y9sv z_4=KEN`Br3vfj_zEBU+~$a;SSNS?n2viqCUvrd@z;jdv|0oDOICJn$^Acc1F;<7<5Q@Dq##Fp+PnCslKA8|d!-Ea1TCeQR~_7RTLF8H^P z2YtYW^~68c6ZzD_Mxo9cp>48m$Ngg`^ayxO1BUTuB;{iZ`PdRZwg}>8e=kn0}gJnrh(+jm~Vc7EpzpT};WhuC@{Kc)^hg9~#x{LfN{TY+1lQ|vm-b(K6P zigi6gx8?KJ;e7bD>u?op%s>B39j0KTc##=`sl(HOth1|t%-Q#}K3nH$9+2yo0J%N=vJM>}^EC;`*r#fp92j3* z>$d}$gVkCmAGZ$m`%t}eUCH+S&vjgH&r2P#dFMCpvR|L3#zOGP8fyk}uABUq!gF1! zOmY}06^ui-<@46qR`|DTOo5ACV|`$wcx$Xyw=;8H7W}56O_&X}gu&IerG45tKJBu` zhOuRhJPl-xy#i$J`gW5Vnh0c#ou&0rI#1()tg(}U+@5|}W8}$v?Ey0OF7QL092g%t zQ=bWB9xl*2`Rs45OYCUhT*r2PbI|9p+jE<(7xH83a6Py%m%%rs4mSb|&2{mfl4qxf zu1Dy$eBL^Y!mnM2&9K>ZxC%Ckw+>Ufotf)W;AhWuL2$Kg37>X3A6rUqtIDO0ZUC|l z?*TG*+kvdJ^6rwCF+l3?YW)+f_eI~VL;7VMk|*=^43M$EsC9B+eB{jaBY@09oz}_6 ztwW!C;0t%61`FH+UyXG}$Zsw5kT#g(TC^9&6o2G-3YQ_XwQk=7&(T*K_rNRv+TxQj zhM-S$du=(EhI@;@6p%dPz=Zbsb0Bm5J0REnQQLc?4W&wNYtBD#HJtZ-pv~}S?-O5C z=f1;n+3ksq<5m5)*3YdZ`wPzukU0%jN<1-*VW8?K>!t%K=K&e(H?(~VkU4l0$Z^}N z^|R0?>o2PNHaW1rcas1wJ7&f}AM3Hs2xCe^k81iBjvJxpw88Lmh6$GlFyzQ`5=eO!$aWuU``?C$ zeGicK_lAnz3ENVy(0!RW2KhYqRKBbIaTJP;<5-7&j4-z8(6icLVoTQwznsPdFgILc zJaMGd#kYVQi&I8P4Cex=pQ*PwA4vUjAp2dc^+NTMW_;GYLsKt1J)9Xs4tm_;96o4( zaz=~K89>TKS~vHR?C;FYc^f`E_?f-ko*cP9EJt5PIOnW|zET@ZUSkIfuON`Nu+}qU z2dEhMGhds4ls;o*zdHs!J823cz_OU2&1omhAvHwLt%1v6w^K;|z z-0}?jx#flQS>QA0)t1k-&}-dZTVjqKIY6bgkGHhX4~1Vk+6LrjJB{yOp*8_2rna5qB!AH_31*L#e6tO=jbc6XPo4V* zr4X_`=N-=B9IHzB<5=^L=XvxQhK=WN{9`-XW@;PHTQksu;LrpN|}YQ=#AMsomZ2m{`N~5y7@bIDS#+X>Bmvqcy@K z2~2}GIbAYH;&~Lvc25Ict~KRLYZ`kh9;Y0Y|0`MC&x`&Ju7gX}U_9&D`?8*)9utaPJQZ)ts^-uC-I=H%BvwyPRkzH~5tr(pnor=f4>?g8wDp)RqaXy>ea=Xn)) z1csKXp^cqYbyFwR^H$IMJPO!N&27ZCM!3Ih@rfxmN_eD!F&)XK?BO71uuA)jqqzrt{kB8MuY? zS0JyeEq`mF+vi+q^c~gxKLn&aZv5HjKlIhs`Oil!F>+rILAPT}o*?lAr^zuZ3q5Ci zVwwB$VL*=kQM&ErK+2U`$C&%RuVoed*<)_&`|97rmE*Am`ZU)|%XvfaMDZI1hQXKP zuuk*dp!GsrYCmp2mk8P#A(tq0n@j2>;iA5be1Q+STnroY^i3djzwfiJA`hSMvv0I~ zZib%JB+PynnU4B^KlXhLmMNzIU0(&hZxi|_{B`i!&djs3u&oiEf5f2M$BN)s+5h7j z!@%@tnH!1@l66bR$eeU9kX*L{Sy%4?Dc!J*HwOD1bi2Pm4ApyEV&Fbd2i<NK$EBa%#vJGDV?q-C+RDG#2R3=Q^1Nd!^i4pDse#lu z;gJJoweL?46OOmS#&$o{Z3=O!`=tGx8qn4VIn9G^b5i4lQy3UJT$X3l38x)Ea{2`5 z9b^3RLEf=*YdbjEc{FvZK5gO1V`nAw^_sNFTXcf>&jM*n0^^!@bG>j)1G(-ftYdx7 z)Z3Um+I6`cer!%{>4x(o7U#{-?YfK|A)M8b!Yc*cnJBhfCkf{lfaDy(I&!{9Z)2W2 z@cyoR`|AOl>(4qD*h^n+oC^#Ow8YGLa58lJTp*|WkDe~au@Eq< zZ5IPsUsnT}qZ_sTS3t@$S`U;KsnYlLZ_nw^gS|Dl3Vywx)0!IG=;6mP*$myT!T4F= zbEf3rMj+*TT0al#C^4X$BmK^i{ysG_xf3{a@Lu>FY-5D{=0i_ugQ=O&Il@5!Q<_KD zv&A+CNM4(PY``o_&%1+X z!fu3q8lZ=@!Ni+97xzZ-M?VXIl!aQyZ&IJpw{!45zu)9uOP>XPd)w5*7R&!O=n>0* z>MQWsApQq&8FCbme9ZlULcdcUDguWB_udSLS!iQ~Ifz2H$J{v&erJo%>!ZS9GtlMG zxAa2ffWK&$_e*}f=1iVA{XGEIXKYYqvwnNSAo<66MLzNjpg|M zM|U-{xreG=*;Dln4B=g%{NIaoe_OIT|F#3S~;-)Rm6OYqy) z=c;ZY_njVT_ctnC+tNH6-YaDKC%~j zqc)hF6*l!; zi#A4B&mriMpjb@qB5%O&pM}$fK+06u+bW9aXnR zFVCydKt(rI5g5libCH&6#`%cbc1(WTZe3KjC%c5q@0ClNA$e`g`J%hWc<&FDAb#sw z%#6)OtTV!~*$jOe2L)orCb@B-3OymQEx|J7HlSO_`dE9D=Nzo08~k~mGc~?Kup4pD zf1u;%L|rky(pv_q;PcY=AAyvd*8BfK^n-z}Z~ab~TMCT(D)MM!uHES2$44BRg~)BO#D|FWNtAg96IaNoBx+TeE#7VFs* z2Rb>2?X0RSyP0Qpd${s^uBZ(4VsUle5+2#V4OAKMCa1bLh2xOF4s>H038eiNAmu@= z%Wnl;+4@-F{^syqt?h%#klXR9Z&N*v6_@1C5l8i2dQ{^JYJAg+`0nCM`0nCXcb@oZ zz<(~=U5>oeSEzb?$1K#=cNhQv)~=o3yzEspk~F0!siSv9*s1El@Fpy;zJ zM1QiE=(qQ-c0bcv17y2hT0gU2wfh;`-+;Ua^$#G|U)f)_TMDFpXO*=3$^g+f0y%xG z94PvxNy>xD_0Wx1VYhBKXviWtc=fR^2)OAtM z!t-FE`SC#6Sq~huvEX2Y->I7kJ{KLJt? zObDt8jU#c6J4%geu2nTFN2|VT4pQZTvy?eXn`do?1@fOyYY1XItY>XHsxz;wvnA-u z2*1m+8hX&mYxqpzoCGrVnAS7E?1{4O?NcRxH$)`o_W&8^A*V}TjsQ}B3P{-0|rj@Jt-4qTlwKb%8WqfaCJ?#~A3IUcwmrtV^26V9oN1%uFY z=g4yR*%I%UfsCum`J!Kcfy8?=koF#P#6I96$w#tL^mRb4zYBd+exQ8>+-skM@~?f| z->#mk%x_l*%8OA?$L*`9fV=*0`_@;5f=fd<&X2}%erWSq?&rKIpU-*M_Cj9A^iX4P zvNf`)Gk)tGpZDIHe{I2hF&|sd)(H38ZP4xGd+5$;6;4Q8SKK0TH3J#v>J_5@0!Uqs z=f|}=ZdZ-&xpZ{nI5o8CaQB#eae?hWoEaUF?sl_5AM>ATqHJpyg4XKb8DQs~PF zV@pG?WRXBjEjafH?-($INcuP0fE;TZd2HQx6sTpqe#xC~k?`i&2Wem{_0U(tF_>%$+E z^+y1?UTP}YQd9a^a$bQra*U&mbLz^zEisU~L*2@Aa(%UmJR%&jK*|SNe|DYdZve@$ zYD~|iV;V0|!&0ZZxTHT%pyOG{e%sYi*ta~*J!=ZFQq1LNz3}iCuH8a3dnXhJ}LVBKJQoqN4gmugc_D60H&@pQw`ZB^})-33beaw1Ucqf6Z z`mQ}<9w7A+ zv}KI_^|t02=Q{(fKf@@hLVWGKKi%GTW`7uqzKw9qr$bNifDAG7n)9}BP6BC*YCQ`K zzAo$jxkKW+>rIJ!Es$~c{+o>XU?BA$11V2vJ-AESzW`+4uLIdmj>C<4$6-?kj}@qc z$pa2t9qg|@IFHPMFC%=;aS8OSRUZ*G$c-ffWNZno2Lppt^h1er#2(4VuYf!jp8m1q z>l;AkVbxw)|1gmH&7VrU>dz!!wLseCcya!|<3*rbk?I!sy5ig+>z1^vbI(_%?$fI# z^jbQh@p4>S&sU?HuTVo)E^yCT-tjqS$qYn}LqQe7wfGoZi;ryXtellNZ+STXyybtg zjj825;AVs+Tv?5|ojWhY)N*E+j7d~uYWN^G?_prNzpTqv4|2CzG-!}}cuWJCq3UpMH5TVq!<*~Wz?I`wuQl};r%T;& zGWA{PJbVk{*NvTqxW!z{J_Ie9UaAyDtOD`|k53jt7C% z4d=1~oaYVhIOqMv!a7`mK8TLt@?Z~A#m`!G3px4^MvmecVZa312g#j&jso?|AU$EYk<^c z--NKXs9}wVsew)7@L8hI`Z49WzC8@r2;TH{Y$suUw_ZuKB(izWyF}&O4^S zezkl!_)hJorZ#p};ih7BL~|E)_{t(|+gXiV-$_*lDwKT{{qOewxAVg}gY)4ds0|~0 zt5Ep}Jnv#1+k*d;T1;xps2ca&C9_xT(LjxRk30U!AoqTJSldIN%K9vjQ?7&UI3L75 z8>B*S$+}6qq~0gJEpx_nAluZxEB5I?>I2`C^$!6_?+GC7bKe*J(hml?_w5%0Y0qeV z{ciF7ERg5RF95l|{O{8K2Or{XC9wYj$f@#4AlIMvk;HN(kn1-Cc|6$-ba&^ErCrrQ zJ(nKTcqZnVv)p~~Ck5tpXC(H)QQg(3{oIS-m~f7mhPFn?DFS_#b&j7atx-ATom>ZY zu5p`h0#cvdMf8~^qUV5=;%=e`F!EK6N8+>Qrq-V|yXWZhTjs`!VpY*N7IUYZo0`I_v5 zL11o%tUGO%CmH=trr1jUde)V~HQwsd90CN2sT7Uln(WlRm_2&b* zzJH_mt_D*7iPqO?UB=<`*2hzQFY)dI`^e!E9jX7vzgq-I=&oGG)b9{{~s!^$L z3AYcfl$?A9Bv(A=Hl=&#lEvl|7(N5Ffqt#$+-%*{?sm)nZs=jxdyD_@V)2=~x<*Ct zN54M=lHWsG_v3N=LG9-;4{ePw4-27(wZY^ec8&0GfN^jlkG}%B{vE9s`VMWHTnc>d<`7#Z@rQuUQsMF= zAmiT!q`ar?_B-6t%I&$f++ZOuH zNW<9n=Vb}n8gch&=+m^p)P-}M#GY=hQ3>t)Y%FvAY#_P%)o0Pc;L*l@($;!2=Z!^Q zMwr9t(8JnbcxJB`o+%(X#ef`p2N**Pv@HX&{r7ZCe+4q`cl7!~wU%To1?qc~ha3CK zK(}ivdqa&1;*T7@2c+Dub-R{|4r!?+Gmnjh%?N$eLAQP65~v;g(Z{1eN?Pl-k47IK z3CqVyA0N?M5C{I~V+@cI(z>};bw91kdva8s_1i_C1&)y>&OV3c$5{LIrEYpoZ#@<#;wc`$a zOcC^HgfTTjw_{3em6*an#*}-hMr9EnIS03h?e!N#e*)_m({^BcV%qyJh-vuYEiv)h zW-@d;rf61T%Kb_BCxPl^;e5g?V*A6ZqQ3`Z+`ro<`U13NjCTRs6Jr+s3)NOf?WPt> z>RMvtxH-`67~^kBj6opzr*~k#dtErc@n^C9WV`6MU>#$61lXRK9)Z7(axc{0#I((d zX}3>I$+sn@5Rfrtch#s2;v?tIf7RpkH_?BBb&P2Xusty~k3aA+rO>Alj?*US_Bf?K zkeDJs#-xBb#7EA~yJCAUC;IQ+mzZ_~+Y?hm{eh2Z3Hmg`m{vizW6FLcF~xw4$pHp; z3+M3P#a8j5=N_zV>O|0Oy-=R7Hy1h?hZlEy2Yb_3sK+oC7!Y+eVqVr%k?$e6+)GF>Zz~;@vmkMy0II(aZ3hir! z>}DT~qE90n+a~JVi6BfLc}2>EUqWLL7%UdsWnCqPSjk|w{Ubnbo(6hj`f`Do$|oK8 znCj4{5ymtfdc=-tppHpn5UBcz?fm`{Q?ycIS`FmpDWEr|LhonE{tIF%IU+wM2YOlu zX~rorT6kxHw1t7mp~5*cTx`dW52nU^E^j?AHX-;t4A*88*@o!`#=ylDtNZyJj8 z-xC;N-ubV6J+_Cj>iY*K&(7rhm};Th*L;!RAP)R7hJjpWOj@^n%)$1?he7&cnwEQf zxyJz?U%}sspD-}4eLSswZr1ucK#s=;S_g-`-^|>(9}dBzTH@u@>Oi-@>l@xI9OA&3 z=JQh^r6+9kJyN%^<8MHJ-niLziN`1FW;t{_$2s(u(tSP+q-0ur^AZsLwcEkkGc~+Lc0c5_Lf!t;jkTuZl zImu^#AoWXttd%RZ{YI_t1oF|u_kirXCw%c-y;A$M$6*)ZuybPSu+K3qG1KR8==ONT zeVVTlRuNw~J_#UivyAGSsug$^e#qaJaTkzz{aEY8x-UC#!^5pPo4ifOIwQDL&}GPowuzXF06~350~5v;x`SXzXUL)eSV}l6v4*4^wDk30#d#P zWFGA?o&KfP`LVgpM;jx|({kwcnC4#8+%!5bVPC;AeSRHCPFLx5%Yfv6vtEzyk6(fB zPX8J6ecl}~_kC%{f?t8}P8YJ9@g0k8jga?b=rL`ee{zdVZ*VaIOiq#IpPbO(w)zE- zZH_*v!R31bkhZT^W@bUkQvUX>Uk8gsCxHUCvFOMUDDQa%PUzG-N~ zIps7U^|>gJ9}ZhQ92nPj9}c0c_*1}`=JN=U zKDPlWuWP$K$JI{VPyASCgubKD?YS)tpUkUzeXxq^@ zk57*4X6Sa#WAGKiAN`&Hq(roi&s(P8oxGQ|JTWDo6+}-!oYv z&SUUR@s|ach~~2#NVyM4-#^xN+xOTLTgPwa0*)nfJriw=Fc0&f+Z;kW#h=m`2Zq6m zV{ikIV|2IX`7n^29@XpZF~}8K zgueE7&PD%8%l{haVc02L&p4v*W888Q(!W8BZXg!Z(~-r_&? zG2-7N{+3{watE+2|4rzt&G;?C@+(O(iGi z#~FlfpK}DeJ1SZ({r(+D=>{A9$n(N@&W_(9xuQm0(O9MCH4RV~G!Il~udG(5t{J4J ztglf=ZW^o(+v=!MJBO%2d;IUq1bUPx_pLgfH>yaVhojFr^f?}V)}zk}=yM|aoP<7) zK%YmV&&jygb5*N*J@S0hVXf_@#j5nymbcObI(1T=LWseAuS;d?XYJf~Sh#m4^X?@{ z{P@j7#_xXTxA(s9{F=`p2N$@dp_sL85o5greME_=rJ2S>gqFvKnvYm7C4)a;3gSXFbruf+m-#E50LcR&; z>$QPA$RWMl5zCE^O5Py6W@y_oAluypWLv`zzqHlrH(knSwx3@i&K$=&^kamar$gVQ z4Tf|4Cdaj;fwZZc9Tf(DaxB)iBY>=rV}UMr{oYBxcauIs?(O_uugUE;i{~!rX>x%W zp3#Kk>Iq;N9LZw`kWvF1+l>Ua#c$>r9p|?Y+ZbUUmqSl$gW;FG#c?gcTQSFI9&@$r z9w6I20Cf53+N|~&`;s#c%-Dy}j}da727RkG7|yvBj%x|uF8t!a@NL3zD%P>xA|S_p z8L+L~rpUd6W53nnxf6QYWu^9wefAE=wFK`(Zo!c}=4#tLKsUEQZ*KketL0~XvE2IY zYkoF!uJQ={87aoXHt6>Lm$=Jun`D6G9|1Do!SBgj907(AH`na|QfgL8%ufNipMMJE z7#Hh4%;&X!`{&GA9Ur5G*v5!k|IpLgU}`CQx8quZt8i`w&g6Thw%r6|yE}li?~mW^ z&%v+y?0kM>p{HFoEj1MTzT;Ywz%V$HN3pgY4J5y*z_$3UK;Ip$@eLNg%|86py}~a9 z%xE5W+#|N;Q%T3QWPu5AB#)`uwg?!&+GRj5 zzkxo_3vBru{PtS>%Fo63GAw?P`-EQ{82k_6@gk7Y12*#O2lVpu+dt-`?~ab|Dr{qf z`CAJ;tqt@`pV|8z*AiUqsFdb0SKICZvfTqfFF(Kh^=arhzscCf2>DHiZs#xY1IKNW z28O|rJo;(dDM0c&6X^2O$1lG*b{qNe+{$`)>=&_dJ}rX(qOY{XQ4D=J^j7of8V74| z&Ilye;16X!jR13+|GEdn_CByP+RlB@;U?-nAmjWI(2a9H=Q*q3yQ6io(ZiqdY=NEz zQcRtMe(bn<6d3%G@K^_=yaQyre`vdzNB!z#@@&n|dOy6&*|M4QusPVq2>H#2Zr4d} zt>ZRv{u9SyaAcmAY1`vK@_P#C=1)JXJ*dF-L+GpE$omOC@ALgv+s!e07C0Cw#zGu= zRvXNiq*9J+$pINx6xa!t?0-r9IM5@=2iGllNZLLGnWq?kCO4?C`&0ET`dJazynHL$VWNT8RWUu~^K-yP+bw)kbBx2mn5 zI;5hUM^Iau$1-hu97ujo0o&p?@w|@ni((ri%wG(;%`g5l$8C}VM!=Cg`f1xKK=L~i z=;h}(zFR*BKXraSzaaFq%cf;~o&R=RON6*icsu~4yapt{9olZL?FSavH#ejIj$Ydz zsP<;x-0g{(`)2h8t;Zzz3kPfP86l8)&-`5W%>;DL?HOPk`QWz)EKkDWH?JOU{X0qJMiW3tWTz^>4r0SybkE*Ss#ZBJ*PbFqW`^p{Xoqb2dW>AX&G`C zLr#p8Vqp{Xv^JPA4L;+zmI#n}j%?C%4|I-GM%xpxyJPwr8J`G{?K7~Kpj`rbH?{}H zFy35O|7+>vG9br#2arf(;`tHm}($ZsO_v^E%i!RH;< z5(Z{9k2{_d+jBs+dlBg6=l479NyBe({%^yXSljxpd#l+ZAn#@m}68st?}H z<7M8~dK1HP^ksxOUIl%pHkceow>YjP4a{n;JN_WH8raxwB+#27|w|o9oLcu(v|}{F9^qe+I9+%`@xyOwz#J|z}@6_r^R!R56|dU z;h6wBFA0wwKuQg4Zf=2Ietz}aJnxI;wotyA-xT^b!edbedR7}uzLT#yt|bPfEv$6~ zOl$sMep%+hk*`Rb*+AxhE|Br<1af>o(7HLVEp*Mf{2Pe*UwqBUv1AS&fiEM)Sl9wR zs|_Xxp+7mUCHtDhmjp7tC@_OOFvii_q|MKP+-}()rOo3&#{LwL?aW;7JJy#R_^~cD z{yf&D;LiwS-wHje4JP);pB>i{2ab*x0Tg(95sFXO7N& z4t|xfe15gi<8%x${8E2&Ts;fS{#AI))wV@Iwp#{t`RRLm{d_)~N}%ud-p4cfTW|5( z1bvOmriEYZ9pRS&Mt2F1AH1z?K=RuPBtLmR#{Kqx-g7Pc8}}vOZ2!3X)pUDpjJXTA zu7y7%ocp#wPiuo2ljuhhPZCI5=wrR#1F7d=4}U0az7M2r*&bjTksq3$*PfAluymq}}lI z+eb?-Z$Cf3aUo~sCWL;Ba7?B_PiuqWoc%=jIG+l?7%&3<nGmmKyu#!bh-PyudD%m`+fg~eJ;T|BizT9Ll0{M*R#)f$qCIDvq=~_^BV_JkHgM= zEe)Ofk*dJh>YO%}Ngpo&+3)&t*|u+Y*~j((xy>0phA7SfUje#fp+DR5J6C+9>5J`i z?VYQbE5@}A{)~`g(Uq;w6;r*1cMixn<9#GIDIoPQFp9h|woO3Vp6VrSJ_RzLU&6MO ztALyX?$>>q`N!{gv;!>nI<_%Fehtvm+F=hvRt_2$wm%1qgIp5W#t8i+p{KQBUmSyU z95q8!LdSBZw%r6|yE}kw@hkaG$N7b@jS=z-Lr-gi;g=XJ{L;V}IFd&{Z94_Xc4q?H z;V& zXU0#9LZzBD6u^RB)@gQw)oY?JI*hHZH#byqtNaAMGq1# z319{s$>V|1VtWlpemj6|@!Rw{_~k5q>gv|~oUy_s0!)J=c`O?vw#R|w_Y|-#evRnc zZ!G<6W_%Oa#)v!r`S8meEL_wfIEI2FdDLs$B|!390Bnn2$>NUl3t<~0?)(Sc9^b^F z!Y>UBfg^eR14uapHu9?nw#9GF=iryI_+@?gMGq4$31UciJO!kD3M9W`tZR$kv}-z^ zzZkYL;?952?fm5q7cNd6>QD37F-~kXSVw*%fo<{I`8oInm*n$vpxgY?t<_C1@g-1VaI|aygX9B(a z{Elyvm$si@A($)hua_H{i zc&dz3Zi~W4Vdprdfz%_gb6nylNN&?W>I%r*hk?$qlIJsk+-Br)`Z^2fjuX%w zr{ncF0o`!|y5j`&j+5Vaf5O-Q?~N1dpL`p^!3g!0(6?%XssH3jI?`cL3S8=0wTgIw0G;17xoM0d(`TpJ%S8-|)redB3^e&&FJk(|q_d!d$F^ zp4JA&LcY!^5_<$lJ$JIMG3d-i3YbG4xNhBaY5NY4+b=*{ws{E1T&x4K-Tv+bjaI zmX-nA%Fo!FbbhSo^W7K^pv~0xO!zaxTr7m1)drJ`*qIW05=j1GAmhsbnG1Cm=Ej+_ zuJ{aTdo+;SKLuo)Pl3!uvF>Ak^<8rF7poCd-y!s4gubUiPiupTEjmm1CV;f1fg!}h zIClVPtAWj(?}4-%?t^_kTiyBvx%<^J;~{^QXo-jQAaE}1v zXUn=5&kwBguX2&CMow>9zkT?ZuK(~qN{jl7w+^&Z}gV-xf=kYaeJzakuRz&N-v z&r`K+5s>Yc0p0xX=bd>IZvns0`Ods3wl%{1#-PWv!SD>9Cma&M&{t)76_D#6(E4>z ziRC6B`3AZNRQK}{E8b<&GtftM3SoJ6v6?*}-;t{8hVMxgM+3b&t6o^wh3k43!Orhb z6~_XDx~V~PD%GHw{nenlergcf1VJj$y9?U%!}~+}sAB5$aje<4SoI#qZA-8%{$_T? zw#91DxL_<$S*$AObXAqmE60_^0{F4Y2=uD?-Bi`g5>+*>JUgUIC)EY(GYjg~sBhJ) zx?2}0MT)g`KWW~r(M|LSo)bKSFLQFlAUl)0i_jls6_%yu3<(7%)FKdv+s7+R`^ z;&)?*&gh%#ovq09Om~l!N6R9C{>7^Q%rdsgm1K*fL%J8K?i)+Nr$i;bTd!Wk-$wRJ zj^IqaE?%#;XkWJO-6qhtSoIx;UNc3p&e2YhA*DsCv<`jmE?1%N)T{5{&$&(ay|K*I zldy$oL;PIt)xFyUIt5fGjDzH^lZsqbuRg=yLcOh55A^J$dXDQ9GkI3m)vKZSTcg{0 zb=@{3V_Ax^L_HuEgJxhXYf4lN@?C@RtHF5HjH}3stqitO*t)5j85J^~GfR|nPrbSe ze?==85BAJfHkH>#>afhM!y&zjRj=)(s{5R7stdNu+*_|sOV+E1+ZT=;Sf{X#ede!2 z-FVu%^@qBt1h>i=Ws(EbX!`DYRl2HPtv1|R*)TVI+DILvkGc}KpTNvwH51$D{V5i} zkDmn3$0&#Nn80W7zIrtmf91C${vO4u$L?a)ZGIUzTkBX`+soAwv`yh}EZbxJ^L3o* zV|#^m%BPR23EkC*IcKX8GiRw0b!Vv&frzTi^~?6j^h$I8O)omf{VdqsZx?gF#cxfI zD-KD{ySn?YTOV`aV-3a)e;YLquO6rCbnE~wY!qxI@({7t)4V)N=e{#DMY zLOoXE_fGq$GR_6e6Xxy6x_b2#{ub)(yn59L%y)rd%KiPcH-=cHeaW#thB5zydz^hjs zT&e~K4prTAW!Y|-u4ym7DEW;jM}4ElX9DZW)QE8{)g2g&ZDAh_``~fCa=~nmba_l4 zAGVaL^mFxU$n*88d8OnrU&q*5ZS*lCYGTg)33ElZUTx*}j2U^NJ|2Hr-KVYRZNs^2 zehKEXu9(Y;RrU@1R{3@uL)`fn`y19NtW&Ml;l7TyZI2?=gMDB>O2PLl_;dT99euEG z+WNj;uVQyetogdx2fOBPkFoIf7pUm0DvmA6c8a2=_$5xVUrWV$LAwe`Ki|IlsHA-i^9Mos@4x9CON*Q#wIi zgujX3lQ_J(doF?zh5zb4+6I2cU zQf51k9yqzPI=K$#1;>J~Tl3viw(|rPDr#x()dOdCR%hz=f!RIO?A=9b#`Z4iBpl<9 zn_sGq#+)!|W{K)KL(eyLsEN1g)gimgF&Q;!*;;aoV{skMuXt`XtW*s{?uH>>!)BJL zVTfzkW&N^&VOX9&5cUDESHWJXh6Vbmep&2Cy#lJ&Vm(*P@27$v*Q*KmOXA$qJw{u$ zs>8Y|C(tiYmg|;n*(>FEl0@0Ma}Kr%{j*-3g+FgUUR%`=o{QqSMvOVCqKMMRuf&Zu(19Fwwe(AvfBrIS<7sF5>ro8=6?33^6yqnyEbZCu(= zNX)n7o5n-_6NmrQ!hhDu_Nf*Ali0OJnv*Tk9MlV=l&xUhXdkEPV`5vmm9q<0HVDHz zbkE7v!>=18U$a+stlKNwK4jbO<3U>)a<7qjGaa>MQoG3dpw0Qy_R6Ecq~>uvN%1Fv ztShG!_w(5CWn3w{gSFYw<&7^qzm+wtV`a*wzWAwo z+$-^2d)?lp^jsPSZ}ZZa^Tol6B`aBk>kQtw8$X7?fe$^drV2Vkv9m_ zxJVgHiiZdFF{x+It4}jetXZY8VxQ02E0+USPPS_@?uEI18(A4SIa#MN@={T9$*_MK zI;(=tG~UY4w(K&Q-@jMZKXb1<9j-g-_QQf##N-u>yE!K@A9_v-E~=D^a#b=;dC#Ie z_T?qz(suu=Ibr`Za?OO1YusK|DNE@E*SM4(K zLcE+b9(|Nwo}oi{S$eM|FM?Lg{>HdL8<8+g$C%GQf3K9iV6T%iNq0gRvb>kZoHgM4 zo9KV!tdWaJ=3;m;ke#(_D2i(vqG>Eoh{F@sN$WArrFup>hIdOn=N_ym*b|$hL*`+2 zoc<`d@_k(Id)8CV`Uxk!WGn1h5B^m?S0i%%UKs~sci6W1xRJ;2gufVCSqH7uKr7X< z@d&)m&=p~&boKU1d3&-^(?sYkq^;Q-_sX4biPBay4EfVb6VwIl_q0Lx2K_br%3Ai1 z82iT>_KynL+Qq)2@dv{(DU+k`-z!f6J`MX}mcPT-IUDYT&+LR|cXm1cu63~QZe?D! z!oOV|OF?smG#^{Lw5Cv=H)o8^6|#AuNt$wvoBN2Tj?1cR$60SVyWZ`bzjDLEsTm{_ibPGNnTvILAEU%N-Ku=fYs@ZR9 zrFn|o20ml-3bfhiP`=Kb07gF7`UqE9Vm=biy7F=5p)P;sfxU7QP?|p)F5=@>2f*2% zzHOo$)eD3?bSb}AF8T(vf48OmD89+maW^w%{CTS!yaY znMESFZftUb)}3JA!YkG<>rad9E&3$^K(?6ibiom72%*iYRZ@IYs7E zya#y7l~aoQd5pdY9*oW?Yv#4ROxl+zdod=R|Grn=1B`#vj-!tk^>^Vpjs8#nY_I$Q zxXGkvoKFaoNSdHxwNxa_MP(O5PdU!<>D7|?C1n5)yLx>*&~YPO?c{3chyEr53AEq( ztNKBz?I>F%Wl8Am_j~1cK-%VW zthQh^ca@#?%y`;K&y3@&%>?6D_oU(Eow{DLZ2ea5S%=(HgWQ9BIpyS@7srj9{(R;v zy-*<+%&n30SZDj#Z+h5oYNwEGRQCbDDu>4B8@l=MOZUo|kTs@w8?fa1Sc?03tX$YF z?9ONgt-N?t`ANJ?;<+J-B?l!{*)4Ad`tPx0=HrPoIqRm+WY6#9+`LOxPQA9QPVK+S zr9rQ(a8s`yT_+3O@YNpp>Zr`G>y|TXyJgnZ<>O|(jW{i4CvALLOkTEFBa@8FMaT!` zbJa4wzFTeso-p~D_!Gjo|MSu$HtRaS$jHevBu|`yoRn+`HU_aEx~sYV=vl+iR)vgj z>X!EbAy0|o6vkZmVRk>`OvFC4m3?Syl09dF{Y>>OIcRs%(rj3Mc6+zH2?+J0J{}yd zk;Cj;Z-gGNfgWGO9^djHx?`uS3UZZ_TdtBsF~gR|p>57H+mn?M{YA0f zz1?yvP^{O(gN^XC9C~@q8LE@7f@Xqjjbs`BS1eY^<#UyCDSYUXbWGwSs>vc=b>{Th z)0w+-GP>FBZg~YT`*BNKK5k{B8f2qtX+45$q;*f0-UsNN0e1=LiHhfv+!~oZzgxZm zJgw#Gd?C{*zWoB`>l3!FD9(Jr-y-=~r#^1wtGKM6LhC=jHqQym?s?0#l)p}vF6x$t zp4%;lT=`L)J&<&I4)DVhry>)uZccWt)wN^JwF_s-_;|N`1$e-f9mQ#TNt%d099k>t z!=bs%W!>_9pz)J-9DF>nV-3C$>SfK6(R>-*Nb#(wdcDOBGXCOjdG1TP<$%i{#dRE` zX%aivNE5UYTdK%gE@;=%G~;Xd%%VMire4}DmtGDnxN@U7<3~E-oa%>L-n;Rnymy}R zgVW38bk?EXJ2?JPDDJmP7tn3z>$>ITz^+d@T4C)delyUmVJep_s0>|Y!fqw}fQD1P zrt$GeKH~kdgAZ272U!Po6ZUjz?s%3dmvvKkHT6xg=3KcxZtt9mdZt26p7XHk+Lqaq zktdd}?UqyD&@Ba*H-c*)(-qQAn#+NEonFGnjV^u4&Wu?r>`9p$yCrx_x1>JpXvD`2 z|0|R1;cmGf@a1klEa;6%?_#x_%~_E?8!5+>zg#1o@9dVJ1Gl;IeLUb5Spwd~S#znf zU}caM-6}d(Pq%92YtAX~vyJ50IK_FzWc$fYN~7l7Rw?Vd zIJa58l1Xes7ZAKn!d0kiu#+XA{2~z?5!17Ix1PrBddd=S_(k&FGBevP!*_Jc?_8UF zJh6wmf<$!9u+p`TquL&$53A$66z9BD%Xw*?>^x%6ONq0%zYN|T&Fl2{TECsAX5@h; z*?b%=#=O&kEALotl=*wQz}b>?BiB1icS=M9_nZfy)edwuXW4cfbdKb!5x2} z{YtmI>Va-g*5thGhiSUSHB$4{ZkYfYkJ>i)xH@?KLHEixeCn_}`*$*@N*~MI;c_mBb1sObPPep${CAzIUyeU@X`l+s$@@cz3hP|&8*Hh=IgU{nK*Q0`&%KIN4w=u zfUk4%!>p`t?(psUfe*!h)-5wY?w@pA>E{Vy+_iXVf_CkZ3f_LuCmDAQx>`<6dU|!$|NO06&i;FMR43$x89fcMxH&5wcRL$vsAE@!?3!$p*2QLtp%W;q z>XEBf_sDg}bWE5d!i}zgG|@QD|6W?V7S^#AYGlKdyRR_!2D(00?l4O0%a-@Z-+`0W z7mX9~aVu*fXCR{IFu z6D}u&bGPHAGxxqz6`dVwn?U;N$tw0K^nQQrmgG{me9n}`c$u>7U3D~$x%bir+sb4c zd_r}<=H6_oGTYHuA&s=72D&MyAM?xI@+%kFfG zga3uFPq{W|ypQ{Nf}Q9xlkCk_k6Gm4R#uF2)~c1ZBgQ_-@#l4t-O?l92CBcPeN_Hj z8U|1B(pVYf-4Pk2C}S+nNcOBA`3!KeDUZA!9(0sT$6>dx=pNP*<6c$oH@JUZE|+rl zs+pl4S)XDIOn%+N(C?GR>|>;h#!CcGoWk8(aEd)Q&4*Hw9p~b?X>PYje~&zSphuQn z8-3i^cpT*ZwP0*KGRDS3*HL<%bnfku2Y~MT87mE2s4#fEyGIUa7;~)gKJMo+`@&Rg zK0NUlKDQi~nnlivlt10mBddGqyD5u46DP#U332GhytKhZ<#G|aS8RN;s;@C;2y{it zua?Gow%|T+e>jjuc4tXLpv+$#Ee0$#Lw-K)6eUX z@h{mi@NuitLZ1aMjNFxOMQ{IP^OCUv%G8c`=$g#cqFZW~%z+-c;@LeitL14Oi|Y*j zA;ONjIy8O-*FBWGH1bPH{-C8yTAnofQ{IgC!a8(V)Pv3-eo>EH2*kcjJ-Xhww@`c; zXbi*B%?3a3xjoXUVa&0{`*dileSUu(Ta?_Xy1{)Wa@%k)1$V>k6+|K`Ts$2AQy zL+1@W@_oS9LHJ>&o`bd?t=FzC&V_I8k*5IveB*~1dGN@pPD4jdH&Wc&(Pyz&m>+`!(BsAGdN*{RwxcR$s6keZl&Z>I?GJVQrU@ z&EY9VH;Q~7o)K#~-&Vr+%aM7HegL`!jQncGtJ2_mKiDH>U#CqP#$KZM+kkBvM){?< zmQlqR==B%kQzE?+LH+#I&_>$ZAoK9gz$3=NAHZ7UEdgTA^?vb0cE)833H2Ba* zp?_Du#{0ORCwP06ygj#0-U9FU<{+bcVbEMA%};X6z)o)XKm0Ae0RPW5OW~d#dEUM7 z|2*|>ER&6MEu5p9OxOb9jT%lnHI0u)@+rTkOrcC6zfi7FMl_AvpEONLy>@&yuZ$0^ zR=W9CI6jLTC4OIz41Wf?bnS`Yu`=jt6KyC-r{j32Ms6hE*wG$&!sYex;MSPj3U7Z0 zy07W9y+_i!pQgt-yB5mwYQwEFg{A#3rbt-Ojs_D9H8Ok z(KJ3D$tUnC^2sFULh?#!xrR2mE1B!zUO8No+|zitS&zp#+x@Ike#ZIb-`QJQ=tt3B zUge#eR!QSEtEK!oG{IcA(zj@Od-kyBX)CYR`+9y>haK~2#?R{Hnxc9&Z~fT# zhLgr?ezTUD-~5kzWbGr+uxIlU4Wmv!jgCF}X36vGl6=uHbS8cpmv8*>9{Da{>toJ5 z+}T%}J=N&Ym(^yhTBaZEk-LD~{#nOV_bJA)G`ub*b*!~=c-%?jRi<#|rGC*PmjR{a zMZ!-~o*!?|@wJ@kuye6zy1ccGL&uS>mifgVdC7mWzUC|)L~vt6XVb-h)*~MQ!gM}v zbSj5db6-^{4O1(&%+tR{)&;`*wg~RVIsZhD{0|VBS1-)0kFHf2tCz7h81dim&0V1E zUu>IwJUCD(2bQblnYk(%;ZCAn?X#dawaab@dL-07Do;2XIR2eRzejn00}i+{eLQGn zukTvf>qDAMjr7VrfZz5=nD#M^9$~mwjsf8vg^vg8*UI|C5vy#oeyoiE{Y4KkJJc(K zsjwdC3Yk-FxuW6Fv6n8fhjOvAT+jWK)4pTLV%O!2t}C6}#Wwbk4HEC_l`jJQ-*n^Y z;a1K^#%JF>h50?D%aik8S1GT{l}loxsI@nDAwd)TcVvbCcIdsu0`b(fH+3lAWp0TV;^l;RiKMr8 z-q_2s_iRsM+u_@FoEno;k8dip&9Be5W^D@$-7a$nwzpRv1^T~b`{m>6GqEjwI(nAV zkn4AeeF^5)Np6qK*YwKEYkMW<^7?q!Ihix@S@H(?R2*0J)Pw>!G&dVVba}$hVM*RK}JqOzOP>9tq1$+(zAPI%lW<1 z`EA>0AGbD9O%m*n$^ImDApf;8pGH4?d4*g~SOYpzSHC+($PTGMCR+Rj*tOrn46=HMUjIMt2iuj}O`UWlY5GQGeZIUqx`CjuG4A4jh+p=&w9=Qs?R$5-d=eMnACbyE?;gi_`>^o zTq_fk` zV+Wzr3cQM@df5-uGxfNPh9+a+CSZ& z(4i2g=~u#`N5Z3F>@8loK^yzjVZ;6H%{@Ha)X(~8|3t5R4Y*G0(0PN;D&FwPUYRlV z$_coi$MQDr;HOG>o8jxm<|)&m_i~HyZ`OdyP}zL1d>mME<@>n3!!vJ;3+(6U3!!QF zNhSP5+jjITz4B{d*Y|Zy^qg;bjq29?IP-=XIbaJi%9do6xVOxC+ehxSzS=9(K-QE4 zZ;If$#!J$ev0;sVtyc!VzLGDB+kAdlaCuBFho`lA`=og@c95NBo_Xj%4-IX%&6RVW ztN6GUG}5z^~&c=+qmcS@Sr7-mJ!y&?rPc1ez_aIx*NW_ zd!lV=eW7)}Ip3HK?k|)3lXkB+b3&QZKkSuf|ERb4Ha&vx#2%iyH;}F_ZO;ywe`5}T z@az!59W5XI3F83R`+n|sJv?X+@Eb%s!#8~6%!le0bFAMadnA1M=ns135#aV8xb?vt zD1P`jd`-hx%cXcEkKvWf8)cgJt&|&yH}i$<@{if`fbd>1f^WqJlfJ86P>t(*=cB#y zOu)ReqzyhEjI5TCq}#U}0%@R+i8HWa4f?{pBddI9Has&WdpSGy%Krh=$LUiC-yfRW zD=m}UnWxc(5nlP9y>gqzDIX$Eh>M1ESM8VK#($~MD_xKE7Ior29$dRxt_|CHS(#i` zLJzY~^vWLqGrw8*27PZLjlNHb#A%xdQ+XyDZqtT#{eGNzD@J& z-fidA$E~c3Jef3aFpLRsUB!U|mWs2Qk9v=t#W@QoHFvpv6 zWX*M~5w)jJ{kd0O3&a+*Oz4I*LK=IAqq?b@k~9*<9ZhAWPksvcdS^c@_~$bD=bW=g z2>oAV-QK{SKE9(*t^sCEJJ6f>xb9z?W~@){0*YyT+~YItoBQOMfaNnfU-sM^4M+a; z)46^uozW-lonb%VH9qdr$u~(M*(a|)8(#He?E^MPKJMmw zc2A#t6Yyy}8io$VPviQT+TJIJ0RLKaI;bb7r4>3Gnkp!=PC| zjajd1SF~2fPwA6)0cK7c(6_PgRU2JOr3qgq?(RqI{AjurT-UbhPfE&Q9{n<{ZJwcB z$4jxFd`+L!<(T(>ceD%5MZ=&4KaFWuCGE1~Ubv)BHod6No}EM4P}dRT9EmgIgD&s+ zeXlhdcev0!iY@Zug)2>sOX2J6~Bc@jAA6UxweM7GnnHyZY5 z;NmcRLEoU1o3P*weR8FSGdG%M1&21tAIVFaNF8B2nXhHQUr+O1n72*}`r6y{FTGZ# zZ=ipN`{XHCzmHpg9eT#%{>=I7i2CqoHs`M3${M+nbH!zx@n1L@M|Xrh^e`}wd^ACOU zDa~WX7`kIG(6DQP2V7aDcqE^WTN=84Tc4cv_P(MX*~jhLFlQ$#n=p@ZOP@UB);`(u zD0OQYoOcxO1G+Vg`HAAnU!wW!yNT;$Mf+{fuJiC-d++xq|K88sITe&I>s{pH^~i!Z zN&NkNvgHHNtE!UK(qM8kY^s|Ar&Z4%PUuYL+d>N_<#F{hzGZerw&(N8xCnO#D-y-1HxIOnuzk z|MPxNeR`QWNUoCEV|{WzFz3=oa5vYTpY4<90^yy3k6YbS9NiQ8C)(d}#J#aIwyd77 zEv4VN75Y8;g+AH%#Xjl$nYL5qLw62_ZX)^9 zmo~uX%xT<5wl78X(7~26*#e(7Hm$oVWfyW?2fQP}8V_x2j2uPZ+U(szR?m{b*)chr zd!`*n5=$+Grfg%TVcggWFJK=!w-(tlE;HYN27vg_?Oa4~9ouVG$*m9e$xnckOXK4~ zdnLBp0DDn;eZx{+p>`e_&hnrfGT_(x4{S&|AMH%z{{&gw>6-GU4(NU6 z|JX~U-}z2Mrt$E^R^;{IJ)(MjJ@aM}Ppl2v4EneC%k z{oQ85yZ?qPpmEHzrt|SgUfmCnHI%nc*Y(Ti03+wCJp!~=&id1^?*j!_UMcS9Gy5j} zMF+ZM_D!Q3L^m{NWrj?1zkCYld|cb6>ll3yae8kRiBr0O_hwuAWft(y1%8-WkGZwd z$#|N(U-f&~iJlwUEBb9aR4Ip8OIu24B~Tq*SJ94iyu-+4)r^O2>(YjP8ExyAN5i%* z(^lfNt?Ghj zzW*8LdoHVX4c1=|<(|Y}PaM)TbuFAP+0K5s@63L=#nlzT-T2O=`sEvd9bahB$4wgi z8;thLd&i(ty_a@$Vf-HW;m}zmo#utVPVeuR#{he$M&nKh<9y|%aeldqx{druzx)`; zF52<(aWf|R4mLxdyDO!eHMxiRX=hK-yA?ezw-5Hq%YfTWe)th-%pB^zLz-wj=MgV` z;!K^#3z^4p=J9+pkGd8!bi?R-CyYI;xw9~GT5cV(F}e`e`t*zYWz|dirS(4@of9X- z8Cwi5u6R$ar4;vP?qo0Qms^31=GQ%RQDtLmd#2%c0$EeGm~<;ReI%`~XN{D_Jo$L= z?rM4WLN#_@RoHzQ+r%Z~mm|Y{;^vq=KXw)0nz=W*v}3YLW}n?Jr=Q<1Pn&j7r>>9E zaPywt+7K)q?3dFngBFba4C&c(RbG#VvzEMcK^DCf?P{U#Eq1(}Y{@*oyk8yxrhjQ^ z!^flc(%4Q;IQhi9^QlJW!bjMI?;ngYzr0`G1k(Cz_)+Sfq zRob_YhQCASORw*j{nz%(wqI%cEbT|b=&P5;+=0Tk)Xu|}fe*Jg^-D0_FIQ?ARb^5I zPttckYZGgIyW-a>-Gsdj{$uoQBy?rNu}w`U2n@RaEncMuU=`&p7E-1m%goNVvd@Q*!QivW>e6;o)tC(o2{<|wV8GIS_R?^&*V)* z_EY_G#eMzq@Dq%;wrOFL!4G|=U!Kq~(rUbq`+4*|b|Tm!?yN!$_N*d-@@?fO>lgpd)(MVKg3o@1?izU(fUHCX5}Bwy~3`uzT3K()rnbxdZr}sRMrE;m#k`k~zDY zJ)lbFzuGTrzQ&&a8`^|ivo)*FWfGiK;(99!`;??IuX0{Pm%)h~-Kua6tsw&T&8 zZ8J{LMJvFMkCt)_giH3pEB$JPh5r zvNV1LXL3%G#xEy$VU@g)G#4U|ZhzeHE`68E$p9X3utE;Pw~d_|5{9XBuA29x&=Ihx zb?V&;duI(Yb2($L;hzMSU3*G#KX0JEMBpzkfsa#1UUzIGM^K*n&&d?}<>$cI@5268 z8~lvN7y}K14m94!{XD_5V)88H)RA=2uW2@T_8NIMdwl)K${l$}yWW*BujsJK+4J&` z_siB_^vnIO4j&ILsgz6DbJFw`d;KM|Cx&CgIJPhL%NK#CTwWjd>|@5wST1rW`$WH7 z2qb^+`p9`o@hgB-7`9wx@Vg01hhfmJ;`afEG>ow*#kGuL9^xbAg8Ow_`P}s>)N3LC zFa5G#Uh*sUKCfQIUj`Ihy`{LH$NHf-MQu3p#`eJRy-xUEhu*0#?_w>TC9}Wnm)`)p z{y<+0f8A~HTfjHRu!pLarU_&BmsPJj)~=d$g>6OV z5B>5h;8U(1AGf?OVl!6ceMf1N<$Zit=TH6eVc-c@hK~m?tCp9sZe9$3nt*PM?o>V9 zX7Ism-YPZF?t0o?C#M`)v0=1zCw;z)=FjdM(>i`SPU*8G9}LJWSjY zH%rp!STEzxY|{Y#76#-|m(Rz8m#meS@HS}EVKg)O&{LHg$7S?b9rSDKfHeM>?VFDq z`%w0jO;am*&G;|ZN?|+myJJAcHJ{=8>kR%sJDGD=md5+IpU1xO;?6ILPG`beO`39Y zl`^|+Kz;#y%9Z2e!R9rx`A#F_7vBKeHnC5iHXxS(i<(czX0E~D?Ie&@>a@dS1r_)*f; zo)34NQ0}yAzm~PnTg4=H%-%ai)8|F*bS@l_dw@{?<>OHs+o2n8) z{;z#JsK@^7VAv|?RBfS`u_s?#C)Mm_`E!+G)ZpM}S=#$zsDQ&)DKz<60xx7AZ&c}zH9rrl(>77(g{kihi zH@Qw`t{jjr1Jf?AkK1<#&Npk9kdX$v6+l>41b1Z>h;MrJN?AT`+M08|4klb%HD3~46M3FFH6Vxn#Mo*WvYO%- z0oQ35^H7TWdF-00Ro`0bdEKrVQx0#qUPIZyoGZu2jjhRX_(;BLyrYxzF>=g~g(}9M zx6jOH4E^hPZb04)Jni!PxSe;_5p=%=y5BP8$>fx=^x6Tw=`kSv|6}_Q!A;-M|6K!J z0{-2LA7<#|prMcXRe9(`&#j!jn~-yB(pDEEuOE+<3`ucdT(dF<0mE5wL2Zd zIw_EE)`a+6}?+eM;Su8-CbMT8d49Mq!wm;kc`M6!vjp!55VJFcm9P{)E zoA4uXyl%9 zH}mfqkpBQepPN2z^xIR$sybu*?CKl-CKNomd!x+C( z+|N_`cDFN}Z`R!Sdk5qlKp!+aFA?08m$_#^J_v;SV+1cLZ~Wc? zxfTfNFM@mYH~#Sf`3E4RzXf8hXlsPhxe!4T?ZGemJ9GV ztG;Igwh#LslVAZ(2({30J0%8-KtL(&z1~d8^(n#l1(@_Ic~%Jl5|J>oDwY9e>4qzP4k!Jb$Z@{vE$LYm${Qn)AnCM&%iZWx3(LaRea48 z%zYTfo~8H&!0o1uasnR7V{E_k4ob`)Z|4^_jBB zyB>6*vwwn@1H0sE#teJG2(DwmoBxt@#kb^3))R!Dr!rb@JLAmRWdm|n1G1ay6XHwA ziNJNPd>=Ra;E|$E$oi#%H>Cay4+Q2kkLswBt2sj!Ob^`)*=V%+i-kBRSn8_0sHQNHXC|`rb^-xr|9>5nz$otlnuwN zU6PLdA<7TVt6?3N$-X(>ra~{>(BGNxO!F-v`Vs6XlO52Cv8%Z^Cihau9n^n&(u8H~ zeUth!;kzW{>*xxcs_bsFXLP^wsrt1a5!6~~vi`pxKRn~zvurD1r`0qsuifPS|_zuTlEXctSt(7ySmMy+h+?70;= zcq{9C>k)jAzda^zXUyKh8faiG6z$&AYozo1K{*1LZ!zfGLDH!GVLHR3?(|m?!qB}b{D8~Tv9Rv*{t>XU!-0sRR#Uptdc@s<<)$VvHGbl}$ z4qAJY&^C%W(Xd0nqARZy_w%_s(7ZWwHF8G_zpUk+s}kB(pPth%88s4KIhO?T|OvZ1&nQ_hNagU{QHF6?eb~-3eLVuKD`@`85(?;aqVYcN z=P`GR>f4-~A^XO+QT%(h8M2#llN)5_ZG-YN;1*Y|k6WFn+f$~To>A#vX%l``xS966 zb5Q18ULUt_T#I%%_APx5xpWfV`;I~R5Kvud`x3#;oM0dO?m@W}2*2AG#Z|6~rm<&| zR*7oW?U`gY?C)}mOuuVT{sLU*>W<)AH+&=i5yk|twnvoV<8I!UJ~1e}KRH;mPx0~K zJoa{I&)bi()=u{X-Jx9^IVMzgUV%$$?3nqu{kF_{F&t;y)_+vjgq~x}9cx0e$Jwud+g$!AuDl<=TFGd- zl{VGcHqF~FGFhDhwlLp^hy8%LcV+r`;xyjt2ZQ4D97Wrwyz)Zr(=xW>3$*EngR*BG zeKL9q>`TC3_oG2MN5kkt1UF+bAe$b6=73vF8s-h#i%2^1MamYXlRlG9=C}j)>a{*8 zc4Gs#>7+WH0%Zl$oUL>Ykqe67Ni_bnW3mo8LHCKHk8xH5!gt&eTP8vYnSE&@*$F*;hL48$Nvfa}L{lY~>A~p=Eg5^#2aZeSrUl$q$R3weTI; z)mYFkMVtJ~wC(SVJMciQ9d{oOw#205JL*nOM1S$uLAeKb%H@mVx~59fI9+ur7?Rfl zp|0A;gHP4SrYab%hd zzCH9ijGR4<-2S2=IRaRItos{!3i_J$!@cz5R}RS(P@3KkH?sbUJy~am`d?-})hTTu zn=D-nUwJ2xp7Ed{g$EPH!*$O`piYgWbGv8 zo!53tSF|(!)**Q*5Z(nvapi&0G*))M=H#-wzRlEqefet1zJ5r~zLqg;*71XOA~@xi zq_MuDhQ;0Z8y+(sP~JAabIU&8mK)?Ouiw^7N%l>&d3s1@!m>R)fgM~+u+6iOvoh2Y zZRpT9OF3-)Cbv+|R$00ZdVJ%MJmSjnar`Qs-NqS6zva<*7GDq?x8>;lcVcMmMWhW=-tAS^cFVMd7gaiNJ`fc!d5c*BfFyn9MLHBznY4q+k>Qm9k z$!5HCJRhuL%`{11h5a~o*_z8$fLcaD3GZC=_~*f5{Sw`7|$P2<7yV)8ur z!G6wg4Z7y|rd@DbOm0JeZT##p7JLsU8=O(WH^SHOUeD>PUsx$G%wemWW`D!(#(X<# zqPkGzJCs-WSNQG2;XLrx-^Yz#9(+hqkMVW-$dIi1clhAfR37AgyoXyIWrO%{Gz~pa zNBl>~I6(0`ARZnxmP_M7v=8(4Tw(IeA@}^-ko*d`I{^)a--zIxukVPYB;M*2npLMz z9P=64XLO4A>55G@Nq&AvYJLE{XnDl>c%lQ}nopX0nogaIi#4zE+DY{@%ijOpA^8EY zYdv|5T!L5ycAnXnwf z{jlJ`8aY6}8%KGTq^2jH*MNSLOBKBk`>k`Dt$@1$YSsp6jnCO6nIFU9>lX1z{SEUCm? zd^4u@`q1{#|3=y3MvVg6o{Krc}o|8>Hl4fzs~}N5i>a@zR;TCE-QKxi@=FOkTr!Y)-Bf=Sx3h z`6g$w>XdvN2z?p)c;cNk^3KKQ^CtI&tnU}d&R{~nscI#jaXW9*kf42sRb}1t$LYDeIcU>$R6PIAvlfibKHV1j!nIa-8E8=VL3DPR+LTns zQ*zEmH+CLgeCG&lnf5I5cBVchOTdkqUhnxh8`YqT)jMJh-_Vef8I5zarTBTk-LCvn zJd%g@`sLgAZScM{-wjmWXXwY-JvOG~VxVl3ZFi@K2dChx7rHp*$*7%g0-v<4$?eb* z-`O6w<8I`fMt#qu-x}r(E^ANec6?__t_I9INOR}Mx>Wu}yuQPY#8)&Ak8E3~ zHX=*$g8g1z;tcL1)NXuf<&~DM*`vr2_Kn!7C&Z;R&U{bQeHX``kcx9`;!o#2>Eupq z33u?mt6j{^sq5boV`|>6JRjAxoH9n>pSp^rhryz!R>_2(IgwG@1UC+y{hdBDhP_Igpa;fiO)3cWGoWCFcWS znh5UF%oD$9XeCVqcWGvc|AUt%g1a=?R7$=Egmp!5mnJowl6L`N8Xvd&-g*hY`Ksfl zV`;|?`FS)YmjZ2DEKNjkQ+|gOh(B{|B~1i(X^s*f>|03_!CjhZ;(y?!iQq2H_! z2-_3EU7Gf1rsUN?m?na|G)u$}pR`N#o-|XSH-9=j=jL;iU;) z!*{k9tL4gE1MdXurSU=Jw7g0fuD{ef{koLA4+!gx;I7{6YuWz+zussVXG}kh=A-P? zjVVb3Vc8Mf*x~TTc2Q+`{G?r*lHUQw*R@oLdGA7;-d{!IkYT*E<~{v!^DZZEzLD37 z{zBFvZ#lG+18@DCCbABr zxkovqJVbTv?2U2G9Wm}%X5NyL8{W#Ew?oH=`Xacti#>s~QkvGxFYlgjOv(3w&)w?u z@X2VH`R04BOy0nG{58lHS0P)h9|`S<*}IMZrJ_H*68}rw>HlL&ehb8Q+V(|p=%4i- z2{Y}Y-M6Ns3$Xh%>5`>kbk<8_?2O%>eVOLND`cfz)5x+*cco^;aN-y{4)N-hHCUD-Zv?lURd*u`d9uVXP8TZ~ITc7l!O9mRT?{&GtG z50Ks3{}{9w=lqB*An}qcjfd}f>4Tw|458QTWe)0(aGOUTf~(_lHS{vIP$REI);WX? z*k$Rsyb$}yCeB~%7JM@n+kz*p$2fbQ=A4cEllo>#t_5z>wzOljiEPq3$@g;6iD>wT zfV)jQr3B|LWtlZZ8A2W*kB}!)o|F~S_~F5p3ihie-LK}WJ9#U#FDCmI>t!IWxX$}Y!zGd~xV~Dh=RW&&?B5$W%WOEdVh0#dW{&elJ8Pny zdyn=bEA6s(OD+11m=oSl&a!6z|7_FeiMM0CLsEZC$&Y~>_vjc)1h@C)tzuSK#>)LO zVcvx%nf^6=_%|u}mga58zId7MOwVyHORN3zCKGo9@da15ruFeiezp51zxft5W$3r7 zgfjdz`YoVl=BN4Cihbe5w8!$TR^?koJ>0CeQ+3kIv}cj_Ol}hS8#3tM;fJT$zDDs1 z>Ze}PN=aI0n`ZcoIlrlX2-=UA4@(~~ujLuJLH!NcJ5~*U0r04)OL+i5Tr^x|ywWf! zNh6VZ3~wdRM)ul`6Y*?tT7{gJTPwTJ5n(@EF#8a^ai(@yz8N2uD^I6wZr@b==fHIu z#`u)tejayG(p9hRLyXpa=ZT{-!tVfiT#mgD2rW)?qxtSQ!+wVAEqyN_F-6_cOuH=a5y zBS0wA`ndUqa&ViVjVP?jyY;}d+PjZikh^Y$?%yXfUBglU9yx`AP3_;}`-kyk#=X>;w#_~ss4e@{rlk|UgM{ooJ~}MNfiaiQ z$E~cw`HTCP;29P23~03>>F#aodDDG(?++q%=^o+Yqv7b}s7K4vHd&jrb<#X#`JQQG z8ePsyhvg%{c$Xc&2(JBDq#v(gJpi}gX?0prT=&#y8vE9-4u1%Iv-^lScUhS)T`Qe0 zAC|WO4}|skc)0#vJaOBQ9?}xc`Go-1YUA_$OhXq%~9^>+=+J%I1~IvNK5^U_$G zq&n>9@g0loRP-C0tK_>#RebvhUvJ5xjj5Y2zB6&}u>2!n{rJ)EbV-<*iz<1PG(QJ! zNm@E8#r<-Et!rfK<9s{2fG4C(eB9V=nDxQkKJ=YBHZ1Q29yMh#SIFZ^_t9|V3_qQ= zV-eqPUm2FR2Zp6u&zrBS;2U+-@@mFk-+D#j;5QLm@64P|wUB47zd;#B?@0ZG>-jJm zE>YakqrpYVH`5qNK>>#HPm9aNe$_DoMW^AqM7Am9~ z`9OURjDMRw5?DOjj%@_j`yI+uJ#YM*!}4apyc08Yf?pnRd1zR6J;UZL#ZSz`IMMg# zKDe?+`gkzBMuw3y8y+)Pex*6z0Wo!4>Op0$$s)3DqLJgRwg{@?|Qe+BrRE2k8X z3*7Z;qDoIJa|t`-UDxG zMYiT{LtowIJ5=A1<89w0XY%q9`4Mo`)#>9woqjvZ+0}P4zee6^m6^38@|?;M`JLu5 zX9LRT{1#u(_ks;3?l$6|c4cZ>ACKfWuD>Jhmf2=DVFxLGrtA(}?yAaJrf$4Hz> zj~v@LA|D4reXx(4`^S^mR+#yP-ZM2L@-ZO1FY@u=p=x<3*Ct&H?CYdH;%~jfc9R-%iQ&I#}5C{u(tzyG+g;CJW*{pHSFU+O2grYrFbMC z@saY~c#dxxk!t||4Upo8fmv5>DSkp8=)8RgZ3l`z+&$d9@8TT1m3D8XKU?9MTc?_r ztX%;1c-H4&(JsLHU5V)E#yiv|eU5V#GGpP45&2c;i1hbqzv1tMtFOjrIQ-pD7tZyW zBl2Y+JU`5MxRJ5c|MEN)XPz-4KLM6pz9`O~T$)DL&m4N7y(6*@*wv5xr{6h+@9yl1-U(aY!yBP}Bl0$2 z@;)o$`FQZwn7oz!#n@yc2OIl(wPjL&m<4>jseL75*p#cp)&Tn^WUl-LtV>{)x|Ozk z+}>xdd}q_H1$-MG7?CG{M_nEtw{Jd9;I~dbtC6D@j^KxIMB)RsJ|DL_CH%dh|2V;i zY(u#;9K`Z3<7{~JJF@7d642VZWwqh0k;3y>BNvazAy>AK2NTsYu~;R~h9=ji9nIOV z=4CqcT_XGW2I7q`e#S-bF*-9Na@D0H)`mdWm4^p6SIf<$`v=lJi}7qtV&m3j`Kj^s zm)wIca)&{U(s7_4q_KXF(K)7(CzI%2 zyfleta4(o^kzMFtccFjXb-d^o$ed{pF;C`Q{E?MgY(49yG#uNvjmZ|-c+9-(D%x;r zJR%I=+2r2-h@HdmNhbGt~?vL^q04c$ghDt zCasj?i-Px14Z4Dha3O&cRE=yw3k;u-LzY6 zP})C(+yN}OcKUc?*BWHdZP+nwimd&Dj+6S9#0DN~*ZJ0bb5?C#!@Xnt2P5)nV9yBS zr0<;Bk7AQ?6L$7-_?m`OhNkiHNIqfh==}uwbg%K_tPhqJwc9CrSt-(j*@xO?>GLCU z`xh7!txLlen+^UrVRNp2jrVaskJ^5&56(WR-sk|lPuEyGV0E~ss1CPy_mm<&arzo$ z{{z-`IkRFPmSXBndvec`@wpNCA#h;Sj)#w1Jtls=OV)QN!=-*YA}<5NGoOzKThx!u zR&-_SCHln-=IzNEd2%@>zh77@Pe22|$i>y)U%^s4@#-@jKDD8%sLyw2g(}JZ*NA)- zC>zuE(ve(An9}29rTmpNe+TxMbm#=Ue9p#x-q^jXykhLxYI%=i<&~M`5h?!@GJ(tM zw5_))a;%eDiavl92xz8N8~fWtjp`;!TT%a{ha4-o~+e3l1uQAd9x=e zZE)At{`83a1$flu_3w~e$FWBw`itfR5q;{m9)$6Q$5nzt|6 z=$q03a>P>osO)bTE&8(ZaVs0v%g!TF*)aHWwS1ZJ`aJT>r@22$9ILi_8!xMDxn;7n zV^r#Pj#@pMp}92%e;#48`xy(z7Zrak@Hq`*t(M|iMllcZk#ZZ|TYmaszkMC!zGPJP zoIYy5vubpt>;c3ZKRWDVe!TgfV;3@yYhUKnQF$M5;F+|=?5*~lyoP-SNNYH4D8(cB zly5I0&*jj4?javw-m_>O=`{xbU&3a!EH?& zfIpi#Vi?Z95{5Yo43hwgWpM5HVmVmir)j=7>1$8Rs3Io+cb>% zE5##uh}U|G<$|A3H}u?o&ZxW*2;cuiaGh`D-qik4c{>oENqyYjsqn^yzHOq7+|3v4 zIzUz&XU#olRL(iiwl|8ir>LyxrLla1?L;ARR-#%f| zcLpzt$%~+o$Qxg?M#>~}*{B=_LK=(UF3r({qjDSw(?oEWrv0U(G6IBYBDhPFx_ne# z1lTn6H(7@N4L^)I@$g_H?`ue7Xj|8Ruw6L+c6RKD$&Tf1vMF~8?nXDofwP=IeXVq1}` z;Y|s8BhG(v`>6B-(+9L~dagqDc4xzA90hr46J1qjZ$lT-jxI#MkxRYy6bBzrnUSs7 zo|T;b&_64Y#K?E>4fXbs4Q-3gA>8b@ob9t;9hFZ5jnAfCI>+z>#h1UvUZG*6Eyevj z>i20A_8XhfVQ#Yiw~A-KK<9YMe0#3=Z8y93V^8y?QMn$Nadkv-<_dYk4>RL{KC2ym z)CNB~*&DVJpC8s%92rmbcGLejDv6m!*;}z= z+KfE7;Ve8!Erq94kG`+2!Pcu@UbWC5SMc4OUFe^i3vjedU1uC(v+qJT*msI~YZan2 zn%A*Y`EGK(NV z&4Yf=rueek>8G>}61X?6}N_eVf}MM}IRa zANcL4T+I!Op9WBMie{g}KM$bR4ISbf~Q zQF+i0eyl|>7i z$K=m|IiFUdLs7Yv{a3?k+Q%d{VaKHukL2^p>m=VZPg0(Sy$G0b<(1-*d@1%?)`x!Q zu(NGUt_7_8qH;WaJ0TpJD+vqq9U?p^(Z$=SM)jp+`Vcy@YjY z7__AEKJMo+{!Hze+xc3uyiPWaNe6IUv0VHpf)4;UxpGT!KTpt(oD2VUwk#RbHe~A= z@_z|f@XA&EVPMgfTZ;R6?7G={(sg5ag&Tv}J!4XN+L*-dw)7Xl%^r{c9mZJilJlpI z$&Z21=ctd{Z-KQIzXfLbAvSNFWAaJh_Ou;iAGdck_zx_-t7%rh>8<)L5w}CRJ!YAA zEV*{*ezO$zj>%WM$K(l9F7gI(LY$ls$DNIzHeB0-WAXq{T-zSL;scSpcX#eiY~oBX zW%&+E(b~>*t?VKlXOzhqxd9wgTT!f**^Pt$vsrw_)X9@<)DFQj>!}-=kob@ z(8~UuQ~8+pdg(Iu^3|f+XnW^e&7Ctk_!WDotf3?5YX1B&*?+;9G(Ok1#m57_!M1zC z$S?Ee+qCR*Cf{7S96fJLnw~W#<0hZ-_Gp;huiEYX`4YN`qK4b<8?%g&rNOtHlm@v% zcJXgJX?!Q~_FEl%qa)2XhUniZT`S+|30|;PUVzQY_L0^?^L%*ffKGHQkvHaFI40ef zj7jzLT)#cs*xVnf$*6ad4!r|g#%6jEn}P*=RdZ&HAqU3~j>#l&j>{jx*OkdST@%R) z&H`id0-&hp^YCEnTG@Ko*tE>Ac!!~1>dOnAX^BrCdw&b>TCqt&XYuMW>6#kjzDfJR zJS0oQxHI(96!$#FEaJxixi?QaPXXI5wqxSs)^_i#=v+A6{xq_M`NnT}S2k6=2eEX< znIZG0F?q{0e9)EcKV zE9jdy97gw9)H>0&R$UMF4${m&^D_OeG5Ip!`#AB#64;%ciWgQOv}lpVMP< z`r^7(n-^+VwM@oW#geV7vhNv_e+Ldc-;SG)TN^s_4X=V(%et2?TOH8St?Z-k9g|yJ zJ|DOKPMgu~;oJ8~tJ_nV2C+p9uQ_6QjXBF~jmcK_=N9&|kQZk@JSJBIAusmvpmmkB zPE{4mJs7%x7S2}n(1W?pV{M>-l~!Qa3+Ru|gRwh?w-7dV2Ru>3X-6sU=W})B&3MD} zQXd?XR{}R{UR{HW(L z=hl6@a`AwINzjs;==K;mP3*=onrMRExBzFM# z7(lkm*|Xr$&yUIKFR-UyqHUs2gsb1!XgGR8FP+)HM~p0MWzB8)aKUb9^PJ9?wGByq zaZKI_Tw=;a9wT0eGqw_xAB|_Oy!63HwTvuc>kA!jhL>;dD!%s5*t_@|?&A5L5;U>B zYlXiA+sg3CXZ(MxoLdX#e$ku(GSGzS`@%Zpf&|}tACn&f@*+ov@FvB#JqXQf80n)p zJe9nWe5CR5;+uQgX5QSJIpf<{bI>MmO;{Ilo8pfFH@Uh>aX*iJ!@kL$kMc&d0mrp; zTc8a_-vVtE_XA}2{KI2%?st**UHK8*&?I}({Fposc;q%mr>r%_uK?O#Y{#G!_wyJZ zCL@kBW*FZ_{XP{sF6KwqZ0Dn6@;%^?DNFUO&;fDk1Hg|nz8m0Md_#9BYfNE{|V(XrhYw<`;{qYw{Lt( zw{o6r;XK*Qd6Ih}qoIZ`pP}y~a>zU{KlvGbweFMXy6=R3RVK;UZ+dpbc@qci$8rsF z^!M=CKfPK>wN!BMLL<$I;ZiExQHDb z_c29(o5tS*-#0A1zuCcequ`Z0Lb)%m-h*q$qu)e~N56@v?_<)<_FfL#Ox>rhVJwY| z&;FG;bDw+`xF%z1D~hYkQj*5dDKgrDeXyNp&WAxGXtwA#9?UUaF7i)Rup1RTC ze@`+3YapdK{mgoyq_-xrxT$hl_tA$}#FG&b?}4Knxs@zg$9HM~#eT^&9i zjK*XXJ#GRz55IGrXyd)fgO+!gvh~d_?<(A_hU-rw-|%95fISE4k9o($y)1iZEZtzj zH@|+L^lKdL({w)W=T+Zdt>`~mlQn9`l2w{GM`@y3eZc9N8{6nt?UPHg`|KHB=Z-b0 z_-lZ>T^*&kpU2qhjzsNe$~zgi8)9-pE@GRWF>8sv>gWgd$;1cuN$fJ)2OkfvtduL! ztzMCK@|vD5(Hfh)%beT3xKEaWl*tR-o)Csg%};Y~nVieIE{E^OKfF)w1pN2>ewcmd za_ULtYt`)q->H`G(6)bphi-tEdiVR;h#t-E6Grcn26Z5S!dn;^hAs6jil$oi0@(bXcNjrv7oHa!{T~mIzx!2P-1oj&e){jr7 zay;m+kZ$&$#w7Q(U26(vtfAe^`}WDVfzP=*d_3T6o6M~e)p-Sk5$TJtOM`ud0x*T#wP7J`fbN?luj~AC(0|W&SDkgJJ89kSi39ThoWK5)}?@k)o9Y~TgzIh)V&0~as2!HZV z^=`|LGj|~e?OmFgW9VD`x5ppeC!YjvGx=FJgc}<}FFa@~6W&7=YUc6b(}K<7GP17H z4)i;Hoc$44(EK&}rbEAhUT4CZ{%s$=hwMCQniZV;ZpskK(7Rgut@*l0S(Y!1JH_@}{v<{h1Z4mn*w3XDKJ~j4IAJ=g4+^b4OcX(-brf@>WA|s$fH9ZWeU? zrouK%c1h=^{qjTL32lSEr&vA(+wQYW*nbc%FSl*eG(PULg9@%Npd^YI&5qiCON~Zon43{=tw#5wG@E%#%6m zD1U$k)h>FecfWkBf4@9x`lM?Jn~oF0u?_Om>H3GxI^Vlr%HOx&y(LB!?c-|q$okN` zkoc+l<)y%$N7*}6W;E-jG+ZL-?Aa~yJ%pl6eiXiF=w*3}lf{pn_pq)B#Nty z5ls_Zy|(CYqUCY;ZQj}@n)RRKjLThg{{M0IF7R;|Rpa;hl{QV2wB6pDUXwJvP)Z3U zTttL$u}PCA*(RH`AmWNqDXyu{-Q3`Si6$BA{!c9RDL&Znsq6XC=@Tvb5 zpRaPg?!6b#H<35@7RG$AHWbDyGQw1ReCFj=Q#P}9Z|6?dyEn=`+c(OYXSy-q;nx4h zCSf)-`-D1*XzC)>B)JPV%83_ll-o6)vFmFy_)E~f?b6b49}nVa+mMdbw*_h0H$PSg zwKc5roR(=!+d%ztk5|dOA*?NWY9#lGjndP%Q5Js4eLuC<;43fMD0NCBj)wbq5XY?F z^vhBDW$#8g<=l;uyhHm8{m;YS?9Hw;eAc#kA@L9oga_VdTK6{=H3cySy*G@GZ^NhXzesvyB)pm^JXvQyns-L^YG)ZW2b(h zdTK9Wy5>{W;(%_swG5j~g9+ zfE->=-#LN$TBv(9&X15O)c(Va<9hB_Nu!(t9F=ix>){j5sWQ&z80%@>KQ_v_z?m+d zkK23R)|SV;dvCu-y!0y@s@`K*u%~`@A;qSUyh~-cc&=ps)FN z5==ShW4XU=l+|M!^Y2&rxLxmO3I6$mQ!10s6t*_VszE-fr_4Ua7_j+lTQ33b+BW%% zjdD2eSW%b7YxuhLM_C>Ks|lv8)u zI`Q#nLy2tY&HF>Z_m%cHz4ysmHG5otW6v|Tpiw?k*(i6KG{{U5&B)Og>kQ6!Ri90t z*S%8zl1AAFv-SCOu14O}eZb)IMtK@I^N)6}_Hm=zMb@mOZyB2b*S@)B zjWU-wsdH?3eLPyNclQ_Lvv#(G*3s0ju{put5&2$*yu57pEVt*aknFlfIr88}x!tAd z<56ve)NV)Lj?KmdAF1YTyzosvUE|hhd()p*a{jS0l|ZiKJk&dzQy!|)jeqns^mWdK zGUm(}TbZhTMPuTm3jAqJm*eOcN5gC8R;wdPTiNz-@>Y**yKaJPn`DlxJa`)N_F>4| z8)Pv&R-y}h=c=Bb`_+(^F!v)`2KI!6nRiBu!b}<) zj9q8i-1lG)nD?oCPNTdV*cPWjnB7ydKl8%u83}ebDaMl8-WWZg9e)kyHcA$_$)ut3 zsnyNKa5H5u4eW)PZ%_(wmqqG6OKEkJ2zx;D$QreckU(0Xg_-{c+xp<*aG?2VAd9_ik1hw(F!tX|4J&2iSNQGEGu_G^6PGp8m$-zX0OO(wq6sla2- zhgsKEkK^;$^OKp_s$j1|*xC-yR>kyxlbt|Iq7N~9eBwflIm)Yw1_*iFs zp2hlH!TPM@yMM4z-T=l-9<>kJJL~cI^ux{g>ZxXYEy<5Bokwl|S|n-8wZ#Bx5K)*}N$D>&XsGewS zQkHM8;O_n?_f|((dr5yT^*WvUQyHo1+l_KQFl5qDn?~j@`0UwU;l8fVXUFy`#`c@P zCFc2bGw0m8Mh?dHfKQRc_Zp=KIO%*_4S z8%6N~+Fa^;+|B7J_MMM3O6T_*rT1x_2l*~x@!QdUSd`&>8hQGCDfF&8Smh|Km6z!! zk2lIDVE9+0q4{9kD1HslaDgp%G497PbNz;b1&-C5qDLM@k4*o}KH4b%0(QByeB8AE z>F5d5w0|XQ#W?=*dkgPM<@Pqp@xP$mOhI zB+shMjN|cyWd7JFB`-4XeO&WSf1N;+falH5CzF)h+)>|WW~bmD?{>mWT`^zge%~nf zkFpM#?_q1{N5Gk1u<}NLn=ym0pczf_01)$p zICamQ70_J&sw!`i9l+eLI$6fY9Z#;JN$RE-czirsi0voxOL><39yLRb8n2MUklWXf z%;3Fu_8#oL=3}c@iaeE`-6S6eO8abI4)BhIbd1lCR?hQ|A4$kt&@rv;Rezy#B$3j) zv-2fW*Ce}v?LW484RFnC3cKL*o8)gm{FZ=^M{_0#H;L-3UVy!JJ$7IlIPbvYv5^tS zS<9H$vgb9)4AOYe!OdVUd+W!U)Ut~0Djz%hTVt9W6xrXe4Mfi(D`D3 zK4ANnzKelf;cWIxm&7(r0&`#IRn^vk*!-F56&Jo*qxBk~LoG9)Rwgc2U0&OW^JBe4OhYU6mb2@V#_flRO2~U2O9n;?#c;57i=Q-es)6 zT0U}7we0w0bsjIjq*|WeW%1-S)lzp^wLAw@eWqIa`O|nx&w$Gn)p9*)DBYebJUc1y z{fHh<+hG`&sW$wpp)SLPH(&;$={DS3E&sE-T7LY+YB>|oznFghe^tx(@2r;h0s0pm zv7r2jSEtFLFP6%>r+8QDQS5%dJw=w@Nju+gfXvx7NoMSfWEvCPWNato8z!1bFMZK_ zHW{pcF$HzGvL!RFI3B0{!lGn0hAtqjX3pULIQSF5U`#`Hc-tdQ(g?h)G>R7RfcxpF|Ays{ zAWIM16SgwyTlt*8){;)}o_)#Igk4Nt*m-iqKoW}XP^KGBk z`A5r;iP7co2A#3Zsy?WM_g1mBVUC{yfAYsoau)ES%a@Nw)l;PUk-5X>?aMUfIlNR3 zM@C#h{U%b>3HQ$G9&M5>K<%e(o%pz(JucYeZh^Tuu_>-o(wRa!gO4}KKY<>Nul<>` zJjK8G(4j9u&_<|Nka7pnEn1Z|*i zMNilI7%7*rUpGnfZ3j3bJruK?^SW0Guzuo>wR0! ze^;gWUq4k=ZK%wcHbu@Fei8ZMPxREwY@7P{|LHBm?euNa=VN;r*XLFKt)7j4xN-XB z&~)klXOlb&Y;);{IJy=1^sP~!u6xb=-DDEf`AN^Oj6P{Z{i9(w)07;8l%#1ipzGGwXZEPjQ&h^%U`wjvwE=9k|Ed zrq=vYqxL&JjQf8#N%R`y{&HK#A&$&Y#AC{@eJ^KiWAf!!e^B(_rS#t!#L@l!z#C2S zIxy(c_HmWd=kCQu$F)sb^Q7}m?>woRwn@$gUUl(&+RdLV57{Jj zpRwifaq~VWMY4KKA&-+}tYVXFn!ZWSRDP}do&yYiI<#JwhKBpNA1C^+3i&U}d?S5$ zH+^_PeYq^yU~VjAOkSDu=k2h%;5e^j*T^4<@4Z)k`9IZi?CVzkQ2d+xUjLs0eh0rN zy-~o=<@a6>f7kz3ORdz{^d5Py{B=%^+_|tut_1SmfnHNtBVSxnBcB9(`l6*ZvU(-= zj{%?l74Vw%H8K^5>2)8ikp~a2k*2rW?o@DDY zdufe4`iUA@bdgO@@s>~4$j$s$`El@$8d-B0aCregi{Gd1CY{m)q?CzcGRa4W&6mTF zDc2)Y9*9g?i%hv3nQ{>_n_=srYWpTh193h0 zxZ1%~Q4dvbRuB57H0KwbKc}baerc}i-_UKbW@;S|e`u4;J!_NnTy5*v$MqfK#mMMx zrcTFqIr^wyYlFa@ZO+}2nN9M2U_jF_^)TDu3(wvp54vvD0}2;zT*P=L9*J zHwF%ljy88rjdbbOvus4!bS|Xt%C(!M|8s@D8{l61$ZeaX0f^hj$2IGkpT3e6=70ax z6|(oy8hPR;HBzO4{Asw-S3SnO^RpUBE0MpLJ_7BhPt};cyiY&z*UWdhiS*Ua)yS7# zo=C6z3v=Vx#P25lUL&7+Wg>klwp>fzn3&F&p>3x;1DYBBG@r?Xxtm>TA;$j>zIU1} zw}$_P-wJVf)$|o|@r(lg6@FhZ)8b?Bzsm2+78b(4#&3lybBhajGrtdCQi%T) zzo*s|@GtRu*18q>_U-@LCaD@Mv~PfW?VG!QlN|fNrhNPQxUqlQoj-leugeY8iCxnf zn-6W0n}DI+c8vLWv9CX89g4=Hdpp&MWBj zvX#y9@kPz@;4+)Oj|cakI_y5$?!}nvM!Ao>xLLjhB(Jq`eLQ+`y1aZDQtgsCwtyN0!_ zm22x~$l4v=KH&Umaz6Wm9rf6l+=_mBSWk9oYu!hV>Yj2)^mJ|4Zh zOx_LODXc$cJ#;6Db{^v$#-p3%18;4XCKuPoqYJ0Wg~+VEtRc%DnU&*BD>Rwz+vDs9 zsRuc%S)Q+NmP=gx05|7k*6uWSLbLn_$oq(^FERL{6PvMFwfWX?ADY1q;%7j`zw?bq5q=Hq7X_eimZ zv7jr8?kbbJ*yCPHdm8_dYH^#Hl(y%DWs~g5ndNUtxH&47m^7GfmKE=&KCid=4RFm@ zZwYpq%n4_O?Gn&T{bIxUo@N;bhF`P!_i<~J_-U0XtS=0=?p9GH)yQ3|Mz6-U@haqs zD<$*(X1O02cX{&h%F8O`ve7H#LnGH=<9MZ}m%mA5wvj+4Z!UhOr(gpO5=-t*xrsRN;?jhqYDJK5qK{Zf@Oa4xFyq?D@5N zChz)9pD6rpE9~I1-`VL`YfPM-rywR2`#A$eV z)JLLEk8VlGE#otI<7K8?%lv!=XG0frzx1Q%ROj*rBQtJYG;a#$t*m1im6Yi}N0!Ku z;|Zx5v2QUX?r)Y0fPVe1_M4tcgO@$fECVr(`Csw(0S~(T72}#lJ`Q!`r)&L5PG=se z;Jz+?B|EsSn@w1~&Zk-)@$l97 z-Q?{!RVUQL`pftFvduF3FfxTpJH&O)!mhKJPvs=nkB5KU%)P&Oet;jk-VyTYQQc(T z*0%TG?HmhwF&(|G#~0BSJ+!5@S9ZQhbWB21|C|G3&GGisTT4{CqO{o=qH+OBWfMv=Zx(963T z=nq$3-Bu#EQ7<>zzMhWPqPmI}`3zuWq-m37nvM~80=l-ze(06!*ehU% zn%`}Bww_7$t@fPPyE8w1l1yiAvui~eemYxZ#^x3o{)1bWJ>1lBH1P~M`tUUQ@MxKw z(VLLdQ_Q`$+I7gJN!yRTqeTuqwZ+{@qOVV&(T{u{(`Q%{>2HaXTOh3+Yj3sdU!s+vX}+jMHs4~?@^Nb~jvtVE=A#bmTuol_$#hAJEd3O1W8!Gr6w^58@OaF* z%;`oqZqH>*TXBBy_7<7Fu|*z_(@7Q4%CSS}WlR;(tvzwM>aNtX+1$moHqz^*AnDn0 zo~G}cxp?#lX?Sak`~s-Em9pv@J$|IY*BnFnl}24^xR3jB%zl2KH^Q9Evy*ss&BrI^ zu`P1MaV>JKOE1K=u0oz@XQ}LDJohlp=g?P})7*S8Sq3g|kyW3eov+cpOk8w;A&oiH z+GA+d5PE7gU$hQJ=9&(BDVwex4Hfk;M#wV5TD2Had-oIUqN5LP0OslsCF{P zZ=WjJYg^>A*R{wME=?bgo-C6mnLEGDUT8ro|L#}165EeTY1*q2TF;xxxO7rv=Qb_2)U?)n0LC=~w+uuW<37UO;# zyS}VreW}^+`Xbc#|H+rZV4N4;8OeUBMXvrb^Q8N(k4MazJ@k*TZ{SDqYb|mUPDC> zwsT$4_)HxliT`eq1HQ$4;L`PRJ1447TV#K0S8VQa+dL&Cd4G#^KF}hGFWLI=apR}v zbmynWcv&J*?Yl*>68fuZWOH0Fj+>aA2QQIWm`rKn|BQka#NJ!@K z7CHN;Eplg^F5^4#vljU*8Vi<}Q!Vbaifqll*a+Z1w9F+J9wqT5^lSc_}{#!VW?Ht_0Q zsW5HU*m2e>4I|FPaMnOCFA=_j4`N>7<=fn%Y{5p!>N%E)*)Jz;{~M0SVf|7`zR)6% z0jWD(AM$X!FFkO-`_ej{Tg{k5@APtu{04}9B>K3$hnKet9OE^`jFBzS;i(>A@UJcM zGBD!4AL7&ve7e^9bbD`L=0v}us&}>PiM2gMMlpJ^7nw(ahP!MTvmPGxA&2yq%K7wF z-oZ}WG3WU(%gwO96Vzt!^!?cEslIQw@8@s^??AS2rqm+$neWj@dP7>YtwOfZuFGlj z<@NY!@Z=rq>xK7YH-tCOBKsYqb1?OePPS^wX6Xf%d_~K}cZz6624au8ls&3>_j?EL zFwDnC2;U6f1g^l2b+h~q=rL(YfLmQ{?57jEjSZfzR`=NWW?sm-*FtvWOF6U#TtML_LWsSCBy(s^KL zo}rCEuU9(#UU_^xh!=mqX0x0R`1g3y0gdxD4>$Hs%w6*~SXm)by;J=R;axxb4!5S; ziwwMKvz)kkv-5wc{i2x0`1E;9Jap^_Zk9P~H_NV-uASK*hBVI0Jf8i&!4d0BMoXpP z_|5V<&~&%06Cbzd#GGA|e|JtitX68K&kZpb9lcpv-nv;Xad89OY#U2tBmH&u4r`+l_w}(uH_Hnf@caHXn`a+aIe5YI z*jWray`W2PyUugQUQh3aFOY$^(LaveEJwLCd|cnzGiMDsqlZPxwR2^IrlC3^+Dd&h zB#+xH8yhyu1uhLAuUw@z#Tyt0>!rlyL1l3(3mm;)Ss<(7=DuOlmqE-PO!X=mx=H0h zyk*tPo3@NGea}|k$kng`@@DJvVCr)n^|?xnw#J25nQv=cbOQCuh>Ndw7naqy2V-Z9 zpAvWfGXG7@Q}t1$a@6*vvVQy+Sv>MK#!{mU{&cgH|7^2l?y+s)WD_Y+-vtgBlEhKCKVe^emu7+8EF<8G) z#&D-=>q^UO_rkmhyf@^iG&(KjuX^-aZVt=WuQ?-c;f%bQGxANGk)JT(j9jN)b3WP2 z`6PSx`Ph-0^T{0f+Q5C(`h%kCs3t1x+-#oo;MU4D2S!zAZ(<9j-9A!W zQ+!i(Inip)LBqQucJDG<=Itr$U8o}L;Lx2@pK~ZM_&3mKLpXK_v2Kqba zopIBX9~WG`z%E^7(e?CO=P$_Ywr!i4^3`I4%H4~3dkgiO)_QE1DI0cjrn!AK-+y!8 zMZ))^dDBRPcZtU4Y%58vu4jR%}mwg_S{$$B4X_e0d z!)6bHY~$m~!+tZnv{jZbYc;wkD@zsAXn&8#&Lx~L?+E9T=%6w=s6HVF?J$zKeVZ{o zH)-B>;aoYk2Q+u;hE%VL9E|MHcVMf$4m|S>+omCoJ_z3U-lrdC*OvABU0b%%&rF|5 z9W05%TIEKd{yv+&kK4ODd0!d!?vB|lTB9TNJTe!1gEDigj={t54;H;A++VN?X}IV(#1?x&^nt>AENDtJtd1~KF2?Ry zd#ijU*(!-|+H!@s_N9==&W+ey>?n*~J-@ScFhiE0cXKxX_Kc|>zYeKZS=~h+b>9eZ zQ%^G_eR8Y(8_50B>R^4`-1F@y8Jj#jDQoqr_%y6X|J>aw{{f!TbgN6GntKULdelz7 z+|WC>waTdbrt%eV+67*Oe`ILgJF-tKPS5Df2`{2Yx0T9mJqg*3d^dadlrhf2lTqyy zsb%jN+-vEbCE541${WDW|0aK0&XJi0pViYUyOj3968Yc|wvL=xOc}>N9Q66XO&X@( zhc1MNblL=(rkhWP`uD#T(Wg@A&KlYGEg=7H%p{}!$V~Je*hw-7Y=3Ja=-?gmx|8CR!|Knbn z-j|B=`9S9i`BL`^dCKIzkpDNJm7lgkb}}UNFXlfD+MVxNAq$J>J?~v1o!cj-b1AgT zKRhv=A3-al+;KX_R_50I+sZWYeLeSGf-T#Uy#+~C=i9V~8JWkt{c5BkrE45BFVF|a zu4t8mu4G(|+xaiRbw7-bWp)q#XBnqC_{y$ol|IKC;KmNb_Wc|@3$AXJ2V*`D&#zg` zeSv=`=8Jyf`~X<@fF0vLZv8X9j*Po+zp`P5<43c$Y~LvZ1Ff>+ORdu5((!R!n-<%< z)ArQCty>wbt0UA0WJR53=_0gTb-IZn!J^Tpk;`dvn?m=4*K5ovZE_UZrNy=>6XePc%IrGytBHpy2 zRyotf4RBoxm~V1~SN;IG%Govfxb?X`Qx@$o{Vi+G+)cb0k|EALz~e6M0M|P}oUJaR zugn~=c^UX&tNa9r`%8cq=le(G`%y379&Tmhir@^**c6U%wvBu>_VZRba=2B}583(( zaFciROi#DUBfwYzu`!91oP5;%m0P}v^nD-N7 zt#T1CX7a%#P(-sb-!fn3%j+wK`Htzoyu;rAN~^Svx5}FDx^dv)R_Ab%e{Mz(w*6E0 zvnGwv6Q%E;t+MoA)Tc`$#9=Jrv3+*&e*0`z>tM0^Y{g$|J-(#$y)En(%p8JVN~W~Q z?ZBw}mXAlwAHDQB-_M<`KkT#Wt=}uFE5e`f;x<{aq)qC+XUpy5#?SGNP!_GzI@9(| zO_kiyZF1;a+a!0rJFh6FnSM}${b-w94D`Ei__(zzLhp*~xU|E}09l(ieCt)U$qm4x z8qa*Q%-}CVd&Z@u;XWS3QMrnJgv#AMy|M~DG`0=up*Nnh&3li?adWWiDorhr!4+-t zC*YW@t(Op2S%Q1x@JcZ+cScu^BO4?C>79-nYTKlBWt&`J(xDF!7SbfZRkt<|orBTO z8H*O%&-%6A7xO0NIQA-|__9Y&C2QN{F5oGb#}Frv@Cv+A#B1%au^)(a>6{uFS$h;) zF7_jGaGNY#PuU;Vva5W?nMyI;wXGb|Chr7dd)NSXJekAWc+#>4sl*osqfeiPtH!tXxo~4t19LF%p*Y=ene)lFw4(nKR9$h^be*@#(l^!Yh53f6_O-=?baOwNF z@iX$gzss|2oTiP<+s`McMImz{cP3}yqsYk8;98zxcq)(NeHiq8D^rVDJ1beAvD@MO zGkeq7^7Uz2#-_YNdVBHn-(&pzTgzEyK;sv#m-|1d3Gbz)?R!wGCz%z`ttMWJ|3-}j-C@;Cubz9X)mkmbp8ml)E7GMxztf%CM(pD$@lmo8N0Ae z#y{RB>xOK;ecZf{-hq#J?a%BZ>&_-Gz?sT>++;b9`FRR^Or>=Ky-HVJ>Rj!iT(5^+vGLC>T|G(^>Onp?5Qqolf!ql1$vo~#(BQSV|}dMEqVPt zTj1)0O68!QL0LNb74D6HmAUZiGVm$p7~rHI*t+p?Yx`SRSG12B9f7W0%(1LmIdkTW zRc!SF-n>$KSk~M0vu*MaFyPYk@o07=vu~yUCODfS4Ye9G?WA+1q9p zCiu=gLwou0Hpwbo%feiwc-2qo1Mb_!xTcej!#Di2&G|p~5@XAGlO|^ZgAcXIpMkm` z@g4QgguXH{x{&o{fuY~`-8N~^Fw#>#ANS*0S!FJ=${bm|*ZFvHZP4%?#x2nA(g|^0 z6K9K0Gv~K^?W|Y5qC{4V&y#uhN>{t|hJN}C@JyVRhgTlOxohvC+?6?mdot@~ZM3E^ zFDC7n*R%Mdyjrz2f9V6yJ{YC@dF!wpE-6=DorPHML?#noO)OZd9)>k!tR%-Qp5Jc|lDu8Zs->0nfO2K5pbR^xcM+Jzx5*lnt2sGxDfDcy-@gOrOa+x9AMY*ca;?0>$Cc zN|m*dvs1{~>;sBrZQ~b^`M`Mw%<8P8bwwAowh+vxdbd@MYM0%>F3k&JK5qNP#Itry zC#~tk_q~`;t6JOTNWi>jNSKe?`#MHHV%HezE749J0CRt?-_?FwOp|~o+EON4Qur$1 zOko*w<1)tBvJKc7E>T}=Gdc5NZ)^QLxicSZvYXmv6v&uwpl|kZ({9{TYiXCAK;qYK zj_)m|aX-iB(ReBDpLVy)Pk|fM#<>z(bM{$tx!XLG_+`k~s$0|W=YX91elhN+VcsD= zz1RwX`J_tsLT{oU=h?#SL&*Ke^M} zW$AkuPi`FuaOI(#gQv91D?sck(Z?gz1?oG7o|ioPee{#3w#$xpQuj~Tya%}Zejnd{ z$oqbPHG@xm(snq>>6Ic zHGlR@$dBv%vW`s}X6(JELf(@q=j@5SobK_4rtsb%=Z^HhzI)r{8^Eb1ZPqkBXCz$j z9Q$XDA)k)HFs^;`bq;gPZ049*vT}D`V#`?lc*CTX(GipOryn0+Uv7EQ%uU_<)zhn; zhqcw@U6XqBK06Bc?~QEQQ@Fcn=gxBJKc`)8KbN-ng_}31gOJ8J_jru|IKBTkX6(w) z8c=t-e;ZwbOu1MxA83~^p4l$_CO!5h9$tAMdaYi4Q)`AS-d%3aB6U5Ct|^skn1{S` zO*^mAuEXzdmy5PD@3{1Q+}OObx2i$Dt)aX%yUiJ8)|8F@lXH{!+_H zdeBve7SfsjeLhnb&O^T2E-l}q51eD`D8#jFA&>3DtdS|!$od7ca$lhj+xnr;?`xOW z0AJ4cX=dD=fL4~pv-&jr0oGGD%=g*fw#%~LA@688W|hh;)&jNxuxp)b~z95ZGQrq zDKECC|7@4b0iVyOMQbW#4SKaT=+)MsD_B!#)1d1nN_vcegXRX_F-EP74R?d`{G0aubahh`1Cxz?wnfr^6FaYeoFx_ zJGxfR-sIuUwQ|ur3V7<&TB#(S#+T34%GWY_KkR6^wOwKEeg@vkg-N+P z<|oXj*>~M4?b_q^7&lPmc0aX1s#YZBQ^1(U*D=LBtoR^M`ZP4-XNPm6A#{GwK2n>M zB}ylaVmydPxSzJMuVo*;EUL2h$i`})eBhU`W@I(#uadC?lk)zxN!jVr_i=MysUAH{ z;S4?*ze%R^WB;P0%%iTq?cxTwS(~s~s7uPvfcQ-+ACKN!A@3c>zGrlrY(oY>S2rB{ zFU$K{%{twp-pxX9V*Hu0FKjq8DHj3t&)9nLaXX(p$$awoe&>@U)3lMncO!%6<_*uu z*cZ=jf8hPnhNS!hxYnf|;BG$7G$-Y0z@LwOn(YJa!Z}7(r3kY>q-&Y>?%gU4$)xNB z#`PPjOCIOUEtN8~gSRB5;#pfR2)KsqbkxxhZ30O0BC=_G(>x z3wT-6)cZcV<`>gk_^e6O2?@!8!+mq7sO{=pBag}vKo~VJog0A4L z$OcCc=H37qGi^=Y6YopPVSwL`K281DEaL9NBJOlp%Sxxqi(Ffd9*TUNl-q#^f2(aq z9wyLKhA!~9zO5RUw2?VXpW^+tGn2CJLrF>ej&zhZGTq>JL92>sLlvU<4}f~5iA=yX zjeH!!{dDbnSc}W+1w|AM4kcwtgbb@6=Mw80Lu1F~&kd_3{K^(c3s zN2W^VlB5g)*P6JDGwAjm7xpAU81?JpGdV|~zm=(6pW+R-%abw+*f(04zdbzau8{8W za@oXwbx{g`+8ekhaHIlhuQ zT0|W!W-eM1@auY?{S|W0Piy7qpVt~+=a7sXp!CzBZFpiobZGbPhhDO`R!;aut+CVd z)A=N{WxovReWc$B?M^`Rq4<;hUh!lB@8>c6fs8#spLme5`g_|}K5p-rPM>hc z)Sky3$$8unoW~u`dEENK_mlP;+vzjJjg==?^ohh*-k z8%av`$)r5x@)6)F&(rty{$Rs1N#01|BhHtqkH_Cj%v&j&U*enpvF)b#-TbbexzglE z@w@mf{5SpWs+Cf!b-WRsepHHS}9*X!{Jx2lp{X4QocbvjX!wTN-4c& zrTq7`1$_H;D`oEI3wS@je?CyaPq}5K+(!8&}ECiD%wf zxwSl9MwM1?u8_^+Q{+hIuLGIamJMOAimpQK^auZ(l()T$4zCdGnk(bO`;4_NuRm8ezFO_3!ZxAnLJ4*kMj-iZ#Nd7-g(RbrZ&DM(1^Nw!Q+s z8u+T=;|^dvm(WZ)Ut~4C0}o)oxk$<=r_u(YRUcV~FEn%oqm{B8TeLYn%aBW#%D}s~ z$d7>={%Gsd$D?@%$h?cOf=d&BU4^WpPpqa7EE`h4iP-bd2h{K9*!#E0vh7B#idCshbNvCVd>N#q)zDY;=3)pk%-Boj+fb<8?*dp^jxJ4RX z)ciSp6FjlbSaowgk2!zZhrVas4DyR_Yj~G)*12eCy6BtB3C>USEU4i_Tacx;$h9V2 z&OqT6O&4BsE())}tM4ZUytDOvFy25;sedWP+S%l9#}@ZK7_tOm@%H8*2qnoT3@*w#Yj!+afP(8Fa5Z^aW(hI}Gg}=%s(QeN1_LJcy@dAH9*Xe}VeHBS_QU zlU(_KxigC&{~mm+A$#iGW2E5W3I>JUpK;gr+AVSiaIMQrh$G|ohCG$EQ)G5jCFXWF z<3PvLu#ny=d|i$#l?6jOj+RT+^;_ghU{upp`F4DX!8hEnMP83-^{Yhj-N3?^Y+V%N znnpej-wD#ywx@qp-MU5o1^9cLkVZU@NB4|#=&QWRYUGqPq#K=DE~oZRmaXWJt53j3 zp_M|D<%~sh=DgeP&$YZw@Y}uj$@H1F{mPwbs?Nw&7f+O{%$!gE9RBeZS^1MKc{`K< zcV!!Ubc-y8FZQMB^u> zv+#}D{bqvSF>d$xUz7M|z1BgJCA3=Ki5R~dozXX$OYWm@e*<057J2*ATjVMCy%1*% z!_TES`qh2(=OTVBGizP)PxKui-us3)6|gd83)@Vr2hcV$^~@ZW8QmgHFKm&W zf1wV{d@x&noWYEfb@2 z$<9jV{m;->2WNH23xI#-?9)tN+f(Rk+q4`;cQ%{%sXJu$!5wnaUv2(;+}r0_A0w(q z8cndTMMmZ9C^xS|j+oye7nry@rzDu$na_*D%-I{V^+IYqqX>FUWuMzm9;1L=1yTvw{yh`;X;Kx22}mcmet3L z>nKSb8T*V!3iee>GxkHQS#$Q3WNf>$hHdDOw*vhpUHVdeF^z~mkLfSe*C8Ep9AIr| zG+szEe%7cDRDe=+XIGro!W?tJtgcE@Jti9Y>~E7!oW9r6GW+rkC7+NU7b z=$LG1=#Z^|ons}yqYq4%4~&+`>Af@M1L_xP(>zj)hqKYlTXNypsJMh4-oX^yXEOr0Ua zsEvYjbjY#51)8>*W2PDWebBCO-_vj(_v4s0+GBN_`SI9`e0xdlO=Y`zj^Y?<{y^QAIT{kv(WQvw}g zmf>RNC;A1x@-ylc!L$Q=jk+_LKR(nUOI~qpGmfqZ{5)Wt(nz-$58}*FpRLv&Up~?y zi-33y^YQ$dbdNie)_5r!FL7Rnycf91eJ{XG-LO`l-ywen;{G4tjwf?Lhg<{Lxt;aF z!=oDYX;F==hA)(2pApEFD|_al6Pb%n1e=RJ`CW{WPy9ZY{z8ZR1sHwU$+`5ykj9y> z&!gpZV>rE=dIe%x!pE&Vb0_l57xz2Haw^xla%OMukUs#vyyer(KIReLkfyWR@m3*y zw|2<4fYN`^rnP!D$-VGi_4S6{6qzdsci$Pjx7nHT%EQ(Z8lC*ZK;4>r6uCJ%IfmK4W^P(4TalPM^NAL*5SLw5&qshbN>-fdAj* z_(#c;sYm9r!EA>d_;80Dm9b;P$88%n(1u6sw+#naU+w+AdHcQJcSHOg>)+j4$4DaobSAf)awU5$qSQk{z@afTY6>{A;wv(gNbs} z-IMC<+%iM`PniDSK)%fVq>CrsPdX~jv#wFkcF5^~wdG)q@^S4`=ujlrA@hLPZ+3ty z4{ewxeE9i}{Cgoj9vzD=lle37ldJJYr^?vxku63UoBy(H6X32-55LeMe+A+>BEU@^ zk$?ZtA)f_|zdNNdzbSsZ(q6S`hq#qT{CHH0L{lem4n520)smT2?#|IwQmk`Hdt-Ze zpi@@f(iyaSKy&2~___eUe*`qgm%X)9=G_+Z1vJMu#y8&%ub-YzGdfu2x@E{f%aGZY z?ZN)joMmtyxE{Z{J7QnI_+&exQ~n8L{_Vyex|WcJ9OUzuI%eD+)hX`-{CFXaaqIEe zJfS!1DJM_($=X4l)aIT%E=3Mn%6i8dZDr5V3Q0F~O8N1fGWj*zhCXh~!W%1$1I~wY zcD(cM-U*%ZYv33YN87oW#yIqOv@Gycozy8O1Ae@a#-7;c(Rk!>;O+QI-B`$DfIFUK zQ>WyBxEulQcp92JWf+Kg0^H=0`rh0r?**PY#i=$MgM?ev$8&lr7QODI0;(*9+whY2@4I zapldW7(-oQc|#i0n$Hu*>n7gTqIdy~cs}mpWw&+8KLEdeLmK19=W+Eka7w2<3HbFC z(&(!`kBirEYNvbz@Z*Iv=5n7WF5f#lW&UYledYoh<@52leD9)sKv2GbM)^G4wkK^# zA2L40J10x0+WWB{XWz|S13dK~ZBr&v=*GS;pzF9T=H(kckC$e$r&B%+6sH-|ZJHqs z{k|X9)o<>7)YJRJ`VDE+lh5PgWwv+9mjJ(ALK-rz&tvMB{ni;_-OJEKR=oB}=&G)fqxUf?m0*d1X zbfWsWmxr{;1AB|APjpHva6#nqpx-a1lTN^=d4MO~*C|&5#c=~VaeX|R%b9R5@2u^Z z>eg>;7cT0Qp8+x{tcQS3IsxwLq3)BNavD$^H=q+Yz+K$jC7m+;Qxoejpc6O1UEIv4 zJLP7exc&n=aRc1do9v?9f#P}#=)?_h7q{WEPWcc}951cX>{Kwo|SGit8$%6F0zJ+_7srW!ZHT;|6r%`nVm} z_N*q}1CM>KQ~-j4KyloFPTT-@`S1Tc^AeD+H`+nx zw17_B0C#bRZ|s!+0L5_wI&pp6%qPekb35Ysg!9px=&L}o#FZV}1(huVI_U(sD|`B8 z+72j=8_f9a5$f#P}#=)?_h z7kBWl9r7$t95)NSXTj^xB>3sR{g6(T7lwv4(P-U zaF^%ws~vI`P+V65owxz+;tu}1Lw*Ys#|`Mj4R9AX`9B?UAy8ab0iC!3?&1!--XT8) zisJ@!;s&^h%O3PU9kTR|iFFmwi5uW9Zr%TO$UA}JdJgEs4RDv|jC9Huf#SLf=)?_h z7dIPq%3p!vxB;EG0j_b;CnQTc<$r)!4-nwW!~B*jMRuPS%C{kne&_R;eBsx%tW*9C z_&Sb|MxJ~g_r3lKWMv?brvn;e#K+zDWJagF1@ONY(x?QVN8?ck+1b?HoI)K0xZ_DK zM5hAe>&eF*&)||yX;@m|32?_#Roy9%0dYD3?sytj(Vjre6X422oAj@tj)0(!0vdJX z;~EdWldPqVfS`^78g=C3?t6U)p<@C3?}apEH=oCSZ){zs9C&c}y^zLO@_95KZPH)Y zDOHCS>cGdNjZotiKD?K8tq+MrIq@Y0#fh zx^@G8`?w#^_-xr=&Vq-HO>z?ZEK?6NWbpfp7huf{O^3ZDdh?LReCG3LJoNRMZ*UaGeaHZS&lk`fU*enOAMp7Cn&ZpCv;MzBzJTWV2H|<5h%cZyzP@jMk%y=N=3uun7AD-V9@dY%) zhfe63PI(CSdOpTYWoI7j#tmEXIVzK{oVlivVJ@F#lAU$`>2`Nuk6t4GQ2(|=|fUdJKNO( z{%jThNPS99Qks^r2>0VyU%(4wf4+dNeuVR~gHrN7z#khv&E|Dd`PPfci^P^?3fL3 z$1|`iB@Y8JPk=j~%w;LL28ekA-0?JAo|1P1F;9Ryp2TNTawrh<1i0hL5guJp;0bWY zGf4Oo9#4Qfp8hLSaxV~nE5IF3<|@hs#5@7+cp9#zTtLhd;Et!NKPATlF;9Ry9=RqZ z)j-S>;Et!C@OwR;0CzlfyHm0Sh|>vh$1_a$-#wlHcRcCOrsN_ZPA9+}k6cThftV-2 z9nS#a4|_ZT?sziSQO`h}PJla}wOo&OZlT1n*kly`t8qAw@4qW0pZH2jb}k@)N=ua({7=w!DUCT)!wWcN zAfCYI$Bi0Gr4c>P!XA7|rmU~x=;#tTnt3Pxrt_Ku@JB#dPvG3WsZ^SJr}J*;3|X*) zcdc^q`>5Et{2(PS1EmY?_XFIuNA8Ifcc@ZQx5b_l2e{)IApBX6C%_#~`d9QtAa2(H zcRXW+&w8rB6X1@gkMLVOo&a||iC;4Z0r9s2-0=(${;~^Bi*!5T_I1 zjz^v+&p^x*;Erd2@P|E~0CzkMzh}(=;&cMs@$`+-SAm!(z#UKG1^Oxw^8|RDAHpB@ zcm{mj@zni+It1c$0^IRr34hMx32?`g{3CNT5T_I1jweTW^kRW0z#UH?;kS4^0q%IJ z{=}LJ#NP^V$1_Oy6CO{1JD%j9$ukhA6X1?#jPO}66?g*N@$?aXi^mh-j;HEn@(je^ z3UJ4hCHy&$C%_#~@-O5Wh|>vh$1_IwtiKj`0^ITR6MnD96X1@g>Tl#3h`$x!jweg_ za~@BCJD&6ya{>^j6X1?#@E^1n5c34M<7xOOV+V+N0^IQo6aIIPC&1(U{EPemaXM)q zcRXXzX1!YA@o{Izmn+^I3WQMD9@huRy=~hD^ZyIFVV~OJ(}S$UmlxlclPnOO65jcg{l^`fiUW zsy%==Ih}l9d>M#+^#!=e zGk4^U?UK6y|6SC8=J>`ocS&Vy$mi3Hyp@`zeuKxjSCFmNH)*lWn}OY%zCDuZp#6Xs ztE`L@;QEcxQc0$|5Czi^I z)PLX`_Z zjaL%rlPY8P;tRZffg~^Nl9zyVjjekhx3-Dg(WmW}*7Noq_w*z6t-d#Wb=vgtk?B&m zyG!O@+a>prmc47s9owEN?%FQruB~@(-|15ZHIGZD$kNk|uMKl|uKLnaxpbU+vLjQl zjle#dKGJt}m)s0YUSabX;I0q$-_Rvr0b*NzAGbcQXRDp$7`HvleFAe%yc>OO7QYxt z-VmWbuA`qVADNbaw@3!MWDRhE`7V18!i8{w*W7KT?}j`S$d9A_i2cgg&0TW*7rU(A zRHflxOYwVvoGVi?9>j5FOx)2WYk|0oKK^F@Y6r-Y(TULxi7$7_)4=9hTaN+$!xH)7 z$b7kfh_@n&c;5je5s2%`$1B&e z_ha06daAt6tuL;xp*fQNUYGnYuyduYuKwU~2#&nO~mz&R>&n|h_PpAvcgPE7+7zB@O#TnS3IZkm`>7JjE+PW%;>q z;Q21OABex}Lvj1Ud^u z5zXAmqpr+N2GdjQT2kmMbLcB`=_|bTGS(%Z2Wr>Y{06vd!_0V>Tn@x_8Q{vpoLl#5 zmmCLJJukA8hub$gEA1Pd!}hdG`_cgAxAV+G8G!%Lf3ps_e1y1`v53da7YpPf;vD)~ zmyEl30UqZc|FD+>`TYC1%YR=@TU_Uu6I)HeDCJ3eqDd& za5sOvo-@_AiOlbotARBKx&H6trY)yRLu0pm6) zUUKCI(}OZ*e&RdHle^{pK%AEV*ZlJR%saXz2gKj^ar4GUs)DyYctd=H@g1JUlS?{I zKfS$MJ_^L?2Y8(R8Qn7ZgI@X`9$iu)mkd?Nx%K6;D3$k9GMs-0FvWa)X1BZ-c;*ab zXHBzbn!&#X?ZtJr%{1J{{Ww-$pNqUcN0#p~9@>WWJvqCc&)@HQey7e$`Yp=ce_prT z3&eX-AGiLW@VC4pyloksT_$I9W`8DmeLrQGL;2JXgtwaiA%Q_58|k8$2dN1h?o0Xw=BM= zTdsYdyW;^}{W^wpWGtUg+jNw1{;h7=1)TW>x6k!)sHGGcc|W=(*2KCOyXBogLGCG{nKoqo{1fW}aQ6mVKOs&Z zA}#g75~hb->8J1VJ3QVk)BjQEGd^zKEWFituFsge;tNXfAH+Cie^tub?KSt|e^VUb+jK9R5!&xKhStIJCJY&N$5&1r@ zT#g%`AxDqSlMOv{WGy<3#PilyiWw*DPi6a7x%dNH<%W9K<{lmmO_d?~^&I55_6x-{$CqBURc-`)zJTWV8rE!; z^8lYOpgF#(wOi#Bz~>8Sj!)KYm7@WlFQ7TT+`(IA(fW`tpgF$mAzS4&z)vrrIlh5I zx5{q-pD&;}zP`h#Kfvb;XpS#^IQ0kkd;!hzH5^I(0X|Y{uY1AL!^93}=*O#XL0G}_QIllDi)F0sU1vJOk z&_n$JK3_m{d{ytG{s5oPr>Wnx+0h*F_pZ8@*!2sa;h)$lYk}BbOn~b*kO9UBZ|K_^ zoY90d)L0&mUBi~KhVd4gw}uVs8g^7Uy4ClhSM8D2(WwQW-EIvYX6Z0_Q<>+At@0RP zZ84G2JiM|>-zc6f*6X-i2en*AH!({ZZr&A}H^;-%{k3s%`Q*u z{Ehkpe7=C@_^SR+{Q*8-Ky!TZ3iSv0d;!hz<;JPMe}sGi&GBXbN&NwSdI8Px4ZKSI z0X|`U8BvfadtBWSg7<_4G{={nvQ1tC{PY5v;~SW|O@0ITd;!hz^-bF*_X9p(Ky!TQ@@;Y> z;PVAE$Ja1@o16#ud;!hzRn4US0G}_QIX;<1{Q*8-Ky!S#+0@^hkT0M)zU*A;5Af3q zXpV1SKJ^Fqd;!hz^)00S0G}_QIllBF>JRYw0-ED%SVH{)K3_m{d{xV+KfvenY4)9M z-UF|nEsOWy8xDQ5T~DxS9!f~AZku!;x=n6BQP&OKtIjH-q382>?0UJF^>X%vb-9mq z$H*(lN$JD4$pGNnvII2EKlVWK)@`yD=)KqK3w_+`JdQ-?aRfS#xty_`hW2gp zNx=VBK+|ur=GP^+$rhlX4=Sdi)A4wskCn>D&>t@3Y$CR`j;c!w=e(Ke=?bhqtWF_; zPfyn6ZQEr0B)bOtxYhA5v^xI$seeIcvHl{b$-E6#O0+f$yd@T&8F9j|dKuAjn=_*t z*8PLI6hr$1r@q~O)5oo?!HMD7d7KVSZT$S~K>8Xs-^}ao#(K8N``)`vvYMv)DIZPX z@0@dB^m=|+RE188cP*-r!{&?`-{#r!H2GUJSr*}AzO;Us^q;;>PJa&`9XHzi1^DI} zykUU88{hwjR9*(Nu+{Q0=^Pd_#(3%~k?9pybj`huLjO{#&M#?ksg_ca|omik9h z`r*LKF1=zrh^zfl&vh;)4?zB0$HyH{`jgw_A|U1oaK}@33H=+0c>>(=BtErG4h3SK z0CzlNpWY_3E-ml`xZ}wZ{+!1X;Etz%7v%!tZw0vH$y~OLJ3$4W0CzlfpP^hpoKAqp z`62u{k0OEHKpz<}tYMs$TLNDfFrw+(TBG|F|ZLq|Ds9n!`~8aPDnmfd;u`+@u7p7g|C-w%{|Fnh*O z{C(L?@pFKAwl2R3E9I6RszN_RU4D!sZHeE@@V}0j!P>4F+h;!y*w8bfYbxONWt!>5MpKBhZ<~I&jv+9SaDRt>;d?|i6o^Y-2Ps>*3cSYqs#2)fK+q+qKMuYWi1=h2V5fgtw z|FgV!8PAent6-}vTm3r{@@-i-VtL?S0x#yF$On1qDxBt?np@WGy{nSn%9Hl!Y42?1e{BNxr#q{L_w&?I zf6PPrRM< zVULYlmv;70bvEXJU9i^gf|%K5y1sX!+i-{qe3qw<#94DAVoTZ;dl``nai$-ueYEAc zJQ9y9_;(ink*9u*(}`2zLVt+-dz>NRk~G}66FJb0`#$8i<+waDPk_G-d1BmXw>VjN zF_#p==3JcSexqAQT}$M^IQ_P_}j*X`dKo5 zUuK#0EP0=yc*lC2+DE@z+A*eWR3x*|A9C`d4RN0?qGR{!USqcbLqB`+Aj~Th6yCPz zko}sBr$*FmkGVtDTs#Lnv}T;j!9B=bSK!_p{(g4bkbHGcdcL|Or28damUpG`J|gnS zeM^Nr%F5$h2c=!3uYo$uFYPg|;g62tCKVl(ua=I^S6{H4IN51y{Ep@x`Kkn`{`;IN zPENaExVixSd=9?XaYoH3dC$0OWQV@)NZXLEHlVK(zE^=X|MYxy7EbeSjayFpEM0xJ zLh|)o*O)QfHX~o{Ju6?e9>;cZ$#Tzi{5#I~JLSzuteCpKh0@N>oRzH_=H;v1=I5&} zC%=q0I}LjWw~e#`-Z5=Po)HW3)y!p5F7hR$sW_YLDfljl$Gd2b?$yNi)Eqyhyc&GJ zwk%&&Jb z$fkV#U0t-FTTTmLOyGJeFmr_aH+l;lKk9!I`&Q_fecY3cq>&>2S0ue#iHm zeDzzLc^B&Ir+68I6n64kS8?g$pInn%oUdNOY4(3{GTX4@!hALGqWrkuc5$brg@>x} zYxs=}^dg-*ypS=R* z;p|{Ox11)|yPxoP68FVg`YC?<0Dp65+-oDSZ^ZAI;68iTmHFzxs}PSXbzR-E)8BXp zB!G870$0Yaq$ApAzY1>6S6AZ9WnQ)&ehUz@MC z;yh3CI{h}nkzbH>@`=7jmT`eK5c%WsVvM@w?7cJ8gy-yUG3faq1Y0KtA4-hDJkdG7 zpNa3};Qf?c(Kfq2q<=rt(d|9Nc@N%sU%U--49H|IuR}iPHz;fTn7=bs$AkIm#Fl)u zVwG-Bw=CaHkl#9RW@TOvIQgfgt7)J5)n2&AH39Fkrk7-^;QjgPah#3hiIX|bJMYd{ zcixk)UTf!G8}mne8ur?58_Bl$Ul1SAM@W}Ff5?@t*Vk+{$xwUZUe=z-v*+}2ooQ3>>=Ex0 zO~G%>#9QOOkfYlTx^W3QXAEI}==eBa4f_k$PyR-2ob1#Mzuf@+2RNHQ5TEh=0+*aN z0l$%hIc~x-{zi^@ml1Pbs4HJxiu3S1-M4YF!#zR;|C+C&I3qvjvo)8j-|?QP%=#qP zipKA*Px95if9vr(P9}E;^dmm)DL+o8O&j#;?>*(m$*g}9^xJTDe6H&sCp$Lyd%2(G zt2c3)c8rtB6a7cNdJ?B;$2gh(1iJIp2{=ug#mThs|1)2OaGExYlj%?4=lSXeoW`Fx znf?U-m9I|4Y5a+kSym_XyMEE*Pn=Af4(R<~_V^Pg)1REL^3};WjX!ZR{qcXDua3rP z{E3rURtxlh#%bCoPNq#0p5%1mH0|S-^&IWT96eG6s$6rllNY}$Ibe-C5$8v1^q7s4 z>1SZz8np(e@iR^q8(dekK_3_-KIkzMC)3Xk=tm4*6RS&{Oq(|7)sUXLxaG8L+#`C< zdFtLVxDc_iSGwBk`Js3Z5Wl^-Vz~1jX%q6kjt%eWTK?wCyLz8dIBJc$9A`Uu z+;VJ>QiVN=-20NUYlf?wF?f#{r#eBG9VgQ!G#b9)G&XKo^TzJ4gq*)Y^C|A<;qS5d z$F5OF;>;trTTUz7{m#Pf47GZDx>~wzh{|mrtmd{3QinGWR0lQUH+kv@sD0|v)TC0S zcK40PT^N7i$cXcMLGsIGp+Pwf&hg*JBiDyR)VQACvBw<;y{`yjY&hSizq02|64`I$ znlXu(EUaClZo)Y!Pmf8roHlU4pn<-$P})%6tf9U^%hIr4N%Q-*_mG7bUwcflw&;7#4bRav=M`6jqs(f9hieVTkym7X`AJf_#ydKVUHiUovP9y z2PGYc@uO+aX{u?WF24q!OUVGX@2Z$rl;pf3z)LX zt+Hw$OCQK#%IcUjO@(j@_jN!yZl#<()VBcbUuDW^mU4C)poUKGDWk(GV;khz4ogoN zIeSf0Et7Q_T|hZLql`YTMH#O;eLQU7Wc2G?r#-E*Clw`@y%=fckhWX_*)F?d$~1g` zQv?bX+s7L8salO=!~mcl>d$yS2s$T)>x8ud)_MdCCCwnxu?#7V~f%~0ZPTrxu$>5ppd>%Pu)=1ybWrK5^E5c4EZN%Wo7^8Dd4b#ln zaYihB9qJ1s|3i-L*rAih`uub4QnEACXJz_^uf@p1Zc>*lW3Fv2+Ggw!l{t4rjy(Uy zcj&t$_RQGZ4!t=B;X@bX9445t*Kp`G!t!2b`Bg|0e@_ECe(XGKi}c6kz4V9p^p^Ov z3!XX8&CHSS((TkXyu)srfh9dY$+#U3IU>&CFl|$K#55H+T$lM0P>vIkCO%;9S%7{$ z$MkD9eY)Dir&jQL`SkBfm-dz_bh_$>D_*6-b*jm#n}5EpM-(Xa?IgC%o2I&s)Vh;` zy3UK|OjFWE8PLf*el}3#7NFA~qlau6?Hh(GQ_~uHPCOpv%6CIX%+1I#?}(bQc^-7; z5UxugH!#7pf5$@1iwkra8Tc&6D4^5+V~5T{tgN+T1@SctbMrF2B49qr9JD3Vmw{hT z>zS+5*TJt*J?oR#XYo~h%YnT)B#v?+N10%J4KCLHbseLz4H!LI`}zb>+ARxdVt)rv z_hZYk(^MzQ7494GS&n;wx~%jCnA^^= z=e8TAyguf(=dE&If*f?1d+HoL4)Gh(N=#1LYzMWnj znv)SpFU0qnInPU<O3J%Y%FEPiy+PCG&%4T!G5H!~Q@SauAy4zR0h>^+l+g~9V*=8| z?lhnr7t=P|@*z;>?L#hf<`5t9Ae$89gIc4@%>nxJ_2=0@IW7W<-G`L_MLqU$v#GQ=% zH-|I+A=~A4N_pgc94JSdDQE1^1sI3t*yB+8{|+fLyYF@EC9C|`AP1cidg8!;BFaBr z*L~*`bom`X84uWh#P*kIT*I<^S;LI(JH6=4+V`_wbj~K(1ob?gF4--c?jwpHr1>WFTK;iqWbEB zuu03N>@mHR{gNr$V{ZD?D*Fq_jdt0mqJ2=ll)YuMF1rzFBBzbcn2dRGdVH<1zWN7l z!8IY?n$*{Uxf{?1oU=@fM380zS<_U<8M>^~&eUa}4Rp#LK4>!5^yy|z zPs>c7j5TAfd(FUgQtTQ=`eGZ($mVsDkm`=7Bq+Ck#iF4 zgC-GJc=j~snx`wO{mp?a$I(FH5WVEHeLNjJZXZQ9KHh=PlIIbi`1lqu6(8GSA4EQL zbkWBseQE>B@djnlJ3ewRmu>CkN-N=ywU_&*veZ@9jRtU=LqfO}avKxGCuzf$^Vzl+ zXg`~P1EA|dx}d7Z`f`r8nRuSA+Z{k@yPdb_{pvkH;rjtlIJ3v5ZyDKk9m#)JffpP>c)OEcZD90ax5^FgZY563ew9_4w-vCN^oj~0dL!>P}>1heQhtD}B zd0Utw5vx#O`L=U=5DD7tk!exb^{3uGn|D z%9T2AgB$_MVajc}9D6~WQrsy(Ikr$nT*mGNNdFJ7LOC*bEbF<)G81X$kTMR3oXZ4L zM#B}@=Qijvs_|Kl3xHB)Hsfl%6<1Ly%Ze*guV$;v7RW7DnJrr}Z?cT7SL!l90ysXc3Wkt+Qhjhry1AWC|AmueU~nC z2~cEw=fbJy@PR$`#Pw1b`+|q;eSy@!5d3{zFV#bD4rzl1$azdKZ4kW=dy;!}`Ip?Q z{aJ=I@dx9o*K0T_yHm>c7+0nb(l^GJ?}uDyl^j$RR6n zzwgSP)^OJNxVa<`oc8_Ns8wz~WP2_Nu0CExR_Su4;wbZ$XO~r8H)MN$%y}4X`w-h1D906)F%QS?2}|3~-h`NS&BG?m#AzY)(ti=4tOJd} z08kFouku;yB4`fL$$1{^gC-H!@yIl%eIoEt^6Uv&jst-ri(c}X>yYui%t7Mgq&oQJ zabGaibg8S1*~Q>A2mb1ZDuJ9ZM?b0kEd+|c{->s?;G_EUb&qM?6F||0TeW;GP~_d7 z&~=*v6uB8F>-euI2g#jB?zE{RwoEPEC2bF1cG_VB?5j#=e8#T#q)ob|O|0h$fz3U2 zk~&U>Je!p?{nzm<0wZmDFYX6Qo)dv`oJ3jllF#-r{nU6LMK(U>;Irfj z0-b#gFclwHz&?ol<|zE3_Avky9~S}TSV~#+PW^h>GvNMa+?tMSp@e4xW-T7uOB@*8 zvAyYf*$<};9yn;QPwyab4LA#P{W5#5Pa8R8GUmbQ?s+hcT%%ljYuGJ-T~(YN?>X$; zn?J(_hH)S0Iay`XMh)IF$~QDEOKD&FT6eachW_f~KAk!}etf4x&SU3DyGx&R9WY(B z?LXc59L8rk?gol3du;D}<>SGV>^i}WsqasruivB3- z76TohdRdDk_BzF<(b$9ZvL8r4!<2~oUf}dYw%4J+jOnUnnl9tv>Dr%>NOQ_$-(~jF zcTU`c&ud#-zPhX?FY;=aK11H2#e1m}s>XGW ziHt53UKt0M!saBr8?pgBf{qW%287ZEq^SWpdW_-E!>aVT_?(fZGS&he|L`7g75?U? z{JqvH{pLL8ebgK0Ow@gp{%dY&^7lOOZsiDEPiIIQO;pO1J*9pPzDv1A-Hx&aoied+ zNR#iAfoBZfDIbG295Wq%<9_TQ@IHyZEz+u-cTA+waY!BP!^Fxf=jhnNw;hz6+t033 z^O4qsGgNkvDjbXNMjeKC-wqv#?{|&G^IrV@li!S0g;$SK(Zy-1K`^_p-;V-+WF>0(Dr;4jKM`|k9R~1D{Hy2dZR@WR*QeLh? zn-^7-)f5y|M#2@<)jhHp7-dnos3i8ex&jx5I#Y33q%>Sr9TPVc6`Ri`rLj-dMdo8T zrVf`(pEYaRAu&bC%$c)eLS1nqde(%{6?Lj>Bi=8$ zs3&b5(pqYcSKD2758Y6wvNj#B{1^7v4Y(2Wb@lNo@BE&$?MSPS9Iu*OcAp}x19lx- zdh8Cr8K1?@mG|`9VTZI1SGg@nQz$pb#aDL+u5ZDYesK?9^n2Lb*TT+~_co*zqHZm& zwC9o5iL_2vecwl#A9)*Hc7vPiR6FeIU1jWrv{u+Px$-VVS_j&y!w?`kIbOO+*Y{)M0BJ)UV~G0@-=h9WVl#0oF-oi;<`IL$9AXwx8Kmf5Zj0k5N{)1L_C!kA+90@iTe;I5Qh`LXwv1pLwtq!B=HVn z1F?=+NX#YfN8F9*Co19xx9DBI>{Mf`~4a~rXp z_!#jn;#T7M#0asFcr@`m_RGiY*P(ao`s_tKoahV!{Id;nm>i!HpCLMl_@|rWYXIgK zIXN95R#IF4xa(L1$%ue&t1P&WD%OgHlLmi{5O??$FSPx(or$u}4C zyBq=HVB-7ae}lM@=}%KWk@Df>f0gK`K8yG%@gkNxm$E5;8`DdfekyS*@n+&@8d8sa&`a$+7ampGd^ ziRdSOa<%4ti}*b8K4K&BJYs~njCeS4BGFI$YAf4?_!jXM;?u-t;w8igaV;@KJc4)t zF^i~(A6=!(d!5)$Y$e`Cyo?wn))3bcml1=+1Be0QPQ=fz)a9w`G-eTVh({1VXFI=5 z`G;ua@5^)sqE5^eI#Mg+A5u1oxiG}p(w3K1tS=8csbw|CRxs7JnuAMC z{GBYh1dUXcZ78cL+Ze8PEY#E2@f$PgeM{-{$4z zWyPj-qT+@0=~3aDh2=#Ud|O;8%gZa#?{c5P@oZ_hu15WlJ}j+t_o;T~3+mVdhye5h z{TwMJhfk>uWB3=$D-FXLwTC;+>C*Wl%vhKPi^>siXxlx(bJU{6%WH~i!UfC1>O7W z;i|G?Os(k8T9o08BolmUS1AQPt->uj+)Yz|jq?=q5raN2yC|H>?d;6@)4`Ntv(q_#rK? z@bxLvTJToSmxFrPV)c2u$8LVOT-#;Bu4m*|R@4-g;kKdN2V_)X-l#AV*;kj_37W5XHI46#!xeLtBb18=@`dx<-mt|MYS?2hsLF&e5POKE=w(kl{5l^*=dUh zAhRu;Nfytm(0eA@=2Wj-v2eCoYsP>{B4$oiRncY;nDp$pbRA6-;?netzum>W2n%CX z#iEK(IT}!{jQ7g%Y)wyE!kmO(p4~jRva(#S9d+QC}sR zN}Go%za~5#Z?&Su=II@tvwMha#%8Z#hO=#aDh*C?oll&t~;q$Hu9Fv!`|ZTCLQ)W%;2Mtmf+F(+f7yt1|mF?8pEWr?Zac^v7o+jDFoKd0u&hJQ}+DYa!)Vb%lbZ|QWda;ARmI(0tQ z@2bkp$AmYl-|7?$VHs|rqN<_|>eo6=ckM=ohmsybjpG{aYqu=3pxe+CUHq- zUB@>)lAO`}6Jz0&?+B`?Sk|x`+#bs-& zimEoNUrQbt)v*h$k9~_Pi%O`Uqts8+7COV3;}|KCL1;o(IsQS*YRXrYRn^oMm9MP8 zg%?(#t9?D&k>$%)FRsKw^X@=)5fd`iMOL?n#@!d_iCqUe%d;#wD=Jr`Q|F;VYNww5kU2t)9Lz>@O1x1yvCjf8 zceURXR)Y(o0JY1rU|YZ4Ui$67tS0Z(zAIcL|w ztBcBN(Bt|#oa@F~spEpWvYL6APPr8xkfsOwa+4V&<3z`cv6JH|0L6=-e?qt-1o& zY;&reUcx8r$zeZd9-jJ-inmcO4bovD8N?%Wt7K5uraAMe%)#n5=kvm{s%od-(W?(Q zDYBiD9II8U!d!!^K8UE&ib`{h@U%(S39Pd0fOdCkfJf1lo-u0gL9 zl`siy+T@nu#b4d=3Hh{v$JK_bHivNaDRGKxwt1Dfd_^0lNnFk=iqv46Z)U{|Nn2c5 zd2(%JVQqyoo-mj&5vZB2){RXU;6Kcj&Q@0)gI0vAHk4JM%S@6w)t4JCia0}Ao#A|5 z7SKfAzUwT>+|z-sljl+RM61f@1Q?3#PG-o`7M z`Y#+&PBCc3V-_r1x?r)HKlhPVSc>>As;RABQ8~Y|T2GmG4p>^#6WJ9tc%#8ttPfKg z!W)V=pzNJgLBYJbx`kLf!a7u|@Yv>XM;}2e9wDEq!&ni%BcC=@Vs;BERKK_s*D7=5 zlk@RVCx@ghRGW&ck&M6ZEL!JNnvy~=d~iD2kV8R^wE zB?UFIA1lMmQc$87y@GPAwMwm;w`}Fo6^oWEm^PDlz>ZY~n~JKjxIufdl#0&5T_eX& zsb#i0rCQ!v$WrU%(j79kn{rvGCMX1|^D##i$UTySX5y~Hx?(3}aMidjT)7TC1k`uE zHmP;%Fv;T$s#Ddv_2HUowU;Vbhw!PWRObpiuC*^v>#D=ylhuW4T}>EQa2Kg{o8%S= z-tbmv%8hGl*Wqsx6@U?$)M{0L#0^gLT?p=7Or4JJ zC6ZcGSzC^GS)3Ltt1Kw4L?2ZYY^=s@#EP1A$b7lUtBSNe?lfMe)6g^ll#Wqz(9ANt zIH>l7SC|#?UQ7btCQYdD?o5J?sv;$~fWj&)>`r@$?;%xGQ(1;@F-vN3b)-UFC7*>cjdPPi;>WMhsNsr@a$QXf~8sRE)c1WtD3S)*;IAPLJ)wI{L5{ zBVnDqtfS6Q8d1ab8+6aYov2zDsYS09FAEYqu z%4#s_Y)}XHd@cy9Bc-=WrN^NxkT-;~6dJu^UDwKRvhuQ;&2AHmqZCt?TWvE)OZTNmV3$?84J>}Nr7S^xSV**0I~#Pj zk?n`8o!w@C8?>@QPc$X5nFy<&EHA1>)-F%=L%v10!SCGXudG^#)gApE!X18W3)JCh z^Yy(?y+U%cDYtK`)mGQeXSv)m!K7cJ?g88U@Y>q->%&!sMfQjPKO8MHR5T40ZM^Le+1hp6>@F=Vy}l=lc)| zRvpxPwa**Wd->OVt1jP5pN#)O=JzT;S^XOC(B*seUla9wFD0S=-=}_Hf<`BNQ+=thk@lBX{lQ*OuNw3Phtv;2RzA2~}?^P~H^Z>K%{$EN1Tj~R#m z?)K=b@A}dO57hHpUp9T|qWH>~jEzu3rGv&+-Z?Ia6yX`8N$DSUjMU?P!85x5yy9yS z^?YwDp|F+Id(B^kN%%{s_nKcSlH~uk^;ImN@4qF~|J&AwhWb*P7p{9r_vbCd1cy^h zF!NL4u+;q6_VTym$M#qH9 zkNcjNlIcHSe!j1r(5zom?{yt9@GrXj{^~QB-|IRm?Q_lFUwtd{^F8o{`W605^N&f; z=!6>T@e7o_9?AUsru_9IPH^TeuXQ0eL+icf%`2JTYhJ%8iN2Y7{94}*9slj|W;eor zT79=q)#JfyAMi2t{QiSo9i{fs`Mt)=w#j{uw_(ih6))M;dyUu0)O(G$>C}5&x6h;A zpI9>|WjXa;{q|kzz2>t*>b=^>F4n6Y+ipkY|1`di0zDqQ{K`5->%G>O{i*lr??b5{ z?a@F>sP~#5S5xmb-injxE0gF?qyB&6q4y5*8W+7=C#DAO)Z@o19w$@pb)Qj{?Ud5K z|Mt6dd3$fsnCMYzDdsxX&h<^(<$rv33w$&AfBo^l_DiJ&zUg>*twql-hNqLV*L-2y ztMBXl13a7#P;d5mvnhM=+xB@$?CYr?#q!f1(*AhyXHmZ&?b{yi+yCHWT0evKjg-CW zYuk4uvETN%<~RGuyr=s1-?pz$(Y`rF`@l1)`4ddhK9Zt+dy4kklh`-5>H6_|)~b!N z*Z8sRJ5#jproGqrvH1f()c)wN0H_PrN> zLwy7Dd)+5U#(xd-d)=2ymOtxPy8d4K_T7`{_oCkGx^V{eUibaxQtws&Wcpm@H|r~h zNeqsoJ}Z_I|KXeJ%gAr`5Ap0uebfFqB?*7B{!NxYnSb3Z->ZMVqTcJeKmGN-<7+te zUh$Plz1R32lY~EudN2QXOQN5Yr2M=$bp5>gzmR&b_}D!W(S?Wlbx{qfo# z+WehKu6JzvZ7KSr9!t&toD}V|9?|?eTHi>L`gXDW90o>^*JC@{{<-vLN9{XO^gkzw z|8{=`*%2mK5zfQ?w7!-mAX0KUpc-rzg1{vH9DQw7+fNmBc>pXS%(oaXd6o_8Jei zeRGQTZAt9?)E`KHa+BEG{sdl1GJY)n*(utGQnW8kVsHE3m88CQ`=lq~x9y`T+BYV# zZ>HYdKWuop@AkLtn^UxJOVNH?iuPS8+NZzLcYOmdYW+kr-_qV|KC$m-22;F0sZz8L zB(X33iS~bg`rl01Ykp|?X}oH`~55MSFje@o)1_P0_wPMSq%8v~N$* zzCMY4C*#G;KUqoaBZrPm>Ai@+FZFnwOf+%VUo_*wUZ=ZK+z0qmTt{<~*xUQ0ZAtb? zw*P@7>$GiOn8Y7@o|>KFJd%~7eR`7TQno)iN$lV34 z>yqrJ+sL<~?yd%dS|8}(lAfjmXMi4WtK!5>iX^`1;S_2xZdlh5F9sP}pgHhJme|8f6UetT_e=`09lJFmvM4wB&SNr8t@6|qK)O(H3P1Jk6r*kg#UhUUFy_bKt zenWk-@tsVcZ2TwFKa`|>o}qqMGoUSelX~-Bu%%XirQYm^ZGFZ+^!W99k83CDz2ehP zz1Mmuk~+l>b=_M@Fe^TsrRb?anyUQFUj-`nSIyqdg{H} z_Z7d+?-l>KJ8QjH{|BH@$^l{&E@;(_VU_C+}S^maTb^Cj*j~#8}Qd(bH$+t9L^M13aQ|f=~i*7-$_~~!^{-^P^Y)frE zTgdVC?;dA?H&XMX?z?)N|GWKAXQmi;72l2@ZTF<+M}6(L7E0{W07AxfK0*HAVinXzz7D)b{5?+I#)|&9A99 z_Xqv=>i#gY-QTwT9<=wm|7hFKp#21!n6hoZg!W$J!KV49{c#Y&Hm=Q@w{8?AIMFvh<$ zUg*TiH+s8#re|_OwCyuFvD)b|nH%~{PGGTg-bl)1yJS+f^)Y#c9!I`SR=rI6?K(Y^ zUkFaf_dTsov&)&LpZvw_H(LBpvfRr&(r@iOJ;wK$&YyXN#)Nd+U)sm)-emtbS>^1( z^hPWFW8L7s8lCPl^4&~lJH+^ZZ;j(mv|U0wZDc)cf0-WRyIS)l^uyO4>0epp*Rvec zuasl_mOgU>?NVRr`@JFHs-kW4~mY@nQFqrO*66>*qB-Xzx2s*C(OB44>`4Nhc=wH%FIi zr^n=SywT8SSh-A($%7b=r+fH8y{Vs_FY`Ey-|(4y_h@~>xHWb+ z=yDTyZNF(BvzwvwWimaX{?z-(pP(myEKcn5EPduU&L3WJV(0&puD9<@k9MU#lNhU) zEiWaHJ)bc>md}o-bM-ovFyEMTdmdwYjPE-f7u=88>6w|-b6=6rzFY^*x)RIJ^n~@^ zO84Zu+!`;;=VhNT?u>o1^(f#J7PveVXo_dyJ+S~9M^lkR2FUvLge0AC`^DGa4 zIKLoPy6BHe!%#b=@sV|kMBZ{`Wink)_TM1RiA|YlvR$= zr*YgG^yIVWWt)%di{VM?pFO|XeALJI?D(?nSL^YcwA_P|*zx)!p&giR+ATry?iTlx@Vkh^r#2Z%{*ez@Y{Z7TJ~l> z%VanA6y}&O=`LWAg*YU4q^BId9A%di7(HbaOqEz|VZ1_5Hw-|*Sj{bsyVZ}uq(<=FR)Og^R?|6=p6*L>}@k7jzN$Mu-GFJjuul$YSA z;qj`c(cAY0Ons>LY6qYF{KMpX&m*pwp71=w6;Hu-FOlb-3-K8!!l`OfU4J?3+>uS_V{ z<32&mj`yQt_i5P&7OQer z##{RY!*9>aCf$C&!=&5mo=N}AgP-qF#M;%^+xOEgoAGY1Pd3k2d@jfF06(fY`mEESmhBn)O+@qrO&+IT376LJ2Hv>sJ`_H>$uJTXy5sLPbAT= z_Lvv#_hd}D_PSxxf8)`fOgH;&86&j68T31Yj%N6o&heZviZcJeMaCG) z{GwpSILhqb4DR1EvY9@C$oqB~YV z?d}oHg^5nb!wLL z{XSRHxBqd5QXQNBZ^~^s>;IxL(Eo&bFaJ93o#Xi5aH7VpbsD498at_PqCV#` zogO5Hh=oKm?gKwP*x~iRsIil>_~AZlo{ew+?sE%uzA1<3c0Etap?$TSMY&}!E$_#9 zFrng1@E0BG@U#?aY+tC$yAS$%U75O>u2j@XjlQwg$p^uyQbSDE$xSg?ITJujOpZEo zTTE8YOuj89M;*C4CM##g@V{xwi#l>nOjb?}gE3jZh!X0NofRMwla(_R>SJ=$k(*<( z)BLKvM|NiL&X^o^k^w4|D_@|te!5@>OjvR=|%2_dTVsg}xb7Qh{R+7A!9ChSK zkL;`<^)XpFD^62PjyiHnOjgcH)E1MY&Pvn~la<5Q*&{nEPgjquSDxR->gTLn{vO#` z!2&Tk>c}}USvf0Put#>v55?rDqc4of%2@#;F*)k^6YY^5e;Q-5a#qBam>hNF_L%G% z#P~Qscdyz;y^r#{lm}AoqCALlH|4>U)!Vu~hEPtYoKD$KIfHT*<)M@Vl!sBCN_hn3 z*_208&ZX?9yn=Foav|kCD3?;6NV$&kUX<%8Po=z-@;;QCDDOwPh4SH)+bAogXXe+8 zoTlYY%4w9lC=Z~V^*h~uKFT?i2T~4F9z?m0@?dU&>nRVRzKL==UsNX9Ac9bKHQl&Lqt1Y zSBcj9J@m%DZEo1%DO{qlotU#!%Z)^Jtd>iO9R@=>J!hWA&Z9I&XB0cU+lkRRIz6&X zqsr6Rbeu;2mJ-LlVS$!AY1g(~%h44Y)gsy-qp@qV#?V%szpX)I7wtP%GT%at!BrXy znchMR`aU93mpMlI(a ztNm^Jz8*g<4{0>x#h{;h^>FWcW0zy;w~^oIy|Cq}zIp9@O;2n4&#@bj4MqT`%q)@N zIgT8<<~va*y-{Q628}T3^=L?>~Pq z#(dOAExyn<Q{cz>dwTk-(T zPj?;R#9Pzt8iV&}Y#_D};~LjAn&5JMUC}vC^L30p(&6jeRbwz)W7{4Y3#U*{<9Nx@ zvYJM8ccQx-kIx~N-#%WKA39X?M2T%gJA2C!+OC7>SwCj%U^$_kbvgEU=xkW$)H`sc z#+Gw6MlaCVRF&JpIcJDYj}Tjk{&bz*X7uE@^B0C_M>P7T<8->;;BqY+y}<^L z{GnyN=QsPW+y`}gg@}evt()icS4=h^m~P^`o%uS5jlb9X$?cR)IVO8kRqwne-S9>p z)_$4$qa`HhWO~>TKBDc`QO=^gp7K=6rIdq|%P1F8K8bScXgv>{OgTz@Ips#m8z{F> zuAtmbxsq}xRTzFO}UdY#Tb0V^lqYzBlpp9 z=zM2(5u|?++C8F^WB;1@*w{sB_fX72e=uvTDYt=kTP(X) zrkmuZQ{vm#q_8GO{@3m}v2pYWM@iCTQ=BlUR6B0}vG6FCD!@z^Kh1&LpNrFG`{7*d@>?B&QI zPmnmk;)9Hocs{w&>BW~vo>C$nE5-Z^B42NOawAgsqCh$2F#gq(r-?Wne8M4TkZAii z8|hwr?d0hs&LE$Z=f&5Tf9Zbq2XQ9(WITBB_2plXJR#yNSNlc5DfPDNYy5K>jSXko zFG8MrBFc`npBJC;uQ$FH^0X5VA)l1z#n+dAUF1=jx*ZQCpR}JBUtj)BCC_Z)Vc-+r zX}>7+a>`-+>n4wXlf22o|7hKQ*jLB+ zqOeP~zO%^_BHjo-Da$DjWI=qk$FtOTASw_A$|;BOFG8Mr;!SS8fH&VDS&(7{_wY57 zr=55+>)T)dy2#@nqucKmH(x*en@yfP;%(#`fa*wR1OayY$@ueXzbJW{h_}1>0^WRH z?blA8PU2nUL-+N|zx1)X-2%k#x%v9xUoLs_h&Q&m#$T}a`a1hHkMUPWo_gXkM<)^6LGMMFMzrZz?sJV zxYRG%e!Pu5+lU!1zMu!6+5bhMms1WCziNVRzbxWV7he>1z4>SOdh3rMdGd&U@=19C z55B&xKkCTSKpgGj3wrPw|Dw>#DTk?VD|tGIV_bYu*ri(EZt|q>tlM!c`6NaH9(;Y( zcPe>;#4HzI(1UL_(!H+tOUV-@X1n;JN%(r}-)8c(6Za&atVuzfGVj~_N$C%-{bUz; z{JZG(o8;n)B3}S!Z+u?+$=T!y5vPz(`ag&>RsZV9(?Fc+;){YWfHM{UTGRFOh*t9L zOFn5oaW3e==hc3BiH=kGgrSGcyVJh)JSAF~AUx+*rVvd`yAO1Cxre&y0P7T0m-yiBL-rC5sjkv$$#$b_g62O@%UpIODyXkg30DM94fU;kFIpoPDUgD~+ z@b>0kU-Mrnd7{KiU3}3bd`=~#A~*=Di9D^u23Pw9VVA0Zo#g2zUg6@4CgJPNzrgOg zA7&G`f=~Ps{{>U^uaG=-#H(F=(IkAm`PW3AR$?>wWG;~L*PnRXMxHL>-EO{su5&8$ zl|P`{FNb&!_@o~F^)HV+b;Nt!eEsmRi9Bt@`@k1O(@FpKr~S5*Cw&iH@B7_+fz;bC zhdjB&2f%0gH>h1q*#GqzzvPJ$A9V3W!56@3?GyZ~5_vT+;?UzfQQsTodz9{$tIBoy>YQF~Z zG!yZhHs+sj_2yq+?blA8PU52$UvK^ipJ)5+$@U{Y2EHKdrF{i^^RLhLBTp&uaTi|{ zd;y#(wI6w!iLEX^;p)x5KHHBxox~?BzTW)nv;8Kq{fM*LlrKuZf9#9SBX(1l$;tRsAxAyC+f47mRn>gFW7X@DcXK(fOnqLBwb^FaG z9^&E?uHM?Oul~&=PlR}=#n)SX`{HXLPc!i_i?6r(_BCGG$&0j4+nay6j)(^gJ@?`C!>-`M*`s-gVc?yZol2698>6cXM+d!UXVjK9xH^)Czy07+gj2uJL zfB1ehdAf){a<^YU_yYUtb_)`pceh_Z_#)(~C%)irzkcwwlBb<`p4EP!L<;`LDeWce zk&Hi??|W;%F7l-Br|W$I{d0KmPpW)5Ts(cZ{L%fiD&iLzxe=X!`BhCSz z_~!VBfBNz-3OhOFaO{EG$)ony?Kj`ue*NH^N}eEbfxG?s!B1^NZL1G?zT3#Es;0%Ik-JjpS)1 zZgTVW!@uq1Q3vY!ZUtX#{iT2Qe%!=w6m21=98P_JQ^^w~UhU?if4%W}wO=WD>WSBY zPYRQMHP{=U*M72;Y9z zo+jeCF1`TlK$*ZNGy~wf`;as_+R39O*!@uZ*W@ICP2A|kzrlk=0V5I-)OL$+%h%tG z&p%!FgYoYI7heF0QJl7a;)~-S&>Y6UsTQ9?ePt1fI)?Zcjc?{ZlUMp<@L(7T35g6} z$l^MxMPZyapQC&`eOi2@#SmmHzwhu*L=`Ko)+RYE``1FA4q_qs z`rE(VYMSI?;keK)4aOflrrBpY+|~eZ8i-9%Hr^aa}gETXAjfN~B|&!6ZA${}L1dQV-U`DYVNeLL6>okUaLF3Rd6 z-A|^ze#%pcroKVSA)={oA>}%vsc!@2W}>NAF8#|RX0R7qY1cs<%Jl7&yNO=yVEoxu ztlL4JmB_K3vb2Ys-~H8P-<^A9$K3CIu;hFH`f~8UX8in%85g`0zTngE{PfeeA3gc) zym3qO-gu8P{_NW8yVw5dv@8Gg(u}WPI&S5_ z<*H|2K7RZ$$A8ee?1P=ozI>-}-gm-X zhriTSl6^+W4ey^F z;ZLtP`8N-pe87d@JKzuh%KF2D+mCwi@R2hP-~Z_K`~UEimLKM?-YI|MEq~hh+N|Hd zR=3-_x`s)QG|b-VpxK)X_S$^u+P_>{ls~)Zs8_E(>Y}E7F4{Wz`&;WL-c$eAGcNh- zM~D6Tqk(CI2A;Rao#*|2bK~!K+j7Hh%YS{|a_QIXqfX2|Vb!n`&b;XlXCB*e%dt03 zdFsYR%O@Z>9@Rl+AZhY7C2}0PhK0n=g+q9dFqYtpL*|~=HC0& z59WQfPyKEC9P^9ok4c*oOxykWH+S!xy{+^0$BSOS?A8M>``xGe{_bagJ@jXXz5Ll> zfBbycKW-^}Z_A3Yb5@*x`{eV_{X^loPu@4~$=c)huD$ZEnO9a^_m7H>neTR#PN*tf zxgu@lV-IFOw)BeMEp4Cvd3#$jBra4vk(k^t$z5UAON~-`V%sqx zeRq0U@tdv1Q-@BRI^?+*hCDy*@6S)XsCnWw-*W}{AVEiFF^Qz zitv9M;h%@_e*@tkLii6u_%|W^|AO#uLHLhB_}_={zX{=g62kvC2>)h;|K|vQKf-?` z!v7wGeCAp9Rk_#cVze+c1!A;SMEg#RlD{|LhWE`=i|6~cc2!v8FU z|1yOChY0^)A^eX(__|639M_apoVBm7@P_`iVgzXIWZGQ$4=g#RB9{tqJj4@da# zkMRE?!apD3zY*d88p6L0;opGppN;U}jPSn{;a`OCKMLW05yF2f!oMEj|5t?nM+pCc z2>)pa|7{5WbcFvc2>){s{-Y88 zdm{W#Mfl%~@c#;(9{C|e|2a}oYeBK&I+{#PRWD-ix22>()q|4M}aV+jAH2>*73e;dO8*9iaV2>%}={4YoN z*C71gMEGBf@NY%<4@LN2hw$GQ;eR&5--qzO1L1!O!v6t;|4fAc5`_OD2>%}-{8uCV zD-r&0A^gW8{7*yp{{!KFD8m0~g#U1a|JMlryAl3BMfg98@c%x-e*?mQ0mALHHkx@c%Qy|1*UDBMARrApGYc{Bsfh?;!lIMfksp@P7{BzZb&)PK5u72>-ni z{y7N$H3Mfksm@P7*7e+I(88{uDo@IMgY|1QFR4}|}(5dK#p{69eWe~Iw_Il})P zg#ULC{*NO3$07V5LHLhB`2P^$KNaEMjqu+O;XerBzc0dn9m2m6;lBytzYoIyX@vhk zgnt{tzZv1b0O3Cx;lCHcKNsPD9>RYx!v6+@|IG;hc?kdG5dQx__+N$a{|MoKJ;MJq zg#QeL|4M}aFogeHg#X6~|CbQ{4*11|Az?wYZ3nE zApGA#_+NwY|0lw~2H}4x!v9Ew{~ZYbg$Vy42>&|~{$C^fyAb|W2>a5xB!vG?2>$|v|5}89KEnT1gntvle=@>&ez|6j}ckMKVV;lB#ue-pyL1K~df;lCW=zXaib5W;@|!v9@_|7{5W zpCJ5yhVZ`;;r}Ou{|^xU^$7o8ApGYb{GUho&qnw^j_|(~;r}VZ|E~!Dml6J-Bm4^y z{$mmTw%HP{}l-T2NC{PApEBz{BscgpCSA&L-;?8 z@b5(U?~3sMJHr3>2>$~R{!xVgdW8Q^5&lI8{~saz_eS{diSWN0;XeZ5zYO7jEW*D6 z;r}|qe;dO8V1)mCg#T|5{$C;dFG2Vpi17Cz{J%i>pN#OYM);Q_{5K%{*C6~u2>;6w z{yQW5???DIApD;|_`i+te+J=yCc-}x;r|B0{{)185aGW&!rzbZ|1QG+O@#kYg#U90 z|7i&SixB?bL-@af@IMvd|183Pe}sQ6!vB{D|4|74ClUVdBm5sj_&lQ_E5d&x!v8FUe+k0>DTMzg2>%R(|Bn&=l?eYA5dP;Q{Id}Le?$16j`05r z!v9c&|L+j~ixK_-gnu2v|IY~j6A}JLApBoL__rhc4?*}Z$~#tH@01eji1oxqVl%Oo z*iPI=+)k9S=svoco*vTWWD%zlXA^UYdBjp;9kHJ1=9bTmOm88!6FZ6BME`NR{Heqs zF^_0??dz$la3LtyN%}sI+|LU*a`c=fQAuw)SEI>4)m(U)1Y*kt@phK_KStb&`bu5R zIY1RU^;K7B|3g>C*CU4)&>c7Fze`wnoyN}VbyI?-wHkYx9g#JJF8j$W5mZi`uD%JzIMZ>zj2Z?Po>W&cd%=^MnBU7Oy9=zEGxZ@ z=>aRfndwuRKASuZOwX~>>zF>)dV6%)zyJWX;o2m*s4(X zr@B6c2ZfyaD1)?%%+%?DgQ>Ujdx^pC>Axr1`VYvxC6*E+*Xi`guQWDP=)dD_?n@fJ$P>EXnmzPErq{3OJ%8?Zb$U9{ z@E<B57eb=1$^0z0^H(BLpS^hU$ z`L|pCwXHT{~$sYJ;7->-5kb8Uwp&>^MS?7u!FRPxeo8v@F)`V8(&boBQ6{>g%s3 zE!i=J7`y$rOTB)ocl2R#=oxBx?GQKoicWj9ppQG2kqQG_4WDb!?b_N z+WDg$wELoO z0(m>;-_#wn+tGRW-#uP$&dAC9G3~L9{usHFvf;6D;KWL2 z-(&dGDVw+$Zs|jR(ANXT-pwa5BeHyd(;T4&&2QwY+UlBuNKr*uu}(SnGOeF}+wbx9RGh+r#g`gvSPz5 z$4Zh}4q}tB8`36&gK%V!Rfz_nNQMn*lVOi_qOHL}B^ewm_H2d?X_G++$?OVAn^}8M zw$1l>8=rr^e}De`{PB9cUhjLa`@XOHx__KXy#LSj{Qu|M|CnRXm@p}G(RpJo`|bE- z^8XC~fBwI{0{&p_z@0;g&mA8U{ClKyj%#{`(57Sj%uhmCrHkBqw-C1KIA4Qa`SBsk zuc2O-cpndAN+f4nRolC@jA|n(4-6eHWo%>cw%oC{D9;0L+GUo z{J@@$88w&o3Sokd{4H`i>>a{ah{l9?2nS%jPVjJS)hT`l!ya@l-tlK4q;-r(?-N3+ z&hR5B=>mVhuYXsf#WCwbI21{pGt8|vI2Zd4T_r zP3jTz;V~GkGrSmWy2zhnm9FsM{^mT-TJbd)sI&Y!a=OIdqD6;;Ej2dl$jKi&@=3pN zE%P0RuR<@K<%UCDi%#=2Ow~CqAUaOuRrMjPw!O?94|6R##Y*F+!Jk3(`6q z9>Q%{rgQu>*61RCj&-`iCm-QBkD41#Lxaxovly+5+%mu#=>j($S?xSeJ*wIj9{bDc zaWcH;z#osvZy>ssGPfLE9be{42AP{JoSQm21#3PNG>yz7q3vmY>36UF7($t<_`h7oUL%I?cCXvd-}`tkEUjf{G4< z{r91dbB;y!<NwFD-sLy;)p0%-wNE$~{{g*p zo>!ocF7fHZJeScJz70!tj-SN}UF4A``KCQ-4BvzyI>%2UrHlMIM(7Gp9`4(svpgT` zbb;T)MqTDHC;#|d@g_7pWnbPeuxywXcrz7_8PAeiR#Zfsa4Uci?H~;yfC3Y&*z`%S=1RWqO2o-hl&m(LYRTnGp>uD#3)_l&oNF{ zc+y$EDLTvZv0fMWJydj=x1Q~AiyiYE&jKnuNwg}TTaQPdUg-RRlS zasD0ypL5J}9TN>Y9vP!`nkQni&hq2P=>o4oi!O8bk)A^xnW=V~7hPWM0&lyb+Tlubn^5f(&$+7FdERt&wJSVmVzrYzb5gZ)y!P5^m%0Ds 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 DBRu^4 diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi deleted file mode 100644 index 30b67d8..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi +++ /dev/null @@ -1,28 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import padding - -def check_ansix923_padding(data: bytes) -> bool: ... - -class PKCS7PaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: bytes) -> bytes: ... - def finalize(self) -> bytes: ... - -class PKCS7UnpaddingContext(padding.PaddingContext): - def __init__(self, block_size: int) -> None: ... - def update(self, data: bytes) -> bytes: ... - def finalize(self) -> bytes: ... - -class ObjectIdentifier: - def __init__(self, val: str) -> None: ... - @property - def dotted_string(self) -> str: ... - @property - def _name(self) -> str: ... - -T = typing.TypeVar("T") diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi deleted file mode 100644 index 8010008..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi +++ /dev/null @@ -1,8 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -lib = typing.Any -ffi = typing.Any diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi deleted file mode 100644 index 3b5f208..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi +++ /dev/null @@ -1,7 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -def decode_dss_signature(signature: bytes) -> tuple[int, int]: ... -def encode_dss_signature(r: int, s: int) -> bytes: ... -def parse_spki_for_data(data: bytes) -> bytes: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi deleted file mode 100644 index 09f46b1..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -class _Reasons: - BACKEND_MISSING_INTERFACE: _Reasons - UNSUPPORTED_HASH: _Reasons - UNSUPPORTED_CIPHER: _Reasons - UNSUPPORTED_PADDING: _Reasons - UNSUPPORTED_MGF: _Reasons - UNSUPPORTED_PUBLIC_KEY_ALGORITHM: _Reasons - UNSUPPORTED_ELLIPTIC_CURVE: _Reasons - UNSUPPORTED_SERIALIZATION: _Reasons - UNSUPPORTED_X509: _Reasons - UNSUPPORTED_EXCHANGE_ALGORITHM: _Reasons - UNSUPPORTED_DIFFIE_HELLMAN: _Reasons - UNSUPPORTED_MAC: _Reasons diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi deleted file mode 100644 index e4321be..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi +++ /dev/null @@ -1,117 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import datetime -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes -from cryptography.x509 import ocsp - -class OCSPRequest: - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - @property - def extensions(self) -> x509.Extensions: ... - -class OCSPResponse: - @property - def responses(self) -> typing.Iterator[OCSPSingleResponse]: ... - @property - def response_status(self) -> ocsp.OCSPResponseStatus: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_response_bytes(self) -> bytes: ... - @property - def certificates(self) -> list[x509.Certificate]: ... - @property - def responder_key_hash(self) -> bytes | None: ... - @property - def responder_name(self) -> x509.Name | None: ... - @property - def produced_at(self) -> datetime.datetime: ... - @property - def produced_at_utc(self) -> datetime.datetime: ... - @property - def certificate_status(self) -> ocsp.OCSPCertStatus: ... - @property - def revocation_time(self) -> datetime.datetime | None: ... - @property - def revocation_time_utc(self) -> datetime.datetime | None: ... - @property - def revocation_reason(self) -> x509.ReasonFlags | None: ... - @property - def this_update(self) -> datetime.datetime: ... - @property - def this_update_utc(self) -> datetime.datetime: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def single_extensions(self) -> x509.Extensions: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - -class OCSPSingleResponse: - @property - def certificate_status(self) -> ocsp.OCSPCertStatus: ... - @property - def revocation_time(self) -> datetime.datetime | None: ... - @property - def revocation_time_utc(self) -> datetime.datetime | None: ... - @property - def revocation_reason(self) -> x509.ReasonFlags | None: ... - @property - def this_update(self) -> datetime.datetime: ... - @property - def this_update_utc(self) -> datetime.datetime: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def issuer_key_hash(self) -> bytes: ... - @property - def issuer_name_hash(self) -> bytes: ... - @property - def hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def serial_number(self) -> int: ... - -def load_der_ocsp_request(data: bytes) -> ocsp.OCSPRequest: ... -def load_der_ocsp_response(data: bytes) -> ocsp.OCSPResponse: ... -def create_ocsp_request( - builder: ocsp.OCSPRequestBuilder, -) -> ocsp.OCSPRequest: ... -def create_ocsp_response( - status: ocsp.OCSPResponseStatus, - builder: ocsp.OCSPResponseBuilder | None, - private_key: PrivateKeyTypes | None, - hash_algorithm: hashes.HashAlgorithm | None, -) -> ocsp.OCSPResponse: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi deleted file mode 100644 index 320cef1..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi +++ /dev/null @@ -1,72 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.bindings._rust.openssl import ( - aead, - ciphers, - cmac, - dh, - dsa, - ec, - ed448, - ed25519, - hashes, - hmac, - kdf, - keys, - poly1305, - rsa, - x448, - x25519, -) - -__all__ = [ - "aead", - "ciphers", - "cmac", - "dh", - "dsa", - "ec", - "ed448", - "ed25519", - "hashes", - "hmac", - "kdf", - "keys", - "openssl_version", - "openssl_version_text", - "poly1305", - "raise_openssl_error", - "rsa", - "x448", - "x25519", -] - -CRYPTOGRAPHY_IS_LIBRESSL: bool -CRYPTOGRAPHY_IS_BORINGSSL: bool -CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: bool -CRYPTOGRAPHY_OPENSSL_309_OR_GREATER: bool -CRYPTOGRAPHY_OPENSSL_320_OR_GREATER: bool - -class Providers: ... - -_legacy_provider_loaded: bool -_providers: Providers - -def openssl_version() -> int: ... -def openssl_version_text() -> str: ... -def raise_openssl_error() -> typing.NoReturn: ... -def capture_error_stack() -> list[OpenSSLError]: ... -def is_fips_enabled() -> bool: ... -def enable_fips(providers: Providers) -> None: ... - -class OpenSSLError: - @property - def lib(self) -> int: ... - @property - def reason(self) -> int: ... - @property - def reason_text(self) -> bytes: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi deleted file mode 100644 index 047f49d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi +++ /dev/null @@ -1,103 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -class AESGCM: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_key(key_size: int) -> bytes: ... - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - -class ChaCha20Poly1305: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_key() -> bytes: ... - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - -class AESCCM: - def __init__(self, key: bytes, tag_length: int = 16) -> None: ... - @staticmethod - def generate_key(key_size: int) -> bytes: ... - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - -class AESSIV: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_key(key_size: int) -> bytes: ... - def encrypt( - self, - data: bytes, - associated_data: list[bytes] | None, - ) -> bytes: ... - def decrypt( - self, - data: bytes, - associated_data: list[bytes] | None, - ) -> bytes: ... - -class AESOCB3: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_key(key_size: int) -> bytes: ... - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - -class AESGCMSIV: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_key(key_size: int) -> bytes: ... - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: bytes | None, - ) -> bytes: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi deleted file mode 100644 index 759f3b5..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi +++ /dev/null @@ -1,38 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import ciphers -from cryptography.hazmat.primitives.ciphers import modes - -@typing.overload -def create_encryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag -) -> ciphers.AEADEncryptionContext: ... -@typing.overload -def create_encryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode -) -> ciphers.CipherContext: ... -@typing.overload -def create_decryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.ModeWithAuthenticationTag -) -> ciphers.AEADDecryptionContext: ... -@typing.overload -def create_decryption_ctx( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode -) -> ciphers.CipherContext: ... -def cipher_supported( - algorithm: ciphers.CipherAlgorithm, mode: modes.Mode -) -> bool: ... -def _advance( - ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int -) -> None: ... -def _advance_aad( - ctx: ciphers.AEADEncryptionContext | ciphers.AEADDecryptionContext, n: int -) -> None: ... - -class CipherContext: ... -class AEADEncryptionContext: ... -class AEADDecryptionContext: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi deleted file mode 100644 index 9c03508..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi +++ /dev/null @@ -1,18 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import ciphers - -class CMAC: - def __init__( - self, - algorithm: ciphers.BlockCipherAlgorithm, - backend: typing.Any = None, - ) -> None: ... - def update(self, data: bytes) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, signature: bytes) -> None: ... - def copy(self) -> CMAC: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi deleted file mode 100644 index 08733d7..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi +++ /dev/null @@ -1,51 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import dh - -MIN_MODULUS_SIZE: int - -class DHPrivateKey: ... -class DHPublicKey: ... -class DHParameters: ... - -class DHPrivateNumbers: - def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: ... - def private_key(self, backend: typing.Any = None) -> dh.DHPrivateKey: ... - @property - def x(self) -> int: ... - @property - def public_numbers(self) -> DHPublicNumbers: ... - -class DHPublicNumbers: - def __init__( - self, y: int, parameter_numbers: DHParameterNumbers - ) -> None: ... - def public_key(self, backend: typing.Any = None) -> dh.DHPublicKey: ... - @property - def y(self) -> int: ... - @property - def parameter_numbers(self) -> DHParameterNumbers: ... - -class DHParameterNumbers: - def __init__(self, p: int, g: int, q: int | None = None) -> None: ... - def parameters(self, backend: typing.Any = None) -> dh.DHParameters: ... - @property - def p(self) -> int: ... - @property - def g(self) -> int: ... - @property - def q(self) -> int | None: ... - -def generate_parameters( - generator: int, key_size: int, backend: typing.Any = None -) -> dh.DHParameters: ... -def from_pem_parameters( - data: bytes, backend: typing.Any = None -) -> dh.DHParameters: ... -def from_der_parameters( - data: bytes, backend: typing.Any = None -) -> dh.DHParameters: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi deleted file mode 100644 index 0922a4c..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi +++ /dev/null @@ -1,41 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import dsa - -class DSAPrivateKey: ... -class DSAPublicKey: ... -class DSAParameters: ... - -class DSAPrivateNumbers: - def __init__(self, x: int, public_numbers: DSAPublicNumbers) -> None: ... - @property - def x(self) -> int: ... - @property - def public_numbers(self) -> DSAPublicNumbers: ... - def private_key(self, backend: typing.Any = None) -> dsa.DSAPrivateKey: ... - -class DSAPublicNumbers: - def __init__( - self, y: int, parameter_numbers: DSAParameterNumbers - ) -> None: ... - @property - def y(self) -> int: ... - @property - def parameter_numbers(self) -> DSAParameterNumbers: ... - def public_key(self, backend: typing.Any = None) -> dsa.DSAPublicKey: ... - -class DSAParameterNumbers: - def __init__(self, p: int, q: int, g: int) -> None: ... - @property - def p(self) -> int: ... - @property - def q(self) -> int: ... - @property - def g(self) -> int: ... - def parameters(self, backend: typing.Any = None) -> dsa.DSAParameters: ... - -def generate_parameters(key_size: int) -> dsa.DSAParameters: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi deleted file mode 100644 index 5c3b7bf..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import ec - -class ECPrivateKey: ... -class ECPublicKey: ... - -class EllipticCurvePrivateNumbers: - def __init__( - self, private_value: int, public_numbers: EllipticCurvePublicNumbers - ) -> None: ... - def private_key( - self, backend: typing.Any = None - ) -> ec.EllipticCurvePrivateKey: ... - @property - def private_value(self) -> int: ... - @property - def public_numbers(self) -> EllipticCurvePublicNumbers: ... - -class EllipticCurvePublicNumbers: - def __init__(self, x: int, y: int, curve: ec.EllipticCurve) -> None: ... - def public_key( - self, backend: typing.Any = None - ) -> ec.EllipticCurvePublicKey: ... - @property - def x(self) -> int: ... - @property - def y(self) -> int: ... - @property - def curve(self) -> ec.EllipticCurve: ... - def __eq__(self, other: object) -> bool: ... - -def curve_supported(curve: ec.EllipticCurve) -> bool: ... -def generate_private_key( - curve: ec.EllipticCurve, backend: typing.Any = None -) -> ec.EllipticCurvePrivateKey: ... -def from_private_numbers( - numbers: ec.EllipticCurvePrivateNumbers, -) -> ec.EllipticCurvePrivateKey: ... -def from_public_numbers( - numbers: ec.EllipticCurvePublicNumbers, -) -> ec.EllipticCurvePublicKey: ... -def from_public_bytes( - curve: ec.EllipticCurve, data: bytes -) -> ec.EllipticCurvePublicKey: ... -def derive_private_key( - private_value: int, curve: ec.EllipticCurve -) -> ec.EllipticCurvePrivateKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi deleted file mode 100644 index 5233f9a..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi +++ /dev/null @@ -1,12 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import ed25519 - -class Ed25519PrivateKey: ... -class Ed25519PublicKey: ... - -def generate_key() -> ed25519.Ed25519PrivateKey: ... -def from_private_bytes(data: bytes) -> ed25519.Ed25519PrivateKey: ... -def from_public_bytes(data: bytes) -> ed25519.Ed25519PublicKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi deleted file mode 100644 index 7a06520..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi +++ /dev/null @@ -1,12 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import ed448 - -class Ed448PrivateKey: ... -class Ed448PublicKey: ... - -def generate_key() -> ed448.Ed448PrivateKey: ... -def from_private_bytes(data: bytes) -> ed448.Ed448PrivateKey: ... -def from_public_bytes(data: bytes) -> ed448.Ed448PublicKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi deleted file mode 100644 index 56f3170..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi +++ /dev/null @@ -1,19 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes - -class Hash(hashes.HashContext): - def __init__( - self, algorithm: hashes.HashAlgorithm, backend: typing.Any = None - ) -> None: ... - @property - def algorithm(self) -> hashes.HashAlgorithm: ... - def update(self, data: bytes) -> None: ... - def finalize(self) -> bytes: ... - def copy(self) -> Hash: ... - -def hash_supported(algorithm: hashes.HashAlgorithm) -> bool: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi deleted file mode 100644 index e38d9b5..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi +++ /dev/null @@ -1,21 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes - -class HMAC(hashes.HashContext): - def __init__( - self, - key: bytes, - algorithm: hashes.HashAlgorithm, - backend: typing.Any = None, - ) -> None: ... - @property - def algorithm(self) -> hashes.HashAlgorithm: ... - def update(self, data: bytes) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, signature: bytes) -> None: ... - def copy(self) -> HMAC: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi deleted file mode 100644 index 4b90bb4..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi +++ /dev/null @@ -1,43 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.hashes import HashAlgorithm - -def derive_pbkdf2_hmac( - key_material: bytes, - algorithm: HashAlgorithm, - salt: bytes, - iterations: int, - length: int, -) -> bytes: ... - -class Scrypt: - def __init__( - self, - salt: bytes, - length: int, - n: int, - r: int, - p: int, - backend: typing.Any = None, - ) -> None: ... - def derive(self, key_material: bytes) -> bytes: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... - -class Argon2id: - def __init__( - self, - *, - salt: bytes, - length: int, - iterations: int, - lanes: int, - memory_cost: int, - ad: bytes | None = None, - secret: bytes | None = None, - ) -> None: ... - def derive(self, key_material: bytes) -> bytes: ... - def verify(self, key_material: bytes, expected_key: bytes) -> None: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi deleted file mode 100644 index 6815b7d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi +++ /dev/null @@ -1,33 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric.types import ( - PrivateKeyTypes, - PublicKeyTypes, -) - -def load_der_private_key( - data: bytes, - password: bytes | None, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PrivateKeyTypes: ... -def load_pem_private_key( - data: bytes, - password: bytes | None, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PrivateKeyTypes: ... -def load_der_public_key( - data: bytes, - backend: typing.Any = None, -) -> PublicKeyTypes: ... -def load_pem_public_key( - data: bytes, - backend: typing.Any = None, -) -> PublicKeyTypes: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi deleted file mode 100644 index 2e9b0a9..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -class Poly1305: - def __init__(self, key: bytes) -> None: ... - @staticmethod - def generate_tag(key: bytes, data: bytes) -> bytes: ... - @staticmethod - def verify_tag(key: bytes, data: bytes, tag: bytes) -> None: ... - def update(self, data: bytes) -> None: ... - def finalize(self) -> bytes: ... - def verify(self, tag: bytes) -> None: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi deleted file mode 100644 index ef7752d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi +++ /dev/null @@ -1,55 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import rsa - -class RSAPrivateKey: ... -class RSAPublicKey: ... - -class RSAPrivateNumbers: - def __init__( - self, - p: int, - q: int, - d: int, - dmp1: int, - dmq1: int, - iqmp: int, - public_numbers: RSAPublicNumbers, - ) -> None: ... - @property - def p(self) -> int: ... - @property - def q(self) -> int: ... - @property - def d(self) -> int: ... - @property - def dmp1(self) -> int: ... - @property - def dmq1(self) -> int: ... - @property - def iqmp(self) -> int: ... - @property - def public_numbers(self) -> RSAPublicNumbers: ... - def private_key( - self, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, - ) -> rsa.RSAPrivateKey: ... - -class RSAPublicNumbers: - def __init__(self, e: int, n: int) -> None: ... - @property - def n(self) -> int: ... - @property - def e(self) -> int: ... - def public_key(self, backend: typing.Any = None) -> rsa.RSAPublicKey: ... - -def generate_private_key( - public_exponent: int, - key_size: int, -) -> rsa.RSAPrivateKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi deleted file mode 100644 index da0f3ec..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi +++ /dev/null @@ -1,12 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import x25519 - -class X25519PrivateKey: ... -class X25519PublicKey: ... - -def generate_key() -> x25519.X25519PrivateKey: ... -def from_private_bytes(data: bytes) -> x25519.X25519PrivateKey: ... -def from_public_bytes(data: bytes) -> x25519.X25519PublicKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi deleted file mode 100644 index e51cfeb..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi +++ /dev/null @@ -1,12 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.primitives.asymmetric import x448 - -class X448PrivateKey: ... -class X448PublicKey: ... - -def generate_key() -> x448.X448PrivateKey: ... -def from_private_bytes(data: bytes) -> x448.X448PrivateKey: ... -def from_public_bytes(data: bytes) -> x448.X448PublicKey: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi deleted file mode 100644 index 40514c4..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi +++ /dev/null @@ -1,46 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes -from cryptography.hazmat.primitives.serialization import ( - KeySerializationEncryption, -) -from cryptography.hazmat.primitives.serialization.pkcs12 import ( - PKCS12KeyAndCertificates, - PKCS12PrivateKeyTypes, -) - -class PKCS12Certificate: - def __init__( - self, cert: x509.Certificate, friendly_name: bytes | None - ) -> None: ... - @property - def friendly_name(self) -> bytes | None: ... - @property - def certificate(self) -> x509.Certificate: ... - -def load_key_and_certificates( - data: bytes, - password: bytes | None, - backend: typing.Any = None, -) -> tuple[ - PrivateKeyTypes | None, - x509.Certificate | None, - list[x509.Certificate], -]: ... -def load_pkcs12( - data: bytes, - password: bytes | None, - backend: typing.Any = None, -) -> PKCS12KeyAndCertificates: ... -def serialize_key_and_certificates( - name: bytes | None, - key: PKCS12PrivateKeyTypes | None, - cert: x509.Certificate | None, - cas: typing.Iterable[x509.Certificate | PKCS12Certificate] | None, - encryption_algorithm: KeySerializationEncryption, -) -> bytes: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi deleted file mode 100644 index f9aa81e..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi +++ /dev/null @@ -1,49 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.hazmat.primitives.serialization import pkcs7 - -def serialize_certificates( - certs: list[x509.Certificate], - encoding: serialization.Encoding, -) -> bytes: ... -def encrypt_and_serialize( - builder: pkcs7.PKCS7EnvelopeBuilder, - encoding: serialization.Encoding, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def sign_and_serialize( - builder: pkcs7.PKCS7SignatureBuilder, - encoding: serialization.Encoding, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_der( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_pem( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def decrypt_smime( - data: bytes, - certificate: x509.Certificate, - private_key: rsa.RSAPrivateKey, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... -def load_pem_pkcs7_certificates( - data: bytes, -) -> list[x509.Certificate]: ... -def load_der_pkcs7_certificates( - data: bytes, -) -> list[x509.Certificate]: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi deleted file mode 100644 index ef9f779..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi +++ /dev/null @@ -1,22 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import pkcs7 - -class TestCertificate: - not_after_tag: int - not_before_tag: int - issuer_value_tags: list[int] - subject_value_tags: list[int] - -def test_parse_certificate(data: bytes) -> TestCertificate: ... -def pkcs7_verify( - encoding: serialization.Encoding, - sig: bytes, - msg: bytes | None, - certs: list[x509.Certificate], - options: list[pkcs7.PKCS7Options], -) -> None: ... diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi deleted file mode 100644 index b494fb6..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +++ /dev/null @@ -1,246 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import datetime -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric.ec import ECDSA -from cryptography.hazmat.primitives.asymmetric.padding import PSS, PKCS1v15 -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPublicKeyTypes, - CertificatePublicKeyTypes, - PrivateKeyTypes, -) -from cryptography.x509 import certificate_transparency - -def load_pem_x509_certificate( - data: bytes, backend: typing.Any = None -) -> x509.Certificate: ... -def load_der_x509_certificate( - data: bytes, backend: typing.Any = None -) -> x509.Certificate: ... -def load_pem_x509_certificates( - data: bytes, -) -> list[x509.Certificate]: ... -def load_pem_x509_crl( - data: bytes, backend: typing.Any = None -) -> x509.CertificateRevocationList: ... -def load_der_x509_crl( - data: bytes, backend: typing.Any = None -) -> x509.CertificateRevocationList: ... -def load_pem_x509_csr( - data: bytes, backend: typing.Any = None -) -> x509.CertificateSigningRequest: ... -def load_der_x509_csr( - data: bytes, backend: typing.Any = None -) -> x509.CertificateSigningRequest: ... -def encode_name_bytes(name: x509.Name) -> bytes: ... -def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... -def create_x509_certificate( - builder: x509.CertificateBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, -) -> x509.Certificate: ... -def create_x509_csr( - builder: x509.CertificateSigningRequestBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, -) -> x509.CertificateSigningRequest: ... -def create_x509_crl( - builder: x509.CertificateRevocationListBuilder, - private_key: PrivateKeyTypes, - hash_algorithm: hashes.HashAlgorithm | None, - rsa_padding: PKCS1v15 | PSS | None, -) -> x509.CertificateRevocationList: ... - -class Sct: - @property - def version(self) -> certificate_transparency.Version: ... - @property - def log_id(self) -> bytes: ... - @property - def timestamp(self) -> datetime.datetime: ... - @property - def entry_type(self) -> certificate_transparency.LogEntryType: ... - @property - def signature_hash_algorithm(self) -> hashes.HashAlgorithm: ... - @property - def signature_algorithm( - self, - ) -> certificate_transparency.SignatureAlgorithm: ... - @property - def signature(self) -> bytes: ... - @property - def extension_bytes(self) -> bytes: ... - -class Certificate: - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... - @property - def serial_number(self) -> int: ... - @property - def version(self) -> x509.Version: ... - def public_key(self) -> CertificatePublicKeyTypes: ... - @property - def public_key_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def not_valid_before(self) -> datetime.datetime: ... - @property - def not_valid_before_utc(self) -> datetime.datetime: ... - @property - def not_valid_after(self) -> datetime.datetime: ... - @property - def not_valid_after_utc(self) -> datetime.datetime: ... - @property - def issuer(self) -> x509.Name: ... - @property - def subject(self) -> x509.Name: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> None | PSS | PKCS1v15 | ECDSA: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certificate_bytes(self) -> bytes: ... - @property - def tbs_precertificate_bytes(self) -> bytes: ... - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - def verify_directly_issued_by(self, issuer: Certificate) -> None: ... - -class RevokedCertificate: ... - -class CertificateRevocationList: - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: ... - def get_revoked_certificate_by_serial_number( - self, serial_number: int - ) -> RevokedCertificate | None: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> None | PSS | PKCS1v15 | ECDSA: ... - @property - def issuer(self) -> x509.Name: ... - @property - def next_update(self) -> datetime.datetime | None: ... - @property - def next_update_utc(self) -> datetime.datetime | None: ... - @property - def last_update(self) -> datetime.datetime: ... - @property - def last_update_utc(self) -> datetime.datetime: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certlist_bytes(self) -> bytes: ... - def __eq__(self, other: object) -> bool: ... - def __len__(self) -> int: ... - @typing.overload - def __getitem__(self, idx: int) -> x509.RevokedCertificate: ... - @typing.overload - def __getitem__(self, idx: slice) -> list[x509.RevokedCertificate]: ... - def __iter__(self) -> typing.Iterator[x509.RevokedCertificate]: ... - def is_signature_valid( - self, public_key: CertificateIssuerPublicKeyTypes - ) -> bool: ... - -class CertificateSigningRequest: - def __eq__(self, other: object) -> bool: ... - def __hash__(self) -> int: ... - def public_key(self) -> CertificatePublicKeyTypes: ... - @property - def subject(self) -> x509.Name: ... - @property - def signature_hash_algorithm( - self, - ) -> hashes.HashAlgorithm | None: ... - @property - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: ... - @property - def signature_algorithm_parameters( - self, - ) -> None | PSS | PKCS1v15 | ECDSA: ... - @property - def extensions(self) -> x509.Extensions: ... - @property - def attributes(self) -> x509.Attributes: ... - def public_bytes(self, encoding: serialization.Encoding) -> bytes: ... - @property - def signature(self) -> bytes: ... - @property - def tbs_certrequest_bytes(self) -> bytes: ... - @property - def is_signature_valid(self) -> bool: ... - def get_attribute_for_oid(self, oid: x509.ObjectIdentifier) -> bytes: ... - -class PolicyBuilder: - def time(self, new_time: datetime.datetime) -> PolicyBuilder: ... - def store(self, new_store: Store) -> PolicyBuilder: ... - def max_chain_depth(self, new_max_chain_depth: int) -> PolicyBuilder: ... - def build_client_verifier(self) -> ClientVerifier: ... - def build_server_verifier( - self, subject: x509.verification.Subject - ) -> ServerVerifier: ... - -class VerifiedClient: - @property - def subjects(self) -> list[x509.GeneralName] | None: ... - @property - def chain(self) -> list[x509.Certificate]: ... - -class ClientVerifier: - @property - def validation_time(self) -> datetime.datetime: ... - @property - def store(self) -> Store: ... - @property - def max_chain_depth(self) -> int: ... - def verify( - self, - leaf: x509.Certificate, - intermediates: list[x509.Certificate], - ) -> VerifiedClient: ... - -class ServerVerifier: - @property - def subject(self) -> x509.verification.Subject: ... - @property - def validation_time(self) -> datetime.datetime: ... - @property - def store(self) -> Store: ... - @property - def max_chain_depth(self) -> int: ... - def verify( - self, - leaf: x509.Certificate, - intermediates: list[x509.Certificate], - ) -> list[x509.Certificate]: ... - -class Store: - def __init__(self, certs: list[x509.Certificate]) -> None: ... - -class VerificationError(Exception): - pass diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py deleted file mode 100644 index b509336..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py deleted file mode 100644 index 73c06f7..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +++ /dev/null @@ -1,183 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - - -def cryptography_has_set_cert_cb() -> list[str]: - return [ - "SSL_CTX_set_cert_cb", - "SSL_set_cert_cb", - ] - - -def cryptography_has_ssl_st() -> list[str]: - return [ - "SSL_ST_BEFORE", - "SSL_ST_OK", - "SSL_ST_INIT", - "SSL_ST_RENEGOTIATE", - ] - - -def cryptography_has_tls_st() -> list[str]: - return [ - "TLS_ST_BEFORE", - "TLS_ST_OK", - ] - - -def cryptography_has_ssl_sigalgs() -> list[str]: - return [ - "SSL_CTX_set1_sigalgs_list", - ] - - -def cryptography_has_psk() -> list[str]: - return [ - "SSL_CTX_use_psk_identity_hint", - "SSL_CTX_set_psk_server_callback", - "SSL_CTX_set_psk_client_callback", - ] - - -def cryptography_has_psk_tlsv13() -> list[str]: - return [ - "SSL_CTX_set_psk_find_session_callback", - "SSL_CTX_set_psk_use_session_callback", - "Cryptography_SSL_SESSION_new", - "SSL_CIPHER_find", - "SSL_SESSION_set1_master_key", - "SSL_SESSION_set_cipher", - "SSL_SESSION_set_protocol_version", - ] - - -def cryptography_has_custom_ext() -> list[str]: - return [ - "SSL_CTX_add_client_custom_ext", - "SSL_CTX_add_server_custom_ext", - "SSL_extension_supported", - ] - - -def cryptography_has_tlsv13_functions() -> list[str]: - return [ - "SSL_VERIFY_POST_HANDSHAKE", - "SSL_CTX_set_ciphersuites", - "SSL_verify_client_post_handshake", - "SSL_CTX_set_post_handshake_auth", - "SSL_set_post_handshake_auth", - "SSL_SESSION_get_max_early_data", - "SSL_write_early_data", - "SSL_read_early_data", - "SSL_CTX_set_max_early_data", - ] - - -def cryptography_has_engine() -> list[str]: - return [ - "ENGINE_by_id", - "ENGINE_init", - "ENGINE_finish", - "ENGINE_get_default_RAND", - "ENGINE_set_default_RAND", - "ENGINE_unregister_RAND", - "ENGINE_ctrl_cmd", - "ENGINE_free", - "ENGINE_get_name", - "ENGINE_ctrl_cmd_string", - "ENGINE_load_builtin_engines", - "ENGINE_load_private_key", - "ENGINE_load_public_key", - "SSL_CTX_set_client_cert_engine", - ] - - -def cryptography_has_verified_chain() -> list[str]: - return [ - "SSL_get0_verified_chain", - ] - - -def cryptography_has_srtp() -> list[str]: - return [ - "SSL_CTX_set_tlsext_use_srtp", - "SSL_set_tlsext_use_srtp", - "SSL_get_selected_srtp_profile", - ] - - -def cryptography_has_op_no_renegotiation() -> list[str]: - return [ - "SSL_OP_NO_RENEGOTIATION", - ] - - -def cryptography_has_dtls_get_data_mtu() -> list[str]: - return [ - "DTLS_get_data_mtu", - ] - - -def cryptography_has_ssl_cookie() -> list[str]: - return [ - "SSL_OP_COOKIE_EXCHANGE", - "DTLSv1_listen", - "SSL_CTX_set_cookie_generate_cb", - "SSL_CTX_set_cookie_verify_cb", - ] - - -def cryptography_has_prime_checks() -> list[str]: - return [ - "BN_prime_checks_for_size", - ] - - -def cryptography_has_unexpected_eof_while_reading() -> list[str]: - return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] - - -def cryptography_has_ssl_op_ignore_unexpected_eof() -> list[str]: - return [ - "SSL_OP_IGNORE_UNEXPECTED_EOF", - ] - - -def cryptography_has_get_extms_support() -> list[str]: - return ["SSL_get_extms_support"] - - -# This is a mapping of -# {condition: function-returning-names-dependent-on-that-condition} so we can -# loop over them and delete unsupported names at runtime. It will be removed -# when cffi supports #if in cdef. We use functions instead of just a dict of -# lists so we can use coverage to measure which are used. -CONDITIONAL_NAMES = { - "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, - "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, - "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, - "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, - "Cryptography_HAS_PSK": cryptography_has_psk, - "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, - "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, - "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, - "Cryptography_HAS_ENGINE": cryptography_has_engine, - "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, - "Cryptography_HAS_SRTP": cryptography_has_srtp, - "Cryptography_HAS_OP_NO_RENEGOTIATION": ( - cryptography_has_op_no_renegotiation - ), - "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, - "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, - "Cryptography_HAS_PRIME_CHECKS": cryptography_has_prime_checks, - "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( - cryptography_has_unexpected_eof_while_reading - ), - "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( - cryptography_has_ssl_op_ignore_unexpected_eof - ), - "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, -} diff --git a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py deleted file mode 100644 index d4dfeef..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py +++ /dev/null @@ -1,121 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import os -import sys -import threading -import types -import typing -import warnings - -import cryptography -from cryptography.exceptions import InternalError -from cryptography.hazmat.bindings._rust import _openssl, openssl -from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES - - -def _openssl_assert(ok: bool) -> None: - if not ok: - errors = openssl.capture_error_stack() - - raise InternalError( - "Unknown OpenSSL error. This error is commonly encountered when " - "another library is not cleaning up the OpenSSL error stack. If " - "you are using cryptography with another library that uses " - "OpenSSL try disabling it before reporting a bug. Otherwise " - "please file an issue at https://github.com/pyca/cryptography/" - "issues with information on how to reproduce " - f"this. ({errors!r})", - errors, - ) - - -def build_conditional_library( - lib: typing.Any, - conditional_names: dict[str, typing.Callable[[], list[str]]], -) -> typing.Any: - conditional_lib = types.ModuleType("lib") - conditional_lib._original_lib = lib # type: ignore[attr-defined] - excluded_names = set() - for condition, names_cb in conditional_names.items(): - if not getattr(lib, condition): - excluded_names.update(names_cb()) - - for attr in dir(lib): - if attr not in excluded_names: - setattr(conditional_lib, attr, getattr(lib, attr)) - - return conditional_lib - - -class Binding: - """ - OpenSSL API wrapper. - """ - - lib: typing.ClassVar = None - ffi = _openssl.ffi - _lib_loaded = False - _init_lock = threading.Lock() - - def __init__(self) -> None: - self._ensure_ffi_initialized() - - @classmethod - def _ensure_ffi_initialized(cls) -> None: - with cls._init_lock: - if not cls._lib_loaded: - cls.lib = build_conditional_library( - _openssl.lib, CONDITIONAL_NAMES - ) - cls._lib_loaded = True - - @classmethod - def init_static_locks(cls) -> None: - cls._ensure_ffi_initialized() - - -def _verify_package_version(version: str) -> None: - # Occasionally we run into situations where the version of the Python - # package does not match the version of the shared object that is loaded. - # This may occur in environments where multiple versions of cryptography - # are installed and available in the python path. To avoid errors cropping - # up later this code checks that the currently imported package and the - # shared object that were loaded have the same version and raise an - # ImportError if they do not - so_package_version = _openssl.ffi.string( - _openssl.lib.CRYPTOGRAPHY_PACKAGE_VERSION - ) - if version.encode("ascii") != so_package_version: - raise ImportError( - "The version of cryptography does not match the loaded " - "shared object. This can happen if you have multiple copies of " - "cryptography installed in your Python path. Please try creating " - "a new virtual environment to resolve this issue. " - f"Loaded python version: {version}, " - f"shared object version: {so_package_version}" - ) - - _openssl_assert( - _openssl.lib.OpenSSL_version_num() == openssl.openssl_version(), - ) - - -_verify_package_version(cryptography.__version__) - -Binding.init_static_locks() - -if ( - sys.platform == "win32" - and os.environ.get("PROCESSOR_ARCHITEW6432") is not None -): - warnings.warn( - "You are using cryptography on a 32-bit Python on a 64-bit Windows " - "Operating System. Cryptography will be significantly faster if you " - "switch to using a 64-bit Python.", - UserWarning, - stacklevel=2, - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/decrepit/__init__.py deleted file mode 100644 index 41d7318..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations diff --git a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py deleted file mode 100644 index 41d7318..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations diff --git a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py b/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py deleted file mode 100644 index a7d4aa3..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py +++ /dev/null @@ -1,107 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, - _verify_key_size, -) - - -class ARC4(CipherAlgorithm): - name = "RC4" - key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class TripleDES(BlockCipherAlgorithm): - name = "3DES" - block_size = 64 - key_sizes = frozenset([64, 128, 192]) - - def __init__(self, key: bytes): - if len(key) == 8: - key += key + key - elif len(key) == 16: - key += key[:8] - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class Blowfish(BlockCipherAlgorithm): - name = "Blowfish" - block_size = 64 - key_sizes = frozenset(range(32, 449, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class CAST5(BlockCipherAlgorithm): - name = "CAST5" - block_size = 64 - key_sizes = frozenset(range(40, 129, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class SEED(BlockCipherAlgorithm): - name = "SEED" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class IDEA(BlockCipherAlgorithm): - name = "IDEA" - block_size = 64 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -# This class only allows RC2 with a 128-bit key. No support for -# effective key bits or other key sizes is provided. -class RC2(BlockCipherAlgorithm): - name = "RC2" - block_size = 64 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/__init__.py deleted file mode 100644 index b509336..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py deleted file mode 100644 index ea55ffd..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -# This exists to break an import cycle. It is normally accessible from the -# asymmetric padding module. - - -class AsymmetricPadding(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this padding (e.g. "PSS", "PKCS1"). - """ diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py deleted file mode 100644 index 588a616..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py +++ /dev/null @@ -1,58 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils - -# This exists to break an import cycle. It is normally accessible from the -# ciphers module. - - -class CipherAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "AES", "Camellia"). - """ - - @property - @abc.abstractmethod - def key_sizes(self) -> frozenset[int]: - """ - Valid key sizes for this algorithm in bits - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The size of the key being used as an integer in bits (e.g. 128, 256). - """ - - -class BlockCipherAlgorithm(CipherAlgorithm): - key: bytes - - @property - @abc.abstractmethod - def block_size(self) -> int: - """ - The size of a block as an integer in bits (e.g. 64, 128). - """ - - -def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes: - # Verify that the key is instance of bytes - utils._check_byteslike("key", key) - - # Verify that the key size matches the expected key size - if len(key) * 8 not in algorithm.key_sizes: - raise ValueError( - f"Invalid key size ({len(key) * 8}) for {algorithm.name}." - ) - return key diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py deleted file mode 100644 index 4615772..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py +++ /dev/null @@ -1,169 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils -from cryptography.hazmat.primitives.hashes import HashAlgorithm - -# This exists to break an import cycle. These classes are normally accessible -# from the serialization module. - - -class PBES(utils.Enum): - PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" - PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" - - -class Encoding(utils.Enum): - PEM = "PEM" - DER = "DER" - OpenSSH = "OpenSSH" - Raw = "Raw" - X962 = "ANSI X9.62" - SMIME = "S/MIME" - - -class PrivateFormat(utils.Enum): - PKCS8 = "PKCS8" - TraditionalOpenSSL = "TraditionalOpenSSL" - Raw = "Raw" - OpenSSH = "OpenSSH" - PKCS12 = "PKCS12" - - def encryption_builder(self) -> KeySerializationEncryptionBuilder: - if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): - raise ValueError( - "encryption_builder only supported with PrivateFormat.OpenSSH" - " and PrivateFormat.PKCS12" - ) - return KeySerializationEncryptionBuilder(self) - - -class PublicFormat(utils.Enum): - SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" - PKCS1 = "Raw PKCS#1" - OpenSSH = "OpenSSH" - Raw = "Raw" - CompressedPoint = "X9.62 Compressed Point" - UncompressedPoint = "X9.62 Uncompressed Point" - - -class ParameterFormat(utils.Enum): - PKCS3 = "PKCS3" - - -class KeySerializationEncryption(metaclass=abc.ABCMeta): - pass - - -class BestAvailableEncryption(KeySerializationEncryption): - def __init__(self, password: bytes): - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - self.password = password - - -class NoEncryption(KeySerializationEncryption): - pass - - -class KeySerializationEncryptionBuilder: - def __init__( - self, - format: PrivateFormat, - *, - _kdf_rounds: int | None = None, - _hmac_hash: HashAlgorithm | None = None, - _key_cert_algorithm: PBES | None = None, - ) -> None: - self._format = format - - self._kdf_rounds = _kdf_rounds - self._hmac_hash = _hmac_hash - self._key_cert_algorithm = _key_cert_algorithm - - def kdf_rounds(self, rounds: int) -> KeySerializationEncryptionBuilder: - if self._kdf_rounds is not None: - raise ValueError("kdf_rounds already set") - - if not isinstance(rounds, int): - raise TypeError("kdf_rounds must be an integer") - - if rounds < 1: - raise ValueError("kdf_rounds must be a positive integer") - - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def hmac_hash( - self, algorithm: HashAlgorithm - ) -> KeySerializationEncryptionBuilder: - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "hmac_hash only supported with PrivateFormat.PKCS12" - ) - - if self._hmac_hash is not None: - raise ValueError("hmac_hash already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=algorithm, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def key_cert_algorithm( - self, algorithm: PBES - ) -> KeySerializationEncryptionBuilder: - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "key_cert_algorithm only supported with " - "PrivateFormat.PKCS12" - ) - if self._key_cert_algorithm is not None: - raise ValueError("key_cert_algorithm already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=algorithm, - ) - - def build(self, password: bytes) -> KeySerializationEncryption: - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - return _KeySerializationEncryption( - self._format, - password, - kdf_rounds=self._kdf_rounds, - hmac_hash=self._hmac_hash, - key_cert_algorithm=self._key_cert_algorithm, - ) - - -class _KeySerializationEncryption(KeySerializationEncryption): - def __init__( - self, - format: PrivateFormat, - password: bytes, - *, - kdf_rounds: int | None, - hmac_hash: HashAlgorithm | None, - key_cert_algorithm: PBES | None, - ): - self._format = format - self.password = password - - self._kdf_rounds = kdf_rounds - self._hmac_hash = hmac_hash - self._key_cert_algorithm = key_cert_algorithm diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py deleted file mode 100644 index b509336..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py deleted file mode 100644 index 31c9748..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py +++ /dev/null @@ -1,135 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - -generate_parameters = rust_openssl.dh.generate_parameters - - -DHPrivateNumbers = rust_openssl.dh.DHPrivateNumbers -DHPublicNumbers = rust_openssl.dh.DHPublicNumbers -DHParameterNumbers = rust_openssl.dh.DHParameterNumbers - - -class DHParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> DHPrivateKey: - """ - Generates and returns a DHPrivateKey. - """ - - @abc.abstractmethod - def parameter_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.ParameterFormat, - ) -> bytes: - """ - Returns the parameters serialized as bytes. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DHParameterNumbers: - """ - Returns a DHParameterNumbers. - """ - - -DHParametersWithSerialization = DHParameters -DHParameters.register(rust_openssl.dh.DHParameters) - - -class DHPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DHPublicNumbers: - """ - Returns a DHPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -DHPublicKeyWithSerialization = DHPublicKey -DHPublicKey.register(rust_openssl.dh.DHPublicKey) - - -class DHPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DHPublicKey: - """ - The DHPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: DHPublicKey) -> bytes: - """ - Given peer's DHPublicKey, carry out the key exchange and - return shared key as bytes. - """ - - @abc.abstractmethod - def private_numbers(self) -> DHPrivateNumbers: - """ - Returns a DHPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -DHPrivateKeyWithSerialization = DHPrivateKey -DHPrivateKey.register(rust_openssl.dh.DHPrivateKey) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py deleted file mode 100644 index 6dd34c0..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py +++ /dev/null @@ -1,154 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class DSAParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> DSAPrivateKey: - """ - Generates and returns a DSAPrivateKey. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DSAParameterNumbers: - """ - Returns a DSAParameterNumbers. - """ - - -DSAParametersWithNumbers = DSAParameters -DSAParameters.register(rust_openssl.dsa.DSAParameters) - - -class DSAPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DSAPublicKey: - """ - The DSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> DSAPrivateNumbers: - """ - Returns a DSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -DSAPrivateKeyWithSerialization = DSAPrivateKey -DSAPrivateKey.register(rust_openssl.dsa.DSAPrivateKey) - - -class DSAPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DSAPublicNumbers: - """ - Returns a DSAPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -DSAPublicKeyWithSerialization = DSAPublicKey -DSAPublicKey.register(rust_openssl.dsa.DSAPublicKey) - -DSAPrivateNumbers = rust_openssl.dsa.DSAPrivateNumbers -DSAPublicNumbers = rust_openssl.dsa.DSAPublicNumbers -DSAParameterNumbers = rust_openssl.dsa.DSAParameterNumbers - - -def generate_parameters( - key_size: int, backend: typing.Any = None -) -> DSAParameters: - if key_size not in (1024, 2048, 3072, 4096): - raise ValueError("Key size must be 1024, 2048, 3072, or 4096 bits.") - - return rust_openssl.dsa.generate_parameters(key_size) - - -def generate_private_key( - key_size: int, backend: typing.Any = None -) -> DSAPrivateKey: - parameters = generate_parameters(key_size) - return parameters.generate_private_key() diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py deleted file mode 100644 index da1fbea..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py +++ /dev/null @@ -1,403 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat._oid import ObjectIdentifier -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class EllipticCurveOID: - SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") - SECP224R1 = ObjectIdentifier("1.3.132.0.33") - SECP256K1 = ObjectIdentifier("1.3.132.0.10") - SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") - SECP384R1 = ObjectIdentifier("1.3.132.0.34") - SECP521R1 = ObjectIdentifier("1.3.132.0.35") - BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") - BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") - BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") - SECT163K1 = ObjectIdentifier("1.3.132.0.1") - SECT163R2 = ObjectIdentifier("1.3.132.0.15") - SECT233K1 = ObjectIdentifier("1.3.132.0.26") - SECT233R1 = ObjectIdentifier("1.3.132.0.27") - SECT283K1 = ObjectIdentifier("1.3.132.0.16") - SECT283R1 = ObjectIdentifier("1.3.132.0.17") - SECT409K1 = ObjectIdentifier("1.3.132.0.36") - SECT409R1 = ObjectIdentifier("1.3.132.0.37") - SECT571K1 = ObjectIdentifier("1.3.132.0.38") - SECT571R1 = ObjectIdentifier("1.3.132.0.39") - - -class EllipticCurve(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - The name of the curve. e.g. secp256r1. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - -class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm( - self, - ) -> asym_utils.Prehashed | hashes.HashAlgorithm: - """ - The digest algorithm used with this signature. - """ - - -class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def exchange( - self, algorithm: ECDH, peer_public_key: EllipticCurvePublicKey - ) -> bytes: - """ - Performs a key exchange operation using the provided algorithm with the - provided peer's public key. - """ - - @abc.abstractmethod - def public_key(self) -> EllipticCurvePublicKey: - """ - The EllipticCurvePublicKey for this private key. - """ - - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> EllipticCurvePrivateNumbers: - """ - Returns an EllipticCurvePrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey -EllipticCurvePrivateKey.register(rust_openssl.ec.ECPrivateKey) - - -class EllipticCurvePublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def public_numbers(self) -> EllipticCurvePublicNumbers: - """ - Returns an EllipticCurvePublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @classmethod - def from_encoded_point( - cls, curve: EllipticCurve, data: bytes - ) -> EllipticCurvePublicKey: - utils._check_bytes("data", data) - - if len(data) == 0: - raise ValueError("data must not be an empty byte string") - - if data[0] not in [0x02, 0x03, 0x04]: - raise ValueError("Unsupported elliptic curve point type") - - return rust_openssl.ec.from_public_bytes(curve, data) - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey -EllipticCurvePublicKey.register(rust_openssl.ec.ECPublicKey) - -EllipticCurvePrivateNumbers = rust_openssl.ec.EllipticCurvePrivateNumbers -EllipticCurvePublicNumbers = rust_openssl.ec.EllipticCurvePublicNumbers - - -class SECT571R1(EllipticCurve): - name = "sect571r1" - key_size = 570 - - -class SECT409R1(EllipticCurve): - name = "sect409r1" - key_size = 409 - - -class SECT283R1(EllipticCurve): - name = "sect283r1" - key_size = 283 - - -class SECT233R1(EllipticCurve): - name = "sect233r1" - key_size = 233 - - -class SECT163R2(EllipticCurve): - name = "sect163r2" - key_size = 163 - - -class SECT571K1(EllipticCurve): - name = "sect571k1" - key_size = 571 - - -class SECT409K1(EllipticCurve): - name = "sect409k1" - key_size = 409 - - -class SECT283K1(EllipticCurve): - name = "sect283k1" - key_size = 283 - - -class SECT233K1(EllipticCurve): - name = "sect233k1" - key_size = 233 - - -class SECT163K1(EllipticCurve): - name = "sect163k1" - key_size = 163 - - -class SECP521R1(EllipticCurve): - name = "secp521r1" - key_size = 521 - - -class SECP384R1(EllipticCurve): - name = "secp384r1" - key_size = 384 - - -class SECP256R1(EllipticCurve): - name = "secp256r1" - key_size = 256 - - -class SECP256K1(EllipticCurve): - name = "secp256k1" - key_size = 256 - - -class SECP224R1(EllipticCurve): - name = "secp224r1" - key_size = 224 - - -class SECP192R1(EllipticCurve): - name = "secp192r1" - key_size = 192 - - -class BrainpoolP256R1(EllipticCurve): - name = "brainpoolP256r1" - key_size = 256 - - -class BrainpoolP384R1(EllipticCurve): - name = "brainpoolP384r1" - key_size = 384 - - -class BrainpoolP512R1(EllipticCurve): - name = "brainpoolP512r1" - key_size = 512 - - -_CURVE_TYPES: dict[str, EllipticCurve] = { - "prime192v1": SECP192R1(), - "prime256v1": SECP256R1(), - "secp192r1": SECP192R1(), - "secp224r1": SECP224R1(), - "secp256r1": SECP256R1(), - "secp384r1": SECP384R1(), - "secp521r1": SECP521R1(), - "secp256k1": SECP256K1(), - "sect163k1": SECT163K1(), - "sect233k1": SECT233K1(), - "sect283k1": SECT283K1(), - "sect409k1": SECT409K1(), - "sect571k1": SECT571K1(), - "sect163r2": SECT163R2(), - "sect233r1": SECT233R1(), - "sect283r1": SECT283R1(), - "sect409r1": SECT409R1(), - "sect571r1": SECT571R1(), - "brainpoolP256r1": BrainpoolP256R1(), - "brainpoolP384r1": BrainpoolP384R1(), - "brainpoolP512r1": BrainpoolP512R1(), -} - - -class ECDSA(EllipticCurveSignatureAlgorithm): - def __init__( - self, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - deterministic_signing: bool = False, - ): - from cryptography.hazmat.backends.openssl.backend import backend - - if ( - deterministic_signing - and not backend.ecdsa_deterministic_supported() - ): - raise UnsupportedAlgorithm( - "ECDSA with deterministic signature (RFC 6979) is not " - "supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - self._algorithm = algorithm - self._deterministic_signing = deterministic_signing - - @property - def algorithm( - self, - ) -> asym_utils.Prehashed | hashes.HashAlgorithm: - return self._algorithm - - @property - def deterministic_signing( - self, - ) -> bool: - return self._deterministic_signing - - -generate_private_key = rust_openssl.ec.generate_private_key - - -def derive_private_key( - private_value: int, - curve: EllipticCurve, - backend: typing.Any = None, -) -> EllipticCurvePrivateKey: - if not isinstance(private_value, int): - raise TypeError("private_value must be an integer type.") - - if private_value <= 0: - raise ValueError("private_value must be a positive integer.") - - return rust_openssl.ec.derive_private_key(private_value, curve) - - -class ECDH: - pass - - -_OID_TO_CURVE = { - EllipticCurveOID.SECP192R1: SECP192R1, - EllipticCurveOID.SECP224R1: SECP224R1, - EllipticCurveOID.SECP256K1: SECP256K1, - EllipticCurveOID.SECP256R1: SECP256R1, - EllipticCurveOID.SECP384R1: SECP384R1, - EllipticCurveOID.SECP521R1: SECP521R1, - EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, - EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, - EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, - EllipticCurveOID.SECT163K1: SECT163K1, - EllipticCurveOID.SECT163R2: SECT163R2, - EllipticCurveOID.SECT233K1: SECT233K1, - EllipticCurveOID.SECT233R1: SECT233R1, - EllipticCurveOID.SECT283K1: SECT283K1, - EllipticCurveOID.SECT283R1: SECT283R1, - EllipticCurveOID.SECT409K1: SECT409K1, - EllipticCurveOID.SECT409R1: SECT409R1, - EllipticCurveOID.SECT571K1: SECT571K1, - EllipticCurveOID.SECT571R1: SECT571R1, -} - - -def get_curve_for_oid(oid: ObjectIdentifier) -> type[EllipticCurve]: - try: - return _OID_TO_CURVE[oid] - except KeyError: - raise LookupError( - "The provided object identifier has no matching elliptic " - "curve class" - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py deleted file mode 100644 index 3a26185..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py +++ /dev/null @@ -1,116 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - - -class Ed25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> Ed25519PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed25519.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -Ed25519PublicKey.register(rust_openssl.ed25519.Ed25519PublicKey) - - -class Ed25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> Ed25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed25519.generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> Ed25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed25519.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed25519PublicKey: - """ - The Ed25519PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def sign(self, data: bytes) -> bytes: - """ - Signs the data. - """ - - -Ed25519PrivateKey.register(rust_openssl.ed25519.Ed25519PrivateKey) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py deleted file mode 100644 index 78c82c4..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py +++ /dev/null @@ -1,118 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - - -class Ed448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> Ed448PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """ - Verify the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -if hasattr(rust_openssl, "ed448"): - Ed448PublicKey.register(rust_openssl.ed448.Ed448PublicKey) - - -class Ed448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> Ed448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> Ed448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return rust_openssl.ed448.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed448PublicKey: - """ - The Ed448PublicKey derived from the private key. - """ - - @abc.abstractmethod - def sign(self, data: bytes) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - -if hasattr(rust_openssl, "x448"): - Ed448PrivateKey.register(rust_openssl.ed448.Ed448PrivateKey) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py deleted file mode 100644 index b4babf4..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +++ /dev/null @@ -1,113 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives._asymmetric import ( - AsymmetricPadding as AsymmetricPadding, -) -from cryptography.hazmat.primitives.asymmetric import rsa - - -class PKCS1v15(AsymmetricPadding): - name = "EMSA-PKCS1-v1_5" - - -class _MaxLength: - "Sentinel value for `MAX_LENGTH`." - - -class _Auto: - "Sentinel value for `AUTO`." - - -class _DigestLength: - "Sentinel value for `DIGEST_LENGTH`." - - -class PSS(AsymmetricPadding): - MAX_LENGTH = _MaxLength() - AUTO = _Auto() - DIGEST_LENGTH = _DigestLength() - name = "EMSA-PSS" - _salt_length: int | _MaxLength | _Auto | _DigestLength - - def __init__( - self, - mgf: MGF, - salt_length: int | _MaxLength | _Auto | _DigestLength, - ) -> None: - self._mgf = mgf - - if not isinstance( - salt_length, (int, _MaxLength, _Auto, _DigestLength) - ): - raise TypeError( - "salt_length must be an integer, MAX_LENGTH, " - "DIGEST_LENGTH, or AUTO" - ) - - if isinstance(salt_length, int) and salt_length < 0: - raise ValueError("salt_length must be zero or greater.") - - self._salt_length = salt_length - - @property - def mgf(self) -> MGF: - return self._mgf - - -class OAEP(AsymmetricPadding): - name = "EME-OAEP" - - def __init__( - self, - mgf: MGF, - algorithm: hashes.HashAlgorithm, - label: bytes | None, - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._mgf = mgf - self._algorithm = algorithm - self._label = label - - @property - def algorithm(self) -> hashes.HashAlgorithm: - return self._algorithm - - @property - def mgf(self) -> MGF: - return self._mgf - - -class MGF(metaclass=abc.ABCMeta): - _algorithm: hashes.HashAlgorithm - - -class MGF1(MGF): - MAX_LENGTH = _MaxLength() - - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._algorithm = algorithm - - -def calculate_max_pss_salt_length( - key: rsa.RSAPrivateKey | rsa.RSAPublicKey, - hash_algorithm: hashes.HashAlgorithm, -) -> int: - if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): - raise TypeError("key must be an RSA public or private key") - # bit length - 1 per RFC 3447 - emlen = (key.key_size + 6) // 8 - salt_length = emlen - hash_algorithm.digest_size - 2 - assert salt_length >= 0 - return salt_length diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py deleted file mode 100644 index 905068e..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +++ /dev/null @@ -1,263 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import random -import typing -from math import gcd - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class RSAPrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Decrypts the provided ciphertext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_key(self) -> RSAPublicKey: - """ - The RSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - padding: AsymmetricPadding, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_numbers(self) -> RSAPrivateNumbers: - """ - Returns an RSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -RSAPrivateKeyWithSerialization = RSAPrivateKey -RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey) - - -class RSAPublicKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Encrypts the given plaintext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_numbers(self) -> RSAPublicNumbers: - """ - Returns an RSAPublicNumbers - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - padding: AsymmetricPadding, - algorithm: asym_utils.Prehashed | hashes.HashAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @abc.abstractmethod - def recover_data_from_signature( - self, - signature: bytes, - padding: AsymmetricPadding, - algorithm: hashes.HashAlgorithm | None, - ) -> bytes: - """ - Recovers the original data from the signature. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -RSAPublicKeyWithSerialization = RSAPublicKey -RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey) - -RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers -RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers - - -def generate_private_key( - public_exponent: int, - key_size: int, - backend: typing.Any = None, -) -> RSAPrivateKey: - _verify_rsa_parameters(public_exponent, key_size) - return rust_openssl.rsa.generate_private_key(public_exponent, key_size) - - -def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: - if public_exponent not in (3, 65537): - raise ValueError( - "public_exponent must be either 3 (for legacy compatibility) or " - "65537. Almost everyone should choose 65537 here!" - ) - - if key_size < 1024: - raise ValueError("key_size must be at least 1024-bits.") - - -def _modinv(e: int, m: int) -> int: - """ - Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 - """ - x1, x2 = 1, 0 - a, b = e, m - while b > 0: - q, r = divmod(a, b) - xn = x1 - q * x2 - a, b, x1, x2 = b, r, x2, xn - return x1 % m - - -def rsa_crt_iqmp(p: int, q: int) -> int: - """ - Compute the CRT (q ** -1) % p value from RSA primes p and q. - """ - return _modinv(q, p) - - -def rsa_crt_dmp1(private_exponent: int, p: int) -> int: - """ - Compute the CRT private_exponent % (p - 1) value from the RSA - private_exponent (d) and p. - """ - return private_exponent % (p - 1) - - -def rsa_crt_dmq1(private_exponent: int, q: int) -> int: - """ - Compute the CRT private_exponent % (q - 1) value from the RSA - private_exponent (d) and q. - """ - return private_exponent % (q - 1) - - -def rsa_recover_private_exponent(e: int, p: int, q: int) -> int: - """ - Compute the RSA private_exponent (d) given the public exponent (e) - and the RSA primes p and q. - - This uses the Carmichael totient function to generate the - smallest possible working value of the private exponent. - """ - # This lambda_n is the Carmichael totient function. - # The original RSA paper uses the Euler totient function - # here: phi_n = (p - 1) * (q - 1) - # Either version of the private exponent will work, but the - # one generated by the older formulation may be larger - # than necessary. (lambda_n always divides phi_n) - # - # TODO: Replace with lcm(p - 1, q - 1) once the minimum - # supported Python version is >= 3.9. - lambda_n = (p - 1) * (q - 1) // gcd(p - 1, q - 1) - return _modinv(e, lambda_n) - - -# Controls the number of iterations rsa_recover_prime_factors will perform -# to obtain the prime factors. -_MAX_RECOVERY_ATTEMPTS = 500 - - -def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]: - """ - Compute factors p and q from the private exponent d. We assume that n has - no more than two factors. This function is adapted from code in PyCrypto. - """ - # reject invalid values early - if 17 != pow(17, e * d, n): - raise ValueError("n, d, e don't match") - # See 8.2.2(i) in Handbook of Applied Cryptography. - ktot = d * e - 1 - # The quantity d*e-1 is a multiple of phi(n), even, - # and can be represented as t*2^s. - t = ktot - while t % 2 == 0: - t = t // 2 - # Cycle through all multiplicative inverses in Zn. - # The algorithm is non-deterministic, but there is a 50% chance - # any candidate a leads to successful factoring. - # See "Digitalized Signatures and Public Key Functions as Intractable - # as Factorization", M. Rabin, 1979 - spotted = False - tries = 0 - while not spotted and tries < _MAX_RECOVERY_ATTEMPTS: - a = random.randint(2, n - 1) - tries += 1 - k = t - # Cycle through all values a^{t*2^i}=a^k - while k < ktot: - cand = pow(a, k, n) - # Check if a^k is a non-trivial root of unity (mod n) - if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: - # We have found a number such that (cand-1)(cand+1)=0 (mod n). - # Either of the terms divides n. - p = gcd(cand + 1, n) - spotted = True - break - k *= 2 - if not spotted: - raise ValueError("Unable to compute factors p and q from exponent d.") - # Found ! - q, r = divmod(n, p) - assert r == 0 - p, q = sorted((p, q), reverse=True) - return (p, q) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py deleted file mode 100644 index 1fe4eaf..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.hazmat.primitives.asymmetric import ( - dh, - dsa, - ec, - ed448, - ed25519, - rsa, - x448, - x25519, -) - -# Every asymmetric key type -PublicKeyTypes = typing.Union[ - dh.DHPublicKey, - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] -PUBLIC_KEY_TYPES = PublicKeyTypes -utils.deprecated( - PUBLIC_KEY_TYPES, - __name__, - "Use PublicKeyTypes instead", - utils.DeprecatedIn40, - name="PUBLIC_KEY_TYPES", -) -# Every asymmetric key type -PrivateKeyTypes = typing.Union[ - dh.DHPrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - x25519.X25519PrivateKey, - x448.X448PrivateKey, -] -PRIVATE_KEY_TYPES = PrivateKeyTypes -utils.deprecated( - PRIVATE_KEY_TYPES, - __name__, - "Use PrivateKeyTypes instead", - utils.DeprecatedIn40, - name="PRIVATE_KEY_TYPES", -) -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate public key types -CertificateIssuerPrivateKeyTypes = typing.Union[ - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, -] -CERTIFICATE_PRIVATE_KEY_TYPES = CertificateIssuerPrivateKeyTypes -utils.deprecated( - CERTIFICATE_PRIVATE_KEY_TYPES, - __name__, - "Use CertificateIssuerPrivateKeyTypes instead", - utils.DeprecatedIn40, - name="CERTIFICATE_PRIVATE_KEY_TYPES", -) -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate private key types -CertificateIssuerPublicKeyTypes = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, -] -CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = CertificateIssuerPublicKeyTypes -utils.deprecated( - CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, - __name__, - "Use CertificateIssuerPublicKeyTypes instead", - utils.DeprecatedIn40, - name="CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES", -) -# This type removes DHPublicKey. x448/x25519 can be a public key -# but cannot be used in signing so they are allowed here. -CertificatePublicKeyTypes = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] -CERTIFICATE_PUBLIC_KEY_TYPES = CertificatePublicKeyTypes -utils.deprecated( - CERTIFICATE_PUBLIC_KEY_TYPES, - __name__, - "Use CertificatePublicKeyTypes instead", - utils.DeprecatedIn40, - name="CERTIFICATE_PUBLIC_KEY_TYPES", -) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py deleted file mode 100644 index 826b956..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py +++ /dev/null @@ -1,24 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.primitives import hashes - -decode_dss_signature = asn1.decode_dss_signature -encode_dss_signature = asn1.encode_dss_signature - - -class Prehashed: - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of HashAlgorithm.") - - self._algorithm = algorithm - self._digest_size = algorithm.digest_size - - @property - def digest_size(self) -> int: - return self._digest_size diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py deleted file mode 100644 index 0cfa36e..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py +++ /dev/null @@ -1,109 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - - -class X25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> X25519PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x25519.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -X25519PublicKey.register(rust_openssl.x25519.X25519PublicKey) - - -class X25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> X25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - return rust_openssl.x25519.generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> X25519PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x25519.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X25519PublicKey: - """ - Returns the public key associated with this private key - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X25519PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ - - -X25519PrivateKey.register(rust_openssl.x25519.X25519PrivateKey) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py deleted file mode 100644 index 86086ab..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py +++ /dev/null @@ -1,112 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import _serialization - - -class X448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> X448PublicKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.from_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def public_bytes_raw(self) -> bytes: - """ - The raw bytes of the public key. - Equivalent to public_bytes(Raw, Raw). - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - -if hasattr(rust_openssl, "x448"): - X448PublicKey.register(rust_openssl.x448.X448PublicKey) - - -class X448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> X448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> X448PrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return rust_openssl.x448.from_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X448PublicKey: - """ - Returns the public key associated with this private key - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def private_bytes_raw(self) -> bytes: - """ - The raw bytes of the private key. - Equivalent to private_bytes(Raw, Raw, NoEncryption()). - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X448PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ - - -if hasattr(rust_openssl, "x448"): - X448PrivateKey.register(rust_openssl.x448.X448PrivateKey) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py deleted file mode 100644 index 10c15d0..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.base import ( - AEADCipherContext, - AEADDecryptionContext, - AEADEncryptionContext, - Cipher, - CipherContext, -) - -__all__ = [ - "AEADCipherContext", - "AEADDecryptionContext", - "AEADEncryptionContext", - "BlockCipherAlgorithm", - "Cipher", - "CipherAlgorithm", - "CipherContext", -] diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py deleted file mode 100644 index c8a582d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = [ - "AESCCM", - "AESGCM", - "AESGCMSIV", - "AESOCB3", - "AESSIV", - "ChaCha20Poly1305", -] - -AESGCM = rust_openssl.aead.AESGCM -ChaCha20Poly1305 = rust_openssl.aead.ChaCha20Poly1305 -AESCCM = rust_openssl.aead.AESCCM -AESSIV = rust_openssl.aead.AESSIV -AESOCB3 = rust_openssl.aead.AESOCB3 -AESGCMSIV = rust_openssl.aead.AESGCMSIV diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py deleted file mode 100644 index f9fa8a5..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +++ /dev/null @@ -1,183 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - ARC4 as ARC4, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - CAST5 as CAST5, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - IDEA as IDEA, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - SEED as SEED, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - Blowfish as Blowfish, -) -from cryptography.hazmat.decrepit.ciphers.algorithms import ( - TripleDES as TripleDES, -) -from cryptography.hazmat.primitives._cipheralgorithm import _verify_key_size -from cryptography.hazmat.primitives.ciphers import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) - - -class AES(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - # 512 added to support AES-256-XTS, which uses 512-bit keys - key_sizes = frozenset([128, 192, 256, 512]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class AES128(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([128]) - key_size = 128 - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - -class AES256(BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([256]) - key_size = 256 - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - -class Camellia(BlockCipherAlgorithm): - name = "camellia" - block_size = 128 - key_sizes = frozenset([128, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -utils.deprecated( - ARC4, - __name__, - "ARC4 has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.ARC4 and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", - utils.DeprecatedIn43, - name="ARC4", -) - - -utils.deprecated( - TripleDES, - __name__, - "TripleDES has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0.", - utils.DeprecatedIn43, - name="TripleDES", -) - -utils.deprecated( - Blowfish, - __name__, - "Blowfish has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.Blowfish and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 45.0.0.", - utils.DeprecatedIn37, - name="Blowfish", -) - - -utils.deprecated( - CAST5, - __name__, - "CAST5 has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.CAST5 and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 45.0.0.", - utils.DeprecatedIn37, - name="CAST5", -) - - -utils.deprecated( - IDEA, - __name__, - "IDEA has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.IDEA and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 45.0.0.", - utils.DeprecatedIn37, - name="IDEA", -) - - -utils.deprecated( - SEED, - __name__, - "SEED has been moved to " - "cryptography.hazmat.decrepit.ciphers.algorithms.SEED and " - "will be removed from " - "cryptography.hazmat.primitives.ciphers.algorithms in 45.0.0.", - utils.DeprecatedIn37, - name="SEED", -) - - -class ChaCha20(CipherAlgorithm): - name = "ChaCha20" - key_sizes = frozenset([256]) - - def __init__(self, key: bytes, nonce: bytes): - self.key = _verify_key_size(self, key) - utils._check_byteslike("nonce", nonce) - - if len(nonce) != 16: - raise ValueError("nonce must be 128-bits (16 bytes)") - - self._nonce = nonce - - @property - def nonce(self) -> bytes: - return self._nonce - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class SM4(BlockCipherAlgorithm): - name = "SM4" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py deleted file mode 100644 index ebfa805..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py +++ /dev/null @@ -1,145 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm -from cryptography.hazmat.primitives.ciphers import modes - - -class CipherContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: bytes) -> bytes: - """ - Processes the provided bytes through the cipher and returns the results - as bytes. - """ - - @abc.abstractmethod - def update_into(self, data: bytes, buf: bytes) -> int: - """ - Processes the provided bytes and writes the resulting data into the - provided buffer. Returns the number of bytes written. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Returns the results of processing the final block as bytes. - """ - - @abc.abstractmethod - def reset_nonce(self, nonce: bytes) -> None: - """ - Resets the nonce for the cipher context to the provided value. - Raises an exception if it does not support reset or if the - provided nonce does not have a valid length. - """ - - -class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def authenticate_additional_data(self, data: bytes) -> None: - """ - Authenticates the provided bytes. - """ - - -class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def finalize_with_tag(self, tag: bytes) -> bytes: - """ - Returns the results of processing the final block as bytes and allows - delayed passing of the authentication tag. - """ - - -class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> bytes: - """ - Returns tag bytes. This is only available after encryption is - finalized. - """ - - -Mode = typing.TypeVar( - "Mode", bound=typing.Optional[modes.Mode], covariant=True -) - - -class Cipher(typing.Generic[Mode]): - def __init__( - self, - algorithm: CipherAlgorithm, - mode: Mode, - backend: typing.Any = None, - ) -> None: - if not isinstance(algorithm, CipherAlgorithm): - raise TypeError("Expected interface of CipherAlgorithm.") - - if mode is not None: - # mypy needs this assert to narrow the type from our generic - # type. Maybe it won't some time in the future. - assert isinstance(mode, modes.Mode) - mode.validate_for_algorithm(algorithm) - - self.algorithm = algorithm - self.mode = mode - - @typing.overload - def encryptor( - self: Cipher[modes.ModeWithAuthenticationTag], - ) -> AEADEncryptionContext: ... - - @typing.overload - def encryptor( - self: _CIPHER_TYPE, - ) -> CipherContext: ... - - def encryptor(self): - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if self.mode.tag is not None: - raise ValueError( - "Authentication tag must be None when encrypting." - ) - - return rust_openssl.ciphers.create_encryption_ctx( - self.algorithm, self.mode - ) - - @typing.overload - def decryptor( - self: Cipher[modes.ModeWithAuthenticationTag], - ) -> AEADDecryptionContext: ... - - @typing.overload - def decryptor( - self: _CIPHER_TYPE, - ) -> CipherContext: ... - - def decryptor(self): - return rust_openssl.ciphers.create_decryption_ctx( - self.algorithm, self.mode - ) - - -_CIPHER_TYPE = Cipher[ - typing.Union[ - modes.ModeWithNonce, - modes.ModeWithTweak, - None, - modes.ECB, - modes.ModeWithInitializationVector, - ] -] - -CipherContext.register(rust_openssl.ciphers.CipherContext) -AEADEncryptionContext.register(rust_openssl.ciphers.AEADEncryptionContext) -AEADDecryptionContext.register(rust_openssl.ciphers.AEADDecryptionContext) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py deleted file mode 100644 index 1dd2cc1..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py +++ /dev/null @@ -1,268 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers import algorithms - - -class Mode(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "ECB", "CBC"). - """ - - @abc.abstractmethod - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - """ - Checks that all the necessary invariants of this (mode, algorithm) - combination are met. - """ - - -class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def initialization_vector(self) -> bytes: - """ - The value of the initialization vector for this mode as bytes. - """ - - -class ModeWithTweak(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tweak(self) -> bytes: - """ - The value of the tweak for this mode as bytes. - """ - - -class ModeWithNonce(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def nonce(self) -> bytes: - """ - The value of the nonce for this mode as bytes. - """ - - -class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> bytes | None: - """ - The value of the tag supplied to the constructor of this mode. - """ - - -def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: - if algorithm.key_size > 256 and algorithm.name == "AES": - raise ValueError( - "Only 128, 192, and 256 bit keys are allowed for this AES mode" - ) - - -def _check_iv_length( - self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm -) -> None: - iv_len = len(self.initialization_vector) - if iv_len * 8 != algorithm.block_size: - raise ValueError(f"Invalid IV size ({iv_len}) for {self.name}.") - - -def _check_nonce_length( - nonce: bytes, name: str, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{name} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - if len(nonce) * 8 != algorithm.block_size: - raise ValueError(f"Invalid nonce size ({len(nonce)}) for {name}.") - - -def _check_iv_and_key_length( - self: ModeWithInitializationVector, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{self} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - _check_aes_key_length(self, algorithm) - _check_iv_length(self, algorithm) - - -class CBC(ModeWithInitializationVector): - name = "CBC" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class XTS(ModeWithTweak): - name = "XTS" - - def __init__(self, tweak: bytes): - utils._check_byteslike("tweak", tweak) - - if len(tweak) != 16: - raise ValueError("tweak must be 128-bits (16 bytes)") - - self._tweak = tweak - - @property - def tweak(self) -> bytes: - return self._tweak - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): - raise TypeError( - "The AES128 and AES256 classes do not support XTS, please use " - "the standard AES class instead." - ) - - if algorithm.key_size not in (256, 512): - raise ValueError( - "The XTS specification requires a 256-bit key for AES-128-XTS" - " and 512-bit key for AES-256-XTS" - ) - - -class ECB(Mode): - name = "ECB" - - validate_for_algorithm = _check_aes_key_length - - -class OFB(ModeWithInitializationVector): - name = "OFB" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB(ModeWithInitializationVector): - name = "CFB" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB8(ModeWithInitializationVector): - name = "CFB8" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CTR(ModeWithNonce): - name = "CTR" - - def __init__(self, nonce: bytes): - utils._check_byteslike("nonce", nonce) - self._nonce = nonce - - @property - def nonce(self) -> bytes: - return self._nonce - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - _check_nonce_length(self.nonce, self.name, algorithm) - - -class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): - name = "GCM" - _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 - _MAX_AAD_BYTES = (2**64) // 8 - - def __init__( - self, - initialization_vector: bytes, - tag: bytes | None = None, - min_tag_length: int = 16, - ): - # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive - # This is a sane limit anyway so we'll enforce it here. - utils._check_byteslike("initialization_vector", initialization_vector) - if len(initialization_vector) < 8 or len(initialization_vector) > 128: - raise ValueError( - "initialization_vector must be between 8 and 128 bytes (64 " - "and 1024 bits)." - ) - self._initialization_vector = initialization_vector - if tag is not None: - utils._check_bytes("tag", tag) - if min_tag_length < 4: - raise ValueError("min_tag_length must be >= 4") - if len(tag) < min_tag_length: - raise ValueError( - f"Authentication tag must be {min_tag_length} bytes or " - "longer." - ) - self._tag = tag - self._min_tag_length = min_tag_length - - @property - def tag(self) -> bytes | None: - return self._tag - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - "GCM requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - block_size_bytes = algorithm.block_size // 8 - if self._tag is not None and len(self._tag) > block_size_bytes: - raise ValueError( - f"Authentication tag cannot be more than {block_size_bytes} " - "bytes." - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/cmac.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/cmac.py deleted file mode 100644 index 2c67ce2..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/cmac.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = ["CMAC"] -CMAC = rust_openssl.cmac.CMAC diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py deleted file mode 100644 index 3975c71..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import hmac - - -def bytes_eq(a: bytes, b: bytes) -> bool: - if not isinstance(a, bytes) or not isinstance(b, bytes): - raise TypeError("a and b must be bytes.") - - return hmac.compare_digest(a, b) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/hashes.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/hashes.py deleted file mode 100644 index b819e39..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/hashes.py +++ /dev/null @@ -1,242 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = [ - "MD5", - "SHA1", - "SHA3_224", - "SHA3_256", - "SHA3_384", - "SHA3_512", - "SHA224", - "SHA256", - "SHA384", - "SHA512", - "SHA512_224", - "SHA512_256", - "SHAKE128", - "SHAKE256", - "SM3", - "BLAKE2b", - "BLAKE2s", - "ExtendableOutputFunction", - "Hash", - "HashAlgorithm", - "HashContext", -] - - -class HashAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this algorithm (e.g. "sha256", "md5"). - """ - - @property - @abc.abstractmethod - def digest_size(self) -> int: - """ - The size of the resulting digest in bytes. - """ - - @property - @abc.abstractmethod - def block_size(self) -> int | None: - """ - The internal block size of the hash function, or None if the hash - function does not use blocks internally (e.g. SHA3). - """ - - -class HashContext(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm(self) -> HashAlgorithm: - """ - A HashAlgorithm that will be used by this context. - """ - - @abc.abstractmethod - def update(self, data: bytes) -> None: - """ - Processes the provided bytes through the hash. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalizes the hash context and returns the hash digest as bytes. - """ - - @abc.abstractmethod - def copy(self) -> HashContext: - """ - Return a HashContext that is a copy of the current context. - """ - - -Hash = rust_openssl.hashes.Hash -HashContext.register(Hash) - - -class ExtendableOutputFunction(metaclass=abc.ABCMeta): - """ - An interface for extendable output functions. - """ - - -class SHA1(HashAlgorithm): - name = "sha1" - digest_size = 20 - block_size = 64 - - -class SHA512_224(HashAlgorithm): # noqa: N801 - name = "sha512-224" - digest_size = 28 - block_size = 128 - - -class SHA512_256(HashAlgorithm): # noqa: N801 - name = "sha512-256" - digest_size = 32 - block_size = 128 - - -class SHA224(HashAlgorithm): - name = "sha224" - digest_size = 28 - block_size = 64 - - -class SHA256(HashAlgorithm): - name = "sha256" - digest_size = 32 - block_size = 64 - - -class SHA384(HashAlgorithm): - name = "sha384" - digest_size = 48 - block_size = 128 - - -class SHA512(HashAlgorithm): - name = "sha512" - digest_size = 64 - block_size = 128 - - -class SHA3_224(HashAlgorithm): # noqa: N801 - name = "sha3-224" - digest_size = 28 - block_size = None - - -class SHA3_256(HashAlgorithm): # noqa: N801 - name = "sha3-256" - digest_size = 32 - block_size = None - - -class SHA3_384(HashAlgorithm): # noqa: N801 - name = "sha3-384" - digest_size = 48 - block_size = None - - -class SHA3_512(HashAlgorithm): # noqa: N801 - name = "sha3-512" - digest_size = 64 - block_size = None - - -class SHAKE128(HashAlgorithm, ExtendableOutputFunction): - name = "shake128" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SHAKE256(HashAlgorithm, ExtendableOutputFunction): - name = "shake256" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class MD5(HashAlgorithm): - name = "md5" - digest_size = 16 - block_size = 64 - - -class BLAKE2b(HashAlgorithm): - name = "blake2b" - _max_digest_size = 64 - _min_digest_size = 1 - block_size = 128 - - def __init__(self, digest_size: int): - if digest_size != 64: - raise ValueError("Digest size must be 64") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class BLAKE2s(HashAlgorithm): - name = "blake2s" - block_size = 64 - _max_digest_size = 32 - _min_digest_size = 1 - - def __init__(self, digest_size: int): - if digest_size != 32: - raise ValueError("Digest size must be 32") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SM3(HashAlgorithm): - name = "sm3" - digest_size = 32 - block_size = 64 diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/hmac.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/hmac.py deleted file mode 100644 index a9442d5..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/hmac.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import hashes - -__all__ = ["HMAC"] - -HMAC = rust_openssl.hmac.HMAC -hashes.HashContext.register(HMAC) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py deleted file mode 100644 index 79bb459..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc - - -class KeyDerivationFunction(metaclass=abc.ABCMeta): - @abc.abstractmethod - def derive(self, key_material: bytes) -> bytes: - """ - Deterministically generates and returns a new key based on the existing - key material. - """ - - @abc.abstractmethod - def verify(self, key_material: bytes, expected_key: bytes) -> None: - """ - Checks whether the key generated by the key material matches the - expected derived key. Raises an exception if they do not match. - """ diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/argon2.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/argon2.py deleted file mode 100644 index 405fc8d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/argon2.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -Argon2id = rust_openssl.kdf.Argon2id -KeyDerivationFunction.register(Argon2id) - -__all__ = ["Argon2id"] diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py deleted file mode 100644 index 96d9d4c..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py +++ /dev/null @@ -1,124 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes, hmac -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -def _int_to_u32be(n: int) -> bytes: - return n.to_bytes(length=4, byteorder="big") - - -def _common_args_checks( - algorithm: hashes.HashAlgorithm, - length: int, - otherinfo: bytes | None, -) -> None: - max_length = algorithm.digest_size * (2**32 - 1) - if length > max_length: - raise ValueError(f"Cannot derive keys larger than {max_length} bits.") - if otherinfo is not None: - utils._check_bytes("otherinfo", otherinfo) - - -def _concatkdf_derive( - key_material: bytes, - length: int, - auxfn: typing.Callable[[], hashes.HashContext], - otherinfo: bytes, -) -> bytes: - utils._check_byteslike("key_material", key_material) - output = [b""] - outlen = 0 - counter = 1 - - while length > outlen: - h = auxfn() - h.update(_int_to_u32be(counter)) - h.update(key_material) - h.update(otherinfo) - output.append(h.finalize()) - outlen += len(output[-1]) - counter += 1 - - return b"".join(output)[:length] - - -class ConcatKDFHash(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - otherinfo: bytes | None, - backend: typing.Any = None, - ): - _common_args_checks(algorithm, length, otherinfo) - self._algorithm = algorithm - self._length = length - self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" - - self._used = False - - def _hash(self) -> hashes.Hash: - return hashes.Hash(self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - return _concatkdf_derive( - key_material, self._length, self._hash, self._otherinfo - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class ConcatKDFHMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: bytes | None, - otherinfo: bytes | None, - backend: typing.Any = None, - ): - _common_args_checks(algorithm, length, otherinfo) - self._algorithm = algorithm - self._length = length - self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" - - if algorithm.block_size is None: - raise TypeError(f"{algorithm.name} is unsupported for ConcatKDF") - - if salt is None: - salt = b"\x00" * algorithm.block_size - else: - utils._check_bytes("salt", salt) - - self._salt = salt - - self._used = False - - def _hmac(self) -> hmac.HMAC: - return hmac.HMAC(self._salt, self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - return _concatkdf_derive( - key_material, self._length, self._hmac, self._otherinfo - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py deleted file mode 100644 index ee562d2..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +++ /dev/null @@ -1,101 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes, hmac -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class HKDF(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: bytes | None, - info: bytes | None, - backend: typing.Any = None, - ): - self._algorithm = algorithm - - if salt is None: - salt = b"\x00" * self._algorithm.digest_size - else: - utils._check_bytes("salt", salt) - - self._salt = salt - - self._hkdf_expand = HKDFExpand(self._algorithm, length, info) - - def _extract(self, key_material: bytes) -> bytes: - h = hmac.HMAC(self._salt, self._algorithm) - h.update(key_material) - return h.finalize() - - def derive(self, key_material: bytes) -> bytes: - utils._check_byteslike("key_material", key_material) - return self._hkdf_expand.derive(self._extract(key_material)) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class HKDFExpand(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - info: bytes | None, - backend: typing.Any = None, - ): - self._algorithm = algorithm - - max_length = 255 * algorithm.digest_size - - if length > max_length: - raise ValueError( - f"Cannot derive keys larger than {max_length} octets." - ) - - self._length = length - - if info is None: - info = b"" - else: - utils._check_bytes("info", info) - - self._info = info - - self._used = False - - def _expand(self, key_material: bytes) -> bytes: - output = [b""] - counter = 1 - - while self._algorithm.digest_size * (len(output) - 1) < self._length: - h = hmac.HMAC(key_material, self._algorithm) - h.update(output[-1]) - h.update(self._info) - h.update(bytes([counter])) - output.append(h.finalize()) - counter += 1 - - return b"".join(output)[: self._length] - - def derive(self, key_material: bytes) -> bytes: - utils._check_byteslike("key_material", key_material) - if self._used: - raise AlreadyFinalized - - self._used = True - return self._expand(key_material) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py deleted file mode 100644 index 802b484..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +++ /dev/null @@ -1,302 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - InvalidKey, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.primitives import ( - ciphers, - cmac, - constant_time, - hashes, - hmac, -) -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class Mode(utils.Enum): - CounterMode = "ctr" - - -class CounterLocation(utils.Enum): - BeforeFixed = "before_fixed" - AfterFixed = "after_fixed" - MiddleFixed = "middle_fixed" - - -class _KBKDFDeriver: - def __init__( - self, - prf: typing.Callable, - mode: Mode, - length: int, - rlen: int, - llen: int | None, - location: CounterLocation, - break_location: int | None, - label: bytes | None, - context: bytes | None, - fixed: bytes | None, - ): - assert callable(prf) - - if not isinstance(mode, Mode): - raise TypeError("mode must be of type Mode") - - if not isinstance(location, CounterLocation): - raise TypeError("location must be of type CounterLocation") - - if break_location is None and location is CounterLocation.MiddleFixed: - raise ValueError("Please specify a break_location") - - if ( - break_location is not None - and location != CounterLocation.MiddleFixed - ): - raise ValueError( - "break_location is ignored when location is not" - " CounterLocation.MiddleFixed" - ) - - if break_location is not None and not isinstance(break_location, int): - raise TypeError("break_location must be an integer") - - if break_location is not None and break_location < 0: - raise ValueError("break_location must be a positive integer") - - if (label or context) and fixed: - raise ValueError( - "When supplying fixed data, label and context are ignored." - ) - - if rlen is None or not self._valid_byte_length(rlen): - raise ValueError("rlen must be between 1 and 4") - - if llen is None and fixed is None: - raise ValueError("Please specify an llen") - - if llen is not None and not isinstance(llen, int): - raise TypeError("llen must be an integer") - - if llen == 0: - raise ValueError("llen must be non-zero") - - if label is None: - label = b"" - - if context is None: - context = b"" - - utils._check_bytes("label", label) - utils._check_bytes("context", context) - self._prf = prf - self._mode = mode - self._length = length - self._rlen = rlen - self._llen = llen - self._location = location - self._break_location = break_location - self._label = label - self._context = context - self._used = False - self._fixed_data = fixed - - @staticmethod - def _valid_byte_length(value: int) -> bool: - if not isinstance(value, int): - raise TypeError("value must be of type int") - - value_bin = utils.int_to_bytes(1, value) - if not 1 <= len(value_bin) <= 4: - return False - return True - - def derive(self, key_material: bytes, prf_output_size: int) -> bytes: - if self._used: - raise AlreadyFinalized - - utils._check_byteslike("key_material", key_material) - self._used = True - - # inverse floor division (equivalent to ceiling) - rounds = -(-self._length // prf_output_size) - - output = [b""] - - # For counter mode, the number of iterations shall not be - # larger than 2^r-1, where r <= 32 is the binary length of the counter - # This ensures that the counter values used as an input to the - # PRF will not repeat during a particular call to the KDF function. - r_bin = utils.int_to_bytes(1, self._rlen) - if rounds > pow(2, len(r_bin) * 8) - 1: - raise ValueError("There are too many iterations.") - - fixed = self._generate_fixed_input() - - if self._location == CounterLocation.BeforeFixed: - data_before_ctr = b"" - data_after_ctr = fixed - elif self._location == CounterLocation.AfterFixed: - data_before_ctr = fixed - data_after_ctr = b"" - else: - if isinstance( - self._break_location, int - ) and self._break_location > len(fixed): - raise ValueError("break_location offset > len(fixed)") - data_before_ctr = fixed[: self._break_location] - data_after_ctr = fixed[self._break_location :] - - for i in range(1, rounds + 1): - h = self._prf(key_material) - - counter = utils.int_to_bytes(i, self._rlen) - input_data = data_before_ctr + counter + data_after_ctr - - h.update(input_data) - - output.append(h.finalize()) - - return b"".join(output)[: self._length] - - def _generate_fixed_input(self) -> bytes: - if self._fixed_data and isinstance(self._fixed_data, bytes): - return self._fixed_data - - l_val = utils.int_to_bytes(self._length * 8, self._llen) - - return b"".join([self._label, b"\x00", self._context, l_val]) - - -class KBKDFHMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - mode: Mode, - length: int, - rlen: int, - llen: int | None, - location: CounterLocation, - label: bytes | None, - context: bytes | None, - fixed: bytes | None, - backend: typing.Any = None, - *, - break_location: int | None = None, - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported hash algorithm.", - _Reasons.UNSUPPORTED_HASH, - ) - - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.hmac_supported(algorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported hmac algorithm.", - _Reasons.UNSUPPORTED_HASH, - ) - - self._algorithm = algorithm - - self._deriver = _KBKDFDeriver( - self._prf, - mode, - length, - rlen, - llen, - location, - break_location, - label, - context, - fixed, - ) - - def _prf(self, key_material: bytes) -> hmac.HMAC: - return hmac.HMAC(key_material, self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - return self._deriver.derive(key_material, self._algorithm.digest_size) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class KBKDFCMAC(KeyDerivationFunction): - def __init__( - self, - algorithm, - mode: Mode, - length: int, - rlen: int, - llen: int | None, - location: CounterLocation, - label: bytes | None, - context: bytes | None, - fixed: bytes | None, - backend: typing.Any = None, - *, - break_location: int | None = None, - ): - if not issubclass( - algorithm, ciphers.BlockCipherAlgorithm - ) or not issubclass(algorithm, ciphers.CipherAlgorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported cipher algorithm.", - _Reasons.UNSUPPORTED_CIPHER, - ) - - self._algorithm = algorithm - self._cipher: ciphers.BlockCipherAlgorithm | None = None - - self._deriver = _KBKDFDeriver( - self._prf, - mode, - length, - rlen, - llen, - location, - break_location, - label, - context, - fixed, - ) - - def _prf(self, _: bytes) -> cmac.CMAC: - assert self._cipher is not None - - return cmac.CMAC(self._cipher) - - def derive(self, key_material: bytes) -> bytes: - self._cipher = self._algorithm(key_material) - - assert self._cipher is not None - - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.cmac_algorithm_supported(self._cipher): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported cipher algorithm.", - _Reasons.UNSUPPORTED_CIPHER, - ) - - return self._deriver.derive(key_material, self._cipher.block_size // 8) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py deleted file mode 100644 index 82689eb..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ /dev/null @@ -1,62 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - InvalidKey, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives import constant_time, hashes -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class PBKDF2HMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: bytes, - iterations: int, - backend: typing.Any = None, - ): - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.pbkdf2_hmac_supported(algorithm): - raise UnsupportedAlgorithm( - f"{algorithm.name} is not supported for PBKDF2.", - _Reasons.UNSUPPORTED_HASH, - ) - self._used = False - self._algorithm = algorithm - self._length = length - utils._check_bytes("salt", salt) - self._salt = salt - self._iterations = iterations - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized("PBKDF2 instances can only be used once.") - self._used = True - - return rust_openssl.kdf.derive_pbkdf2_hmac( - key_material, - self._algorithm, - self._salt, - self._iterations, - self._length, - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - derived_key = self.derive(key_material) - if not constant_time.bytes_eq(derived_key, expected_key): - raise InvalidKey("Keys do not match.") diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py deleted file mode 100644 index f791cee..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import sys - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -# This is used by the scrypt tests to skip tests that require more memory -# than the MEM_LIMIT -_MEM_LIMIT = sys.maxsize // 2 - -Scrypt = rust_openssl.kdf.Scrypt -KeyDerivationFunction.register(Scrypt) - -__all__ = ["Scrypt"] diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py deleted file mode 100644 index 6e38366..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -def _int_to_u32be(n: int) -> bytes: - return n.to_bytes(length=4, byteorder="big") - - -class X963KDF(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - sharedinfo: bytes | None, - backend: typing.Any = None, - ): - max_len = algorithm.digest_size * (2**32 - 1) - if length > max_len: - raise ValueError(f"Cannot derive keys larger than {max_len} bits.") - if sharedinfo is not None: - utils._check_bytes("sharedinfo", sharedinfo) - - self._algorithm = algorithm - self._length = length - self._sharedinfo = sharedinfo - self._used = False - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - utils._check_byteslike("key_material", key_material) - output = [b""] - outlen = 0 - counter = 1 - - while self._length > outlen: - h = hashes.Hash(self._algorithm) - h.update(key_material) - h.update(_int_to_u32be(counter)) - if self._sharedinfo is not None: - h.update(self._sharedinfo) - output.append(h.finalize()) - outlen += len(output[-1]) - counter += 1 - - return b"".join(output)[: self._length] - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py deleted file mode 100644 index b93d87d..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py +++ /dev/null @@ -1,177 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.primitives.ciphers import Cipher -from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import ECB -from cryptography.hazmat.primitives.constant_time import bytes_eq - - -def _wrap_core( - wrapping_key: bytes, - a: bytes, - r: list[bytes], -) -> bytes: - # RFC 3394 Key Wrap - 2.2.1 (index method) - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - n = len(r) - for j in range(6): - for i in range(n): - # every encryption operation is a discrete 16 byte chunk (because - # AES has a 128-bit block size) and since we're using ECB it is - # safe to reuse the encryptor for the entire operation - b = encryptor.update(a + r[i]) - a = ( - int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") - r[i] = b[-8:] - - assert encryptor.finalize() == b"" - - return a + b"".join(r) - - -def aes_key_wrap( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(key_to_wrap) < 16: - raise ValueError("The key to wrap must be at least 16 bytes") - - if len(key_to_wrap) % 8 != 0: - raise ValueError("The key to wrap must be a multiple of 8 bytes") - - a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, a, r) - - -def _unwrap_core( - wrapping_key: bytes, - a: bytes, - r: list[bytes], -) -> tuple[bytes, list[bytes]]: - # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - n = len(r) - for j in reversed(range(6)): - for i in reversed(range(n)): - atr = ( - int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") + r[i] - # every decryption operation is a discrete 16 byte chunk so - # it is safe to reuse the decryptor for the entire operation - b = decryptor.update(atr) - a = b[:8] - r[i] = b[-8:] - - assert decryptor.finalize() == b"" - return a, r - - -def aes_key_wrap_with_padding( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - aiv = b"\xa6\x59\x59\xa6" + len(key_to_wrap).to_bytes( - length=4, byteorder="big" - ) - # pad the key to wrap if necessary - pad = (8 - (len(key_to_wrap) % 8)) % 8 - key_to_wrap = key_to_wrap + b"\x00" * pad - if len(key_to_wrap) == 8: - # RFC 5649 - 4.1 - exactly 8 octets after padding - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - b = encryptor.update(aiv + key_to_wrap) - assert encryptor.finalize() == b"" - return b - else: - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, aiv, r) - - -def aes_key_unwrap_with_padding( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 16: - raise InvalidUnwrap("Must be at least 16 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(wrapped_key) == 16: - # RFC 5649 - 4.2 - exactly two 64-bit blocks - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - out = decryptor.update(wrapped_key) - assert decryptor.finalize() == b"" - a = out[:8] - data = out[8:] - n = 1 - else: - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - encrypted_aiv = r.pop(0) - n = len(r) - a, r = _unwrap_core(wrapping_key, encrypted_aiv, r) - data = b"".join(r) - - # 1) Check that MSB(32,A) = A65959A6. - # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let - # MLI = LSB(32,A). - # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of - # the output data are zero. - mli = int.from_bytes(a[4:], byteorder="big") - b = (8 * n) - mli - if ( - not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") - or not 8 * (n - 1) < mli <= 8 * n - or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b)) - ): - raise InvalidUnwrap() - - if b == 0: - return data - else: - return data[:-b] - - -def aes_key_unwrap( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 24: - raise InvalidUnwrap("Must be at least 24 bytes") - - if len(wrapped_key) % 8 != 0: - raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - a = r.pop(0) - a, r = _unwrap_core(wrapping_key, a, r) - if not bytes_eq(a, aiv): - raise InvalidUnwrap() - - return b"".join(r) - - -class InvalidUnwrap(Exception): - pass diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/padding.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/padding.py deleted file mode 100644 index b2a3f1c..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/padding.py +++ /dev/null @@ -1,183 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized -from cryptography.hazmat.bindings._rust import ( - PKCS7PaddingContext, - PKCS7UnpaddingContext, - check_ansix923_padding, -) - - -class PaddingContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: bytes) -> bytes: - """ - Pads the provided bytes and returns any available data as bytes. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalize the padding, returns bytes. - """ - - -def _byte_padding_check(block_size: int) -> None: - if not (0 <= block_size <= 2040): - raise ValueError("block_size must be in range(0, 2041).") - - if block_size % 8 != 0: - raise ValueError("block_size must be a multiple of 8.") - - -def _byte_padding_update( - buffer_: bytes | None, data: bytes, block_size: int -) -> tuple[bytes, bytes]: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - utils._check_byteslike("data", data) - - buffer_ += bytes(data) - - finished_blocks = len(buffer_) // (block_size // 8) - - result = buffer_[: finished_blocks * (block_size // 8)] - buffer_ = buffer_[finished_blocks * (block_size // 8) :] - - return buffer_, result - - -def _byte_padding_pad( - buffer_: bytes | None, - block_size: int, - paddingfn: typing.Callable[[int], bytes], -) -> bytes: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - pad_size = block_size // 8 - len(buffer_) - return buffer_ + paddingfn(pad_size) - - -def _byte_unpadding_update( - buffer_: bytes | None, data: bytes, block_size: int -) -> tuple[bytes, bytes]: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - utils._check_byteslike("data", data) - - buffer_ += bytes(data) - - finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) - - result = buffer_[: finished_blocks * (block_size // 8)] - buffer_ = buffer_[finished_blocks * (block_size // 8) :] - - return buffer_, result - - -def _byte_unpadding_check( - buffer_: bytes | None, - block_size: int, - checkfn: typing.Callable[[bytes], int], -) -> bytes: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - if len(buffer_) != block_size // 8: - raise ValueError("Invalid padding bytes.") - - valid = checkfn(buffer_) - - if not valid: - raise ValueError("Invalid padding bytes.") - - pad_size = buffer_[-1] - return buffer_[:-pad_size] - - -class PKCS7: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return PKCS7PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return PKCS7UnpaddingContext(self.block_size) - - -PaddingContext.register(PKCS7PaddingContext) -PaddingContext.register(PKCS7UnpaddingContext) - - -class ANSIX923: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return _ANSIX923PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return _ANSIX923UnpaddingContext(self.block_size) - - -class _ANSIX923PaddingContext(PaddingContext): - _buffer: bytes | None - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_padding_update( - self._buffer, data, self.block_size - ) - return result - - def _padding(self, size: int) -> bytes: - return bytes([0]) * (size - 1) + bytes([size]) - - def finalize(self) -> bytes: - result = _byte_padding_pad( - self._buffer, self.block_size, self._padding - ) - self._buffer = None - return result - - -class _ANSIX923UnpaddingContext(PaddingContext): - _buffer: bytes | None - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_unpadding_update( - self._buffer, data, self.block_size - ) - return result - - def finalize(self) -> bytes: - result = _byte_unpadding_check( - self._buffer, - self.block_size, - check_ansix923_padding, - ) - self._buffer = None - return result diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py deleted file mode 100644 index 7f5a77a..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -__all__ = ["Poly1305"] - -Poly1305 = rust_openssl.poly1305.Poly1305 diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py deleted file mode 100644 index 07b2264..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat.primitives._serialization import ( - BestAvailableEncryption, - Encoding, - KeySerializationEncryption, - NoEncryption, - ParameterFormat, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) -from cryptography.hazmat.primitives.serialization.base import ( - load_der_parameters, - load_der_private_key, - load_der_public_key, - load_pem_parameters, - load_pem_private_key, - load_pem_public_key, -) -from cryptography.hazmat.primitives.serialization.ssh import ( - SSHCertificate, - SSHCertificateBuilder, - SSHCertificateType, - SSHCertPrivateKeyTypes, - SSHCertPublicKeyTypes, - SSHPrivateKeyTypes, - SSHPublicKeyTypes, - load_ssh_private_key, - load_ssh_public_identity, - load_ssh_public_key, -) - -__all__ = [ - "BestAvailableEncryption", - "Encoding", - "KeySerializationEncryption", - "NoEncryption", - "ParameterFormat", - "PrivateFormat", - "PublicFormat", - "SSHCertPrivateKeyTypes", - "SSHCertPublicKeyTypes", - "SSHCertificate", - "SSHCertificateBuilder", - "SSHCertificateType", - "SSHPrivateKeyTypes", - "SSHPublicKeyTypes", - "_KeySerializationEncryption", - "load_der_parameters", - "load_der_private_key", - "load_der_public_key", - "load_pem_parameters", - "load_pem_private_key", - "load_pem_public_key", - "load_ssh_private_key", - "load_ssh_public_identity", - "load_ssh_public_key", -] diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py deleted file mode 100644 index e7c998b..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat.bindings._rust import openssl as rust_openssl - -load_pem_private_key = rust_openssl.keys.load_pem_private_key -load_der_private_key = rust_openssl.keys.load_der_private_key - -load_pem_public_key = rust_openssl.keys.load_pem_public_key -load_der_public_key = rust_openssl.keys.load_der_public_key - -load_pem_parameters = rust_openssl.dh.from_pem_parameters -load_der_parameters = rust_openssl.dh.from_der_parameters diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py deleted file mode 100644 index 549e1f9..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py +++ /dev/null @@ -1,156 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography import x509 -from cryptography.hazmat.bindings._rust import pkcs12 as rust_pkcs12 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives._serialization import PBES as PBES -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - rsa, -) -from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes - -__all__ = [ - "PBES", - "PKCS12Certificate", - "PKCS12KeyAndCertificates", - "PKCS12PrivateKeyTypes", - "load_key_and_certificates", - "load_pkcs12", - "serialize_key_and_certificates", -] - -PKCS12PrivateKeyTypes = typing.Union[ - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, -] - - -PKCS12Certificate = rust_pkcs12.PKCS12Certificate - - -class PKCS12KeyAndCertificates: - def __init__( - self, - key: PrivateKeyTypes | None, - cert: PKCS12Certificate | None, - additional_certs: list[PKCS12Certificate], - ): - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - if cert is not None and not isinstance(cert, PKCS12Certificate): - raise TypeError("cert must be a PKCS12Certificate object or None") - if not all( - isinstance(add_cert, PKCS12Certificate) - for add_cert in additional_certs - ): - raise TypeError( - "all values in additional_certs must be PKCS12Certificate" - " objects" - ) - self._key = key - self._cert = cert - self._additional_certs = additional_certs - - @property - def key(self) -> PrivateKeyTypes | None: - return self._key - - @property - def cert(self) -> PKCS12Certificate | None: - return self._cert - - @property - def additional_certs(self) -> list[PKCS12Certificate]: - return self._additional_certs - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PKCS12KeyAndCertificates): - return NotImplemented - - return ( - self.key == other.key - and self.cert == other.cert - and self.additional_certs == other.additional_certs - ) - - def __hash__(self) -> int: - return hash((self.key, self.cert, tuple(self.additional_certs))) - - def __repr__(self) -> str: - fmt = ( - "" - ) - return fmt.format(self.key, self.cert, self.additional_certs) - - -load_key_and_certificates = rust_pkcs12.load_key_and_certificates -load_pkcs12 = rust_pkcs12.load_pkcs12 - - -_PKCS12CATypes = typing.Union[ - x509.Certificate, - PKCS12Certificate, -] - - -def serialize_key_and_certificates( - name: bytes | None, - key: PKCS12PrivateKeyTypes | None, - cert: x509.Certificate | None, - cas: typing.Iterable[_PKCS12CATypes] | None, - encryption_algorithm: serialization.KeySerializationEncryption, -) -> bytes: - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - - if not isinstance( - encryption_algorithm, serialization.KeySerializationEncryption - ): - raise TypeError( - "Key encryption algorithm must be a " - "KeySerializationEncryption instance" - ) - - if key is None and cert is None and not cas: - raise ValueError("You must supply at least one of key, cert, or cas") - - return rust_pkcs12.serialize_key_and_certificates( - name, key, cert, cas, encryption_algorithm - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py deleted file mode 100644 index 882e345..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py +++ /dev/null @@ -1,369 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import email.base64mime -import email.generator -import email.message -import email.policy -import io -import typing - -from cryptography import utils, x509 -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa -from cryptography.utils import _check_byteslike - -load_pem_pkcs7_certificates = rust_pkcs7.load_pem_pkcs7_certificates - -load_der_pkcs7_certificates = rust_pkcs7.load_der_pkcs7_certificates - -serialize_certificates = rust_pkcs7.serialize_certificates - -PKCS7HashTypes = typing.Union[ - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -] - -PKCS7PrivateKeyTypes = typing.Union[ - rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey -] - - -class PKCS7Options(utils.Enum): - Text = "Add text/plain MIME type" - Binary = "Don't translate input data into canonical MIME format" - DetachedSignature = "Don't embed data in the PKCS7 structure" - NoCapabilities = "Don't embed SMIME capabilities" - NoAttributes = "Don't embed authenticatedAttributes" - NoCerts = "Don't embed signer certificate" - - -class PKCS7SignatureBuilder: - def __init__( - self, - data: bytes | None = None, - signers: list[ - tuple[ - x509.Certificate, - PKCS7PrivateKeyTypes, - PKCS7HashTypes, - padding.PSS | padding.PKCS1v15 | None, - ] - ] = [], - additional_certs: list[x509.Certificate] = [], - ): - self._data = data - self._signers = signers - self._additional_certs = additional_certs - - def set_data(self, data: bytes) -> PKCS7SignatureBuilder: - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return PKCS7SignatureBuilder(data, self._signers) - - def add_signer( - self, - certificate: x509.Certificate, - private_key: PKCS7PrivateKeyTypes, - hash_algorithm: PKCS7HashTypes, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ) -> PKCS7SignatureBuilder: - if not isinstance( - hash_algorithm, - ( - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ): - raise TypeError( - "hash_algorithm must be one of hashes.SHA224, " - "SHA256, SHA384, or SHA512" - ) - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance( - private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) - ): - raise TypeError("Only RSA & EC keys are supported at this time.") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - return PKCS7SignatureBuilder( - self._data, - [ - *self._signers, - (certificate, private_key, hash_algorithm, rsa_padding), - ], - ) - - def add_certificate( - self, certificate: x509.Certificate - ) -> PKCS7SignatureBuilder: - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - return PKCS7SignatureBuilder( - self._data, self._signers, [*self._additional_certs, certificate] - ) - - def sign( - self, - encoding: serialization.Encoding, - options: typing.Iterable[PKCS7Options], - backend: typing.Any = None, - ) -> bytes: - if len(self._signers) == 0: - raise ValueError("Must have at least one signer") - if self._data is None: - raise ValueError("You must add data to sign") - options = list(options) - if not all(isinstance(x, PKCS7Options) for x in options): - raise ValueError("options must be from the PKCS7Options enum") - if encoding not in ( - serialization.Encoding.PEM, - serialization.Encoding.DER, - serialization.Encoding.SMIME, - ): - raise ValueError( - "Must be PEM, DER, or SMIME from the Encoding enum" - ) - - # Text is a meaningless option unless it is accompanied by - # DetachedSignature - if ( - PKCS7Options.Text in options - and PKCS7Options.DetachedSignature not in options - ): - raise ValueError( - "When passing the Text option you must also pass " - "DetachedSignature" - ) - - if PKCS7Options.Text in options and encoding in ( - serialization.Encoding.DER, - serialization.Encoding.PEM, - ): - raise ValueError( - "The Text option is only available for SMIME serialization" - ) - - # No attributes implies no capabilities so we'll error if you try to - # pass both. - if ( - PKCS7Options.NoAttributes in options - and PKCS7Options.NoCapabilities in options - ): - raise ValueError( - "NoAttributes is a superset of NoCapabilities. Do not pass " - "both values." - ) - - return rust_pkcs7.sign_and_serialize(self, encoding, options) - - -class PKCS7EnvelopeBuilder: - def __init__( - self, - *, - _data: bytes | None = None, - _recipients: list[x509.Certificate] | None = None, - ): - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.rsa_encryption_supported(padding=padding.PKCS1v15()): - raise UnsupportedAlgorithm( - "RSA with PKCS1 v1.5 padding is not supported by this version" - " of OpenSSL.", - _Reasons.UNSUPPORTED_PADDING, - ) - self._data = _data - self._recipients = _recipients if _recipients is not None else [] - - def set_data(self, data: bytes) -> PKCS7EnvelopeBuilder: - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return PKCS7EnvelopeBuilder(_data=data, _recipients=self._recipients) - - def add_recipient( - self, - certificate: x509.Certificate, - ) -> PKCS7EnvelopeBuilder: - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance(certificate.public_key(), rsa.RSAPublicKey): - raise TypeError("Only RSA keys are supported at this time.") - - return PKCS7EnvelopeBuilder( - _data=self._data, - _recipients=[ - *self._recipients, - certificate, - ], - ) - - def encrypt( - self, - encoding: serialization.Encoding, - options: typing.Iterable[PKCS7Options], - ) -> bytes: - if len(self._recipients) == 0: - raise ValueError("Must have at least one recipient") - if self._data is None: - raise ValueError("You must add data to encrypt") - options = list(options) - if not all(isinstance(x, PKCS7Options) for x in options): - raise ValueError("options must be from the PKCS7Options enum") - if encoding not in ( - serialization.Encoding.PEM, - serialization.Encoding.DER, - serialization.Encoding.SMIME, - ): - raise ValueError( - "Must be PEM, DER, or SMIME from the Encoding enum" - ) - - # Only allow options that make sense for encryption - if any( - opt not in [PKCS7Options.Text, PKCS7Options.Binary] - for opt in options - ): - raise ValueError( - "Only the following options are supported for encryption: " - "Text, Binary" - ) - elif PKCS7Options.Text in options and PKCS7Options.Binary in options: - # OpenSSL accepts both options at the same time, but ignores Text. - # We fail defensively to avoid unexpected outputs. - raise ValueError( - "Cannot use Binary and Text options at the same time" - ) - - return rust_pkcs7.encrypt_and_serialize(self, encoding, options) - - -pkcs7_decrypt_der = rust_pkcs7.decrypt_der -pkcs7_decrypt_pem = rust_pkcs7.decrypt_pem -pkcs7_decrypt_smime = rust_pkcs7.decrypt_smime - - -def _smime_signed_encode( - data: bytes, signature: bytes, micalg: str, text_mode: bool -) -> bytes: - # This function works pretty hard to replicate what OpenSSL does - # precisely. For good and for ill. - - m = email.message.Message() - m.add_header("MIME-Version", "1.0") - m.add_header( - "Content-Type", - "multipart/signed", - protocol="application/x-pkcs7-signature", - micalg=micalg, - ) - - m.preamble = "This is an S/MIME signed message\n" - - msg_part = OpenSSLMimePart() - msg_part.set_payload(data) - if text_mode: - msg_part.add_header("Content-Type", "text/plain") - m.attach(msg_part) - - sig_part = email.message.MIMEPart() - sig_part.add_header( - "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" - ) - sig_part.add_header("Content-Transfer-Encoding", "base64") - sig_part.add_header( - "Content-Disposition", "attachment", filename="smime.p7s" - ) - sig_part.set_payload( - email.base64mime.body_encode(signature, maxlinelen=65) - ) - del sig_part["MIME-Version"] - m.attach(sig_part) - - fp = io.BytesIO() - g = email.generator.BytesGenerator( - fp, - maxheaderlen=0, - mangle_from_=False, - policy=m.policy.clone(linesep="\r\n"), - ) - g.flatten(m) - return fp.getvalue() - - -def _smime_enveloped_encode(data: bytes) -> bytes: - m = email.message.Message() - m.add_header("MIME-Version", "1.0") - m.add_header("Content-Disposition", "attachment", filename="smime.p7m") - m.add_header( - "Content-Type", - "application/pkcs7-mime", - smime_type="enveloped-data", - name="smime.p7m", - ) - m.add_header("Content-Transfer-Encoding", "base64") - - m.set_payload(email.base64mime.body_encode(data, maxlinelen=65)) - - return m.as_bytes(policy=m.policy.clone(linesep="\n", max_line_length=0)) - - -def _smime_enveloped_decode(data: bytes) -> bytes: - m = email.message_from_bytes(data) - if m.get_content_type() not in { - "application/x-pkcs7-mime", - "application/pkcs7-mime", - }: - raise ValueError("Not an S/MIME enveloped message") - return bytes(m.get_payload(decode=True)) - - -def _smime_remove_text_headers(data: bytes) -> bytes: - m = email.message_from_bytes(data) - # Using get() instead of get_content_type() since it has None as default, - # where the latter has "text/plain". Both methods are case-insensitive. - content_type = m.get("content-type") - if content_type is None: - raise ValueError( - "Decrypted MIME data has no 'Content-Type' header. " - "Please remove the 'Text' option to parse it manually." - ) - if "text/plain" not in content_type: - raise ValueError( - f"Decrypted MIME data content type is '{content_type}', not " - "'text/plain'. Remove the 'Text' option to parse it manually." - ) - return bytes(m.get_payload(decode=True)) - - -class OpenSSLMimePart(email.message.MIMEPart): - # A MIMEPart subclass that replicates OpenSSL's behavior of not including - # a newline if there are no headers. - def _write_headers(self, generator) -> None: - if list(self.raw_items()): - generator._write_headers(self) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py deleted file mode 100644 index c01afb0..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +++ /dev/null @@ -1,1569 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import binascii -import enum -import os -import re -import typing -import warnings -from base64 import encodebytes as _base64_encode -from dataclasses import dataclass - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed25519, - padding, - rsa, -) -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.hazmat.primitives.ciphers import ( - AEADDecryptionContext, - Cipher, - algorithms, - modes, -) -from cryptography.hazmat.primitives.serialization import ( - Encoding, - KeySerializationEncryption, - NoEncryption, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) - -try: - from bcrypt import kdf as _bcrypt_kdf - - _bcrypt_supported = True -except ImportError: - _bcrypt_supported = False - - def _bcrypt_kdf( - password: bytes, - salt: bytes, - desired_key_bytes: int, - rounds: int, - ignore_few_rounds: bool = False, - ) -> bytes: - raise UnsupportedAlgorithm("Need bcrypt module") - - -_SSH_ED25519 = b"ssh-ed25519" -_SSH_RSA = b"ssh-rsa" -_SSH_DSA = b"ssh-dss" -_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" -_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" -_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" -_CERT_SUFFIX = b"-cert-v01@openssh.com" - -# U2F application string suffixed pubkey -_SK_SSH_ED25519 = b"sk-ssh-ed25519@openssh.com" -_SK_SSH_ECDSA_NISTP256 = b"sk-ecdsa-sha2-nistp256@openssh.com" - -# These are not key types, only algorithms, so they cannot appear -# as a public key type -_SSH_RSA_SHA256 = b"rsa-sha2-256" -_SSH_RSA_SHA512 = b"rsa-sha2-512" - -_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") -_SK_MAGIC = b"openssh-key-v1\0" -_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" -_SK_END = b"-----END OPENSSH PRIVATE KEY-----" -_BCRYPT = b"bcrypt" -_NONE = b"none" -_DEFAULT_CIPHER = b"aes256-ctr" -_DEFAULT_ROUNDS = 16 - -# re is only way to work on bytes-like data -_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) - -# padding for max blocksize -_PADDING = memoryview(bytearray(range(1, 1 + 16))) - - -@dataclass -class _SSHCipher: - alg: type[algorithms.AES] - key_len: int - mode: type[modes.CTR] | type[modes.CBC] | type[modes.GCM] - block_len: int - iv_len: int - tag_len: int | None - is_aead: bool - - -# ciphers that are actually used in key wrapping -_SSH_CIPHERS: dict[bytes, _SSHCipher] = { - b"aes256-ctr": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.CTR, - block_len=16, - iv_len=16, - tag_len=None, - is_aead=False, - ), - b"aes256-cbc": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.CBC, - block_len=16, - iv_len=16, - tag_len=None, - is_aead=False, - ), - b"aes256-gcm@openssh.com": _SSHCipher( - alg=algorithms.AES, - key_len=32, - mode=modes.GCM, - block_len=16, - iv_len=12, - tag_len=16, - is_aead=True, - ), -} - -# map local curve name to key type -_ECDSA_KEY_TYPE = { - "secp256r1": _ECDSA_NISTP256, - "secp384r1": _ECDSA_NISTP384, - "secp521r1": _ECDSA_NISTP521, -} - - -def _get_ssh_key_type(key: SSHPrivateKeyTypes | SSHPublicKeyTypes) -> bytes: - if isinstance(key, ec.EllipticCurvePrivateKey): - key_type = _ecdsa_key_type(key.public_key()) - elif isinstance(key, ec.EllipticCurvePublicKey): - key_type = _ecdsa_key_type(key) - elif isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): - key_type = _SSH_RSA - elif isinstance(key, (dsa.DSAPrivateKey, dsa.DSAPublicKey)): - key_type = _SSH_DSA - elif isinstance( - key, (ed25519.Ed25519PrivateKey, ed25519.Ed25519PublicKey) - ): - key_type = _SSH_ED25519 - else: - raise ValueError("Unsupported key type") - - return key_type - - -def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: - """Return SSH key_type and curve_name for private key.""" - curve = public_key.curve - if curve.name not in _ECDSA_KEY_TYPE: - raise ValueError( - f"Unsupported curve for ssh private key: {curve.name!r}" - ) - return _ECDSA_KEY_TYPE[curve.name] - - -def _ssh_pem_encode( - data: bytes, - prefix: bytes = _SK_START + b"\n", - suffix: bytes = _SK_END + b"\n", -) -> bytes: - return b"".join([prefix, _base64_encode(data), suffix]) - - -def _check_block_size(data: bytes, block_len: int) -> None: - """Require data to be full blocks""" - if not data or len(data) % block_len != 0: - raise ValueError("Corrupt data: missing padding") - - -def _check_empty(data: bytes) -> None: - """All data should have been parsed.""" - if data: - raise ValueError("Corrupt data: unparsed data") - - -def _init_cipher( - ciphername: bytes, - password: bytes | None, - salt: bytes, - rounds: int, -) -> Cipher[modes.CBC | modes.CTR | modes.GCM]: - """Generate key + iv and return cipher.""" - if not password: - raise ValueError("Key is password-protected.") - - ciph = _SSH_CIPHERS[ciphername] - seed = _bcrypt_kdf( - password, salt, ciph.key_len + ciph.iv_len, rounds, True - ) - return Cipher( - ciph.alg(seed[: ciph.key_len]), - ciph.mode(seed[ciph.key_len :]), - ) - - -def _get_u32(data: memoryview) -> tuple[int, memoryview]: - """Uint32""" - if len(data) < 4: - raise ValueError("Invalid data") - return int.from_bytes(data[:4], byteorder="big"), data[4:] - - -def _get_u64(data: memoryview) -> tuple[int, memoryview]: - """Uint64""" - if len(data) < 8: - raise ValueError("Invalid data") - return int.from_bytes(data[:8], byteorder="big"), data[8:] - - -def _get_sshstr(data: memoryview) -> tuple[memoryview, memoryview]: - """Bytes with u32 length prefix""" - n, data = _get_u32(data) - if n > len(data): - raise ValueError("Invalid data") - return data[:n], data[n:] - - -def _get_mpint(data: memoryview) -> tuple[int, memoryview]: - """Big integer.""" - val, data = _get_sshstr(data) - if val and val[0] > 0x7F: - raise ValueError("Invalid data") - return int.from_bytes(val, "big"), data - - -def _to_mpint(val: int) -> bytes: - """Storage format for signed bigint.""" - if val < 0: - raise ValueError("negative mpint not allowed") - if not val: - return b"" - nbytes = (val.bit_length() + 8) // 8 - return utils.int_to_bytes(val, nbytes) - - -class _FragList: - """Build recursive structure without data copy.""" - - flist: list[bytes] - - def __init__(self, init: list[bytes] | None = None) -> None: - self.flist = [] - if init: - self.flist.extend(init) - - def put_raw(self, val: bytes) -> None: - """Add plain bytes""" - self.flist.append(val) - - def put_u32(self, val: int) -> None: - """Big-endian uint32""" - self.flist.append(val.to_bytes(length=4, byteorder="big")) - - def put_u64(self, val: int) -> None: - """Big-endian uint64""" - self.flist.append(val.to_bytes(length=8, byteorder="big")) - - def put_sshstr(self, val: bytes | _FragList) -> None: - """Bytes prefixed with u32 length""" - if isinstance(val, (bytes, memoryview, bytearray)): - self.put_u32(len(val)) - self.flist.append(val) - else: - self.put_u32(val.size()) - self.flist.extend(val.flist) - - def put_mpint(self, val: int) -> None: - """Big-endian bigint prefixed with u32 length""" - self.put_sshstr(_to_mpint(val)) - - def size(self) -> int: - """Current number of bytes""" - return sum(map(len, self.flist)) - - def render(self, dstbuf: memoryview, pos: int = 0) -> int: - """Write into bytearray""" - for frag in self.flist: - flen = len(frag) - start, pos = pos, pos + flen - dstbuf[start:pos] = frag - return pos - - def tobytes(self) -> bytes: - """Return as bytes""" - buf = memoryview(bytearray(self.size())) - self.render(buf) - return buf.tobytes() - - -class _SSHFormatRSA: - """Format for RSA keys. - - Public: - mpint e, n - Private: - mpint n, e, d, iqmp, p, q - """ - - def get_public( - self, data: memoryview - ) -> tuple[tuple[int, int], memoryview]: - """RSA public fields""" - e, data = _get_mpint(data) - n, data = _get_mpint(data) - return (e, n), data - - def load_public( - self, data: memoryview - ) -> tuple[rsa.RSAPublicKey, memoryview]: - """Make RSA public key from data.""" - (e, n), data = self.get_public(data) - public_numbers = rsa.RSAPublicNumbers(e, n) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> tuple[rsa.RSAPrivateKey, memoryview]: - """Make RSA private key from data.""" - n, data = _get_mpint(data) - e, data = _get_mpint(data) - d, data = _get_mpint(data) - iqmp, data = _get_mpint(data) - p, data = _get_mpint(data) - q, data = _get_mpint(data) - - if (e, n) != pubfields: - raise ValueError("Corrupt data: rsa field mismatch") - dmp1 = rsa.rsa_crt_dmp1(d, p) - dmq1 = rsa.rsa_crt_dmq1(d, q) - public_numbers = rsa.RSAPublicNumbers(e, n) - private_numbers = rsa.RSAPrivateNumbers( - p, q, d, dmp1, dmq1, iqmp, public_numbers - ) - private_key = private_numbers.private_key() - return private_key, data - - def encode_public( - self, public_key: rsa.RSAPublicKey, f_pub: _FragList - ) -> None: - """Write RSA public key""" - pubn = public_key.public_numbers() - f_pub.put_mpint(pubn.e) - f_pub.put_mpint(pubn.n) - - def encode_private( - self, private_key: rsa.RSAPrivateKey, f_priv: _FragList - ) -> None: - """Write RSA private key""" - private_numbers = private_key.private_numbers() - public_numbers = private_numbers.public_numbers - - f_priv.put_mpint(public_numbers.n) - f_priv.put_mpint(public_numbers.e) - - f_priv.put_mpint(private_numbers.d) - f_priv.put_mpint(private_numbers.iqmp) - f_priv.put_mpint(private_numbers.p) - f_priv.put_mpint(private_numbers.q) - - -class _SSHFormatDSA: - """Format for DSA keys. - - Public: - mpint p, q, g, y - Private: - mpint p, q, g, y, x - """ - - def get_public(self, data: memoryview) -> tuple[tuple, memoryview]: - """DSA public fields""" - p, data = _get_mpint(data) - q, data = _get_mpint(data) - g, data = _get_mpint(data) - y, data = _get_mpint(data) - return (p, q, g, y), data - - def load_public( - self, data: memoryview - ) -> tuple[dsa.DSAPublicKey, memoryview]: - """Make DSA public key from data.""" - (p, q, g, y), data = self.get_public(data) - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> tuple[dsa.DSAPrivateKey, memoryview]: - """Make DSA private key from data.""" - (p, q, g, y), data = self.get_public(data) - x, data = _get_mpint(data) - - if (p, q, g, y) != pubfields: - raise ValueError("Corrupt data: dsa field mismatch") - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) - private_key = private_numbers.private_key() - return private_key, data - - def encode_public( - self, public_key: dsa.DSAPublicKey, f_pub: _FragList - ) -> None: - """Write DSA public key""" - public_numbers = public_key.public_numbers() - parameter_numbers = public_numbers.parameter_numbers - self._validate(public_numbers) - - f_pub.put_mpint(parameter_numbers.p) - f_pub.put_mpint(parameter_numbers.q) - f_pub.put_mpint(parameter_numbers.g) - f_pub.put_mpint(public_numbers.y) - - def encode_private( - self, private_key: dsa.DSAPrivateKey, f_priv: _FragList - ) -> None: - """Write DSA private key""" - self.encode_public(private_key.public_key(), f_priv) - f_priv.put_mpint(private_key.private_numbers().x) - - def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: - parameter_numbers = public_numbers.parameter_numbers - if parameter_numbers.p.bit_length() != 1024: - raise ValueError("SSH supports only 1024 bit DSA keys") - - -class _SSHFormatECDSA: - """Format for ECDSA keys. - - Public: - str curve - bytes point - Private: - str curve - bytes point - mpint secret - """ - - def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): - self.ssh_curve_name = ssh_curve_name - self.curve = curve - - def get_public( - self, data: memoryview - ) -> tuple[tuple[memoryview, memoryview], memoryview]: - """ECDSA public fields""" - curve, data = _get_sshstr(data) - point, data = _get_sshstr(data) - if curve != self.ssh_curve_name: - raise ValueError("Curve name mismatch") - if point[0] != 4: - raise NotImplementedError("Need uncompressed point") - return (curve, point), data - - def load_public( - self, data: memoryview - ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: - """Make ECDSA public key from data.""" - (_, point), data = self.get_public(data) - public_key = ec.EllipticCurvePublicKey.from_encoded_point( - self.curve, point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> tuple[ec.EllipticCurvePrivateKey, memoryview]: - """Make ECDSA private key from data.""" - (curve_name, point), data = self.get_public(data) - secret, data = _get_mpint(data) - - if (curve_name, point) != pubfields: - raise ValueError("Corrupt data: ecdsa field mismatch") - private_key = ec.derive_private_key(secret, self.curve) - return private_key, data - - def encode_public( - self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList - ) -> None: - """Write ECDSA public key""" - point = public_key.public_bytes( - Encoding.X962, PublicFormat.UncompressedPoint - ) - f_pub.put_sshstr(self.ssh_curve_name) - f_pub.put_sshstr(point) - - def encode_private( - self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList - ) -> None: - """Write ECDSA private key""" - public_key = private_key.public_key() - private_numbers = private_key.private_numbers() - - self.encode_public(public_key, f_priv) - f_priv.put_mpint(private_numbers.private_value) - - -class _SSHFormatEd25519: - """Format for Ed25519 keys. - - Public: - bytes point - Private: - bytes point - bytes secret_and_point - """ - - def get_public( - self, data: memoryview - ) -> tuple[tuple[memoryview], memoryview]: - """Ed25519 public fields""" - point, data = _get_sshstr(data) - return (point,), data - - def load_public( - self, data: memoryview - ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: - """Make Ed25519 public key from data.""" - (point,), data = self.get_public(data) - public_key = ed25519.Ed25519PublicKey.from_public_bytes( - point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> tuple[ed25519.Ed25519PrivateKey, memoryview]: - """Make Ed25519 private key from data.""" - (point,), data = self.get_public(data) - keypair, data = _get_sshstr(data) - - secret = keypair[:32] - point2 = keypair[32:] - if point != point2 or (point,) != pubfields: - raise ValueError("Corrupt data: ed25519 field mismatch") - private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) - return private_key, data - - def encode_public( - self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList - ) -> None: - """Write Ed25519 public key""" - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_pub.put_sshstr(raw_public_key) - - def encode_private( - self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList - ) -> None: - """Write Ed25519 private key""" - public_key = private_key.public_key() - raw_private_key = private_key.private_bytes( - Encoding.Raw, PrivateFormat.Raw, NoEncryption() - ) - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_keypair = _FragList([raw_private_key, raw_public_key]) - - self.encode_public(public_key, f_priv) - f_priv.put_sshstr(f_keypair) - - -def load_application(data) -> tuple[memoryview, memoryview]: - """ - U2F application strings - """ - application, data = _get_sshstr(data) - if not application.tobytes().startswith(b"ssh:"): - raise ValueError( - "U2F application string does not start with b'ssh:' " - f"({application})" - ) - return application, data - - -class _SSHFormatSKEd25519: - """ - The format of a sk-ssh-ed25519@openssh.com public key is: - - string "sk-ssh-ed25519@openssh.com" - string public key - string application (user-specified, but typically "ssh:") - """ - - def load_public( - self, data: memoryview - ) -> tuple[ed25519.Ed25519PublicKey, memoryview]: - """Make Ed25519 public key from data.""" - public_key, data = _lookup_kformat(_SSH_ED25519).load_public(data) - _, data = load_application(data) - return public_key, data - - -class _SSHFormatSKECDSA: - """ - The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: - - string "sk-ecdsa-sha2-nistp256@openssh.com" - string curve name - ec_point Q - string application (user-specified, but typically "ssh:") - """ - - def load_public( - self, data: memoryview - ) -> tuple[ec.EllipticCurvePublicKey, memoryview]: - """Make ECDSA public key from data.""" - public_key, data = _lookup_kformat(_ECDSA_NISTP256).load_public(data) - _, data = load_application(data) - return public_key, data - - -_KEY_FORMATS = { - _SSH_RSA: _SSHFormatRSA(), - _SSH_DSA: _SSHFormatDSA(), - _SSH_ED25519: _SSHFormatEd25519(), - _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), - _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), - _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), - _SK_SSH_ED25519: _SSHFormatSKEd25519(), - _SK_SSH_ECDSA_NISTP256: _SSHFormatSKECDSA(), -} - - -def _lookup_kformat(key_type: bytes): - """Return valid format or throw error""" - if not isinstance(key_type, bytes): - key_type = memoryview(key_type).tobytes() - if key_type in _KEY_FORMATS: - return _KEY_FORMATS[key_type] - raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") - - -SSHPrivateKeyTypes = typing.Union[ - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ed25519.Ed25519PrivateKey, -] - - -def load_ssh_private_key( - data: bytes, - password: bytes | None, - backend: typing.Any = None, -) -> SSHPrivateKeyTypes: - """Load private key from OpenSSH custom encoding.""" - utils._check_byteslike("data", data) - if password is not None: - utils._check_bytes("password", password) - - m = _PEM_RC.search(data) - if not m: - raise ValueError("Not OpenSSH private key format") - p1 = m.start(1) - p2 = m.end(1) - data = binascii.a2b_base64(memoryview(data)[p1:p2]) - if not data.startswith(_SK_MAGIC): - raise ValueError("Not OpenSSH private key format") - data = memoryview(data)[len(_SK_MAGIC) :] - - # parse header - ciphername, data = _get_sshstr(data) - kdfname, data = _get_sshstr(data) - kdfoptions, data = _get_sshstr(data) - nkeys, data = _get_u32(data) - if nkeys != 1: - raise ValueError("Only one key supported") - - # load public key data - pubdata, data = _get_sshstr(data) - pub_key_type, pubdata = _get_sshstr(pubdata) - kformat = _lookup_kformat(pub_key_type) - pubfields, pubdata = kformat.get_public(pubdata) - _check_empty(pubdata) - - if (ciphername, kdfname) != (_NONE, _NONE): - ciphername_bytes = ciphername.tobytes() - if ciphername_bytes not in _SSH_CIPHERS: - raise UnsupportedAlgorithm( - f"Unsupported cipher: {ciphername_bytes!r}" - ) - if kdfname != _BCRYPT: - raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") - blklen = _SSH_CIPHERS[ciphername_bytes].block_len - tag_len = _SSH_CIPHERS[ciphername_bytes].tag_len - # load secret data - edata, data = _get_sshstr(data) - # see https://bugzilla.mindrot.org/show_bug.cgi?id=3553 for - # information about how OpenSSH handles AEAD tags - if _SSH_CIPHERS[ciphername_bytes].is_aead: - tag = bytes(data) - if len(tag) != tag_len: - raise ValueError("Corrupt data: invalid tag length for cipher") - else: - _check_empty(data) - _check_block_size(edata, blklen) - salt, kbuf = _get_sshstr(kdfoptions) - rounds, kbuf = _get_u32(kbuf) - _check_empty(kbuf) - ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) - dec = ciph.decryptor() - edata = memoryview(dec.update(edata)) - if _SSH_CIPHERS[ciphername_bytes].is_aead: - assert isinstance(dec, AEADDecryptionContext) - _check_empty(dec.finalize_with_tag(tag)) - else: - # _check_block_size requires data to be a full block so there - # should be no output from finalize - _check_empty(dec.finalize()) - else: - # load secret data - edata, data = _get_sshstr(data) - _check_empty(data) - blklen = 8 - _check_block_size(edata, blklen) - ck1, edata = _get_u32(edata) - ck2, edata = _get_u32(edata) - if ck1 != ck2: - raise ValueError("Corrupt data: broken checksum") - - # load per-key struct - key_type, edata = _get_sshstr(edata) - if key_type != pub_key_type: - raise ValueError("Corrupt data: key type mismatch") - private_key, edata = kformat.load_private(edata, pubfields) - # We don't use the comment - _, edata = _get_sshstr(edata) - - # yes, SSH does padding check *after* all other parsing is done. - # need to follow as it writes zero-byte padding too. - if edata != _PADDING[: len(edata)]: - raise ValueError("Corrupt data: invalid padding") - - if isinstance(private_key, dsa.DSAPrivateKey): - warnings.warn( - "SSH DSA keys are deprecated and will be removed in a future " - "release.", - utils.DeprecatedIn40, - stacklevel=2, - ) - - return private_key - - -def _serialize_ssh_private_key( - private_key: SSHPrivateKeyTypes, - password: bytes, - encryption_algorithm: KeySerializationEncryption, -) -> bytes: - """Serialize private key with OpenSSH custom encoding.""" - utils._check_bytes("password", password) - if isinstance(private_key, dsa.DSAPrivateKey): - warnings.warn( - "SSH DSA key support is deprecated and will be " - "removed in a future release", - utils.DeprecatedIn40, - stacklevel=4, - ) - - key_type = _get_ssh_key_type(private_key) - kformat = _lookup_kformat(key_type) - - # setup parameters - f_kdfoptions = _FragList() - if password: - ciphername = _DEFAULT_CIPHER - blklen = _SSH_CIPHERS[ciphername].block_len - kdfname = _BCRYPT - rounds = _DEFAULT_ROUNDS - if ( - isinstance(encryption_algorithm, _KeySerializationEncryption) - and encryption_algorithm._kdf_rounds is not None - ): - rounds = encryption_algorithm._kdf_rounds - salt = os.urandom(16) - f_kdfoptions.put_sshstr(salt) - f_kdfoptions.put_u32(rounds) - ciph = _init_cipher(ciphername, password, salt, rounds) - else: - ciphername = kdfname = _NONE - blklen = 8 - ciph = None - nkeys = 1 - checkval = os.urandom(4) - comment = b"" - - # encode public and private parts together - f_public_key = _FragList() - f_public_key.put_sshstr(key_type) - kformat.encode_public(private_key.public_key(), f_public_key) - - f_secrets = _FragList([checkval, checkval]) - f_secrets.put_sshstr(key_type) - kformat.encode_private(private_key, f_secrets) - f_secrets.put_sshstr(comment) - f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) - - # top-level structure - f_main = _FragList() - f_main.put_raw(_SK_MAGIC) - f_main.put_sshstr(ciphername) - f_main.put_sshstr(kdfname) - f_main.put_sshstr(f_kdfoptions) - f_main.put_u32(nkeys) - f_main.put_sshstr(f_public_key) - f_main.put_sshstr(f_secrets) - - # copy result info bytearray - slen = f_secrets.size() - mlen = f_main.size() - buf = memoryview(bytearray(mlen + blklen)) - f_main.render(buf) - ofs = mlen - slen - - # encrypt in-place - if ciph is not None: - ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) - - return _ssh_pem_encode(buf[:mlen]) - - -SSHPublicKeyTypes = typing.Union[ - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - dsa.DSAPublicKey, - ed25519.Ed25519PublicKey, -] - -SSHCertPublicKeyTypes = typing.Union[ - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - ed25519.Ed25519PublicKey, -] - - -class SSHCertificateType(enum.Enum): - USER = 1 - HOST = 2 - - -class SSHCertificate: - def __init__( - self, - _nonce: memoryview, - _public_key: SSHPublicKeyTypes, - _serial: int, - _cctype: int, - _key_id: memoryview, - _valid_principals: list[bytes], - _valid_after: int, - _valid_before: int, - _critical_options: dict[bytes, bytes], - _extensions: dict[bytes, bytes], - _sig_type: memoryview, - _sig_key: memoryview, - _inner_sig_type: memoryview, - _signature: memoryview, - _tbs_cert_body: memoryview, - _cert_key_type: bytes, - _cert_body: memoryview, - ): - self._nonce = _nonce - self._public_key = _public_key - self._serial = _serial - try: - self._type = SSHCertificateType(_cctype) - except ValueError: - raise ValueError("Invalid certificate type") - self._key_id = _key_id - self._valid_principals = _valid_principals - self._valid_after = _valid_after - self._valid_before = _valid_before - self._critical_options = _critical_options - self._extensions = _extensions - self._sig_type = _sig_type - self._sig_key = _sig_key - self._inner_sig_type = _inner_sig_type - self._signature = _signature - self._cert_key_type = _cert_key_type - self._cert_body = _cert_body - self._tbs_cert_body = _tbs_cert_body - - @property - def nonce(self) -> bytes: - return bytes(self._nonce) - - def public_key(self) -> SSHCertPublicKeyTypes: - # make mypy happy until we remove DSA support entirely and - # the underlying union won't have a disallowed type - return typing.cast(SSHCertPublicKeyTypes, self._public_key) - - @property - def serial(self) -> int: - return self._serial - - @property - def type(self) -> SSHCertificateType: - return self._type - - @property - def key_id(self) -> bytes: - return bytes(self._key_id) - - @property - def valid_principals(self) -> list[bytes]: - return self._valid_principals - - @property - def valid_before(self) -> int: - return self._valid_before - - @property - def valid_after(self) -> int: - return self._valid_after - - @property - def critical_options(self) -> dict[bytes, bytes]: - return self._critical_options - - @property - def extensions(self) -> dict[bytes, bytes]: - return self._extensions - - def signature_key(self) -> SSHCertPublicKeyTypes: - sigformat = _lookup_kformat(self._sig_type) - signature_key, sigkey_rest = sigformat.load_public(self._sig_key) - _check_empty(sigkey_rest) - return signature_key - - def public_bytes(self) -> bytes: - return ( - bytes(self._cert_key_type) - + b" " - + binascii.b2a_base64(bytes(self._cert_body), newline=False) - ) - - def verify_cert_signature(self) -> None: - signature_key = self.signature_key() - if isinstance(signature_key, ed25519.Ed25519PublicKey): - signature_key.verify( - bytes(self._signature), bytes(self._tbs_cert_body) - ) - elif isinstance(signature_key, ec.EllipticCurvePublicKey): - # The signature is encoded as a pair of big-endian integers - r, data = _get_mpint(self._signature) - s, data = _get_mpint(data) - _check_empty(data) - computed_sig = asym_utils.encode_dss_signature(r, s) - hash_alg = _get_ec_hash_alg(signature_key.curve) - signature_key.verify( - computed_sig, bytes(self._tbs_cert_body), ec.ECDSA(hash_alg) - ) - else: - assert isinstance(signature_key, rsa.RSAPublicKey) - if self._inner_sig_type == _SSH_RSA: - hash_alg = hashes.SHA1() - elif self._inner_sig_type == _SSH_RSA_SHA256: - hash_alg = hashes.SHA256() - else: - assert self._inner_sig_type == _SSH_RSA_SHA512 - hash_alg = hashes.SHA512() - signature_key.verify( - bytes(self._signature), - bytes(self._tbs_cert_body), - padding.PKCS1v15(), - hash_alg, - ) - - -def _get_ec_hash_alg(curve: ec.EllipticCurve) -> hashes.HashAlgorithm: - if isinstance(curve, ec.SECP256R1): - return hashes.SHA256() - elif isinstance(curve, ec.SECP384R1): - return hashes.SHA384() - else: - assert isinstance(curve, ec.SECP521R1) - return hashes.SHA512() - - -def _load_ssh_public_identity( - data: bytes, - _legacy_dsa_allowed=False, -) -> SSHCertificate | SSHPublicKeyTypes: - utils._check_byteslike("data", data) - - m = _SSH_PUBKEY_RC.match(data) - if not m: - raise ValueError("Invalid line format") - key_type = orig_key_type = m.group(1) - key_body = m.group(2) - with_cert = False - if key_type.endswith(_CERT_SUFFIX): - with_cert = True - key_type = key_type[: -len(_CERT_SUFFIX)] - if key_type == _SSH_DSA and not _legacy_dsa_allowed: - raise UnsupportedAlgorithm( - "DSA keys aren't supported in SSH certificates" - ) - kformat = _lookup_kformat(key_type) - - try: - rest = memoryview(binascii.a2b_base64(key_body)) - except (TypeError, binascii.Error): - raise ValueError("Invalid format") - - if with_cert: - cert_body = rest - inner_key_type, rest = _get_sshstr(rest) - if inner_key_type != orig_key_type: - raise ValueError("Invalid key format") - if with_cert: - nonce, rest = _get_sshstr(rest) - public_key, rest = kformat.load_public(rest) - if with_cert: - serial, rest = _get_u64(rest) - cctype, rest = _get_u32(rest) - key_id, rest = _get_sshstr(rest) - principals, rest = _get_sshstr(rest) - valid_principals = [] - while principals: - principal, principals = _get_sshstr(principals) - valid_principals.append(bytes(principal)) - valid_after, rest = _get_u64(rest) - valid_before, rest = _get_u64(rest) - crit_options, rest = _get_sshstr(rest) - critical_options = _parse_exts_opts(crit_options) - exts, rest = _get_sshstr(rest) - extensions = _parse_exts_opts(exts) - # Get the reserved field, which is unused. - _, rest = _get_sshstr(rest) - sig_key_raw, rest = _get_sshstr(rest) - sig_type, sig_key = _get_sshstr(sig_key_raw) - if sig_type == _SSH_DSA and not _legacy_dsa_allowed: - raise UnsupportedAlgorithm( - "DSA signatures aren't supported in SSH certificates" - ) - # Get the entire cert body and subtract the signature - tbs_cert_body = cert_body[: -len(rest)] - signature_raw, rest = _get_sshstr(rest) - _check_empty(rest) - inner_sig_type, sig_rest = _get_sshstr(signature_raw) - # RSA certs can have multiple algorithm types - if ( - sig_type == _SSH_RSA - and inner_sig_type - not in [_SSH_RSA_SHA256, _SSH_RSA_SHA512, _SSH_RSA] - ) or (sig_type != _SSH_RSA and inner_sig_type != sig_type): - raise ValueError("Signature key type does not match") - signature, sig_rest = _get_sshstr(sig_rest) - _check_empty(sig_rest) - return SSHCertificate( - nonce, - public_key, - serial, - cctype, - key_id, - valid_principals, - valid_after, - valid_before, - critical_options, - extensions, - sig_type, - sig_key, - inner_sig_type, - signature, - tbs_cert_body, - orig_key_type, - cert_body, - ) - else: - _check_empty(rest) - return public_key - - -def load_ssh_public_identity( - data: bytes, -) -> SSHCertificate | SSHPublicKeyTypes: - return _load_ssh_public_identity(data) - - -def _parse_exts_opts(exts_opts: memoryview) -> dict[bytes, bytes]: - result: dict[bytes, bytes] = {} - last_name = None - while exts_opts: - name, exts_opts = _get_sshstr(exts_opts) - bname: bytes = bytes(name) - if bname in result: - raise ValueError("Duplicate name") - if last_name is not None and bname < last_name: - raise ValueError("Fields not lexically sorted") - value, exts_opts = _get_sshstr(exts_opts) - if len(value) > 0: - value, extra = _get_sshstr(value) - if len(extra) > 0: - raise ValueError("Unexpected extra data after value") - result[bname] = bytes(value) - last_name = bname - return result - - -def load_ssh_public_key( - data: bytes, backend: typing.Any = None -) -> SSHPublicKeyTypes: - cert_or_key = _load_ssh_public_identity(data, _legacy_dsa_allowed=True) - public_key: SSHPublicKeyTypes - if isinstance(cert_or_key, SSHCertificate): - public_key = cert_or_key.public_key() - else: - public_key = cert_or_key - - if isinstance(public_key, dsa.DSAPublicKey): - warnings.warn( - "SSH DSA keys are deprecated and will be removed in a future " - "release.", - utils.DeprecatedIn40, - stacklevel=2, - ) - return public_key - - -def serialize_ssh_public_key(public_key: SSHPublicKeyTypes) -> bytes: - """One-line public key format for OpenSSH""" - if isinstance(public_key, dsa.DSAPublicKey): - warnings.warn( - "SSH DSA key support is deprecated and will be " - "removed in a future release", - utils.DeprecatedIn40, - stacklevel=4, - ) - key_type = _get_ssh_key_type(public_key) - kformat = _lookup_kformat(key_type) - - f_pub = _FragList() - f_pub.put_sshstr(key_type) - kformat.encode_public(public_key, f_pub) - - pub = binascii.b2a_base64(f_pub.tobytes()).strip() - return b"".join([key_type, b" ", pub]) - - -SSHCertPrivateKeyTypes = typing.Union[ - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - ed25519.Ed25519PrivateKey, -] - - -# This is an undocumented limit enforced in the openssh codebase for sshd and -# ssh-keygen, but it is undefined in the ssh certificates spec. -_SSHKEY_CERT_MAX_PRINCIPALS = 256 - - -class SSHCertificateBuilder: - def __init__( - self, - _public_key: SSHCertPublicKeyTypes | None = None, - _serial: int | None = None, - _type: SSHCertificateType | None = None, - _key_id: bytes | None = None, - _valid_principals: list[bytes] = [], - _valid_for_all_principals: bool = False, - _valid_before: int | None = None, - _valid_after: int | None = None, - _critical_options: list[tuple[bytes, bytes]] = [], - _extensions: list[tuple[bytes, bytes]] = [], - ): - self._public_key = _public_key - self._serial = _serial - self._type = _type - self._key_id = _key_id - self._valid_principals = _valid_principals - self._valid_for_all_principals = _valid_for_all_principals - self._valid_before = _valid_before - self._valid_after = _valid_after - self._critical_options = _critical_options - self._extensions = _extensions - - def public_key( - self, public_key: SSHCertPublicKeyTypes - ) -> SSHCertificateBuilder: - if not isinstance( - public_key, - ( - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - ed25519.Ed25519PublicKey, - ), - ): - raise TypeError("Unsupported key type") - if self._public_key is not None: - raise ValueError("public_key already set") - - return SSHCertificateBuilder( - _public_key=public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def serial(self, serial: int) -> SSHCertificateBuilder: - if not isinstance(serial, int): - raise TypeError("serial must be an integer") - if not 0 <= serial < 2**64: - raise ValueError("serial must be between 0 and 2**64") - if self._serial is not None: - raise ValueError("serial already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def type(self, type: SSHCertificateType) -> SSHCertificateBuilder: - if not isinstance(type, SSHCertificateType): - raise TypeError("type must be an SSHCertificateType") - if self._type is not None: - raise ValueError("type already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def key_id(self, key_id: bytes) -> SSHCertificateBuilder: - if not isinstance(key_id, bytes): - raise TypeError("key_id must be bytes") - if self._key_id is not None: - raise ValueError("key_id already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_principals( - self, valid_principals: list[bytes] - ) -> SSHCertificateBuilder: - if self._valid_for_all_principals: - raise ValueError( - "Principals can't be set because the cert is valid " - "for all principals" - ) - if ( - not all(isinstance(x, bytes) for x in valid_principals) - or not valid_principals - ): - raise TypeError( - "principals must be a list of bytes and can't be empty" - ) - if self._valid_principals: - raise ValueError("valid_principals already set") - - if len(valid_principals) > _SSHKEY_CERT_MAX_PRINCIPALS: - raise ValueError( - "Reached or exceeded the maximum number of valid_principals" - ) - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_for_all_principals(self): - if self._valid_principals: - raise ValueError( - "valid_principals already set, can't set " - "valid_for_all_principals" - ) - if self._valid_for_all_principals: - raise ValueError("valid_for_all_principals already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=True, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_before(self, valid_before: int | float) -> SSHCertificateBuilder: - if not isinstance(valid_before, (int, float)): - raise TypeError("valid_before must be an int or float") - valid_before = int(valid_before) - if valid_before < 0 or valid_before >= 2**64: - raise ValueError("valid_before must [0, 2**64)") - if self._valid_before is not None: - raise ValueError("valid_before already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def valid_after(self, valid_after: int | float) -> SSHCertificateBuilder: - if not isinstance(valid_after, (int, float)): - raise TypeError("valid_after must be an int or float") - valid_after = int(valid_after) - if valid_after < 0 or valid_after >= 2**64: - raise ValueError("valid_after must [0, 2**64)") - if self._valid_after is not None: - raise ValueError("valid_after already set") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=valid_after, - _critical_options=self._critical_options, - _extensions=self._extensions, - ) - - def add_critical_option( - self, name: bytes, value: bytes - ) -> SSHCertificateBuilder: - if not isinstance(name, bytes) or not isinstance(value, bytes): - raise TypeError("name and value must be bytes") - # This is O(n**2) - if name in [name for name, _ in self._critical_options]: - raise ValueError("Duplicate critical option name") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=[*self._critical_options, (name, value)], - _extensions=self._extensions, - ) - - def add_extension( - self, name: bytes, value: bytes - ) -> SSHCertificateBuilder: - if not isinstance(name, bytes) or not isinstance(value, bytes): - raise TypeError("name and value must be bytes") - # This is O(n**2) - if name in [name for name, _ in self._extensions]: - raise ValueError("Duplicate extension name") - - return SSHCertificateBuilder( - _public_key=self._public_key, - _serial=self._serial, - _type=self._type, - _key_id=self._key_id, - _valid_principals=self._valid_principals, - _valid_for_all_principals=self._valid_for_all_principals, - _valid_before=self._valid_before, - _valid_after=self._valid_after, - _critical_options=self._critical_options, - _extensions=[*self._extensions, (name, value)], - ) - - def sign(self, private_key: SSHCertPrivateKeyTypes) -> SSHCertificate: - if not isinstance( - private_key, - ( - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - ed25519.Ed25519PrivateKey, - ), - ): - raise TypeError("Unsupported private key type") - - if self._public_key is None: - raise ValueError("public_key must be set") - - # Not required - serial = 0 if self._serial is None else self._serial - - if self._type is None: - raise ValueError("type must be set") - - # Not required - key_id = b"" if self._key_id is None else self._key_id - - # A zero length list is valid, but means the certificate - # is valid for any principal of the specified type. We require - # the user to explicitly set valid_for_all_principals to get - # that behavior. - if not self._valid_principals and not self._valid_for_all_principals: - raise ValueError( - "valid_principals must be set if valid_for_all_principals " - "is False" - ) - - if self._valid_before is None: - raise ValueError("valid_before must be set") - - if self._valid_after is None: - raise ValueError("valid_after must be set") - - if self._valid_after > self._valid_before: - raise ValueError("valid_after must be earlier than valid_before") - - # lexically sort our byte strings - self._critical_options.sort(key=lambda x: x[0]) - self._extensions.sort(key=lambda x: x[0]) - - key_type = _get_ssh_key_type(self._public_key) - cert_prefix = key_type + _CERT_SUFFIX - - # Marshal the bytes to be signed - nonce = os.urandom(32) - kformat = _lookup_kformat(key_type) - f = _FragList() - f.put_sshstr(cert_prefix) - f.put_sshstr(nonce) - kformat.encode_public(self._public_key, f) - f.put_u64(serial) - f.put_u32(self._type.value) - f.put_sshstr(key_id) - fprincipals = _FragList() - for p in self._valid_principals: - fprincipals.put_sshstr(p) - f.put_sshstr(fprincipals.tobytes()) - f.put_u64(self._valid_after) - f.put_u64(self._valid_before) - fcrit = _FragList() - for name, value in self._critical_options: - fcrit.put_sshstr(name) - if len(value) > 0: - foptval = _FragList() - foptval.put_sshstr(value) - fcrit.put_sshstr(foptval.tobytes()) - else: - fcrit.put_sshstr(value) - f.put_sshstr(fcrit.tobytes()) - fext = _FragList() - for name, value in self._extensions: - fext.put_sshstr(name) - if len(value) > 0: - fextval = _FragList() - fextval.put_sshstr(value) - fext.put_sshstr(fextval.tobytes()) - else: - fext.put_sshstr(value) - f.put_sshstr(fext.tobytes()) - f.put_sshstr(b"") # RESERVED FIELD - # encode CA public key - ca_type = _get_ssh_key_type(private_key) - caformat = _lookup_kformat(ca_type) - caf = _FragList() - caf.put_sshstr(ca_type) - caformat.encode_public(private_key.public_key(), caf) - f.put_sshstr(caf.tobytes()) - # Sigs according to the rules defined for the CA's public key - # (RFC4253 section 6.6 for ssh-rsa, RFC5656 for ECDSA, - # and RFC8032 for Ed25519). - if isinstance(private_key, ed25519.Ed25519PrivateKey): - signature = private_key.sign(f.tobytes()) - fsig = _FragList() - fsig.put_sshstr(ca_type) - fsig.put_sshstr(signature) - f.put_sshstr(fsig.tobytes()) - elif isinstance(private_key, ec.EllipticCurvePrivateKey): - hash_alg = _get_ec_hash_alg(private_key.curve) - signature = private_key.sign(f.tobytes(), ec.ECDSA(hash_alg)) - r, s = asym_utils.decode_dss_signature(signature) - fsig = _FragList() - fsig.put_sshstr(ca_type) - fsigblob = _FragList() - fsigblob.put_mpint(r) - fsigblob.put_mpint(s) - fsig.put_sshstr(fsigblob.tobytes()) - f.put_sshstr(fsig.tobytes()) - - else: - assert isinstance(private_key, rsa.RSAPrivateKey) - # Just like Golang, we're going to use SHA512 for RSA - # https://cs.opensource.google/go/x/crypto/+/refs/tags/ - # v0.4.0:ssh/certs.go;l=445 - # RFC 8332 defines SHA256 and 512 as options - fsig = _FragList() - fsig.put_sshstr(_SSH_RSA_SHA512) - signature = private_key.sign( - f.tobytes(), padding.PKCS1v15(), hashes.SHA512() - ) - fsig.put_sshstr(signature) - f.put_sshstr(fsig.tobytes()) - - cert_data = binascii.b2a_base64(f.tobytes()).strip() - # load_ssh_public_identity returns a union, but this is - # guaranteed to be an SSHCertificate, so we cast to make - # mypy happy. - return typing.cast( - SSHCertificate, - load_ssh_public_identity(b"".join([cert_prefix, b" ", cert_data])), - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py deleted file mode 100644 index c1af423..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - - -class InvalidToken(Exception): - pass diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py deleted file mode 100644 index 855a5d2..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py +++ /dev/null @@ -1,100 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import base64 -import typing -from urllib.parse import quote, urlencode - -from cryptography.hazmat.primitives import constant_time, hmac -from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 -from cryptography.hazmat.primitives.twofactor import InvalidToken - -HOTPHashTypes = typing.Union[SHA1, SHA256, SHA512] - - -def _generate_uri( - hotp: HOTP, - type_name: str, - account_name: str, - issuer: str | None, - extra_parameters: list[tuple[str, int]], -) -> str: - parameters = [ - ("digits", hotp._length), - ("secret", base64.b32encode(hotp._key)), - ("algorithm", hotp._algorithm.name.upper()), - ] - - if issuer is not None: - parameters.append(("issuer", issuer)) - - parameters.extend(extra_parameters) - - label = ( - f"{quote(issuer)}:{quote(account_name)}" - if issuer - else quote(account_name) - ) - return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" - - -class HOTP: - def __init__( - self, - key: bytes, - length: int, - algorithm: HOTPHashTypes, - backend: typing.Any = None, - enforce_key_length: bool = True, - ) -> None: - if len(key) < 16 and enforce_key_length is True: - raise ValueError("Key length has to be at least 128 bits.") - - if not isinstance(length, int): - raise TypeError("Length parameter must be an integer type.") - - if length < 6 or length > 8: - raise ValueError("Length of HOTP has to be between 6 and 8.") - - if not isinstance(algorithm, (SHA1, SHA256, SHA512)): - raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") - - self._key = key - self._length = length - self._algorithm = algorithm - - def generate(self, counter: int) -> bytes: - if not isinstance(counter, int): - raise TypeError("Counter parameter must be an integer type.") - - truncated_value = self._dynamic_truncate(counter) - hotp = truncated_value % (10**self._length) - return "{0:0{1}}".format(hotp, self._length).encode() - - def verify(self, hotp: bytes, counter: int) -> None: - if not constant_time.bytes_eq(self.generate(counter), hotp): - raise InvalidToken("Supplied HOTP value does not match.") - - def _dynamic_truncate(self, counter: int) -> int: - ctx = hmac.HMAC(self._key, self._algorithm) - - try: - ctx.update(counter.to_bytes(length=8, byteorder="big")) - except OverflowError: - raise ValueError(f"Counter must be between 0 and {2 ** 64 - 1}.") - - hmac_value = ctx.finalize() - - offset = hmac_value[len(hmac_value) - 1] & 0b1111 - p = hmac_value[offset : offset + 4] - return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF - - def get_provisioning_uri( - self, account_name: str, counter: int, issuer: str | None - ) -> str: - return _generate_uri( - self, "hotp", account_name, issuer, [("counter", int(counter))] - ) diff --git a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py deleted file mode 100644 index b9ed734..0000000 --- a/.venv/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.primitives import constant_time -from cryptography.hazmat.primitives.twofactor import InvalidToken -from cryptography.hazmat.primitives.twofactor.hotp import ( - HOTP, - HOTPHashTypes, - _generate_uri, -) - - -class TOTP: - def __init__( - self, - key: bytes, - length: int, - algorithm: HOTPHashTypes, - time_step: int, - backend: typing.Any = None, - enforce_key_length: bool = True, - ): - self._time_step = time_step - self._hotp = HOTP( - key, length, algorithm, enforce_key_length=enforce_key_length - ) - - def generate(self, time: int | float) -> bytes: - if not isinstance(time, (int, float)): - raise TypeError( - "Time parameter must be an integer type or float type." - ) - - counter = int(time / self._time_step) - return self._hotp.generate(counter) - - def verify(self, totp: bytes, time: int) -> None: - if not constant_time.bytes_eq(self.generate(time), totp): - raise InvalidToken("Supplied TOTP value does not match.") - - def get_provisioning_uri( - self, account_name: str, issuer: str | None - ) -> str: - return _generate_uri( - self._hotp, - "totp", - account_name, - issuer, - [("period", int(self._time_step))], - ) diff --git a/.venv/Lib/site-packages/cryptography/py.typed b/.venv/Lib/site-packages/cryptography/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/cryptography/utils.py b/.venv/Lib/site-packages/cryptography/utils.py deleted file mode 100644 index 706d0ae..0000000 --- a/.venv/Lib/site-packages/cryptography/utils.py +++ /dev/null @@ -1,127 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import enum -import sys -import types -import typing -import warnings - - -# We use a UserWarning subclass, instead of DeprecationWarning, because CPython -# decided deprecation warnings should be invisible by default. -class CryptographyDeprecationWarning(UserWarning): - pass - - -# Several APIs were deprecated with no specific end-of-life date because of the -# ubiquity of their use. They should not be removed until we agree on when that -# cycle ends. -DeprecatedIn36 = CryptographyDeprecationWarning -DeprecatedIn37 = CryptographyDeprecationWarning -DeprecatedIn40 = CryptographyDeprecationWarning -DeprecatedIn41 = CryptographyDeprecationWarning -DeprecatedIn42 = CryptographyDeprecationWarning -DeprecatedIn43 = CryptographyDeprecationWarning - - -def _check_bytes(name: str, value: bytes) -> None: - if not isinstance(value, bytes): - raise TypeError(f"{name} must be bytes") - - -def _check_byteslike(name: str, value: bytes) -> None: - try: - memoryview(value) - except TypeError: - raise TypeError(f"{name} must be bytes-like") - - -def int_to_bytes(integer: int, length: int | None = None) -> bytes: - if length == 0: - raise ValueError("length argument can't be 0") - return integer.to_bytes( - length or (integer.bit_length() + 7) // 8 or 1, "big" - ) - - -class InterfaceNotImplemented(Exception): - pass - - -class _DeprecatedValue: - def __init__(self, value: object, message: str, warning_class): - self.value = value - self.message = message - self.warning_class = warning_class - - -class _ModuleWithDeprecations(types.ModuleType): - def __init__(self, module: types.ModuleType): - super().__init__(module.__name__) - self.__dict__["_module"] = module - - def __getattr__(self, attr: str) -> object: - obj = getattr(self._module, attr) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - obj = obj.value - return obj - - def __setattr__(self, attr: str, value: object) -> None: - setattr(self._module, attr, value) - - def __delattr__(self, attr: str) -> None: - obj = getattr(self._module, attr) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - - delattr(self._module, attr) - - def __dir__(self) -> typing.Sequence[str]: - return ["_module", *dir(self._module)] - - -def deprecated( - value: object, - module_name: str, - message: str, - warning_class: type[Warning], - name: str | None = None, -) -> _DeprecatedValue: - module = sys.modules[module_name] - if not isinstance(module, _ModuleWithDeprecations): - sys.modules[module_name] = module = _ModuleWithDeprecations(module) - dv = _DeprecatedValue(value, message, warning_class) - # Maintain backwards compatibility with `name is None` for pyOpenSSL. - if name is not None: - setattr(module, name, dv) - return dv - - -def cached_property(func: typing.Callable) -> property: - cached_name = f"_cached_{func}" - sentinel = object() - - def inner(instance: object): - cache = getattr(instance, cached_name, sentinel) - if cache is not sentinel: - return cache - result = func(instance) - setattr(instance, cached_name, result) - return result - - return property(inner) - - -# Python 3.10 changed representation of enums. We use well-defined object -# representation and string representation from Python 3.9. -class Enum(enum.Enum): - def __repr__(self) -> str: - return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" - - def __str__(self) -> str: - return f"{self.__class__.__name__}.{self._name_}" diff --git a/.venv/Lib/site-packages/cryptography/x509/__init__.py b/.venv/Lib/site-packages/cryptography/x509/__init__.py deleted file mode 100644 index 8a89d67..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/__init__.py +++ /dev/null @@ -1,267 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.x509 import certificate_transparency, verification -from cryptography.x509.base import ( - Attribute, - AttributeNotFound, - Attributes, - Certificate, - CertificateBuilder, - CertificateRevocationList, - CertificateRevocationListBuilder, - CertificateSigningRequest, - CertificateSigningRequestBuilder, - InvalidVersion, - RevokedCertificate, - RevokedCertificateBuilder, - Version, - load_der_x509_certificate, - load_der_x509_crl, - load_der_x509_csr, - load_pem_x509_certificate, - load_pem_x509_certificates, - load_pem_x509_crl, - load_pem_x509_csr, - random_serial_number, -) -from cryptography.x509.extensions import ( - AccessDescription, - Admission, - Admissions, - AuthorityInformationAccess, - AuthorityKeyIdentifier, - BasicConstraints, - CertificateIssuer, - CertificatePolicies, - CRLDistributionPoints, - CRLNumber, - CRLReason, - DeltaCRLIndicator, - DistributionPoint, - DuplicateExtension, - ExtendedKeyUsage, - Extension, - ExtensionNotFound, - Extensions, - ExtensionType, - FreshestCRL, - GeneralNames, - InhibitAnyPolicy, - InvalidityDate, - IssuerAlternativeName, - IssuingDistributionPoint, - KeyUsage, - MSCertificateTemplate, - NameConstraints, - NamingAuthority, - NoticeReference, - OCSPAcceptableResponses, - OCSPNoCheck, - OCSPNonce, - PolicyConstraints, - PolicyInformation, - PrecertificateSignedCertificateTimestamps, - PrecertPoison, - ProfessionInfo, - ReasonFlags, - SignedCertificateTimestamps, - SubjectAlternativeName, - SubjectInformationAccess, - SubjectKeyIdentifier, - TLSFeature, - TLSFeatureType, - UnrecognizedExtension, - UserNotice, -) -from cryptography.x509.general_name import ( - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, - UnsupportedGeneralNameType, -) -from cryptography.x509.name import ( - Name, - NameAttribute, - RelativeDistinguishedName, -) -from cryptography.x509.oid import ( - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - PublicKeyAlgorithmOID, - SignatureAlgorithmOID, -) - -OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS -OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER -OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS -OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES -OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS -OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE -OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL -OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY -OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME -OID_KEY_USAGE = ExtensionOID.KEY_USAGE -OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS -OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK -OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS -OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS -OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME -OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES -OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS -OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER - -OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 -OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 -OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 -OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 -OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 -OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 -OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 -OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 -OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 -OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 -OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 -OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 -OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 -OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 -OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS - -OID_COMMON_NAME = NameOID.COMMON_NAME -OID_COUNTRY_NAME = NameOID.COUNTRY_NAME -OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT -OID_DN_QUALIFIER = NameOID.DN_QUALIFIER -OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS -OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER -OID_GIVEN_NAME = NameOID.GIVEN_NAME -OID_LOCALITY_NAME = NameOID.LOCALITY_NAME -OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME -OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME -OID_PSEUDONYM = NameOID.PSEUDONYM -OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER -OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME -OID_SURNAME = NameOID.SURNAME -OID_TITLE = NameOID.TITLE - -OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH -OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING -OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION -OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING -OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH -OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING - -OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY -OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER -OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE - -OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER -OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON -OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE - -OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS -OID_OCSP = AuthorityInformationAccessOID.OCSP - -__all__ = [ - "OID_CA_ISSUERS", - "OID_OCSP", - "AccessDescription", - "Admission", - "Admissions", - "Attribute", - "AttributeNotFound", - "Attributes", - "AuthorityInformationAccess", - "AuthorityKeyIdentifier", - "BasicConstraints", - "CRLDistributionPoints", - "CRLNumber", - "CRLReason", - "Certificate", - "CertificateBuilder", - "CertificateIssuer", - "CertificatePolicies", - "CertificateRevocationList", - "CertificateRevocationListBuilder", - "CertificateSigningRequest", - "CertificateSigningRequestBuilder", - "DNSName", - "DeltaCRLIndicator", - "DirectoryName", - "DistributionPoint", - "DuplicateExtension", - "ExtendedKeyUsage", - "Extension", - "ExtensionNotFound", - "ExtensionType", - "Extensions", - "FreshestCRL", - "GeneralName", - "GeneralNames", - "IPAddress", - "InhibitAnyPolicy", - "InvalidVersion", - "InvalidityDate", - "IssuerAlternativeName", - "IssuingDistributionPoint", - "KeyUsage", - "MSCertificateTemplate", - "Name", - "NameAttribute", - "NameConstraints", - "NameOID", - "NamingAuthority", - "NoticeReference", - "OCSPAcceptableResponses", - "OCSPNoCheck", - "OCSPNonce", - "ObjectIdentifier", - "OtherName", - "PolicyConstraints", - "PolicyInformation", - "PrecertPoison", - "PrecertificateSignedCertificateTimestamps", - "ProfessionInfo", - "PublicKeyAlgorithmOID", - "RFC822Name", - "ReasonFlags", - "RegisteredID", - "RelativeDistinguishedName", - "RevokedCertificate", - "RevokedCertificateBuilder", - "SignatureAlgorithmOID", - "SignedCertificateTimestamps", - "SubjectAlternativeName", - "SubjectInformationAccess", - "SubjectKeyIdentifier", - "TLSFeature", - "TLSFeatureType", - "UniformResourceIdentifier", - "UnrecognizedExtension", - "UnsupportedGeneralNameType", - "UserNotice", - "Version", - "certificate_transparency", - "load_der_x509_certificate", - "load_der_x509_crl", - "load_der_x509_csr", - "load_pem_x509_certificate", - "load_pem_x509_certificates", - "load_pem_x509_crl", - "load_pem_x509_csr", - "random_serial_number", - "verification", - "verification", -] diff --git a/.venv/Lib/site-packages/cryptography/x509/base.py b/.venv/Lib/site-packages/cryptography/x509/base.py deleted file mode 100644 index 25b317a..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/base.py +++ /dev/null @@ -1,815 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import datetime -import os -import typing -import warnings - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - padding, - rsa, - x448, - x25519, -) -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPrivateKeyTypes, - CertificatePublicKeyTypes, -) -from cryptography.x509.extensions import ( - Extension, - Extensions, - ExtensionType, - _make_sequence_methods, -) -from cryptography.x509.name import Name, _ASN1Type -from cryptography.x509.oid import ObjectIdentifier - -_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) - -# This must be kept in sync with sign.rs's list of allowable types in -# identify_hash_type -_AllowedHashTypes = typing.Union[ - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA3_224, - hashes.SHA3_256, - hashes.SHA3_384, - hashes.SHA3_512, -] - - -class AttributeNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -def _reject_duplicate_extension( - extension: Extension[ExtensionType], - extensions: list[Extension[ExtensionType]], -) -> None: - # This is quadratic in the number of extensions - for e in extensions: - if e.oid == extension.oid: - raise ValueError("This extension has already been set.") - - -def _reject_duplicate_attribute( - oid: ObjectIdentifier, - attributes: list[tuple[ObjectIdentifier, bytes, int | None]], -) -> None: - # This is quadratic in the number of attributes - for attr_oid, _, _ in attributes: - if attr_oid == oid: - raise ValueError("This attribute has already been set.") - - -def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: - """Normalizes a datetime to a naive datetime in UTC. - - time -- datetime to normalize. Assumed to be in UTC if not timezone - aware. - """ - if time.tzinfo is not None: - offset = time.utcoffset() - offset = offset if offset else datetime.timedelta() - return time.replace(tzinfo=None) - offset - else: - return time - - -class Attribute: - def __init__( - self, - oid: ObjectIdentifier, - value: bytes, - _type: int = _ASN1Type.UTF8String.value, - ) -> None: - self._oid = oid - self._value = value - self._type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Attribute): - return NotImplemented - - return ( - self.oid == other.oid - and self.value == other.value - and self._type == other._type - ) - - def __hash__(self) -> int: - return hash((self.oid, self.value, self._type)) - - -class Attributes: - def __init__( - self, - attributes: typing.Iterable[Attribute], - ) -> None: - self._attributes = list(attributes) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") - - def __repr__(self) -> str: - return f"" - - def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: - for attr in self: - if attr.oid == oid: - return attr - - raise AttributeNotFound(f"No {oid} attribute was found", oid) - - -class Version(utils.Enum): - v1 = 0 - v3 = 2 - - -class InvalidVersion(Exception): - def __init__(self, msg: str, parsed_version: int) -> None: - super().__init__(msg) - self.parsed_version = parsed_version - - -Certificate = rust_x509.Certificate - - -class RevokedCertificate(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - Returns the serial number of the revoked certificate. - """ - - @property - @abc.abstractmethod - def revocation_date(self) -> datetime.datetime: - """ - Returns the date of when this certificate was revoked. - """ - - @property - @abc.abstractmethod - def revocation_date_utc(self) -> datetime.datetime: - """ - Returns the date of when this certificate was revoked as a non-naive - UTC datetime. - """ - - @property - @abc.abstractmethod - def extensions(self) -> Extensions: - """ - Returns an Extensions object containing a list of Revoked extensions. - """ - - -# Runtime isinstance checks need this since the rust class is not a subclass. -RevokedCertificate.register(rust_x509.RevokedCertificate) - - -class _RawRevokedCertificate(RevokedCertificate): - def __init__( - self, - serial_number: int, - revocation_date: datetime.datetime, - extensions: Extensions, - ): - self._serial_number = serial_number - self._revocation_date = revocation_date - self._extensions = extensions - - @property - def serial_number(self) -> int: - return self._serial_number - - @property - def revocation_date(self) -> datetime.datetime: - warnings.warn( - "Properties that return a naïve datetime object have been " - "deprecated. Please switch to revocation_date_utc.", - utils.DeprecatedIn42, - stacklevel=2, - ) - return self._revocation_date - - @property - def revocation_date_utc(self) -> datetime.datetime: - return self._revocation_date.replace(tzinfo=datetime.timezone.utc) - - @property - def extensions(self) -> Extensions: - return self._extensions - - -CertificateRevocationList = rust_x509.CertificateRevocationList -CertificateSigningRequest = rust_x509.CertificateSigningRequest - - -load_pem_x509_certificate = rust_x509.load_pem_x509_certificate -load_der_x509_certificate = rust_x509.load_der_x509_certificate - -load_pem_x509_certificates = rust_x509.load_pem_x509_certificates - -load_pem_x509_csr = rust_x509.load_pem_x509_csr -load_der_x509_csr = rust_x509.load_der_x509_csr - -load_pem_x509_crl = rust_x509.load_pem_x509_crl -load_der_x509_crl = rust_x509.load_der_x509_crl - - -class CertificateSigningRequestBuilder: - def __init__( - self, - subject_name: Name | None = None, - extensions: list[Extension[ExtensionType]] = [], - attributes: list[tuple[ObjectIdentifier, bytes, int | None]] = [], - ): - """ - Creates an empty X.509 certificate request (v1). - """ - self._subject_name = subject_name - self._extensions = extensions - self._attributes = attributes - - def subject_name(self, name: Name) -> CertificateSigningRequestBuilder: - """ - Sets the certificate requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateSigningRequestBuilder( - name, self._extensions, self._attributes - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateSigningRequestBuilder: - """ - Adds an X.509 extension to the certificate request. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateSigningRequestBuilder( - self._subject_name, - [*self._extensions, extension], - self._attributes, - ) - - def add_attribute( - self, - oid: ObjectIdentifier, - value: bytes, - *, - _tag: _ASN1Type | None = None, - ) -> CertificateSigningRequestBuilder: - """ - Adds an X.509 attribute with an OID and associated value. - """ - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - - if not isinstance(value, bytes): - raise TypeError("value must be bytes") - - if _tag is not None and not isinstance(_tag, _ASN1Type): - raise TypeError("tag must be _ASN1Type") - - _reject_duplicate_attribute(oid, self._attributes) - - if _tag is not None: - tag = _tag.value - else: - tag = None - - return CertificateSigningRequestBuilder( - self._subject_name, - self._extensions, - [*self._attributes, (oid, value, tag)], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ) -> CertificateSigningRequest: - """ - Signs the request using the requestor's private key. - """ - if self._subject_name is None: - raise ValueError("A CertificateSigningRequest must have a subject") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - return rust_x509.create_x509_csr( - self, private_key, algorithm, rsa_padding - ) - - -class CertificateBuilder: - _extensions: list[Extension[ExtensionType]] - - def __init__( - self, - issuer_name: Name | None = None, - subject_name: Name | None = None, - public_key: CertificatePublicKeyTypes | None = None, - serial_number: int | None = None, - not_valid_before: datetime.datetime | None = None, - not_valid_after: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - ) -> None: - self._version = Version.v3 - self._issuer_name = issuer_name - self._subject_name = subject_name - self._public_key = public_key - self._serial_number = serial_number - self._not_valid_before = not_valid_before - self._not_valid_after = not_valid_after - self._extensions = extensions - - def issuer_name(self, name: Name) -> CertificateBuilder: - """ - Sets the CA's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateBuilder( - name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def subject_name(self, name: Name) -> CertificateBuilder: - """ - Sets the requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateBuilder( - self._issuer_name, - name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def public_key( - self, - key: CertificatePublicKeyTypes, - ) -> CertificateBuilder: - """ - Sets the requestor's public key (as found in the signing request). - """ - if not isinstance( - key, - ( - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, - ), - ): - raise TypeError( - "Expecting one of DSAPublicKey, RSAPublicKey," - " EllipticCurvePublicKey, Ed25519PublicKey," - " Ed448PublicKey, X25519PublicKey, or " - "X448PublicKey." - ) - if self._public_key is not None: - raise ValueError("The public key may only be set once.") - return CertificateBuilder( - self._issuer_name, - self._subject_name, - key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def serial_number(self, number: int) -> CertificateBuilder: - """ - Sets the certificate serial number. - """ - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive.") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 bits." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def not_valid_before(self, time: datetime.datetime) -> CertificateBuilder: - """ - Sets the certificate activation time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_before is not None: - raise ValueError("The not valid before may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid before date must be on or after" - " 1950 January 1)." - ) - if self._not_valid_after is not None and time > self._not_valid_after: - raise ValueError( - "The not valid before date must be before the not valid after " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - time, - self._not_valid_after, - self._extensions, - ) - - def not_valid_after(self, time: datetime.datetime) -> CertificateBuilder: - """ - Sets the certificate expiration time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_after is not None: - raise ValueError("The not valid after may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid after date must be on or after" - " 1950 January 1." - ) - if ( - self._not_valid_before is not None - and time < self._not_valid_before - ): - raise ValueError( - "The not valid after date must be after the not valid before " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - time, - self._extensions, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateBuilder: - """ - Adds an X.509 extension to the certificate. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - [*self._extensions, extension], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ) -> Certificate: - """ - Signs the certificate using the CA's private key. - """ - if self._subject_name is None: - raise ValueError("A certificate must have a subject name") - - if self._issuer_name is None: - raise ValueError("A certificate must have an issuer name") - - if self._serial_number is None: - raise ValueError("A certificate must have a serial number") - - if self._not_valid_before is None: - raise ValueError("A certificate must have a not valid before time") - - if self._not_valid_after is None: - raise ValueError("A certificate must have a not valid after time") - - if self._public_key is None: - raise ValueError("A certificate must have a public key") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - return rust_x509.create_x509_certificate( - self, private_key, algorithm, rsa_padding - ) - - -class CertificateRevocationListBuilder: - _extensions: list[Extension[ExtensionType]] - _revoked_certificates: list[RevokedCertificate] - - def __init__( - self, - issuer_name: Name | None = None, - last_update: datetime.datetime | None = None, - next_update: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - revoked_certificates: list[RevokedCertificate] = [], - ): - self._issuer_name = issuer_name - self._last_update = last_update - self._next_update = next_update - self._extensions = extensions - self._revoked_certificates = revoked_certificates - - def issuer_name( - self, issuer_name: Name - ) -> CertificateRevocationListBuilder: - if not isinstance(issuer_name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateRevocationListBuilder( - issuer_name, - self._last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def last_update( - self, last_update: datetime.datetime - ) -> CertificateRevocationListBuilder: - if not isinstance(last_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._last_update is not None: - raise ValueError("Last update may only be set once.") - last_update = _convert_to_naive_utc_time(last_update) - if last_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after 1950 January 1." - ) - if self._next_update is not None and last_update > self._next_update: - raise ValueError( - "The last update date must be before the next update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def next_update( - self, next_update: datetime.datetime - ) -> CertificateRevocationListBuilder: - if not isinstance(next_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._next_update is not None: - raise ValueError("Last update may only be set once.") - next_update = _convert_to_naive_utc_time(next_update) - if next_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after 1950 January 1." - ) - if self._last_update is not None and next_update < self._last_update: - raise ValueError( - "The next update date must be after the last update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - next_update, - self._extensions, - self._revoked_certificates, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> CertificateRevocationListBuilder: - """ - Adds an X.509 extension to the certificate revocation list. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - [*self._extensions, extension], - self._revoked_certificates, - ) - - def add_revoked_certificate( - self, revoked_certificate: RevokedCertificate - ) -> CertificateRevocationListBuilder: - """ - Adds a revoked certificate to the CRL. - """ - if not isinstance(revoked_certificate, RevokedCertificate): - raise TypeError("Must be an instance of RevokedCertificate") - - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - self._extensions, - [*self._revoked_certificates, revoked_certificate], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: _AllowedHashTypes | None, - backend: typing.Any = None, - *, - rsa_padding: padding.PSS | padding.PKCS1v15 | None = None, - ) -> CertificateRevocationList: - if self._issuer_name is None: - raise ValueError("A CRL must have an issuer name") - - if self._last_update is None: - raise ValueError("A CRL must have a last update time") - - if self._next_update is None: - raise ValueError("A CRL must have a next update time") - - if rsa_padding is not None: - if not isinstance(rsa_padding, (padding.PSS, padding.PKCS1v15)): - raise TypeError("Padding must be PSS or PKCS1v15") - if not isinstance(private_key, rsa.RSAPrivateKey): - raise TypeError("Padding is only supported for RSA keys") - - return rust_x509.create_x509_crl( - self, private_key, algorithm, rsa_padding - ) - - -class RevokedCertificateBuilder: - def __init__( - self, - serial_number: int | None = None, - revocation_date: datetime.datetime | None = None, - extensions: list[Extension[ExtensionType]] = [], - ): - self._serial_number = serial_number - self._revocation_date = revocation_date - self._extensions = extensions - - def serial_number(self, number: int) -> RevokedCertificateBuilder: - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 bits." - ) - return RevokedCertificateBuilder( - number, self._revocation_date, self._extensions - ) - - def revocation_date( - self, time: datetime.datetime - ) -> RevokedCertificateBuilder: - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._revocation_date is not None: - raise ValueError("The revocation date may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The revocation date must be on or after 1950 January 1." - ) - return RevokedCertificateBuilder( - self._serial_number, time, self._extensions - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> RevokedCertificateBuilder: - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return RevokedCertificateBuilder( - self._serial_number, - self._revocation_date, - [*self._extensions, extension], - ) - - def build(self, backend: typing.Any = None) -> RevokedCertificate: - if self._serial_number is None: - raise ValueError("A revoked certificate must have a serial number") - if self._revocation_date is None: - raise ValueError( - "A revoked certificate must have a revocation date" - ) - return _RawRevokedCertificate( - self._serial_number, - self._revocation_date, - Extensions(self._extensions), - ) - - -def random_serial_number() -> int: - return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/.venv/Lib/site-packages/cryptography/x509/certificate_transparency.py b/.venv/Lib/site-packages/cryptography/x509/certificate_transparency.py deleted file mode 100644 index fb66cc6..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/certificate_transparency.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 - - -class LogEntryType(utils.Enum): - X509_CERTIFICATE = 0 - PRE_CERTIFICATE = 1 - - -class Version(utils.Enum): - v1 = 0 - - -class SignatureAlgorithm(utils.Enum): - """ - Signature algorithms that are valid for SCTs. - - These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). - - See: - """ - - ANONYMOUS = 0 - RSA = 1 - DSA = 2 - ECDSA = 3 - - -SignedCertificateTimestamp = rust_x509.Sct diff --git a/.venv/Lib/site-packages/cryptography/x509/extensions.py b/.venv/Lib/site-packages/cryptography/x509/extensions.py deleted file mode 100644 index fc3e773..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/extensions.py +++ /dev/null @@ -1,2477 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import datetime -import hashlib -import ipaddress -import typing - -from cryptography import utils -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import constant_time, serialization -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPublicKeyTypes, - CertificatePublicKeyTypes, -) -from cryptography.x509.certificate_transparency import ( - SignedCertificateTimestamp, -) -from cryptography.x509.general_name import ( - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, - _IPAddressTypes, -) -from cryptography.x509.name import Name, RelativeDistinguishedName -from cryptography.x509.oid import ( - CRLEntryExtensionOID, - ExtensionOID, - ObjectIdentifier, - OCSPExtensionOID, -) - -ExtensionTypeVar = typing.TypeVar( - "ExtensionTypeVar", bound="ExtensionType", covariant=True -) - - -def _key_identifier_from_public_key( - public_key: CertificatePublicKeyTypes, -) -> bytes: - if isinstance(public_key, RSAPublicKey): - data = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.PKCS1, - ) - elif isinstance(public_key, EllipticCurvePublicKey): - data = public_key.public_bytes( - serialization.Encoding.X962, - serialization.PublicFormat.UncompressedPoint, - ) - else: - # This is a very slow way to do this. - serialized = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.SubjectPublicKeyInfo, - ) - data = asn1.parse_spki_for_data(serialized) - - return hashlib.sha1(data).digest() - - -def _make_sequence_methods(field_name: str): - def len_method(self) -> int: - return len(getattr(self, field_name)) - - def iter_method(self): - return iter(getattr(self, field_name)) - - def getitem_method(self, idx): - return getattr(self, field_name)[idx] - - return len_method, iter_method, getitem_method - - -class DuplicateExtension(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -class ExtensionNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super().__init__(msg) - self.oid = oid - - -class ExtensionType(metaclass=abc.ABCMeta): - oid: typing.ClassVar[ObjectIdentifier] - - def public_bytes(self) -> bytes: - """ - Serializes the extension type to DER. - """ - raise NotImplementedError( - f"public_bytes is not implemented for extension type {self!r}" - ) - - -class Extensions: - def __init__( - self, extensions: typing.Iterable[Extension[ExtensionType]] - ) -> None: - self._extensions = list(extensions) - - def get_extension_for_oid( - self, oid: ObjectIdentifier - ) -> Extension[ExtensionType]: - for ext in self: - if ext.oid == oid: - return ext - - raise ExtensionNotFound(f"No {oid} extension was found", oid) - - def get_extension_for_class( - self, extclass: type[ExtensionTypeVar] - ) -> Extension[ExtensionTypeVar]: - if extclass is UnrecognizedExtension: - raise TypeError( - "UnrecognizedExtension can't be used with " - "get_extension_for_class because more than one instance of the" - " class may be present." - ) - - for ext in self: - if isinstance(ext.value, extclass): - return ext - - raise ExtensionNotFound( - f"No {extclass} extension was found", extclass.oid - ) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") - - def __repr__(self) -> str: - return f"" - - -class CRLNumber(ExtensionType): - oid = ExtensionOID.CRL_NUMBER - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLNumber): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return f"" - - @property - def crl_number(self) -> int: - return self._crl_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityKeyIdentifier(ExtensionType): - oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER - - def __init__( - self, - key_identifier: bytes | None, - authority_cert_issuer: typing.Iterable[GeneralName] | None, - authority_cert_serial_number: int | None, - ) -> None: - if (authority_cert_issuer is None) != ( - authority_cert_serial_number is None - ): - raise ValueError( - "authority_cert_issuer and authority_cert_serial_number " - "must both be present or both None" - ) - - if authority_cert_issuer is not None: - authority_cert_issuer = list(authority_cert_issuer) - if not all( - isinstance(x, GeneralName) for x in authority_cert_issuer - ): - raise TypeError( - "authority_cert_issuer must be a list of GeneralName " - "objects" - ) - - if authority_cert_serial_number is not None and not isinstance( - authority_cert_serial_number, int - ): - raise TypeError("authority_cert_serial_number must be an integer") - - self._key_identifier = key_identifier - self._authority_cert_issuer = authority_cert_issuer - self._authority_cert_serial_number = authority_cert_serial_number - - # This takes a subset of CertificatePublicKeyTypes because an issuer - # cannot have an X25519/X448 key. This introduces some unfortunate - # asymmetry that requires typing users to explicitly - # narrow their type, but we should make this accurate and not just - # convenient. - @classmethod - def from_issuer_public_key( - cls, public_key: CertificateIssuerPublicKeyTypes - ) -> AuthorityKeyIdentifier: - digest = _key_identifier_from_public_key(public_key) - return cls( - key_identifier=digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - @classmethod - def from_issuer_subject_key_identifier( - cls, ski: SubjectKeyIdentifier - ) -> AuthorityKeyIdentifier: - return cls( - key_identifier=ski.digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityKeyIdentifier): - return NotImplemented - - return ( - self.key_identifier == other.key_identifier - and self.authority_cert_issuer == other.authority_cert_issuer - and self.authority_cert_serial_number - == other.authority_cert_serial_number - ) - - def __hash__(self) -> int: - if self.authority_cert_issuer is None: - aci = None - else: - aci = tuple(self.authority_cert_issuer) - return hash( - (self.key_identifier, aci, self.authority_cert_serial_number) - ) - - @property - def key_identifier(self) -> bytes | None: - return self._key_identifier - - @property - def authority_cert_issuer( - self, - ) -> list[GeneralName] | None: - return self._authority_cert_issuer - - @property - def authority_cert_serial_number(self) -> int | None: - return self._authority_cert_serial_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectKeyIdentifier(ExtensionType): - oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER - - def __init__(self, digest: bytes) -> None: - self._digest = digest - - @classmethod - def from_public_key( - cls, public_key: CertificatePublicKeyTypes - ) -> SubjectKeyIdentifier: - return cls(_key_identifier_from_public_key(public_key)) - - @property - def digest(self) -> bytes: - return self._digest - - @property - def key_identifier(self) -> bytes: - return self._digest - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectKeyIdentifier): - return NotImplemented - - return constant_time.bytes_eq(self.digest, other.digest) - - def __hash__(self) -> int: - return hash(self.digest) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityInformationAccess(ExtensionType): - oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS - - def __init__( - self, descriptions: typing.Iterable[AccessDescription] - ) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectInformationAccess(ExtensionType): - oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS - - def __init__( - self, descriptions: typing.Iterable[AccessDescription] - ) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AccessDescription: - def __init__( - self, access_method: ObjectIdentifier, access_location: GeneralName - ) -> None: - if not isinstance(access_method, ObjectIdentifier): - raise TypeError("access_method must be an ObjectIdentifier") - - if not isinstance(access_location, GeneralName): - raise TypeError("access_location must be a GeneralName") - - self._access_method = access_method - self._access_location = access_location - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AccessDescription): - return NotImplemented - - return ( - self.access_method == other.access_method - and self.access_location == other.access_location - ) - - def __hash__(self) -> int: - return hash((self.access_method, self.access_location)) - - @property - def access_method(self) -> ObjectIdentifier: - return self._access_method - - @property - def access_location(self) -> GeneralName: - return self._access_location - - -class BasicConstraints(ExtensionType): - oid = ExtensionOID.BASIC_CONSTRAINTS - - def __init__(self, ca: bool, path_length: int | None) -> None: - if not isinstance(ca, bool): - raise TypeError("ca must be a boolean value") - - if path_length is not None and not ca: - raise ValueError("path_length must be None when ca is False") - - if path_length is not None and ( - not isinstance(path_length, int) or path_length < 0 - ): - raise TypeError( - "path_length must be a non-negative integer or None" - ) - - self._ca = ca - self._path_length = path_length - - @property - def ca(self) -> bool: - return self._ca - - @property - def path_length(self) -> int | None: - return self._path_length - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, BasicConstraints): - return NotImplemented - - return self.ca == other.ca and self.path_length == other.path_length - - def __hash__(self) -> int: - return hash((self.ca, self.path_length)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DeltaCRLIndicator(ExtensionType): - oid = ExtensionOID.DELTA_CRL_INDICATOR - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - @property - def crl_number(self) -> int: - return self._crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DeltaCRLIndicator): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return f"" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLDistributionPoints(ExtensionType): - oid = ExtensionOID.CRL_DISTRIBUTION_POINTS - - def __init__( - self, distribution_points: typing.Iterable[DistributionPoint] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLDistributionPoints): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class FreshestCRL(ExtensionType): - oid = ExtensionOID.FRESHEST_CRL - - def __init__( - self, distribution_points: typing.Iterable[DistributionPoint] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, FreshestCRL): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DistributionPoint: - def __init__( - self, - full_name: typing.Iterable[GeneralName] | None, - relative_name: RelativeDistinguishedName | None, - reasons: frozenset[ReasonFlags] | None, - crl_issuer: typing.Iterable[GeneralName] | None, - ) -> None: - if full_name and relative_name: - raise ValueError( - "You cannot provide both full_name and relative_name, at " - "least one must be None." - ) - if not full_name and not relative_name and not crl_issuer: - raise ValueError( - "Either full_name, relative_name or crl_issuer must be " - "provided." - ) - - if full_name is not None: - full_name = list(full_name) - if not all(isinstance(x, GeneralName) for x in full_name): - raise TypeError( - "full_name must be a list of GeneralName objects" - ) - - if relative_name: - if not isinstance(relative_name, RelativeDistinguishedName): - raise TypeError( - "relative_name must be a RelativeDistinguishedName" - ) - - if crl_issuer is not None: - crl_issuer = list(crl_issuer) - if not all(isinstance(x, GeneralName) for x in crl_issuer): - raise TypeError( - "crl_issuer must be None or a list of general names" - ) - - if reasons and ( - not isinstance(reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in reasons) - ): - raise TypeError("reasons must be None or frozenset of ReasonFlags") - - if reasons and ( - ReasonFlags.unspecified in reasons - or ReasonFlags.remove_from_crl in reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in a " - "DistributionPoint" - ) - - self._full_name = full_name - self._relative_name = relative_name - self._reasons = reasons - self._crl_issuer = crl_issuer - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.reasons == other.reasons - and self.crl_issuer == other.crl_issuer - ) - - def __hash__(self) -> int: - if self.full_name is not None: - fn: tuple[GeneralName, ...] | None = tuple(self.full_name) - else: - fn = None - - if self.crl_issuer is not None: - crl_issuer: tuple[GeneralName, ...] | None = tuple(self.crl_issuer) - else: - crl_issuer = None - - return hash((fn, self.relative_name, self.reasons, crl_issuer)) - - @property - def full_name(self) -> list[GeneralName] | None: - return self._full_name - - @property - def relative_name(self) -> RelativeDistinguishedName | None: - return self._relative_name - - @property - def reasons(self) -> frozenset[ReasonFlags] | None: - return self._reasons - - @property - def crl_issuer(self) -> list[GeneralName] | None: - return self._crl_issuer - - -class ReasonFlags(utils.Enum): - unspecified = "unspecified" - key_compromise = "keyCompromise" - ca_compromise = "cACompromise" - affiliation_changed = "affiliationChanged" - superseded = "superseded" - cessation_of_operation = "cessationOfOperation" - certificate_hold = "certificateHold" - privilege_withdrawn = "privilegeWithdrawn" - aa_compromise = "aACompromise" - remove_from_crl = "removeFromCRL" - - -# These are distribution point bit string mappings. Not to be confused with -# CRLReason reason flags bit string mappings. -# ReasonFlags ::= BIT STRING { -# unused (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# privilegeWithdrawn (7), -# aACompromise (8) } -_REASON_BIT_MAPPING = { - 1: ReasonFlags.key_compromise, - 2: ReasonFlags.ca_compromise, - 3: ReasonFlags.affiliation_changed, - 4: ReasonFlags.superseded, - 5: ReasonFlags.cessation_of_operation, - 6: ReasonFlags.certificate_hold, - 7: ReasonFlags.privilege_withdrawn, - 8: ReasonFlags.aa_compromise, -} - -_CRLREASONFLAGS = { - ReasonFlags.key_compromise: 1, - ReasonFlags.ca_compromise: 2, - ReasonFlags.affiliation_changed: 3, - ReasonFlags.superseded: 4, - ReasonFlags.cessation_of_operation: 5, - ReasonFlags.certificate_hold: 6, - ReasonFlags.privilege_withdrawn: 7, - ReasonFlags.aa_compromise: 8, -} - -# CRLReason ::= ENUMERATED { -# unspecified (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# -- value 7 is not used -# removeFromCRL (8), -# privilegeWithdrawn (9), -# aACompromise (10) } -_CRL_ENTRY_REASON_ENUM_TO_CODE = { - ReasonFlags.unspecified: 0, - ReasonFlags.key_compromise: 1, - ReasonFlags.ca_compromise: 2, - ReasonFlags.affiliation_changed: 3, - ReasonFlags.superseded: 4, - ReasonFlags.cessation_of_operation: 5, - ReasonFlags.certificate_hold: 6, - ReasonFlags.remove_from_crl: 8, - ReasonFlags.privilege_withdrawn: 9, - ReasonFlags.aa_compromise: 10, -} - - -class PolicyConstraints(ExtensionType): - oid = ExtensionOID.POLICY_CONSTRAINTS - - def __init__( - self, - require_explicit_policy: int | None, - inhibit_policy_mapping: int | None, - ) -> None: - if require_explicit_policy is not None and not isinstance( - require_explicit_policy, int - ): - raise TypeError( - "require_explicit_policy must be a non-negative integer or " - "None" - ) - - if inhibit_policy_mapping is not None and not isinstance( - inhibit_policy_mapping, int - ): - raise TypeError( - "inhibit_policy_mapping must be a non-negative integer or None" - ) - - if inhibit_policy_mapping is None and require_explicit_policy is None: - raise ValueError( - "At least one of require_explicit_policy and " - "inhibit_policy_mapping must not be None" - ) - - self._require_explicit_policy = require_explicit_policy - self._inhibit_policy_mapping = inhibit_policy_mapping - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyConstraints): - return NotImplemented - - return ( - self.require_explicit_policy == other.require_explicit_policy - and self.inhibit_policy_mapping == other.inhibit_policy_mapping - ) - - def __hash__(self) -> int: - return hash( - (self.require_explicit_policy, self.inhibit_policy_mapping) - ) - - @property - def require_explicit_policy(self) -> int | None: - return self._require_explicit_policy - - @property - def inhibit_policy_mapping(self) -> int | None: - return self._inhibit_policy_mapping - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificatePolicies(ExtensionType): - oid = ExtensionOID.CERTIFICATE_POLICIES - - def __init__(self, policies: typing.Iterable[PolicyInformation]) -> None: - policies = list(policies) - if not all(isinstance(x, PolicyInformation) for x in policies): - raise TypeError( - "Every item in the policies list must be a " - "PolicyInformation" - ) - - self._policies = policies - - __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificatePolicies): - return NotImplemented - - return self._policies == other._policies - - def __hash__(self) -> int: - return hash(tuple(self._policies)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PolicyInformation: - def __init__( - self, - policy_identifier: ObjectIdentifier, - policy_qualifiers: typing.Iterable[str | UserNotice] | None, - ) -> None: - if not isinstance(policy_identifier, ObjectIdentifier): - raise TypeError("policy_identifier must be an ObjectIdentifier") - - self._policy_identifier = policy_identifier - - if policy_qualifiers is not None: - policy_qualifiers = list(policy_qualifiers) - if not all( - isinstance(x, (str, UserNotice)) for x in policy_qualifiers - ): - raise TypeError( - "policy_qualifiers must be a list of strings and/or " - "UserNotice objects or None" - ) - - self._policy_qualifiers = policy_qualifiers - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyInformation): - return NotImplemented - - return ( - self.policy_identifier == other.policy_identifier - and self.policy_qualifiers == other.policy_qualifiers - ) - - def __hash__(self) -> int: - if self.policy_qualifiers is not None: - pq = tuple(self.policy_qualifiers) - else: - pq = None - - return hash((self.policy_identifier, pq)) - - @property - def policy_identifier(self) -> ObjectIdentifier: - return self._policy_identifier - - @property - def policy_qualifiers( - self, - ) -> list[str | UserNotice] | None: - return self._policy_qualifiers - - -class UserNotice: - def __init__( - self, - notice_reference: NoticeReference | None, - explicit_text: str | None, - ) -> None: - if notice_reference and not isinstance( - notice_reference, NoticeReference - ): - raise TypeError( - "notice_reference must be None or a NoticeReference" - ) - - self._notice_reference = notice_reference - self._explicit_text = explicit_text - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UserNotice): - return NotImplemented - - return ( - self.notice_reference == other.notice_reference - and self.explicit_text == other.explicit_text - ) - - def __hash__(self) -> int: - return hash((self.notice_reference, self.explicit_text)) - - @property - def notice_reference(self) -> NoticeReference | None: - return self._notice_reference - - @property - def explicit_text(self) -> str | None: - return self._explicit_text - - -class NoticeReference: - def __init__( - self, - organization: str | None, - notice_numbers: typing.Iterable[int], - ) -> None: - self._organization = organization - notice_numbers = list(notice_numbers) - if not all(isinstance(x, int) for x in notice_numbers): - raise TypeError("notice_numbers must be a list of integers") - - self._notice_numbers = notice_numbers - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NoticeReference): - return NotImplemented - - return ( - self.organization == other.organization - and self.notice_numbers == other.notice_numbers - ) - - def __hash__(self) -> int: - return hash((self.organization, tuple(self.notice_numbers))) - - @property - def organization(self) -> str | None: - return self._organization - - @property - def notice_numbers(self) -> list[int]: - return self._notice_numbers - - -class ExtendedKeyUsage(ExtensionType): - oid = ExtensionOID.EXTENDED_KEY_USAGE - - def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None: - usages = list(usages) - if not all(isinstance(x, ObjectIdentifier) for x in usages): - raise TypeError( - "Every item in the usages list must be an ObjectIdentifier" - ) - - self._usages = usages - - __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ExtendedKeyUsage): - return NotImplemented - - return self._usages == other._usages - - def __hash__(self) -> int: - return hash(tuple(self._usages)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNoCheck(ExtensionType): - oid = ExtensionOID.OCSP_NO_CHECK - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNoCheck): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(OCSPNoCheck) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertPoison(ExtensionType): - oid = ExtensionOID.PRECERT_POISON - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertPoison): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(PrecertPoison) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeature(ExtensionType): - oid = ExtensionOID.TLS_FEATURE - - def __init__(self, features: typing.Iterable[TLSFeatureType]) -> None: - features = list(features) - if ( - not all(isinstance(x, TLSFeatureType) for x in features) - or len(features) == 0 - ): - raise TypeError( - "features must be a list of elements from the TLSFeatureType " - "enum" - ) - - self._features = features - - __len__, __iter__, __getitem__ = _make_sequence_methods("_features") - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, TLSFeature): - return NotImplemented - - return self._features == other._features - - def __hash__(self) -> int: - return hash(tuple(self._features)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeatureType(utils.Enum): - # status_request is defined in RFC 6066 and is used for what is commonly - # called OCSP Must-Staple when present in the TLS Feature extension in an - # X.509 certificate. - status_request = 5 - # status_request_v2 is defined in RFC 6961 and allows multiple OCSP - # responses to be provided. It is not currently in use by clients or - # servers. - status_request_v2 = 17 - - -_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} - - -class InhibitAnyPolicy(ExtensionType): - oid = ExtensionOID.INHIBIT_ANY_POLICY - - def __init__(self, skip_certs: int) -> None: - if not isinstance(skip_certs, int): - raise TypeError("skip_certs must be an integer") - - if skip_certs < 0: - raise ValueError("skip_certs must be a non-negative integer") - - self._skip_certs = skip_certs - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InhibitAnyPolicy): - return NotImplemented - - return self.skip_certs == other.skip_certs - - def __hash__(self) -> int: - return hash(self.skip_certs) - - @property - def skip_certs(self) -> int: - return self._skip_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class KeyUsage(ExtensionType): - oid = ExtensionOID.KEY_USAGE - - def __init__( - self, - digital_signature: bool, - content_commitment: bool, - key_encipherment: bool, - data_encipherment: bool, - key_agreement: bool, - key_cert_sign: bool, - crl_sign: bool, - encipher_only: bool, - decipher_only: bool, - ) -> None: - if not key_agreement and (encipher_only or decipher_only): - raise ValueError( - "encipher_only and decipher_only can only be true when " - "key_agreement is true" - ) - - self._digital_signature = digital_signature - self._content_commitment = content_commitment - self._key_encipherment = key_encipherment - self._data_encipherment = data_encipherment - self._key_agreement = key_agreement - self._key_cert_sign = key_cert_sign - self._crl_sign = crl_sign - self._encipher_only = encipher_only - self._decipher_only = decipher_only - - @property - def digital_signature(self) -> bool: - return self._digital_signature - - @property - def content_commitment(self) -> bool: - return self._content_commitment - - @property - def key_encipherment(self) -> bool: - return self._key_encipherment - - @property - def data_encipherment(self) -> bool: - return self._data_encipherment - - @property - def key_agreement(self) -> bool: - return self._key_agreement - - @property - def key_cert_sign(self) -> bool: - return self._key_cert_sign - - @property - def crl_sign(self) -> bool: - return self._crl_sign - - @property - def encipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "encipher_only is undefined unless key_agreement is true" - ) - else: - return self._encipher_only - - @property - def decipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "decipher_only is undefined unless key_agreement is true" - ) - else: - return self._decipher_only - - def __repr__(self) -> str: - try: - encipher_only = self.encipher_only - decipher_only = self.decipher_only - except ValueError: - # Users found None confusing because even though encipher/decipher - # have no meaning unless key_agreement is true, to construct an - # instance of the class you still need to pass False. - encipher_only = False - decipher_only = False - - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, KeyUsage): - return NotImplemented - - return ( - self.digital_signature == other.digital_signature - and self.content_commitment == other.content_commitment - and self.key_encipherment == other.key_encipherment - and self.data_encipherment == other.data_encipherment - and self.key_agreement == other.key_agreement - and self.key_cert_sign == other.key_cert_sign - and self.crl_sign == other.crl_sign - and self._encipher_only == other._encipher_only - and self._decipher_only == other._decipher_only - ) - - def __hash__(self) -> int: - return hash( - ( - self.digital_signature, - self.content_commitment, - self.key_encipherment, - self.data_encipherment, - self.key_agreement, - self.key_cert_sign, - self.crl_sign, - self._encipher_only, - self._decipher_only, - ) - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class NameConstraints(ExtensionType): - oid = ExtensionOID.NAME_CONSTRAINTS - - def __init__( - self, - permitted_subtrees: typing.Iterable[GeneralName] | None, - excluded_subtrees: typing.Iterable[GeneralName] | None, - ) -> None: - if permitted_subtrees is not None: - permitted_subtrees = list(permitted_subtrees) - if not permitted_subtrees: - raise ValueError( - "permitted_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in permitted_subtrees): - raise TypeError( - "permitted_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_tree(permitted_subtrees) - - if excluded_subtrees is not None: - excluded_subtrees = list(excluded_subtrees) - if not excluded_subtrees: - raise ValueError( - "excluded_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in excluded_subtrees): - raise TypeError( - "excluded_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_tree(excluded_subtrees) - - if permitted_subtrees is None and excluded_subtrees is None: - raise ValueError( - "At least one of permitted_subtrees and excluded_subtrees " - "must not be None" - ) - - self._permitted_subtrees = permitted_subtrees - self._excluded_subtrees = excluded_subtrees - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameConstraints): - return NotImplemented - - return ( - self.excluded_subtrees == other.excluded_subtrees - and self.permitted_subtrees == other.permitted_subtrees - ) - - def _validate_tree(self, tree: typing.Iterable[GeneralName]) -> None: - self._validate_ip_name(tree) - self._validate_dns_name(tree) - - def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None: - if any( - isinstance(name, IPAddress) - and not isinstance( - name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) - ) - for name in tree - ): - raise TypeError( - "IPAddress name constraints must be an IPv4Network or" - " IPv6Network object" - ) - - def _validate_dns_name(self, tree: typing.Iterable[GeneralName]) -> None: - if any( - isinstance(name, DNSName) and "*" in name.value for name in tree - ): - raise ValueError( - "DNSName name constraints must not contain the '*' wildcard" - " character" - ) - - def __repr__(self) -> str: - return ( - f"" - ) - - def __hash__(self) -> int: - if self.permitted_subtrees is not None: - ps: tuple[GeneralName, ...] | None = tuple(self.permitted_subtrees) - else: - ps = None - - if self.excluded_subtrees is not None: - es: tuple[GeneralName, ...] | None = tuple(self.excluded_subtrees) - else: - es = None - - return hash((ps, es)) - - @property - def permitted_subtrees( - self, - ) -> list[GeneralName] | None: - return self._permitted_subtrees - - @property - def excluded_subtrees( - self, - ) -> list[GeneralName] | None: - return self._excluded_subtrees - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class Extension(typing.Generic[ExtensionTypeVar]): - def __init__( - self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def critical(self) -> bool: - return self._critical - - @property - def value(self) -> ExtensionTypeVar: - return self._value - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid - and self.critical == other.critical - and self.value == other.value - ) - - def __hash__(self) -> int: - return hash((self.oid, self.critical, self.value)) - - -class GeneralNames: - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - general_names = list(general_names) - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - # Return the value of each GeneralName, except for OtherName instances - # which we return directly because it has two important properties not - # just one value. - objs = (i for i in self if isinstance(i, type)) - if type != OtherName: - return [i.value for i in objs] - return list(objs) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(tuple(self._general_names)) - - -class SubjectAlternativeName(ExtensionType): - oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuerAlternativeName(ExtensionType): - oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificateIssuer(ExtensionType): - oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: type[DNSName] - | type[UniformResourceIdentifier] - | type[RFC822Name], - ) -> list[str]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[DirectoryName], - ) -> list[Name]: ... - - @typing.overload - def get_values_for_type( - self, - type: type[RegisteredID], - ) -> list[ObjectIdentifier]: ... - - @typing.overload - def get_values_for_type( - self, type: type[IPAddress] - ) -> list[_IPAddressTypes]: ... - - @typing.overload - def get_values_for_type( - self, type: type[OtherName] - ) -> list[OtherName]: ... - - def get_values_for_type( - self, - type: type[DNSName] - | type[DirectoryName] - | type[IPAddress] - | type[OtherName] - | type[RFC822Name] - | type[RegisteredID] - | type[UniformResourceIdentifier], - ) -> ( - list[_IPAddressTypes] - | list[str] - | list[OtherName] - | list[Name] - | list[ObjectIdentifier] - ): - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificateIssuer): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLReason(ExtensionType): - oid = CRLEntryExtensionOID.CRL_REASON - - def __init__(self, reason: ReasonFlags) -> None: - if not isinstance(reason, ReasonFlags): - raise TypeError("reason must be an element from ReasonFlags") - - self._reason = reason - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLReason): - return NotImplemented - - return self.reason == other.reason - - def __hash__(self) -> int: - return hash(self.reason) - - @property - def reason(self) -> ReasonFlags: - return self._reason - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class InvalidityDate(ExtensionType): - oid = CRLEntryExtensionOID.INVALIDITY_DATE - - def __init__(self, invalidity_date: datetime.datetime) -> None: - if not isinstance(invalidity_date, datetime.datetime): - raise TypeError("invalidity_date must be a datetime.datetime") - - self._invalidity_date = invalidity_date - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InvalidityDate): - return NotImplemented - - return self.invalidity_date == other.invalidity_date - - def __hash__(self) -> int: - return hash(self.invalidity_date) - - @property - def invalidity_date(self) -> datetime.datetime: - return self._invalidity_date - - @property - def invalidity_date_utc(self) -> datetime.datetime: - if self._invalidity_date.tzinfo is None: - return self._invalidity_date.replace(tzinfo=datetime.timezone.utc) - else: - return self._invalidity_date.astimezone(tz=datetime.timezone.utc) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertificateSignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: typing.Iterable[ - SignedCertificateTimestamp - ], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return f"" - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertificateSignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: typing.Iterable[ - SignedCertificateTimestamp - ], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return f"" - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNonce(ExtensionType): - oid = OCSPExtensionOID.NONCE - - def __init__(self, nonce: bytes) -> None: - if not isinstance(nonce, bytes): - raise TypeError("nonce must be bytes") - - self._nonce = nonce - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNonce): - return NotImplemented - - return self.nonce == other.nonce - - def __hash__(self) -> int: - return hash(self.nonce) - - def __repr__(self) -> str: - return f"" - - @property - def nonce(self) -> bytes: - return self._nonce - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPAcceptableResponses(ExtensionType): - oid = OCSPExtensionOID.ACCEPTABLE_RESPONSES - - def __init__(self, responses: typing.Iterable[ObjectIdentifier]) -> None: - responses = list(responses) - if any(not isinstance(r, ObjectIdentifier) for r in responses): - raise TypeError("All responses must be ObjectIdentifiers") - - self._responses = responses - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPAcceptableResponses): - return NotImplemented - - return self._responses == other._responses - - def __hash__(self) -> int: - return hash(tuple(self._responses)) - - def __repr__(self) -> str: - return f"" - - def __iter__(self) -> typing.Iterator[ObjectIdentifier]: - return iter(self._responses) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuingDistributionPoint(ExtensionType): - oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT - - def __init__( - self, - full_name: typing.Iterable[GeneralName] | None, - relative_name: RelativeDistinguishedName | None, - only_contains_user_certs: bool, - only_contains_ca_certs: bool, - only_some_reasons: frozenset[ReasonFlags] | None, - indirect_crl: bool, - only_contains_attribute_certs: bool, - ) -> None: - if full_name is not None: - full_name = list(full_name) - - if only_some_reasons and ( - not isinstance(only_some_reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) - ): - raise TypeError( - "only_some_reasons must be None or frozenset of ReasonFlags" - ) - - if only_some_reasons and ( - ReasonFlags.unspecified in only_some_reasons - or ReasonFlags.remove_from_crl in only_some_reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in an " - "IssuingDistributionPoint" - ) - - if not ( - isinstance(only_contains_user_certs, bool) - and isinstance(only_contains_ca_certs, bool) - and isinstance(indirect_crl, bool) - and isinstance(only_contains_attribute_certs, bool) - ): - raise TypeError( - "only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl and only_contains_attribute_certs " - "must all be boolean." - ) - - # Per RFC5280 Section 5.2.5, the Issuing Distribution Point extension - # in a CRL can have only one of onlyContainsUserCerts, - # onlyContainsCACerts, onlyContainsAttributeCerts set to TRUE. - crl_constraints = [ - only_contains_user_certs, - only_contains_ca_certs, - only_contains_attribute_certs, - ] - - if len([x for x in crl_constraints if x]) > 1: - raise ValueError( - "Only one of the following can be set to True: " - "only_contains_user_certs, only_contains_ca_certs, " - "only_contains_attribute_certs" - ) - - if not any( - [ - only_contains_user_certs, - only_contains_ca_certs, - indirect_crl, - only_contains_attribute_certs, - full_name, - relative_name, - only_some_reasons, - ] - ): - raise ValueError( - "Cannot create empty extension: " - "if only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl, and only_contains_attribute_certs are all False" - ", then either full_name, relative_name, or only_some_reasons " - "must have a value." - ) - - self._only_contains_user_certs = only_contains_user_certs - self._only_contains_ca_certs = only_contains_ca_certs - self._indirect_crl = indirect_crl - self._only_contains_attribute_certs = only_contains_attribute_certs - self._only_some_reasons = only_some_reasons - self._full_name = full_name - self._relative_name = relative_name - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuingDistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.only_contains_user_certs == other.only_contains_user_certs - and self.only_contains_ca_certs == other.only_contains_ca_certs - and self.only_some_reasons == other.only_some_reasons - and self.indirect_crl == other.indirect_crl - and self.only_contains_attribute_certs - == other.only_contains_attribute_certs - ) - - def __hash__(self) -> int: - return hash( - ( - self.full_name, - self.relative_name, - self.only_contains_user_certs, - self.only_contains_ca_certs, - self.only_some_reasons, - self.indirect_crl, - self.only_contains_attribute_certs, - ) - ) - - @property - def full_name(self) -> list[GeneralName] | None: - return self._full_name - - @property - def relative_name(self) -> RelativeDistinguishedName | None: - return self._relative_name - - @property - def only_contains_user_certs(self) -> bool: - return self._only_contains_user_certs - - @property - def only_contains_ca_certs(self) -> bool: - return self._only_contains_ca_certs - - @property - def only_some_reasons( - self, - ) -> frozenset[ReasonFlags] | None: - return self._only_some_reasons - - @property - def indirect_crl(self) -> bool: - return self._indirect_crl - - @property - def only_contains_attribute_certs(self) -> bool: - return self._only_contains_attribute_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class MSCertificateTemplate(ExtensionType): - oid = ExtensionOID.MS_CERTIFICATE_TEMPLATE - - def __init__( - self, - template_id: ObjectIdentifier, - major_version: int | None, - minor_version: int | None, - ) -> None: - if not isinstance(template_id, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - self._template_id = template_id - if ( - major_version is not None and not isinstance(major_version, int) - ) or ( - minor_version is not None and not isinstance(minor_version, int) - ): - raise TypeError( - "major_version and minor_version must be integers or None" - ) - self._major_version = major_version - self._minor_version = minor_version - - @property - def template_id(self) -> ObjectIdentifier: - return self._template_id - - @property - def major_version(self) -> int | None: - return self._major_version - - @property - def minor_version(self) -> int | None: - return self._minor_version - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, MSCertificateTemplate): - return NotImplemented - - return ( - self.template_id == other.template_id - and self.major_version == other.major_version - and self.minor_version == other.minor_version - ) - - def __hash__(self) -> int: - return hash((self.template_id, self.major_version, self.minor_version)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class NamingAuthority: - def __init__( - self, - id: ObjectIdentifier | None, - url: str | None, - text: str | None, - ) -> None: - if id is not None and not isinstance(id, ObjectIdentifier): - raise TypeError("id must be an ObjectIdentifier") - - if url is not None and not isinstance(url, str): - raise TypeError("url must be a str") - - if text is not None and not isinstance(text, str): - raise TypeError("text must be a str") - - self._id = id - self._url = url - self._text = text - - @property - def id(self) -> ObjectIdentifier | None: - return self._id - - @property - def url(self) -> str | None: - return self._url - - @property - def text(self) -> str | None: - return self._text - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NamingAuthority): - return NotImplemented - - return ( - self.id == other.id - and self.url == other.url - and self.text == other.text - ) - - def __hash__(self) -> int: - return hash( - ( - self.id, - self.url, - self.text, - ) - ) - - -class ProfessionInfo: - def __init__( - self, - naming_authority: NamingAuthority | None, - profession_items: typing.Iterable[str], - profession_oids: typing.Iterable[ObjectIdentifier] | None, - registration_number: str | None, - add_profession_info: bytes | None, - ) -> None: - if naming_authority is not None and not isinstance( - naming_authority, NamingAuthority - ): - raise TypeError("naming_authority must be a NamingAuthority") - - profession_items = list(profession_items) - if not all(isinstance(item, str) for item in profession_items): - raise TypeError( - "Every item in the profession_items list must be a str" - ) - - if profession_oids is not None: - profession_oids = list(profession_oids) - if not all( - isinstance(oid, ObjectIdentifier) for oid in profession_oids - ): - raise TypeError( - "Every item in the profession_oids list must be an " - "ObjectIdentifier" - ) - - if registration_number is not None and not isinstance( - registration_number, str - ): - raise TypeError("registration_number must be a str") - - if add_profession_info is not None and not isinstance( - add_profession_info, bytes - ): - raise TypeError("add_profession_info must be bytes") - - self._naming_authority = naming_authority - self._profession_items = profession_items - self._profession_oids = profession_oids - self._registration_number = registration_number - self._add_profession_info = add_profession_info - - @property - def naming_authority(self) -> NamingAuthority | None: - return self._naming_authority - - @property - def profession_items(self) -> list[str]: - return self._profession_items - - @property - def profession_oids(self) -> list[ObjectIdentifier] | None: - return self._profession_oids - - @property - def registration_number(self) -> str | None: - return self._registration_number - - @property - def add_profession_info(self) -> bytes | None: - return self._add_profession_info - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ProfessionInfo): - return NotImplemented - - return ( - self.naming_authority == other.naming_authority - and self.profession_items == other.profession_items - and self.profession_oids == other.profession_oids - and self.registration_number == other.registration_number - and self.add_profession_info == other.add_profession_info - ) - - def __hash__(self) -> int: - if self.profession_oids is not None: - profession_oids = tuple(self.profession_oids) - else: - profession_oids = None - return hash( - ( - self.naming_authority, - tuple(self.profession_items), - profession_oids, - self.registration_number, - self.add_profession_info, - ) - ) - - -class Admission: - def __init__( - self, - admission_authority: GeneralName | None, - naming_authority: NamingAuthority | None, - profession_infos: typing.Iterable[ProfessionInfo], - ) -> None: - if admission_authority is not None and not isinstance( - admission_authority, GeneralName - ): - raise TypeError("admission_authority must be a GeneralName") - - if naming_authority is not None and not isinstance( - naming_authority, NamingAuthority - ): - raise TypeError("naming_authority must be a NamingAuthority") - - profession_infos = list(profession_infos) - if not all( - isinstance(info, ProfessionInfo) for info in profession_infos - ): - raise TypeError( - "Every item in the profession_infos list must be a " - "ProfessionInfo" - ) - - self._admission_authority = admission_authority - self._naming_authority = naming_authority - self._profession_infos = profession_infos - - @property - def admission_authority(self) -> GeneralName | None: - return self._admission_authority - - @property - def naming_authority(self) -> NamingAuthority | None: - return self._naming_authority - - @property - def profession_infos(self) -> list[ProfessionInfo]: - return self._profession_infos - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Admission): - return NotImplemented - - return ( - self.admission_authority == other.admission_authority - and self.naming_authority == other.naming_authority - and self.profession_infos == other.profession_infos - ) - - def __hash__(self) -> int: - return hash( - ( - self.admission_authority, - self.naming_authority, - tuple(self.profession_infos), - ) - ) - - -class Admissions(ExtensionType): - oid = ExtensionOID.ADMISSIONS - - def __init__( - self, - authority: GeneralName | None, - admissions: typing.Iterable[Admission], - ) -> None: - if authority is not None and not isinstance(authority, GeneralName): - raise TypeError("authority must be a GeneralName") - - admissions = list(admissions) - if not all( - isinstance(admission, Admission) for admission in admissions - ): - raise TypeError( - "Every item in the contents_of_admissions list must be an " - "Admission" - ) - - self._authority = authority - self._admissions = admissions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_admissions") - - @property - def authority(self) -> GeneralName | None: - return self._authority - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Admissions): - return NotImplemented - - return ( - self.authority == other.authority - and self._admissions == other._admissions - ) - - def __hash__(self) -> int: - return hash((self.authority, tuple(self._admissions))) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class UnrecognizedExtension(ExtensionType): - def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - self._oid = oid - self._value = value - - @property - def oid(self) -> ObjectIdentifier: # type: ignore[override] - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return ( - f"" - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UnrecognizedExtension): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def public_bytes(self) -> bytes: - return self.value diff --git a/.venv/Lib/site-packages/cryptography/x509/general_name.py b/.venv/Lib/site-packages/cryptography/x509/general_name.py deleted file mode 100644 index 672f287..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/general_name.py +++ /dev/null @@ -1,281 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import abc -import ipaddress -import typing -from email.utils import parseaddr - -from cryptography.x509.name import Name -from cryptography.x509.oid import ObjectIdentifier - -_IPAddressTypes = typing.Union[ - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, -] - - -class UnsupportedGeneralNameType(Exception): - pass - - -class GeneralName(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def value(self) -> typing.Any: - """ - Return the value of the object - """ - - -class RFC822Name(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "RFC822Name values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - name, address = parseaddr(value) - if name or not address: - # parseaddr has found a name (e.g. Name ) or the entire - # value is an empty string. - raise ValueError("Invalid rfc822name value") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> RFC822Name: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RFC822Name): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DNSName(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "DNSName values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> DNSName: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DNSName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class UniformResourceIdentifier(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "URI values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> UniformResourceIdentifier: - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UniformResourceIdentifier): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DirectoryName(GeneralName): - def __init__(self, value: Name) -> None: - if not isinstance(value, Name): - raise TypeError("value must be a Name") - - self._value = value - - @property - def value(self) -> Name: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DirectoryName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class RegisteredID(GeneralName): - def __init__(self, value: ObjectIdentifier) -> None: - if not isinstance(value, ObjectIdentifier): - raise TypeError("value must be an ObjectIdentifier") - - self._value = value - - @property - def value(self) -> ObjectIdentifier: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RegisteredID): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class IPAddress(GeneralName): - def __init__(self, value: _IPAddressTypes) -> None: - if not isinstance( - value, - ( - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, - ), - ): - raise TypeError( - "value must be an instance of ipaddress.IPv4Address, " - "ipaddress.IPv6Address, ipaddress.IPv4Network, or " - "ipaddress.IPv6Network" - ) - - self._value = value - - @property - def value(self) -> _IPAddressTypes: - return self._value - - def _packed(self) -> bytes: - if isinstance( - self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) - ): - return self.value.packed - else: - return ( - self.value.network_address.packed + self.value.netmask.packed - ) - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IPAddress): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class OtherName(GeneralName): - def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: - if not isinstance(type_id, ObjectIdentifier): - raise TypeError("type_id must be an ObjectIdentifier") - if not isinstance(value, bytes): - raise TypeError("value must be a binary string") - - self._type_id = type_id - self._value = value - - @property - def type_id(self) -> ObjectIdentifier: - return self._type_id - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return f"" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OtherName): - return NotImplemented - - return self.type_id == other.type_id and self.value == other.value - - def __hash__(self) -> int: - return hash((self.type_id, self.value)) diff --git a/.venv/Lib/site-packages/cryptography/x509/name.py b/.venv/Lib/site-packages/cryptography/x509/name.py deleted file mode 100644 index 1b6b89d..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/name.py +++ /dev/null @@ -1,465 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import binascii -import re -import sys -import typing -import warnings - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.x509.oid import NameOID, ObjectIdentifier - - -class _ASN1Type(utils.Enum): - BitString = 3 - OctetString = 4 - UTF8String = 12 - NumericString = 18 - PrintableString = 19 - T61String = 20 - IA5String = 22 - UTCTime = 23 - GeneralizedTime = 24 - VisibleString = 26 - UniversalString = 28 - BMPString = 30 - - -_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} -_NAMEOID_DEFAULT_TYPE: dict[ObjectIdentifier, _ASN1Type] = { - NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, - NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, - NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, - NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, -} - -# Type alias -_OidNameMap = typing.Mapping[ObjectIdentifier, str] -_NameOidMap = typing.Mapping[str, ObjectIdentifier] - -#: Short attribute names from RFC 4514: -#: https://tools.ietf.org/html/rfc4514#page-7 -_NAMEOID_TO_NAME: _OidNameMap = { - NameOID.COMMON_NAME: "CN", - NameOID.LOCALITY_NAME: "L", - NameOID.STATE_OR_PROVINCE_NAME: "ST", - NameOID.ORGANIZATION_NAME: "O", - NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", - NameOID.COUNTRY_NAME: "C", - NameOID.STREET_ADDRESS: "STREET", - NameOID.DOMAIN_COMPONENT: "DC", - NameOID.USER_ID: "UID", -} -_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} - -_NAMEOID_LENGTH_LIMIT = { - NameOID.COUNTRY_NAME: (2, 2), - NameOID.JURISDICTION_COUNTRY_NAME: (2, 2), - NameOID.COMMON_NAME: (1, 64), -} - - -def _escape_dn_value(val: str | bytes) -> str: - """Escape special characters in RFC4514 Distinguished Name value.""" - - if not val: - return "" - - # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character - # followed by the hexadecimal encoding of the octets. - if isinstance(val, bytes): - return "#" + binascii.hexlify(val).decode("utf8") - - # See https://tools.ietf.org/html/rfc4514#section-2.4 - val = val.replace("\\", "\\\\") - val = val.replace('"', '\\"') - val = val.replace("+", "\\+") - val = val.replace(",", "\\,") - val = val.replace(";", "\\;") - val = val.replace("<", "\\<") - val = val.replace(">", "\\>") - val = val.replace("\0", "\\00") - - if val[0] in ("#", " "): - val = "\\" + val - if val[-1] == " ": - val = val[:-1] + "\\ " - - return val - - -def _unescape_dn_value(val: str) -> str: - if not val: - return "" - - # See https://tools.ietf.org/html/rfc4514#section-3 - - # special = escaped / SPACE / SHARP / EQUALS - # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE - def sub(m): - val = m.group(1) - # Regular escape - if len(val) == 1: - return val - # Hex-value scape - return chr(int(val, 16)) - - return _RFC4514NameParser._PAIR_RE.sub(sub, val) - - -class NameAttribute: - def __init__( - self, - oid: ObjectIdentifier, - value: str | bytes, - _type: _ASN1Type | None = None, - *, - _validate: bool = True, - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - if _type == _ASN1Type.BitString: - if oid != NameOID.X500_UNIQUE_IDENTIFIER: - raise TypeError( - "oid must be X500_UNIQUE_IDENTIFIER for BitString type." - ) - if not isinstance(value, bytes): - raise TypeError("value must be bytes for BitString") - else: - if not isinstance(value, str): - raise TypeError("value argument must be a str") - - length_limits = _NAMEOID_LENGTH_LIMIT.get(oid) - if length_limits is not None: - min_length, max_length = length_limits - assert isinstance(value, str) - c_len = len(value.encode("utf8")) - if c_len < min_length or c_len > max_length: - msg = ( - f"Attribute's length must be >= {min_length} and " - f"<= {max_length}, but it was {c_len}" - ) - if _validate is True: - raise ValueError(msg) - else: - warnings.warn(msg, stacklevel=2) - - # The appropriate ASN1 string type varies by OID and is defined across - # multiple RFCs including 2459, 3280, and 5280. In general UTF8String - # is preferred (2459), but 3280 and 5280 specify several OIDs with - # alternate types. This means when we see the sentinel value we need - # to look up whether the OID has a non-UTF8 type. If it does, set it - # to that. Otherwise, UTF8! - if _type is None: - _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) - - if not isinstance(_type, _ASN1Type): - raise TypeError("_type must be from the _ASN1Type enum") - - self._oid = oid - self._value = value - self._type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> str | bytes: - return self._value - - @property - def rfc4514_attribute_name(self) -> str: - """ - The short attribute name (for example "CN") if available, - otherwise the OID dotted string. - """ - return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Use short attribute name if available, otherwise fall back to OID - dotted string. - """ - attr_name = ( - attr_name_overrides.get(self.oid) if attr_name_overrides else None - ) - if attr_name is None: - attr_name = self.rfc4514_attribute_name - - return f"{attr_name}={_escape_dn_value(self.value)}" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameAttribute): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def __repr__(self) -> str: - return f"" - - -class RelativeDistinguishedName: - def __init__(self, attributes: typing.Iterable[NameAttribute]): - attributes = list(attributes) - if not attributes: - raise ValueError("a relative distinguished name cannot be empty") - if not all(isinstance(x, NameAttribute) for x in attributes): - raise TypeError("attributes must be an iterable of NameAttribute") - - # Keep list and frozenset to preserve attribute order where it matters - self._attributes = attributes - self._attribute_set = frozenset(attributes) - - if len(self._attribute_set) != len(attributes): - raise ValueError("duplicate attributes are not allowed") - - def get_attributes_for_oid( - self, oid: ObjectIdentifier - ) -> list[NameAttribute]: - return [i for i in self if i.oid == oid] - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Within each RDN, attributes are joined by '+', although that is rarely - used in certificates. - """ - return "+".join( - attr.rfc4514_string(attr_name_overrides) - for attr in self._attributes - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RelativeDistinguishedName): - return NotImplemented - - return self._attribute_set == other._attribute_set - - def __hash__(self) -> int: - return hash(self._attribute_set) - - def __iter__(self) -> typing.Iterator[NameAttribute]: - return iter(self._attributes) - - def __len__(self) -> int: - return len(self._attributes) - - def __repr__(self) -> str: - return f"" - - -class Name: - @typing.overload - def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None: ... - - @typing.overload - def __init__( - self, attributes: typing.Iterable[RelativeDistinguishedName] - ) -> None: ... - - def __init__( - self, - attributes: typing.Iterable[NameAttribute | RelativeDistinguishedName], - ) -> None: - attributes = list(attributes) - if all(isinstance(x, NameAttribute) for x in attributes): - self._attributes = [ - RelativeDistinguishedName([typing.cast(NameAttribute, x)]) - for x in attributes - ] - elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): - self._attributes = typing.cast( - typing.List[RelativeDistinguishedName], attributes - ) - else: - raise TypeError( - "attributes must be a list of NameAttribute" - " or a list RelativeDistinguishedName" - ) - - @classmethod - def from_rfc4514_string( - cls, - data: str, - attr_name_overrides: _NameOidMap | None = None, - ) -> Name: - return _RFC4514NameParser(data, attr_name_overrides or {}).parse() - - def rfc4514_string( - self, attr_name_overrides: _OidNameMap | None = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - For example 'CN=foobar.com,O=Foo Corp,C=US' - - An X.509 name is a two-level structure: a list of sets of attributes. - Each list element is separated by ',' and within each list element, set - elements are separated by '+'. The latter is almost never used in - real world certificates. According to RFC4514 section 2.1 the - RDNSequence must be reversed when converting to string representation. - """ - return ",".join( - attr.rfc4514_string(attr_name_overrides) - for attr in reversed(self._attributes) - ) - - def get_attributes_for_oid( - self, oid: ObjectIdentifier - ) -> list[NameAttribute]: - return [i for i in self if i.oid == oid] - - @property - def rdns(self) -> list[RelativeDistinguishedName]: - return self._attributes - - def public_bytes(self, backend: typing.Any = None) -> bytes: - return rust_x509.encode_name_bytes(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Name): - return NotImplemented - - return self._attributes == other._attributes - - def __hash__(self) -> int: - # TODO: this is relatively expensive, if this looks like a bottleneck - # for you, consider optimizing! - return hash(tuple(self._attributes)) - - def __iter__(self) -> typing.Iterator[NameAttribute]: - for rdn in self._attributes: - yield from rdn - - def __len__(self) -> int: - return sum(len(rdn) for rdn in self._attributes) - - def __repr__(self) -> str: - rdns = ",".join(attr.rfc4514_string() for attr in self._attributes) - return f"" - - -class _RFC4514NameParser: - _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") - _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") - - _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})" - _PAIR_RE = re.compile(_PAIR) - _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" - _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" - _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" - _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" - _STRING_RE = re.compile( - rf""" - ( - ({_LEADCHAR}|{_PAIR}) - ( - ({_STRINGCHAR}|{_PAIR})* - ({_TRAILCHAR}|{_PAIR}) - )? - )? - """, - re.VERBOSE, - ) - _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") - - def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: - self._data = data - self._idx = 0 - - self._attr_name_overrides = attr_name_overrides - - def _has_data(self) -> bool: - return self._idx < len(self._data) - - def _peek(self) -> str | None: - if self._has_data(): - return self._data[self._idx] - return None - - def _read_char(self, ch: str) -> None: - if self._peek() != ch: - raise ValueError - self._idx += 1 - - def _read_re(self, pat) -> str: - match = pat.match(self._data, pos=self._idx) - if match is None: - raise ValueError - val = match.group() - self._idx += len(val) - return val - - def parse(self) -> Name: - """ - Parses the `data` string and converts it to a Name. - - According to RFC4514 section 2.1 the RDNSequence must be - reversed when converting to string representation. So, when - we parse it, we need to reverse again to get the RDNs on the - correct order. - """ - - if not self._has_data(): - return Name([]) - - rdns = [self._parse_rdn()] - - while self._has_data(): - self._read_char(",") - rdns.append(self._parse_rdn()) - - return Name(reversed(rdns)) - - def _parse_rdn(self) -> RelativeDistinguishedName: - nas = [self._parse_na()] - while self._peek() == "+": - self._read_char("+") - nas.append(self._parse_na()) - - return RelativeDistinguishedName(nas) - - def _parse_na(self) -> NameAttribute: - try: - oid_value = self._read_re(self._OID_RE) - except ValueError: - name = self._read_re(self._DESCR_RE) - oid = self._attr_name_overrides.get( - name, _NAME_TO_NAMEOID.get(name) - ) - if oid is None: - raise ValueError - else: - oid = ObjectIdentifier(oid_value) - - self._read_char("=") - if self._peek() == "#": - value = self._read_re(self._HEXSTRING_RE) - value = binascii.unhexlify(value[1:]).decode() - else: - raw_value = self._read_re(self._STRING_RE) - value = _unescape_dn_value(raw_value) - - return NameAttribute(oid, value) diff --git a/.venv/Lib/site-packages/cryptography/x509/ocsp.py b/.venv/Lib/site-packages/cryptography/x509/ocsp.py deleted file mode 100644 index 5a011c4..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/ocsp.py +++ /dev/null @@ -1,344 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import datetime -import typing - -from cryptography import utils, x509 -from cryptography.hazmat.bindings._rust import ocsp -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.types import ( - CertificateIssuerPrivateKeyTypes, -) -from cryptography.x509.base import ( - _EARLIEST_UTC_TIME, - _convert_to_naive_utc_time, - _reject_duplicate_extension, -) - - -class OCSPResponderEncoding(utils.Enum): - HASH = "By Hash" - NAME = "By Name" - - -class OCSPResponseStatus(utils.Enum): - SUCCESSFUL = 0 - MALFORMED_REQUEST = 1 - INTERNAL_ERROR = 2 - TRY_LATER = 3 - SIG_REQUIRED = 5 - UNAUTHORIZED = 6 - - -_ALLOWED_HASHES = ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -) - - -def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: - if not isinstance(algorithm, _ALLOWED_HASHES): - raise ValueError( - "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" - ) - - -class OCSPCertStatus(utils.Enum): - GOOD = 0 - REVOKED = 1 - UNKNOWN = 2 - - -class _SingleResponse: - def __init__( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: datetime.datetime | None, - revocation_time: datetime.datetime | None, - revocation_reason: x509.ReasonFlags | None, - ): - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - _verify_algorithm(algorithm) - if not isinstance(this_update, datetime.datetime): - raise TypeError("this_update must be a datetime object") - if next_update is not None and not isinstance( - next_update, datetime.datetime - ): - raise TypeError("next_update must be a datetime object or None") - - self._cert = cert - self._issuer = issuer - self._algorithm = algorithm - self._this_update = this_update - self._next_update = next_update - - if not isinstance(cert_status, OCSPCertStatus): - raise TypeError( - "cert_status must be an item from the OCSPCertStatus enum" - ) - if cert_status is not OCSPCertStatus.REVOKED: - if revocation_time is not None: - raise ValueError( - "revocation_time can only be provided if the certificate " - "is revoked" - ) - if revocation_reason is not None: - raise ValueError( - "revocation_reason can only be provided if the certificate" - " is revoked" - ) - else: - if not isinstance(revocation_time, datetime.datetime): - raise TypeError("revocation_time must be a datetime object") - - revocation_time = _convert_to_naive_utc_time(revocation_time) - if revocation_time < _EARLIEST_UTC_TIME: - raise ValueError( - "The revocation_time must be on or after" - " 1950 January 1." - ) - - if revocation_reason is not None and not isinstance( - revocation_reason, x509.ReasonFlags - ): - raise TypeError( - "revocation_reason must be an item from the ReasonFlags " - "enum or None" - ) - - self._cert_status = cert_status - self._revocation_time = revocation_time - self._revocation_reason = revocation_reason - - -OCSPRequest = ocsp.OCSPRequest -OCSPResponse = ocsp.OCSPResponse -OCSPSingleResponse = ocsp.OCSPSingleResponse - - -class OCSPRequestBuilder: - def __init__( - self, - request: tuple[ - x509.Certificate, x509.Certificate, hashes.HashAlgorithm - ] - | None = None, - request_hash: tuple[bytes, bytes, int, hashes.HashAlgorithm] - | None = None, - extensions: list[x509.Extension[x509.ExtensionType]] = [], - ) -> None: - self._request = request - self._request_hash = request_hash - self._extensions = extensions - - def add_certificate( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - ) -> OCSPRequestBuilder: - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - _verify_algorithm(algorithm) - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - return OCSPRequestBuilder( - (cert, issuer, algorithm), self._request_hash, self._extensions - ) - - def add_certificate_by_hash( - self, - issuer_name_hash: bytes, - issuer_key_hash: bytes, - serial_number: int, - algorithm: hashes.HashAlgorithm, - ) -> OCSPRequestBuilder: - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - if not isinstance(serial_number, int): - raise TypeError("serial_number must be an integer") - - _verify_algorithm(algorithm) - utils._check_bytes("issuer_name_hash", issuer_name_hash) - utils._check_bytes("issuer_key_hash", issuer_key_hash) - if algorithm.digest_size != len( - issuer_name_hash - ) or algorithm.digest_size != len(issuer_key_hash): - raise ValueError( - "issuer_name_hash and issuer_key_hash must be the same length " - "as the digest size of the algorithm" - ) - - return OCSPRequestBuilder( - self._request, - (issuer_name_hash, issuer_key_hash, serial_number, algorithm), - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> OCSPRequestBuilder: - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPRequestBuilder( - self._request, self._request_hash, [*self._extensions, extension] - ) - - def build(self) -> OCSPRequest: - if self._request is None and self._request_hash is None: - raise ValueError("You must add a certificate before building") - - return ocsp.create_ocsp_request(self) - - -class OCSPResponseBuilder: - def __init__( - self, - response: _SingleResponse | None = None, - responder_id: tuple[x509.Certificate, OCSPResponderEncoding] - | None = None, - certs: list[x509.Certificate] | None = None, - extensions: list[x509.Extension[x509.ExtensionType]] = [], - ): - self._response = response - self._responder_id = responder_id - self._certs = certs - self._extensions = extensions - - def add_response( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: datetime.datetime | None, - revocation_time: datetime.datetime | None, - revocation_reason: x509.ReasonFlags | None, - ) -> OCSPResponseBuilder: - if self._response is not None: - raise ValueError("Only one response per OCSPResponse.") - - singleresp = _SingleResponse( - cert, - issuer, - algorithm, - cert_status, - this_update, - next_update, - revocation_time, - revocation_reason, - ) - return OCSPResponseBuilder( - singleresp, - self._responder_id, - self._certs, - self._extensions, - ) - - def responder_id( - self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate - ) -> OCSPResponseBuilder: - if self._responder_id is not None: - raise ValueError("responder_id can only be set once") - if not isinstance(responder_cert, x509.Certificate): - raise TypeError("responder_cert must be a Certificate") - if not isinstance(encoding, OCSPResponderEncoding): - raise TypeError( - "encoding must be an element from OCSPResponderEncoding" - ) - - return OCSPResponseBuilder( - self._response, - (responder_cert, encoding), - self._certs, - self._extensions, - ) - - def certificates( - self, certs: typing.Iterable[x509.Certificate] - ) -> OCSPResponseBuilder: - if self._certs is not None: - raise ValueError("certificates may only be set once") - certs = list(certs) - if len(certs) == 0: - raise ValueError("certs must not be an empty list") - if not all(isinstance(x, x509.Certificate) for x in certs): - raise TypeError("certs must be a list of Certificates") - return OCSPResponseBuilder( - self._response, - self._responder_id, - certs, - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> OCSPResponseBuilder: - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPResponseBuilder( - self._response, - self._responder_id, - self._certs, - [*self._extensions, extension], - ) - - def sign( - self, - private_key: CertificateIssuerPrivateKeyTypes, - algorithm: hashes.HashAlgorithm | None, - ) -> OCSPResponse: - if self._response is None: - raise ValueError("You must add a response before signing") - if self._responder_id is None: - raise ValueError("You must add a responder_id before signing") - - return ocsp.create_ocsp_response( - OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm - ) - - @classmethod - def build_unsuccessful( - cls, response_status: OCSPResponseStatus - ) -> OCSPResponse: - if not isinstance(response_status, OCSPResponseStatus): - raise TypeError( - "response_status must be an item from OCSPResponseStatus" - ) - if response_status is OCSPResponseStatus.SUCCESSFUL: - raise ValueError("response_status cannot be SUCCESSFUL") - - return ocsp.create_ocsp_response(response_status, None, None, None) - - -load_der_ocsp_request = ocsp.load_der_ocsp_request -load_der_ocsp_response = ocsp.load_der_ocsp_response diff --git a/.venv/Lib/site-packages/cryptography/x509/oid.py b/.venv/Lib/site-packages/cryptography/x509/oid.py deleted file mode 100644 index d4e409e..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/oid.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -from cryptography.hazmat._oid import ( - AttributeOID, - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - OCSPExtensionOID, - PublicKeyAlgorithmOID, - SignatureAlgorithmOID, - SubjectInformationAccessOID, -) - -__all__ = [ - "AttributeOID", - "AuthorityInformationAccessOID", - "CRLEntryExtensionOID", - "CertificatePoliciesOID", - "ExtendedKeyUsageOID", - "ExtensionOID", - "NameOID", - "OCSPExtensionOID", - "ObjectIdentifier", - "PublicKeyAlgorithmOID", - "SignatureAlgorithmOID", - "SubjectInformationAccessOID", -] diff --git a/.venv/Lib/site-packages/cryptography/x509/verification.py b/.venv/Lib/site-packages/cryptography/x509/verification.py deleted file mode 100644 index b836506..0000000 --- a/.venv/Lib/site-packages/cryptography/x509/verification.py +++ /dev/null @@ -1,28 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from __future__ import annotations - -import typing - -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.x509.general_name import DNSName, IPAddress - -__all__ = [ - "ClientVerifier", - "PolicyBuilder", - "ServerVerifier", - "Store", - "Subject", - "VerificationError", - "VerifiedClient", -] - -Store = rust_x509.Store -Subject = typing.Union[DNSName, IPAddress] -VerifiedClient = rust_x509.VerifiedClient -ClientVerifier = rust_x509.ClientVerifier -ServerVerifier = rust_x509.ServerVerifier -PolicyBuilder = rust_x509.PolicyBuilder -VerificationError = rust_x509.VerificationError diff --git a/.venv/Lib/site-packages/distutils-precedence.pth b/.venv/Lib/site-packages/distutils-precedence.pth deleted file mode 100644 index 7f009fe..0000000 --- a/.venv/Lib/site-packages/distutils-precedence.pth +++ /dev/null @@ -1 +0,0 @@ -import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim(); diff --git a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/INSTALLER b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/LICENSE b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/LICENSE deleted file mode 100644 index 474479a..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -"python-ecdsa" Copyright (c) 2010 Brian Warner - -Portions written in 2005 by Peter Pearson and placed in the public domain. - -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/site-packages/ecdsa-0.19.0.dist-info/METADATA b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/METADATA deleted file mode 100644 index b9fd800..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/METADATA +++ /dev/null @@ -1,671 +0,0 @@ -Metadata-Version: 2.1 -Name: ecdsa -Version: 0.19.0 -Summary: ECDSA cryptographic signature library (pure python) -Home-page: http://github.com/tlsfuzzer/python-ecdsa -Author: Brian Warner -Author-email: warner@lothar.com -License: MIT -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: six (>=1.9.0) -Provides-Extra: gmpy -Requires-Dist: gmpy ; extra == 'gmpy' -Provides-Extra: gmpy2 -Requires-Dist: gmpy2 ; extra == 'gmpy2' - -# Pure-Python ECDSA and ECDH - -[![Build Status](https://github.com/tlsfuzzer/python-ecdsa/workflows/GitHub%20CI/badge.svg?branch=master)](https://github.com/tlsfuzzer/python-ecdsa/actions?query=workflow%3A%22GitHub+CI%22+branch%3Amaster) -[![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) -[![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) -![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) -![mutation score](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-mutation-score.json) -[![CodeQL](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml/badge.svg)](https://github.com/tlsfuzzer/python-ecdsa/actions/workflows/codeql.yml) -[![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) -![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) - - -This is an easy-to-use implementation of ECC (Elliptic Curve Cryptography) -with support for ECDSA (Elliptic Curve Digital Signature Algorithm), -EdDSA (Edwards-curve Digital Signature Algorithm) and ECDH -(Elliptic Curve Diffie-Hellman), implemented purely in Python, released under -the MIT license. With this library, you can quickly create key pairs (signing -key and verifying key), sign messages, and verify the signatures. You can -also agree on a shared secret key based on exchanged public keys. -The keys and signatures are very short, making them easy to handle and -incorporate into other protocols. - -**NOTE: This library should not be used in production settings, see [Security](#Security) for more details.** - -## Features - -This library provides key generation, signing, verifying, and shared secret -derivation for five -popular NIST "Suite B" GF(p) (_prime field_) curves, with key lengths of 192, -224, 256, 384, and 521 bits. The "short names" for these curves, as known by -the OpenSSL tool (`openssl ecparam -list_curves`), are: `prime192v1`, -`secp224r1`, `prime256v1`, `secp384r1`, and `secp521r1`. It includes the -256-bit curve `secp256k1` used by Bitcoin. There is also support for the -regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The -"short names" of those curves are: `brainpoolP160r1`, `brainpoolP192r1`, -`brainpoolP224r1`, `brainpoolP256r1`, `brainpoolP320r1`, `brainpoolP384r1`, -`brainpoolP512r1`. Few of the small curves from SEC standard are also -included (mainly to speed-up testing of the library), those are: -`secp112r1`, `secp112r2`, `secp128r1`, and `secp160r1`. -Key generation, siging and verifying is also supported for Ed25519 and -Ed448 curves. -No other curves are included, but it is not too hard to add support for more -curves over prime fields. - -## Dependencies - -This library uses only Python and the 'six' package. It is compatible with -Python 2.6, 2.7, and 3.5+. It also supports execution on alternative -implementations like pypy and pypy3. - -If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic. -Either of them can be installed after this library is installed, -`python-ecdsa` will detect their presence on start-up and use them -automatically. -You should prefer `gmpy2` on Python3 for optimal performance. - -To run the OpenSSL compatibility tests, the 'openssl' tool must be in your -`PATH`. This release has been tested successfully against OpenSSL 0.9.8o, -1.0.0a, 1.0.2f, 1.1.1d and 3.0.1 (among others). - - -## Installation - -This library is available on PyPI, it's recommended to install it using `pip`: - -``` -pip install ecdsa -``` - -In case higher performance is wanted and using native code is not a problem, -it's possible to specify installation together with `gmpy2`: - -``` -pip install ecdsa[gmpy2] -``` - -or (slower, legacy option): -``` -pip install ecdsa[gmpy] -``` - -## Speed - -The following table shows how long this library takes to generate key pairs -(`keygen`), to sign data (`sign`), to verify those signatures (`verify`), -to derive a shared secret (`ecdh`), and -to verify the signatures with no key-specific precomputation (`no PC verify`). -All those values are in seconds. -For convenience, the inverses of those values are also provided: -how many keys per second can be generated (`keygen/s`), how many signatures -can be made per second (`sign/s`), how many signatures can be verified -per second (`verify/s`), how many shared secrets can be derived per second -(`ecdh/s`), and how many signatures with no key specific -precomputation can be verified per second (`no PC verify/s`). The size of raw -signature (generally the smallest -the way a signature can be encoded) is also provided in the `siglen` column. -Use `tox -e speed` to generate this table on your own computer. -On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance: - -``` - siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s - NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43 - NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66 - NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27 - NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00 - NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39 - SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01 - BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92 - BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29 - BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16 - BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25 - BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49 - BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20 - BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61 - SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20 - SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16 - SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16 - SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07 - Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50 - Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69 - - ecdh ecdh/s - NIST192p: 0.00104s 964.89 - NIST224p: 0.00134s 748.63 - NIST256p: 0.00170s 587.08 - NIST384p: 0.00352s 283.90 - NIST521p: 0.00717s 139.51 - SECP256k1: 0.00154s 648.40 - BRAINPOOLP160r1: 0.00082s 1220.70 - BRAINPOOLP192r1: 0.00105s 956.75 - BRAINPOOLP224r1: 0.00136s 734.52 - BRAINPOOLP256r1: 0.00178s 563.32 - BRAINPOOLP320r1: 0.00252s 397.23 - BRAINPOOLP384r1: 0.00376s 266.27 - BRAINPOOLP512r1: 0.00733s 136.35 - SECP112r1: 0.00046s 2180.40 - SECP112r2: 0.00045s 2229.14 - SECP128r1: 0.00054s 1868.15 - SECP160r1: 0.00080s 1243.98 -``` - -To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`. -On the same machine I'm getting the following performance with `gmpy2`: -``` - siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s - NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41 - NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90 - NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82 - NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43 - NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40 - SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32 - BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81 - BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19 - BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87 - BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60 - BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34 - BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85 - BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83 - SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83 - SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28 - SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88 - SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35 - Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02 - Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62 - - ecdh ecdh/s - NIST192p: 0.00050s 1985.70 - NIST224p: 0.00066s 1524.16 - NIST256p: 0.00071s 1413.07 - NIST384p: 0.00127s 788.89 - NIST521p: 0.00230s 434.85 - SECP256k1: 0.00071s 1409.95 - BRAINPOOLP160r1: 0.00042s 2374.65 - BRAINPOOLP192r1: 0.00051s 1960.01 - BRAINPOOLP224r1: 0.00066s 1518.37 - BRAINPOOLP256r1: 0.00071s 1399.90 - BRAINPOOLP320r1: 0.00100s 997.21 - BRAINPOOLP384r1: 0.00129s 777.51 - BRAINPOOLP512r1: 0.00210s 475.99 - SECP112r1: 0.00022s 4457.70 - SECP112r2: 0.00024s 4252.33 - SECP128r1: 0.00028s 3589.31 - SECP160r1: 0.00043s 2305.02 -``` - -(there's also `gmpy` version, execute it using `tox -e speedgmpy`) - -For comparison, a highly optimised implementation (including curve-specific -assembly for some curves), like the one in OpenSSL 1.1.1d, provides the -following performance numbers on the same machine. -Run `openssl speed ecdsa` and `openssl speed ecdh` to reproduce it: -``` - sign verify sign/s verify/s - 192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7 - 224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0 - 256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6 - 384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1 - 521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5 - 256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2 - 384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1 - 512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1 - - sign verify sign/s verify/s - 253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7 - 456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7 - - op op/s - 192 bits ecdh (nistp192) 0.0002s 4853.4 - 224 bits ecdh (nistp224) 0.0001s 15252.1 - 256 bits ecdh (nistp256) 0.0001s 18436.3 - 384 bits ecdh (nistp384) 0.0008s 1292.7 - 521 bits ecdh (nistp521) 0.0003s 2884.7 - 256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5 - 384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0 - 512 bits ecdh (brainpoolP512r1) 0.0014s 694.8 -``` - -Keys and signature can be serialized in different ways (see Usage, below). -For a NIST192p key, the three basic representations require strings of the -following lengths (in bytes): - - to_string: signkey= 24, verifykey= 48, signature=48 - compressed: signkey=n/a, verifykey= 25, signature=n/a - DER: signkey=106, verifykey= 80, signature=55 - PEM: signkey=278, verifykey=162, (no support for PEM signatures) - -## History - -In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a -[message to sci.crypt][1], available from his [download site][2]. In 2010, -Brian Warner wrote a wrapper around this code, to make it a bit easier and -safer to use. In 2020, Hubert Kario included an implementation of elliptic -curve cryptography that uses Jacobian coordinates internally, improving -performance about 20-fold. You are looking at the README for this wrapper. - -[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html -[2]: http://webpages.charter.net/curryfans/peter/downloads.html - -## Testing - -To run the full test suite, do this: - - tox -e coverage - -On an Intel Core i7 4790K @ 4.0GHz, the tests take about 18 seconds to execute. -The test suite uses -[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some -inherent variability in the test suite execution time. - -One part of `test_pyecdsa.py` and `test_ecdh.py` checks compatibility with -OpenSSL, by running the "openssl" CLI tool, make sure it's in your `PATH` if -you want to test compatibility with it (if OpenSSL is missing, too old, or -doesn't support all the curves supported in upstream releases you will see -skipped tests in the above `coverage` run). - -## Security - -This library was not designed with security in mind. If you are processing -data that needs to be protected we suggest you use a quality wrapper around -OpenSSL. [pyca/cryptography](https://cryptography.io) is one example of such -a wrapper. The primary use-case of this library is as a portable library for -interoperability testing and as a teaching tool. - -**This library does not protect against side-channel attacks.** - -Do not allow attackers to measure how long it takes you to generate a key pair -or sign a message. Do not allow attackers to run code on the same physical -machine when key pair generation or signing is taking place (this includes -virtual machines). Do not allow attackers to measure how much power your -computer uses while generating the key pair or signing a message. Do not allow -attackers to measure RF interference coming from your computer while generating -a key pair or signing a message. Note: just loading the private key will cause -key pair generation. Other operations or attack vectors may also be -vulnerable to attacks. **For a sophisticated attacker observing just one -operation with a private key will be sufficient to completely -reconstruct the private key**. - -Please also note that any Pure-python cryptographic library will be vulnerable -to the same side-channel attacks. This is because Python does not provide -side-channel secure primitives (with the exception of -[`hmac.compare_digest()`][3]), making side-channel secure programming -impossible. - -This library depends upon a strong source of random numbers. Do not use it on -a system where `os.urandom()` does not provide cryptographically secure -random numbers. - -[3]: https://docs.python.org/3/library/hmac.html#hmac.compare_digest - -## Usage - -You start by creating a `SigningKey`. You can use this to sign data, by passing -in data as a byte string and getting back the signature (also a byte string). -You can also ask a `SigningKey` to give you the corresponding `VerifyingKey`. -The `VerifyingKey` can be used to verify a signature, by passing it both the -data string and the signature byte string: it either returns True or raises -`BadSignatureError`. - -```python -from ecdsa import SigningKey -sk = SigningKey.generate() # uses NIST192p -vk = sk.verifying_key -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -Each `SigningKey`/`VerifyingKey` is associated with a specific curve, like -NIST192p (the default one). Longer curves are more secure, but take longer to -use, and result in longer keys and signatures. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -The `SigningKey` can be serialized into several different formats: the shortest -is to call `s=sk.to_string()`, and then re-create it with -`SigningKey.from_string(s, curve)` . This short form does not record the -curve, so you must be sure to pass to `from_string()` the same curve you used -for the original key. The short form of a NIST192p-based signing key is just 24 -bytes long. If a point encoding is invalid or it does not lie on the specified -curve, `from_string()` will raise `MalformedPointError`. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -sk_string = sk.to_string() -sk2 = SigningKey.from_string(sk_string, curve=NIST384p) -print(sk_string.hex()) -print(sk2.to_string().hex()) -``` - -Note: while the methods are called `to_string()` the type they return is -actually `bytes`, the "string" part is leftover from Python 2. - -`sk.to_pem()` and `sk.to_der()` will serialize the signing key into the same -formats that OpenSSL uses. The PEM file looks like the familiar ASCII-armored -`"-----BEGIN EC PRIVATE KEY-----"` base64-encoded format, and the DER format -is a shorter binary form of the same data. -`SigningKey.from_pem()/.from_der()` will undo this serialization. These -formats include the curve name, so you do not need to pass in a curve -identifier to the deserializer. In case the file is malformed `from_der()` -and `from_pem()` will raise `UnexpectedDER` or` MalformedPointError`. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -sk_pem = sk.to_pem() -sk2 = SigningKey.from_pem(sk_pem) -# sk and sk2 are the same key -``` - -Likewise, the `VerifyingKey` can be serialized in the same way: -`vk.to_string()/VerifyingKey.from_string()`, `to_pem()/from_pem()`, and -`to_der()/from_der()`. The same `curve=` argument is needed for -`VerifyingKey.from_string()`. - -```python -from ecdsa import SigningKey, VerifyingKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk_string = vk.to_string() -vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p) -# vk and vk2 are the same key - -from ecdsa import SigningKey, VerifyingKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk_pem = vk.to_pem() -vk2 = VerifyingKey.from_pem(vk_pem) -# vk and vk2 are the same key -``` - -There are a couple of different ways to compute a signature. Fundamentally, -ECDSA takes a number that represents the data being signed, and returns a -pair of numbers that represent the signature. The `hashfunc=` argument to -`sk.sign()` and `vk.verify()` is used to turn an arbitrary string into a -fixed-length digest, which is then turned into a number that ECDSA can sign, -and both sign and verify must use the same approach. The default value is -`hashlib.sha1`, but if you use NIST256p or a longer curve, you can use -`hashlib.sha256` instead. - -There are also multiple ways to represent a signature. The default -`sk.sign()` and `vk.verify()` methods present it as a short string, for -simplicity and minimal overhead. To use a different scheme, use the -`sk.sign(sigencode=)` and `vk.verify(sigdecode=)` arguments. There are helper -functions in the `ecdsa.util` module that can be useful here. - -It is also possible to create a `SigningKey` from a "seed", which is -deterministic. This can be used in protocols where you want to derive -consistent signing keys from some other secret, for example when you want -three separate keys and only want to store a single master secret. You should -start with a uniformly-distributed unguessable seed with about `curve.baselen` -bytes of entropy, and then use one of the helper functions in `ecdsa.util` to -convert it into an integer in the correct range, and then finally pass it -into `SigningKey.from_secret_exponent()`, like this: - -```python -import os -from ecdsa import NIST384p, SigningKey -from ecdsa.util import randrange_from_seed__trytryagain - -def make_key(seed): - secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) - return SigningKey.from_secret_exponent(secexp, curve=NIST384p) - -seed = os.urandom(NIST384p.baselen) # or other starting point -sk1a = make_key(seed) -sk1b = make_key(seed) -# note: sk1a and sk1b are the same key -assert sk1a.to_string() == sk1b.to_string() -sk2 = make_key(b"2-"+seed) # different key -assert sk1a.to_string() != sk2.to_string() -``` - -In case the application will verify a lot of signatures made with a single -key, it's possible to precompute some of the internal values to make -signature verification significantly faster. The break-even point occurs at -about 100 signatures verified. - -To perform precomputation, you can call the `precompute()` method -on `VerifyingKey` instance: -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk.precompute() -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -Once `precompute()` was called, all signature verifications with this key will -be faster to execute. - -## OpenSSL Compatibility - -To produce signatures that can be verified by OpenSSL tools, or to verify -signatures that were produced by those tools, use: - -```python -# openssl ecparam -name prime256v1 -genkey -out sk.pem -# openssl ec -in sk.pem -pubout -out vk.pem -# echo "data for signing" > data -# openssl dgst -sha256 -sign sk.pem -out data.sig data -# openssl dgst -sha256 -verify vk.pem -signature data.sig data -# openssl dgst -sha256 -prverify sk.pem -signature data.sig data - -import hashlib -from ecdsa import SigningKey, VerifyingKey -from ecdsa.util import sigencode_der, sigdecode_der - -with open("vk.pem") as f: - vk = VerifyingKey.from_pem(f.read()) - -with open("data", "rb") as f: - data = f.read() - -with open("data.sig", "rb") as f: - signature = f.read() - -assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der) - -with open("sk.pem") as f: - sk = SigningKey.from_pem(f.read(), hashlib.sha256) - -new_signature = sk.sign_deterministic(data, sigencode=sigencode_der) - -with open("data.sig2", "wb") as f: - f.write(new_signature) - -# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data -``` - -Note: if compatibility with OpenSSL 1.0.0 or earlier is necessary, the -`sigencode_string` and `sigdecode_string` from `ecdsa.util` can be used for -respectively writing and reading the signatures. - -The keys also can be written in format that openssl can handle: - -```python -from ecdsa import SigningKey, VerifyingKey - -with open("sk.pem") as f: - sk = SigningKey.from_pem(f.read()) -with open("sk.pem", "wb") as f: - f.write(sk.to_pem()) - -with open("vk.pem") as f: - vk = VerifyingKey.from_pem(f.read()) -with open("vk.pem", "wb") as f: - f.write(vk.to_pem()) -``` - -## Entropy - -Creating a signing key with `SigningKey.generate()` requires some form of -entropy (as opposed to -`from_secret_exponent`/`from_string`/`from_der`/`from_pem`, -which are deterministic and do not require an entropy source). The default -source is `os.urandom()`, but you can pass any other function that behaves -like `os.urandom` as the `entropy=` argument to do something different. This -may be useful in unit tests, where you want to achieve repeatable results. The -`ecdsa.util.PRNG` utility is handy here: it takes a seed and produces a strong -pseudo-random stream from it: - -```python -from ecdsa.util import PRNG -from ecdsa import SigningKey -rng1 = PRNG(b"seed") -sk1 = SigningKey.generate(entropy=rng1) -rng2 = PRNG(b"seed") -sk2 = SigningKey.generate(entropy=rng2) -# sk1 and sk2 are the same key -``` - -Likewise, ECDSA signature generation requires a random number, and each -signature must use a different one (using the same number twice will -immediately reveal the private signing key). The `sk.sign()` method takes an -`entropy=` argument which behaves the same as `SigningKey.generate(entropy=)`. - -## Deterministic Signatures - -If you call `SigningKey.sign_deterministic(data)` instead of `.sign(data)`, -the code will generate a deterministic signature instead of a random one. -This uses the algorithm from RFC6979 to safely generate a unique `k` value, -derived from the private key and the message being signed. Each time you sign -the same message with the same key, you will get the same signature (using -the same `k`). - -This may become the default in a future version, as it is not vulnerable to -failures of the entropy source. - -## Examples - -Create a NIST192p key pair and immediately save both to disk: - -```python -from ecdsa import SigningKey -sk = SigningKey.generate() -vk = sk.verifying_key -with open("private.pem", "wb") as f: - f.write(sk.to_pem()) -with open("public.pem", "wb") as f: - f.write(vk.to_pem()) -``` - -Load a signing key from disk, use it to sign a message (using SHA-1), and write -the signature to disk: - -```python -from ecdsa import SigningKey -with open("private.pem") as f: - sk = SigningKey.from_pem(f.read()) -with open("message", "rb") as f: - message = f.read() -sig = sk.sign(message) -with open("signature", "wb") as f: - f.write(sig) -``` - -Load the verifying key, message, and signature from disk, and verify the -signature (assume SHA-1 hash): - -```python -from ecdsa import VerifyingKey, BadSignatureError -vk = VerifyingKey.from_pem(open("public.pem").read()) -with open("message", "rb") as f: - message = f.read() -with open("signature", "rb") as f: - sig = f.read() -try: - vk.verify(sig, message) - print "good signature" -except BadSignatureError: - print "BAD SIGNATURE" -``` - -Create a NIST521p key pair: - -```python -from ecdsa import SigningKey, NIST521p -sk = SigningKey.generate(curve=NIST521p) -vk = sk.verifying_key -``` - -Create three independent signing keys from a master seed: - -```python -from ecdsa import NIST192p, SigningKey -from ecdsa.util import randrange_from_seed__trytryagain - -def make_key_from_seed(seed, curve=NIST192p): - secexp = randrange_from_seed__trytryagain(seed, curve.order) - return SigningKey.from_secret_exponent(secexp, curve) - -sk1 = make_key_from_seed("1:%s" % seed) -sk2 = make_key_from_seed("2:%s" % seed) -sk3 = make_key_from_seed("3:%s" % seed) -``` - -Load a verifying key from disk and print it using hex encoding in -uncompressed and compressed format (defined in X9.62 and SEC1 standards): - -```python -from ecdsa import VerifyingKey - -with open("public.pem") as f: - vk = VerifyingKey.from_pem(f.read()) - -print("uncompressed: {0}".format(vk.to_string("uncompressed").hex())) -print("compressed: {0}".format(vk.to_string("compressed").hex())) -``` - -Load a verifying key from a hex string from compressed format, output -uncompressed: - -```python -from ecdsa import VerifyingKey, NIST256p - -comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' -vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) -print(vk.to_string("uncompressed").hex()) -``` - -ECDH key exchange with remote party: - -```python -from ecdsa import ECDH, NIST256p - -ecdh = ECDH(curve=NIST256p) -ecdh.generate_private_key() -local_public_key = ecdh.get_public_key() -#send `local_public_key` to remote party and receive `remote_public_key` from remote party -with open("remote_public_key.pem") as e: - remote_public_key = e.read() -ecdh.load_received_public_key_pem(remote_public_key) -secret = ecdh.generate_sharedsecret_bytes() -``` diff --git a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/RECORD b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/RECORD deleted file mode 100644 index e50dd89..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/RECORD +++ /dev/null @@ -1,66 +0,0 @@ -ecdsa-0.19.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -ecdsa-0.19.0.dist-info/LICENSE,sha256=PsqYRXc9LluMydjBGdNF8ApIBuS9Zg1KPWzfnA6di7I,1147 -ecdsa-0.19.0.dist-info/METADATA,sha256=1lr-f8_T7fP7a4DaW2fvXolaKE-C3LnqTjHqdrU1wcE,29679 -ecdsa-0.19.0.dist-info/RECORD,, -ecdsa-0.19.0.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110 -ecdsa-0.19.0.dist-info/top_level.txt,sha256=7ovPHfAPyTou19f8gOSbHm6B9dGjTibWolcCB7Zjovs,6 -ecdsa/__init__.py,sha256=wRUnU3g01MM-mLNemnov-_8B81DlbSrqoFtOO4bZzQQ,1931 -ecdsa/__pycache__/__init__.cpython-311.pyc,, -ecdsa/__pycache__/_compat.cpython-311.pyc,, -ecdsa/__pycache__/_rwlock.cpython-311.pyc,, -ecdsa/__pycache__/_sha3.cpython-311.pyc,, -ecdsa/__pycache__/_version.cpython-311.pyc,, -ecdsa/__pycache__/curves.cpython-311.pyc,, -ecdsa/__pycache__/der.cpython-311.pyc,, -ecdsa/__pycache__/ecdh.cpython-311.pyc,, -ecdsa/__pycache__/ecdsa.cpython-311.pyc,, -ecdsa/__pycache__/eddsa.cpython-311.pyc,, -ecdsa/__pycache__/ellipticcurve.cpython-311.pyc,, -ecdsa/__pycache__/errors.cpython-311.pyc,, -ecdsa/__pycache__/keys.cpython-311.pyc,, -ecdsa/__pycache__/numbertheory.cpython-311.pyc,, -ecdsa/__pycache__/rfc6979.cpython-311.pyc,, -ecdsa/__pycache__/ssh.cpython-311.pyc,, -ecdsa/__pycache__/test_curves.cpython-311.pyc,, -ecdsa/__pycache__/test_der.cpython-311.pyc,, -ecdsa/__pycache__/test_ecdh.cpython-311.pyc,, -ecdsa/__pycache__/test_ecdsa.cpython-311.pyc,, -ecdsa/__pycache__/test_eddsa.cpython-311.pyc,, -ecdsa/__pycache__/test_ellipticcurve.cpython-311.pyc,, -ecdsa/__pycache__/test_jacobi.cpython-311.pyc,, -ecdsa/__pycache__/test_keys.cpython-311.pyc,, -ecdsa/__pycache__/test_malformed_sigs.cpython-311.pyc,, -ecdsa/__pycache__/test_numbertheory.cpython-311.pyc,, -ecdsa/__pycache__/test_pyecdsa.cpython-311.pyc,, -ecdsa/__pycache__/test_rw_lock.cpython-311.pyc,, -ecdsa/__pycache__/test_sha3.cpython-311.pyc,, -ecdsa/__pycache__/util.cpython-311.pyc,, -ecdsa/_compat.py,sha256=5EP735DlmaSb8Slk4BMnh9Z7MvYpTnGS3S_DBmHETwo,4047 -ecdsa/_rwlock.py,sha256=CAwHp2V65ksI8B1UqY7EccK9LaUToiv6pDLVzm44eag,2849 -ecdsa/_sha3.py,sha256=DJs7QLmdkQMU35llyD8HQeAXNvf5sMcujO6oFdScIqI,4747 -ecdsa/_version.py,sha256=rIOw4wECMIfW9NgIJj9cE4QbZiELgE2VQtUGkBNqiu8,498 -ecdsa/curves.py,sha256=EcPE0WRFkjpPMZfVM0-hRQ63CdW5GKTkDRpK-VOy1Zo,15975 -ecdsa/der.py,sha256=y7cSfxxydbgUV6HPIwsEgHuMF5yo21g81T5uassyhvk,14104 -ecdsa/ecdh.py,sha256=Tiirawt5xegVDrY9eS-ATvvfmTIznUyv5fy2k7VnzTk,11011 -ecdsa/ecdsa.py,sha256=e-tlePLMDdt8O5VACr640guulJns4fHfHvds8FDGZtw,31703 -ecdsa/eddsa.py,sha256=IzsGzoGAefcoYjF7DVjFkX5ZJqiK2LTOmAMe6wyf4UU,7170 -ecdsa/ellipticcurve.py,sha256=2mw8LnuRQ6j0oS9Ck3KMSMXozBq740UnHyHBGxriESM,54118 -ecdsa/errors.py,sha256=b4mhnmIpRnEdHzbectHAA5F7O9MtSaI-fYoc13_vBxQ,130 -ecdsa/keys.py,sha256=FkpubL9MGCfkCJ6aVvV5c0bve4ZuBtU3P1dRRBTth0k,65503 -ecdsa/numbertheory.py,sha256=Ad2-mVFaOytMomBC-7d0cJao4tpkVLh4x7vyqj73G6A,17831 -ecdsa/rfc6979.py,sha256=zwzo33lsZJA9r2dSf7HCliI_yIbw5cJ0Ek9tLdRRO40,2850 -ecdsa/ssh.py,sha256=360JY0dbYeZaqx9k_OhlHzPZYZTw5UB5xtK2XLBqb0M,1916 -ecdsa/test_curves.py,sha256=l5N-m4Yo5IAy4a8aJMsBamaSlLAfSoYjYqCj1HDEVpU,13081 -ecdsa/test_der.py,sha256=SKANQM2JuUTxsOkwjB5GRzz8NbMGx9xSEPBWL4oewWY,14933 -ecdsa/test_ecdh.py,sha256=20TEYyGcnynAPS9nlr_k2ed6S7lJ1Z0bbohNU-pEGco,15380 -ecdsa/test_ecdsa.py,sha256=V5Q4Q7uUFfStVdeFtwhXVpFlFi0Nx5J5uru9T3j_3DQ,25037 -ecdsa/test_eddsa.py,sha256=1jfHF_ZSyillv6DLKJZ0SeYThauTCHsyRNAweS_4MEQ,33720 -ecdsa/test_ellipticcurve.py,sha256=6Su-6PWhL82kMGGuH4NIlayAWdSqQFGLqPu94vkJkW4,7637 -ecdsa/test_jacobi.py,sha256=2n30l0nD24RjtgO60MlIWC07-12jBoSgelxPe6OaMdQ,20662 -ecdsa/test_keys.py,sha256=reQ2KtfOsANa59CqvvRWjZm-SaWfscJZVJaE9RFyL94,39415 -ecdsa/test_malformed_sigs.py,sha256=Dg1Dkvgz1tO-KhZ6f9ZRljykz8FBYtld3hq1W2hLLM8,11289 -ecdsa/test_numbertheory.py,sha256=YTtclt_50n_24U__r2JyV3LEqR2BsBmA6IJghwBDz8c,13265 -ecdsa/test_pyecdsa.py,sha256=uu8MaqeV1AYSdK7FsX4D88uSvTKRr7yeoNqVb8ZYEhM,89939 -ecdsa/test_rw_lock.py,sha256=byv0_FTM90cbuHPCI6__LeQJkHL_zYEeVYIBO8e2LLc,7021 -ecdsa/test_sha3.py,sha256=0PkWi7AnTJ10YNfDVqj2SB7adeTbV6DCvMg4n9ULad8,3042 -ecdsa/util.py,sha256=P3lum42zhB2l_4gU_iwRWvdw9DAbnDze-Oge4is1ghI,17646 diff --git a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/WHEEL b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/WHEEL deleted file mode 100644 index 9d8f872..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/top_level.txt b/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/top_level.txt deleted file mode 100644 index aa5efdb..0000000 --- a/.venv/Lib/site-packages/ecdsa-0.19.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -ecdsa diff --git a/.venv/Lib/site-packages/ecdsa/__init__.py b/.venv/Lib/site-packages/ecdsa/__init__.py deleted file mode 100644 index 342538e..0000000 --- a/.venv/Lib/site-packages/ecdsa/__init__.py +++ /dev/null @@ -1,104 +0,0 @@ -# while we don't use six in this file, we did bundle it for a long time, so -# keep as part of module in a virtual way (through __all__) -import six -from .keys import ( - SigningKey, - VerifyingKey, - BadSignatureError, - BadDigestError, - MalformedPointError, -) -from .curves import ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, - BRAINPOOLP160t1, - BRAINPOOLP192t1, - BRAINPOOLP224t1, - BRAINPOOLP256t1, - BRAINPOOLP320t1, - BRAINPOOLP384t1, - BRAINPOOLP512t1, -) -from .ecdh import ( - ECDH, - NoKeyError, - NoCurveError, - InvalidCurveError, - InvalidSharedSecretError, -) -from .der import UnexpectedDER -from . import _version - -# This code comes from http://github.com/tlsfuzzer/python-ecdsa -__all__ = [ - "curves", - "der", - "ecdsa", - "ellipticcurve", - "keys", - "numbertheory", - "test_pyecdsa", - "util", - "six", -] - -_hush_pyflakes = [ - SigningKey, - VerifyingKey, - BadSignatureError, - BadDigestError, - MalformedPointError, - UnexpectedDER, - InvalidCurveError, - NoKeyError, - InvalidSharedSecretError, - ECDH, - NoCurveError, - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, - six.b(""), - BRAINPOOLP160t1, - BRAINPOOLP192t1, - BRAINPOOLP224t1, - BRAINPOOLP256t1, - BRAINPOOLP320t1, - BRAINPOOLP384t1, - BRAINPOOLP512t1, -] -del _hush_pyflakes - -__version__ = _version.get_versions()["version"] diff --git a/.venv/Lib/site-packages/ecdsa/_compat.py b/.venv/Lib/site-packages/ecdsa/_compat.py deleted file mode 100644 index 4558e33..0000000 --- a/.venv/Lib/site-packages/ecdsa/_compat.py +++ /dev/null @@ -1,138 +0,0 @@ -""" -Common functions for providing cross-python version compatibility. -""" -import sys -import re -import binascii -from six import integer_types - - -def str_idx_as_int(string, index): - """Take index'th byte from string, return as integer""" - val = string[index] - if isinstance(val, integer_types): - return val - return ord(val) - - -if sys.version_info < (3, 0): # pragma: no branch - import platform - - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - # flake8 runs on py3 where `buffer` indeed doesn't exist... - return buffer(buffer_object) # noqa: F821 - - def hmac_compat(ret): - return ret - - if ( - sys.version_info < (2, 7) - or sys.version_info < (2, 7, 4) - or platform.system() == "Java" - ): # pragma: no branch - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text) - - def compat26_str(val): - return str(val) - - def bit_length(val): - if val == 0: - return 0 - return len(bin(val)) - 2 - - else: - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text, flags=re.UNICODE) - - def compat26_str(val): - return val - - def bit_length(val): - """Return number of bits necessary to represent an integer.""" - return val.bit_length() - - def b2a_hex(val): - return binascii.b2a_hex(compat26_str(val)) - - def a2b_hex(val): - try: - return bytearray(binascii.a2b_hex(val)) - except Exception as e: - raise ValueError("base16 error: %s" % e) - - def bytes_to_int(val, byteorder): - """Convert bytes to an int.""" - if not val: - return 0 - if byteorder == "big": - return int(b2a_hex(val), 16) - if byteorder == "little": - return int(b2a_hex(val[::-1]), 16) - raise ValueError("Only 'big' and 'little' endian supported") - - def int_to_bytes(val, length=None, byteorder="big"): - """Return number converted to bytes""" - if length is None: - length = byte_length(val) - if byteorder == "big": - return bytearray( - (val >> i) & 0xFF for i in reversed(range(0, length * 8, 8)) - ) - if byteorder == "little": - return bytearray( - (val >> i) & 0xFF for i in range(0, length * 8, 8) - ) - raise ValueError("Only 'big' or 'little' endian supported") - -else: - - def hmac_compat(data): - return data - - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - return memoryview(buffer_object).cast("B") - - def compat26_str(val): - return val - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text, flags=re.UNICODE) - - def a2b_hex(val): - try: - return bytearray(binascii.a2b_hex(bytearray(val, "ascii"))) - except Exception as e: - raise ValueError("base16 error: %s" % e) - - # pylint: disable=invalid-name - # pylint is stupid here and doesn't notice it's a function, not - # constant - bytes_to_int = int.from_bytes - # pylint: enable=invalid-name - - def bit_length(val): - """Return number of bits necessary to represent an integer.""" - return val.bit_length() - - def int_to_bytes(val, length=None, byteorder="big"): - """Convert integer to bytes.""" - if length is None: - length = byte_length(val) - # for gmpy we need to convert back to native int - if not isinstance(val, int): - val = int(val) - return bytearray(val.to_bytes(length=length, byteorder=byteorder)) - - -def byte_length(val): - """Return number of bytes necessary to represent an integer.""" - length = bit_length(val) - return (length + 7) // 8 diff --git a/.venv/Lib/site-packages/ecdsa/_rwlock.py b/.venv/Lib/site-packages/ecdsa/_rwlock.py deleted file mode 100644 index 010e498..0000000 --- a/.venv/Lib/site-packages/ecdsa/_rwlock.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright Mateusz Kobos, (c) 2011 -# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ -# released under the MIT licence - -import threading - - -__author__ = "Mateusz Kobos" - - -class RWLock: - """ - Read-Write locking primitive - - Synchronization object used in a solution of so-called second - readers-writers problem. In this problem, many readers can simultaneously - access a share, and a writer has an exclusive access to this share. - Additionally, the following constraints should be met: - 1) no reader should be kept waiting if the share is currently opened for - reading unless a writer is also waiting for the share, - 2) no writer should be kept waiting for the share longer than absolutely - necessary. - - The implementation is based on [1, secs. 4.2.2, 4.2.6, 4.2.7] - with a modification -- adding an additional lock (C{self.__readers_queue}) - -- in accordance with [2]. - - Sources: - [1] A.B. Downey: "The little book of semaphores", Version 2.1.5, 2008 - [2] P.J. Courtois, F. Heymans, D.L. Parnas: - "Concurrent Control with 'Readers' and 'Writers'", - Communications of the ACM, 1971 (via [3]) - [3] http://en.wikipedia.org/wiki/Readers-writers_problem - """ - - def __init__(self): - """ - A lock giving an even higher priority to the writer in certain - cases (see [2] for a discussion). - """ - self.__read_switch = _LightSwitch() - self.__write_switch = _LightSwitch() - self.__no_readers = threading.Lock() - self.__no_writers = threading.Lock() - self.__readers_queue = threading.Lock() - - def reader_acquire(self): - self.__readers_queue.acquire() - self.__no_readers.acquire() - self.__read_switch.acquire(self.__no_writers) - self.__no_readers.release() - self.__readers_queue.release() - - def reader_release(self): - self.__read_switch.release(self.__no_writers) - - def writer_acquire(self): - self.__write_switch.acquire(self.__no_readers) - self.__no_writers.acquire() - - def writer_release(self): - self.__no_writers.release() - self.__write_switch.release(self.__no_readers) - - -class _LightSwitch: - """An auxiliary "light switch"-like object. The first thread turns on the - "switch", the last one turns it off (see [1, sec. 4.2.2] for details).""" - - def __init__(self): - self.__counter = 0 - self.__mutex = threading.Lock() - - def acquire(self, lock): - self.__mutex.acquire() - self.__counter += 1 - if self.__counter == 1: - lock.acquire() - self.__mutex.release() - - def release(self, lock): - self.__mutex.acquire() - self.__counter -= 1 - if self.__counter == 0: - lock.release() - self.__mutex.release() diff --git a/.venv/Lib/site-packages/ecdsa/_sha3.py b/.venv/Lib/site-packages/ecdsa/_sha3.py deleted file mode 100644 index 2db0058..0000000 --- a/.venv/Lib/site-packages/ecdsa/_sha3.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Implementation of the SHAKE-256 algorithm for Ed448 -""" - -try: - import hashlib - - hashlib.new("shake256").digest(64) - - def shake_256(msg, outlen): - return hashlib.new("shake256", msg).digest(outlen) - -except (TypeError, ValueError): - - from ._compat import bytes_to_int, int_to_bytes - - # From little endian. - def _from_le(s): - return bytes_to_int(s, byteorder="little") - - # Rotate a word x by b places to the left. - def _rol(x, b): - return ((x << b) | (x >> (64 - b))) & (2**64 - 1) - - # Do the SHA-3 state transform on state s. - def _sha3_transform(s): - ROTATIONS = [ - 0, - 1, - 62, - 28, - 27, - 36, - 44, - 6, - 55, - 20, - 3, - 10, - 43, - 25, - 39, - 41, - 45, - 15, - 21, - 8, - 18, - 2, - 61, - 56, - 14, - ] - PERMUTATION = [ - 1, - 6, - 9, - 22, - 14, - 20, - 2, - 12, - 13, - 19, - 23, - 15, - 4, - 24, - 21, - 8, - 16, - 5, - 3, - 18, - 17, - 11, - 7, - 10, - ] - RC = [ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, - ] - - for rnd in range(0, 24): - # AddColumnParity (Theta) - c = [0] * 5 - d = [0] * 5 - for i in range(0, 25): - c[i % 5] ^= s[i] - for i in range(0, 5): - d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) - for i in range(0, 25): - s[i] ^= d[i % 5] - # RotateWords (Rho) - for i in range(0, 25): - s[i] = _rol(s[i], ROTATIONS[i]) - # PermuteWords (Pi) - t = s[PERMUTATION[0]] - for i in range(0, len(PERMUTATION) - 1): - s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] - s[PERMUTATION[-1]] = t - # NonlinearMixRows (Chi) - for i in range(0, 25, 5): - t = [ - s[i], - s[i + 1], - s[i + 2], - s[i + 3], - s[i + 4], - s[i], - s[i + 1], - ] - for j in range(0, 5): - s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) - # AddRoundConstant (Iota) - s[0] ^= RC[rnd] - - # Reinterpret octet array b to word array and XOR it to state s. - def _reinterpret_to_words_and_xor(s, b): - for j in range(0, len(b) // 8): - s[j] ^= _from_le(b[8 * j : 8 * j + 8]) - - # Reinterpret word array w to octet array and return it. - def _reinterpret_to_octets(w): - mp = bytearray() - for j in range(0, len(w)): - mp += int_to_bytes(w[j], 8, byteorder="little") - return mp - - def _sha3_raw(msg, r_w, o_p, e_b): - """Semi-generic SHA-3 implementation""" - r_b = 8 * r_w - s = [0] * 25 - # Handle whole blocks. - idx = 0 - blocks = len(msg) // r_b - for i in range(0, blocks): - _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) - idx += r_b - _sha3_transform(s) - # Handle last block padding. - m = bytearray(msg[idx:]) - m.append(o_p) - while len(m) < r_b: - m.append(0) - m[len(m) - 1] |= 128 - # Handle padded last block. - _reinterpret_to_words_and_xor(s, m) - _sha3_transform(s) - # Output. - out = bytearray() - while len(out) < e_b: - out += _reinterpret_to_octets(s[:r_w]) - _sha3_transform(s) - return out[:e_b] - - def shake_256(msg, outlen): - return _sha3_raw(msg, 17, 31, outlen) diff --git a/.venv/Lib/site-packages/ecdsa/_version.py b/.venv/Lib/site-packages/ecdsa/_version.py deleted file mode 100644 index cf329eb..0000000 --- a/.venv/Lib/site-packages/ecdsa/_version.py +++ /dev/null @@ -1,21 +0,0 @@ - -# This file was generated by 'versioneer.py' (0.21) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -{ - "date": "2024-04-08T20:59:55+0200", - "dirty": false, - "error": null, - "full-revisionid": "be70016f8911f79e891a65dcfcb602e5ba866ed3", - "version": "0.19.0" -} -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) diff --git a/.venv/Lib/site-packages/ecdsa/curves.py b/.venv/Lib/site-packages/ecdsa/curves.py deleted file mode 100644 index 38e3a75..0000000 --- a/.venv/Lib/site-packages/ecdsa/curves.py +++ /dev/null @@ -1,590 +0,0 @@ -from __future__ import division - -from six import PY2 -from . import der, ecdsa, ellipticcurve, eddsa -from .util import orderlen, number_to_string, string_to_number -from ._compat import normalise_bytes, bit_length - - -# orderlen was defined in this module previously, so keep it in __all__, -# will need to mark it as deprecated later -__all__ = [ - "UnknownCurveError", - "orderlen", - "Curve", - "SECP112r1", - "SECP112r2", - "SECP128r1", - "SECP160r1", - "NIST192p", - "NIST224p", - "NIST256p", - "NIST384p", - "NIST521p", - "curves", - "find_curve", - "curve_by_name", - "SECP256k1", - "BRAINPOOLP160r1", - "BRAINPOOLP160t1", - "BRAINPOOLP192r1", - "BRAINPOOLP192t1", - "BRAINPOOLP224r1", - "BRAINPOOLP224t1", - "BRAINPOOLP256r1", - "BRAINPOOLP256t1", - "BRAINPOOLP320r1", - "BRAINPOOLP320t1", - "BRAINPOOLP384r1", - "BRAINPOOLP384t1", - "BRAINPOOLP512r1", - "BRAINPOOLP512t1", - "PRIME_FIELD_OID", - "CHARACTERISTIC_TWO_FIELD_OID", - "Ed25519", - "Ed448", -] - - -PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) -CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) - - -class UnknownCurveError(Exception): - pass - - -class Curve: - def __init__(self, name, curve, generator, oid, openssl_name=None): - self.name = name - self.openssl_name = openssl_name # maybe None - self.curve = curve - self.generator = generator - self.order = generator.order() - if isinstance(curve, ellipticcurve.CurveEdTw): - # EdDSA keys are special in that both private and public - # are the same size (as it's defined only with compressed points) - - # +1 for the sign bit and then round up - self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 - self.verifying_key_length = self.baselen - else: - self.baselen = orderlen(self.order) - self.verifying_key_length = 2 * orderlen(curve.p()) - self.signature_length = 2 * self.baselen - self.oid = oid - if oid: - self.encoded_oid = der.encode_oid(*oid) - - def __eq__(self, other): - if isinstance(other, Curve): - return ( - self.curve == other.curve and self.generator == other.generator - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - def __repr__(self): - return self.name - - def to_der(self, encoding=None, point_encoding="uncompressed"): - """Serialise the curve parameters to binary string. - - :param str encoding: the format to save the curve parameters in. - Default is ``named_curve``, with fallback being the ``explicit`` - if the OID is not set for the curve. - :param str point_encoding: the point encoding of the generator when - explicit curve encoding is used. Ignored for ``named_curve`` - format. - - :return: DER encoded ECParameters structure - :rtype: bytes - """ - if encoding is None: - if self.oid: - encoding = "named_curve" - else: - encoding = "explicit" - - if encoding not in ("named_curve", "explicit"): - raise ValueError( - "Only 'named_curve' and 'explicit' encodings supported" - ) - - if encoding == "named_curve": - if not self.oid: - raise UnknownCurveError( - "Can't encode curve using named_curve encoding without " - "associated curve OID" - ) - return der.encode_oid(*self.oid) - elif isinstance(self.curve, ellipticcurve.CurveEdTw): - assert encoding == "explicit" - raise UnknownCurveError( - "Twisted Edwards curves don't support explicit encoding" - ) - - # encode the ECParameters sequence - curve_p = self.curve.p() - version = der.encode_integer(1) - field_id = der.encode_sequence( - der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) - ) - curve = der.encode_sequence( - der.encode_octet_string( - number_to_string(self.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(self.curve.b() % curve_p, curve_p) - ), - ) - base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) - order = der.encode_integer(self.generator.order()) - seq_elements = [version, field_id, curve, base, order] - if self.curve.cofactor(): - cofactor = der.encode_integer(self.curve.cofactor()) - seq_elements.append(cofactor) - - return der.encode_sequence(*seq_elements) - - def to_pem(self, encoding=None, point_encoding="uncompressed"): - """ - Serialise the curve parameters to the :term:`PEM` format. - - :param str encoding: the format to save the curve parameters in. - Default is ``named_curve``, with fallback being the ``explicit`` - if the OID is not set for the curve. - :param str point_encoding: the point encoding of the generator when - explicit curve encoding is used. Ignored for ``named_curve`` - format. - - :return: PEM encoded ECParameters structure - :rtype: str - """ - return der.topem( - self.to_der(encoding, point_encoding), "EC PARAMETERS" - ) - - @staticmethod - def from_der(data, valid_encodings=None): - """Decode the curve parameters from DER file. - - :param data: the binary string to decode the parameters from - :type data: :term:`bytes-like object` - :param valid_encodings: set of names of allowed encodings, by default - all (set by passing ``None``), supported ones are ``named_curve`` - and ``explicit`` - :type valid_encodings: :term:`set-like object` - """ - if not valid_encodings: - valid_encodings = set(("named_curve", "explicit")) - if not all(i in ["named_curve", "explicit"] for i in valid_encodings): - raise ValueError( - "Only named_curve and explicit encodings supported" - ) - data = normalise_bytes(data) - if not der.is_sequence(data): - if "named_curve" not in valid_encodings: - raise der.UnexpectedDER( - "named_curve curve parameters not allowed" - ) - oid, empty = der.remove_object(data) - if empty: - raise der.UnexpectedDER("Unexpected data after OID") - return find_curve(oid) - - if "explicit" not in valid_encodings: - raise der.UnexpectedDER("explicit curve parameters not allowed") - - seq, empty = der.remove_sequence(data) - if empty: - raise der.UnexpectedDER( - "Unexpected data after ECParameters structure" - ) - # decode the ECParameters sequence - version, rest = der.remove_integer(seq) - if version != 1: - raise der.UnexpectedDER("Unknown parameter encoding format") - field_id, rest = der.remove_sequence(rest) - curve, rest = der.remove_sequence(rest) - base_bytes, rest = der.remove_octet_string(rest) - order, rest = der.remove_integer(rest) - cofactor = None - if rest: - # the ASN.1 specification of ECParameters allows for future - # extensions of the sequence, so ignore the remaining bytes - cofactor, _ = der.remove_integer(rest) - - # decode the ECParameters.fieldID sequence - field_type, rest = der.remove_object(field_id) - if field_type == CHARACTERISTIC_TWO_FIELD_OID: - raise UnknownCurveError("Characteristic 2 curves unsupported") - if field_type != PRIME_FIELD_OID: - raise UnknownCurveError( - "Unknown field type: {0}".format(field_type) - ) - prime, empty = der.remove_integer(rest) - if empty: - raise der.UnexpectedDER( - "Unexpected data after ECParameters.fieldID.Prime-p element" - ) - - # decode the ECParameters.curve sequence - curve_a_bytes, rest = der.remove_octet_string(curve) - curve_b_bytes, rest = der.remove_octet_string(rest) - # seed can be defined here, but we don't parse it, so ignore `rest` - - curve_a = string_to_number(curve_a_bytes) - curve_b = string_to_number(curve_b_bytes) - - curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) - - # decode the ECParameters.base point - - base = ellipticcurve.PointJacobi.from_bytes( - curve_fp, - base_bytes, - valid_encodings=("uncompressed", "compressed", "hybrid"), - order=order, - generator=True, - ) - tmp_curve = Curve("unknown", curve_fp, base, None) - - # if the curve matches one of the well-known ones, use the well-known - # one in preference, as it will have the OID and name associated - for i in curves: - if tmp_curve == i: - return i - return tmp_curve - - @classmethod - def from_pem(cls, string, valid_encodings=None): - """Decode the curve parameters from PEM file. - - :param str string: the text string to decode the parameters from - :param valid_encodings: set of names of allowed encodings, by default - all (set by passing ``None``), supported ones are ``named_curve`` - and ``explicit`` - :type valid_encodings: :term:`set-like object` - """ - if not PY2 and isinstance(string, str): # pragma: no branch - string = string.encode() - - ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") - if ec_param_index == -1: - raise der.UnexpectedDER("EC PARAMETERS PEM header not found") - - return cls.from_der( - der.unpem(string[ec_param_index:]), valid_encodings - ) - - -# the SEC curves -SECP112r1 = Curve( - "SECP112r1", - ecdsa.curve_112r1, - ecdsa.generator_112r1, - (1, 3, 132, 0, 6), - "secp112r1", -) - - -SECP112r2 = Curve( - "SECP112r2", - ecdsa.curve_112r2, - ecdsa.generator_112r2, - (1, 3, 132, 0, 7), - "secp112r2", -) - - -SECP128r1 = Curve( - "SECP128r1", - ecdsa.curve_128r1, - ecdsa.generator_128r1, - (1, 3, 132, 0, 28), - "secp128r1", -) - - -SECP160r1 = Curve( - "SECP160r1", - ecdsa.curve_160r1, - ecdsa.generator_160r1, - (1, 3, 132, 0, 8), - "secp160r1", -) - - -# the NIST curves -NIST192p = Curve( - "NIST192p", - ecdsa.curve_192, - ecdsa.generator_192, - (1, 2, 840, 10045, 3, 1, 1), - "prime192v1", -) - - -NIST224p = Curve( - "NIST224p", - ecdsa.curve_224, - ecdsa.generator_224, - (1, 3, 132, 0, 33), - "secp224r1", -) - - -NIST256p = Curve( - "NIST256p", - ecdsa.curve_256, - ecdsa.generator_256, - (1, 2, 840, 10045, 3, 1, 7), - "prime256v1", -) - - -NIST384p = Curve( - "NIST384p", - ecdsa.curve_384, - ecdsa.generator_384, - (1, 3, 132, 0, 34), - "secp384r1", -) - - -NIST521p = Curve( - "NIST521p", - ecdsa.curve_521, - ecdsa.generator_521, - (1, 3, 132, 0, 35), - "secp521r1", -) - - -SECP256k1 = Curve( - "SECP256k1", - ecdsa.curve_secp256k1, - ecdsa.generator_secp256k1, - (1, 3, 132, 0, 10), - "secp256k1", -) - - -BRAINPOOLP160r1 = Curve( - "BRAINPOOLP160r1", - ecdsa.curve_brainpoolp160r1, - ecdsa.generator_brainpoolp160r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), - "brainpoolP160r1", -) - - -BRAINPOOLP160t1 = Curve( - "BRAINPOOLP160t1", - ecdsa.curve_brainpoolp160t1, - ecdsa.generator_brainpoolp160t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 2), - "brainpoolP160t1", -) - - -BRAINPOOLP192r1 = Curve( - "BRAINPOOLP192r1", - ecdsa.curve_brainpoolp192r1, - ecdsa.generator_brainpoolp192r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), - "brainpoolP192r1", -) - - -BRAINPOOLP192t1 = Curve( - "BRAINPOOLP192t1", - ecdsa.curve_brainpoolp192t1, - ecdsa.generator_brainpoolp192t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 4), - "brainpoolP192t1", -) - - -BRAINPOOLP224r1 = Curve( - "BRAINPOOLP224r1", - ecdsa.curve_brainpoolp224r1, - ecdsa.generator_brainpoolp224r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), - "brainpoolP224r1", -) - - -BRAINPOOLP224t1 = Curve( - "BRAINPOOLP224t1", - ecdsa.curve_brainpoolp224t1, - ecdsa.generator_brainpoolp224t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 6), - "brainpoolP224t1", -) - - -BRAINPOOLP256r1 = Curve( - "BRAINPOOLP256r1", - ecdsa.curve_brainpoolp256r1, - ecdsa.generator_brainpoolp256r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), - "brainpoolP256r1", -) - - -BRAINPOOLP256t1 = Curve( - "BRAINPOOLP256t1", - ecdsa.curve_brainpoolp256t1, - ecdsa.generator_brainpoolp256t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 8), - "brainpoolP256t1", -) - - -BRAINPOOLP320r1 = Curve( - "BRAINPOOLP320r1", - ecdsa.curve_brainpoolp320r1, - ecdsa.generator_brainpoolp320r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), - "brainpoolP320r1", -) - - -BRAINPOOLP320t1 = Curve( - "BRAINPOOLP320t1", - ecdsa.curve_brainpoolp320t1, - ecdsa.generator_brainpoolp320t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 10), - "brainpoolP320t1", -) - - -BRAINPOOLP384r1 = Curve( - "BRAINPOOLP384r1", - ecdsa.curve_brainpoolp384r1, - ecdsa.generator_brainpoolp384r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), - "brainpoolP384r1", -) - - -BRAINPOOLP384t1 = Curve( - "BRAINPOOLP384t1", - ecdsa.curve_brainpoolp384t1, - ecdsa.generator_brainpoolp384t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 12), - "brainpoolP384t1", -) - - -BRAINPOOLP512r1 = Curve( - "BRAINPOOLP512r1", - ecdsa.curve_brainpoolp512r1, - ecdsa.generator_brainpoolp512r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), - "brainpoolP512r1", -) - - -BRAINPOOLP512t1 = Curve( - "BRAINPOOLP512t1", - ecdsa.curve_brainpoolp512t1, - ecdsa.generator_brainpoolp512t1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 14), - "brainpoolP512t1", -) - - -Ed25519 = Curve( - "Ed25519", - eddsa.curve_ed25519, - eddsa.generator_ed25519, - (1, 3, 101, 112), -) - - -Ed448 = Curve( - "Ed448", - eddsa.curve_ed448, - eddsa.generator_ed448, - (1, 3, 101, 113), -) - - -# no order in particular, but keep previously added curves first -curves = [ - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, - BRAINPOOLP160t1, - BRAINPOOLP192t1, - BRAINPOOLP224t1, - BRAINPOOLP256t1, - BRAINPOOLP320t1, - BRAINPOOLP384t1, - BRAINPOOLP512t1, -] - - -def find_curve(oid_curve): - """Select a curve based on its OID - - :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the - curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. - - :raises UnknownCurveError: When the oid doesn't match any of the supported - curves - - :rtype: ~ecdsa.curves.Curve - """ - for c in curves: - if c.oid == oid_curve: - return c - raise UnknownCurveError( - "I don't know about the curve with oid %s." - "I only know about these: %s" % (oid_curve, [c.name for c in curves]) - ) - - -def curve_by_name(name): - """Select a curve based on its name. - - Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. - Note that ``name`` is case-sensitve. - - :param str name: Name of the curve to return, like ``NIST256p`` or - ``prime256v1`` - - :raises UnknownCurveError: When the name doesn't match any of the supported - curves - - :rtype: ~ecdsa.curves.Curve - """ - for c in curves: - if name == c.name or (c.openssl_name and name == c.openssl_name): - return c - raise UnknownCurveError( - "Curve with name {0!r} unknown, only curves supported: {1}".format( - name, [c.name for c in curves] - ) - ) diff --git a/.venv/Lib/site-packages/ecdsa/der.py b/.venv/Lib/site-packages/ecdsa/der.py deleted file mode 100644 index b291485..0000000 --- a/.venv/Lib/site-packages/ecdsa/der.py +++ /dev/null @@ -1,409 +0,0 @@ -from __future__ import division - -import binascii -import base64 -import warnings -from itertools import chain -from six import int2byte, text_type -from ._compat import compat26_str, str_idx_as_int - - -class UnexpectedDER(Exception): - pass - - -def encode_constructed(tag, value): - return int2byte(0xA0 + tag) + encode_length(len(value)) + value - - -def encode_integer(r): - assert r >= 0 # can't support negative numbers yet - h = ("%x" % r).encode() - if len(h) % 2: - h = b"0" + h - s = binascii.unhexlify(h) - num = str_idx_as_int(s, 0) - if num <= 0x7F: - return b"\x02" + encode_length(len(s)) + s - else: - # DER integers are two's complement, so if the first byte is - # 0x80-0xff then we need an extra 0x00 byte to prevent it from - # looking negative. - return b"\x02" + encode_length(len(s) + 1) + b"\x00" + s - - -# sentry object to check if an argument was specified (used to detect -# deprecated calling convention) -_sentry = object() - - -def encode_bitstring(s, unused=_sentry): - """ - Encode a binary string as a BIT STRING using :term:`DER` encoding. - - Note, because there is no native Python object that can encode an actual - bit string, this function only accepts byte strings as the `s` argument. - The byte string is the actual bit string that will be encoded, padded - on the right (least significant bits, looking from big endian perspective) - to the first full byte. If the bit string has a bit length that is multiple - of 8, then the padding should not be included. For correct DER encoding - the padding bits MUST be set to 0. - - Number of bits of padding need to be provided as the `unused` parameter. - In case they are specified as None, it means the number of unused bits - is already encoded in the string as the first byte. - - The deprecated call convention specifies just the `s` parameters and - encodes the number of unused bits as first parameter (same convention - as with None). - - Empty string must be encoded with `unused` specified as 0. - - Future version of python-ecdsa will make specifying the `unused` argument - mandatory. - - :param s: bytes to encode - :type s: bytes like object - :param unused: number of bits at the end of `s` that are unused, must be - between 0 and 7 (inclusive) - :type unused: int or None - - :raises ValueError: when `unused` is too large or too small - - :return: `s` encoded using DER - :rtype: bytes - """ - encoded_unused = b"" - len_extra = 0 - if unused is _sentry: - warnings.warn( - "Legacy call convention used, unused= needs to be specified", - DeprecationWarning, - ) - elif unused is not None: - if not 0 <= unused <= 7: - raise ValueError("unused must be integer between 0 and 7") - if unused: - if not s: - raise ValueError("unused is non-zero but s is empty") - last = str_idx_as_int(s, -1) - if last & (2**unused - 1): - raise ValueError("unused bits must be zeros in DER") - encoded_unused = int2byte(unused) - len_extra = 1 - return b"\x03" + encode_length(len(s) + len_extra) + encoded_unused + s - - -def encode_octet_string(s): - return b"\x04" + encode_length(len(s)) + s - - -def encode_oid(first, second, *pieces): - assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second - body = b"".join( - chain( - [encode_number(40 * first + second)], - (encode_number(p) for p in pieces), - ) - ) - return b"\x06" + encode_length(len(body)) + body - - -def encode_sequence(*encoded_pieces): - total_len = sum([len(p) for p in encoded_pieces]) - return b"\x30" + encode_length(total_len) + b"".join(encoded_pieces) - - -def encode_number(n): - b128_digits = [] - while n: - b128_digits.insert(0, (n & 0x7F) | 0x80) - n = n >> 7 - if not b128_digits: - b128_digits.append(0) - b128_digits[-1] &= 0x7F - return b"".join([int2byte(d) for d in b128_digits]) - - -def is_sequence(string): - return string and string[:1] == b"\x30" - - -def remove_constructed(string): - s0 = str_idx_as_int(string, 0) - if (s0 & 0xE0) != 0xA0: - raise UnexpectedDER( - "wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0 - ) - tag = s0 & 0x1F - length, llen = read_length(string[1:]) - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - return tag, body, rest - - -def remove_sequence(string): - if not string: - raise UnexpectedDER("Empty string does not encode a sequence") - if string[:1] != b"\x30": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n) - length, lengthlength = read_length(string[1:]) - if length > len(string) - 1 - lengthlength: - raise UnexpectedDER("Length longer than the provided buffer") - endseq = 1 + lengthlength + length - return string[1 + lengthlength : endseq], string[endseq:] - - -def remove_octet_string(string): - if string[:1] != b"\x04": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n) - length, llen = read_length(string[1:]) - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - return body, rest - - -def remove_object(string): - if not string: - raise UnexpectedDER( - "Empty string does not encode an object identifier" - ) - if string[:1] != b"\x06": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n) - length, lengthlength = read_length(string[1:]) - body = string[1 + lengthlength : 1 + lengthlength + length] - rest = string[1 + lengthlength + length :] - if not body: - raise UnexpectedDER("Empty object identifier") - if len(body) != length: - raise UnexpectedDER( - "Length of object identifier longer than the provided buffer" - ) - numbers = [] - while body: - n, ll = read_number(body) - numbers.append(n) - body = body[ll:] - n0 = numbers.pop(0) - if n0 < 80: - first = n0 // 40 - else: - first = 2 - second = n0 - (40 * first) - numbers.insert(0, first) - numbers.insert(1, second) - return tuple(numbers), rest - - -def remove_integer(string): - if not string: - raise UnexpectedDER( - "Empty string is an invalid encoding of an integer" - ) - if string[:1] != b"\x02": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n) - length, llen = read_length(string[1:]) - if length > len(string) - 1 - llen: - raise UnexpectedDER("Length longer than provided buffer") - if length == 0: - raise UnexpectedDER("0-byte long encoding of integer") - numberbytes = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - msb = str_idx_as_int(numberbytes, 0) - if not msb < 0x80: - raise UnexpectedDER("Negative integers are not supported") - # check if the encoding is the minimal one (DER requirement) - if length > 1 and not msb: - # leading zero byte is allowed if the integer would have been - # considered a negative number otherwise - smsb = str_idx_as_int(numberbytes, 1) - if smsb < 0x80: - raise UnexpectedDER( - "Invalid encoding of integer, unnecessary " - "zero padding bytes" - ) - return int(binascii.hexlify(numberbytes), 16), rest - - -def read_number(string): - number = 0 - llen = 0 - if str_idx_as_int(string, 0) == 0x80: - raise UnexpectedDER("Non minimal encoding of OID subidentifier") - # base-128 big endian, with most significant bit set in all but the last - # byte - while True: - if llen >= len(string): - raise UnexpectedDER("ran out of length bytes") - number = number << 7 - d = str_idx_as_int(string, llen) - number += d & 0x7F - llen += 1 - if not d & 0x80: - break - return number, llen - - -def encode_length(l): - assert l >= 0 - if l < 0x80: - return int2byte(l) - s = ("%x" % l).encode() - if len(s) % 2: - s = b"0" + s - s = binascii.unhexlify(s) - llen = len(s) - return int2byte(0x80 | llen) + s - - -def read_length(string): - if not string: - raise UnexpectedDER("Empty string can't encode valid length value") - num = str_idx_as_int(string, 0) - if not (num & 0x80): - # short form - return (num & 0x7F), 1 - # else long-form: b0&0x7f is number of additional base256 length bytes, - # big-endian - llen = num & 0x7F - if not llen: - raise UnexpectedDER("Invalid length encoding, length of length is 0") - if llen > len(string) - 1: - raise UnexpectedDER("Length of length longer than provided buffer") - # verify that the encoding is minimal possible (DER requirement) - msb = str_idx_as_int(string, 1) - if not msb or llen == 1 and msb < 0x80: - raise UnexpectedDER("Not minimal encoding of length") - return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen - - -def remove_bitstring(string, expect_unused=_sentry): - """ - Remove a BIT STRING object from `string` following :term:`DER`. - - The `expect_unused` can be used to specify if the bit string should - have the amount of unused bits decoded or not. If it's an integer, any - read BIT STRING that has number of unused bits different from specified - value will cause UnexpectedDER exception to be raised (this is especially - useful when decoding BIT STRINGS that have DER encoded object in them; - DER encoding is byte oriented, so the unused bits will always equal 0). - - If the `expect_unused` is specified as None, the first element returned - will be a tuple, with the first value being the extracted bit string - while the second value will be the decoded number of unused bits. - - If the `expect_unused` is unspecified, the decoding of byte with - number of unused bits will not be attempted and the bit string will be - returned as-is, the callee will be required to decode it and verify its - correctness. - - Future version of python will require the `expected_unused` parameter - to be specified. - - :param string: string of bytes to extract the BIT STRING from - :type string: bytes like object - :param expect_unused: number of bits that should be unused in the BIT - STRING, or None, to return it to caller - :type expect_unused: int or None - - :raises UnexpectedDER: when the encoding does not follow DER. - - :return: a tuple with first element being the extracted bit string and - the second being the remaining bytes in the string (if any); if the - `expect_unused` is specified as None, the first element of the returned - tuple will be a tuple itself, with first element being the bit string - as bytes and the second element being the number of unused bits at the - end of the byte array as an integer - :rtype: tuple - """ - if not string: - raise UnexpectedDER("Empty string does not encode a bitstring") - if expect_unused is _sentry: - warnings.warn( - "Legacy call convention used, expect_unused= needs to be" - " specified", - DeprecationWarning, - ) - num = str_idx_as_int(string, 0) - if string[:1] != b"\x03": - raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) - length, llen = read_length(string[1:]) - if not length: - raise UnexpectedDER("Invalid length of bit string, can't be 0") - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - if expect_unused is not _sentry: - unused = str_idx_as_int(body, 0) - if not 0 <= unused <= 7: - raise UnexpectedDER("Invalid encoding of unused bits") - if expect_unused is not None and expect_unused != unused: - raise UnexpectedDER("Unexpected number of unused bits") - body = body[1:] - if unused: - if not body: - raise UnexpectedDER("Invalid encoding of empty bit string") - last = str_idx_as_int(body, -1) - # verify that all the unused bits are set to zero (DER requirement) - if last & (2**unused - 1): - raise UnexpectedDER("Non zero padding bits in bit string") - if expect_unused is None: - body = (body, unused) - return body, rest - - -# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) - - -# signatures: (from RFC3279) -# ansi-X9-62 OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) 10045 } -# -# id-ecSigType OBJECT IDENTIFIER ::= { -# ansi-X9-62 signatures(4) } -# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { -# id-ecSigType 1 } -# so 1,2,840,10045,4,1 -# so 0x42, .. .. - -# Ecdsa-Sig-Value ::= SEQUENCE { -# r INTEGER, -# s INTEGER } - -# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } -# -# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } - -# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) -# secp224r1 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) curve(0) 33 } -# and the secp384r1 is (t=06,l=05,v=2b81040022) -# secp384r1 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) curve(0) 34 } - - -def unpem(pem): - if isinstance(pem, text_type): # pragma: no branch - pem = pem.encode() - - d = b"".join( - [ - l.strip() - for l in pem.split(b"\n") - if l and not l.startswith(b"-----") - ] - ) - return base64.b64decode(d) - - -def topem(der, name): - b64 = base64.b64encode(compat26_str(der)) - lines = [("-----BEGIN %s-----\n" % name).encode()] - lines.extend( - [b64[start : start + 76] + b"\n" for start in range(0, len(b64), 76)] - ) - lines.append(("-----END %s-----\n" % name).encode()) - return b"".join(lines) diff --git a/.venv/Lib/site-packages/ecdsa/ecdh.py b/.venv/Lib/site-packages/ecdsa/ecdh.py deleted file mode 100644 index 7f697d9..0000000 --- a/.venv/Lib/site-packages/ecdsa/ecdh.py +++ /dev/null @@ -1,336 +0,0 @@ -""" -Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations. -""" - -from .util import number_to_string -from .ellipticcurve import INFINITY -from .keys import SigningKey, VerifyingKey - - -__all__ = [ - "ECDH", - "NoKeyError", - "NoCurveError", - "InvalidCurveError", - "InvalidSharedSecretError", -] - - -class NoKeyError(Exception): - """ECDH. Key not found but it is needed for operation.""" - - pass - - -class NoCurveError(Exception): - """ECDH. Curve not set but it is needed for operation.""" - - pass - - -class InvalidCurveError(Exception): - """ - ECDH. Raised in case the public and private keys use different curves. - """ - - pass - - -class InvalidSharedSecretError(Exception): - """ECDH. Raised in case the shared secret we obtained is an INFINITY.""" - - pass - - -class ECDH(object): - """ - Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol. - - Allows two parties, each having an elliptic-curve public-private key - pair, to establish a shared secret over an insecure channel - """ - - def __init__(self, curve=None, private_key=None, public_key=None): - """ - ECDH init. - - Call can be initialised without parameters, then the first operation - (loading either key) will set the used curve. - All parameters must be ultimately set before shared secret - calculation will be allowed. - - :param curve: curve for operations - :type curve: Curve - :param private_key: `my` private key for ECDH - :type private_key: SigningKey - :param public_key: `their` public key for ECDH - :type public_key: VerifyingKey - """ - self.curve = curve - self.private_key = None - self.public_key = None - if private_key: - self.load_private_key(private_key) - if public_key: - self.load_received_public_key(public_key) - - def _get_shared_secret(self, remote_public_key): - if not self.private_key: - raise NoKeyError( - "Private key needs to be set to create shared secret" - ) - if not self.public_key: - raise NoKeyError( - "Public key needs to be set to create shared secret" - ) - if not ( - self.private_key.curve == self.curve == remote_public_key.curve - ): - raise InvalidCurveError( - "Curves for public key and private key is not equal." - ) - - # shared secret = PUBKEYtheirs * PRIVATEKEYours - result = ( - remote_public_key.pubkey.point - * self.private_key.privkey.secret_multiplier - ) - if result == INFINITY: - raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") - - return result.x() - - def set_curve(self, key_curve): - """ - Set the working curve for ecdh operations. - - :param key_curve: curve from `curves` module - :type key_curve: Curve - """ - self.curve = key_curve - - def generate_private_key(self): - """ - Generate local private key for ecdh operation with curve that was set. - - :raises NoCurveError: Curve must be set before key generation. - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - raise NoCurveError("Curve must be set prior to key generation.") - return self.load_private_key(SigningKey.generate(curve=self.curve)) - - def load_private_key(self, private_key): - """ - Load private key from SigningKey (keys.py) object. - - Needs to have the same curve as was set with set_curve method. - If curve is not set - it sets from this SigningKey - - :param private_key: Initialised SigningKey class - :type private_key: SigningKey - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - self.curve = private_key.curve - if self.curve != private_key.curve: - raise InvalidCurveError("Curve mismatch.") - self.private_key = private_key - return self.private_key.get_verifying_key() - - def load_private_key_bytes(self, private_key): - """ - Load private key from byte string. - - Uses current curve and checks if the provided key matches - the curve of ECDH key agreement. - Key loads via from_string method of SigningKey class - - :param private_key: private key in bytes string format - :type private_key: :term:`bytes-like object` - - :raises NoCurveError: Curve must be set before loading. - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - raise NoCurveError("Curve must be set prior to key load.") - return self.load_private_key( - SigningKey.from_string(private_key, curve=self.curve) - ) - - def load_private_key_der(self, private_key_der): - """ - Load private key from DER byte string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only DER format supported is the RFC5915 - Look at keys.py:SigningKey.from_der() - - :param private_key_der: string with the DER encoding of private ECDSA - key - :type private_key_der: string - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - return self.load_private_key(SigningKey.from_der(private_key_der)) - - def load_private_key_pem(self, private_key_pem): - """ - Load private key from PEM string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only PEM format supported is the RFC5915 - Look at keys.py:SigningKey.from_pem() - it needs to have `EC PRIVATE KEY` section - - :param private_key_pem: string with PEM-encoded private ECDSA key - :type private_key_pem: string - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - return self.load_private_key(SigningKey.from_pem(private_key_pem)) - - def get_public_key(self): - """ - Provides a public key that matches the local private key. - - Needs to be sent to the remote party. - - :return: public (verifying) key from local private key. - :rtype: VerifyingKey - """ - return self.private_key.get_verifying_key() - - def load_received_public_key(self, public_key): - """ - Load public key from VerifyingKey (keys.py) object. - - Needs to have the same curve as set as current for ecdh operation. - If curve is not set - it sets it from VerifyingKey. - - :param public_key: Initialised VerifyingKey class - :type public_key: VerifyingKey - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - if not self.curve: - self.curve = public_key.curve - if self.curve != public_key.curve: - raise InvalidCurveError("Curve mismatch.") - self.public_key = public_key - - def load_received_public_key_bytes( - self, public_key_str, valid_encodings=None - ): - """ - Load public key from byte string. - - Uses current curve and checks if key length corresponds to - the current curve. - Key loads via from_string method of VerifyingKey class - - :param public_key_str: public key in bytes string format - :type public_key_str: :term:`bytes-like object` - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - """ - return self.load_received_public_key( - VerifyingKey.from_string( - public_key_str, self.curve, valid_encodings - ) - ) - - def load_received_public_key_der(self, public_key_der): - """ - Load public key from DER byte string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only DER format supported is the RFC5912 - Look at keys.py:VerifyingKey.from_der() - - :param public_key_der: string with the DER encoding of public ECDSA key - :type public_key_der: string - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - return self.load_received_public_key( - VerifyingKey.from_der(public_key_der) - ) - - def load_received_public_key_pem(self, public_key_pem): - """ - Load public key from PEM string. - - Compares the curve of the PEM-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only PEM format supported is the RFC5912 - Look at keys.py:VerifyingKey.from_pem() - - :param public_key_pem: string with PEM-encoded public ECDSA key - :type public_key_pem: string - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - return self.load_received_public_key( - VerifyingKey.from_pem(public_key_pem) - ) - - def generate_sharedsecret_bytes(self): - """ - Generate shared secret from local private key and remote public key. - - The objects needs to have both private key and received public key - before generation is allowed. - - :raises InvalidCurveError: public_key curve not the same as self.curve - :raises NoKeyError: public_key or private_key is not set - - :return: shared secret - :rtype: bytes - """ - return number_to_string( - self.generate_sharedsecret(), self.private_key.curve.curve.p() - ) - - def generate_sharedsecret(self): - """ - Generate shared secret from local private key and remote public key. - - The objects needs to have both private key and received public key - before generation is allowed. - - It's the same for local and remote party, - shared secret(local private key, remote public key) == - shared secret(local public key, remote private key) - - :raises InvalidCurveError: public_key curve not the same as self.curve - :raises NoKeyError: public_key or private_key is not set - - :return: shared secret - :rtype: int - """ - return self._get_shared_secret(self.public_key) diff --git a/.venv/Lib/site-packages/ecdsa/ecdsa.py b/.venv/Lib/site-packages/ecdsa/ecdsa.py deleted file mode 100644 index 9284ace..0000000 --- a/.venv/Lib/site-packages/ecdsa/ecdsa.py +++ /dev/null @@ -1,1094 +0,0 @@ -#! /usr/bin/env python - -""" -Low level implementation of Elliptic-Curve Digital Signatures. - -.. note :: - You're most likely looking for the :py:class:`~ecdsa.keys` module. - This is a low-level implementation of the ECDSA that operates on - integers, not byte strings. - -NOTE: This a low level implementation of ECDSA, for normal applications -you should be looking at the keys.py module. - -Classes and methods for elliptic-curve signatures: -private keys, public keys, signatures, -and definitions of prime-modulus curves. - -Example: - -.. code-block:: python - - # (In real-life applications, you would probably want to - # protect against defects in SystemRandom.) - from random import SystemRandom - randrange = SystemRandom().randrange - - # Generate a public/private key pair using the NIST Curve P-192: - - g = generator_192 - n = g.order() - secret = randrange( 1, n ) - pubkey = Public_key( g, g * secret ) - privkey = Private_key( pubkey, secret ) - - # Signing a hash value: - - hash = randrange( 1, n ) - signature = privkey.sign( hash, randrange( 1, n ) ) - - # Verifying a signature for a hash value: - - if pubkey.verifies( hash, signature ): - print_("Demo verification succeeded.") - else: - print_("*** Demo verification failed.") - - # Verification fails if the hash value is modified: - - if pubkey.verifies( hash-1, signature ): - print_("**** Demo verification failed to reject tampered hash.") - else: - print_("Demo verification correctly rejected tampered hash.") - -Revision history: - 2005.12.31 - Initial version. - - 2008.11.25 - Substantial revisions introducing new classes. - - 2009.05.16 - Warn against using random.randrange in real applications. - - 2009.05.17 - Use random.SystemRandom by default. - -Originally written in 2005 by Peter Pearson and placed in the public domain, -modified as part of the python-ecdsa package. -""" - -import warnings -from six import int2byte -from . import ellipticcurve -from . import numbertheory -from .util import bit_length -from ._compat import remove_whitespace - - -class RSZeroError(RuntimeError): - pass - - -class InvalidPointError(RuntimeError): - pass - - -class Signature(object): - """ - ECDSA signature. - - :ivar int r: the ``r`` element of the ECDSA signature - :ivar int s: the ``s`` element of the ECDSA signature - """ - - def __init__(self, r, s): - self.r = r - self.s = s - - def recover_public_keys(self, hash, generator): - """ - Returns two public keys for which the signature is valid - - :param int hash: signed hash - :param AbstractPoint generator: is the generator used in creation - of the signature - :rtype: tuple(Public_key, Public_key) - :return: a pair of public keys that can validate the signature - """ - curve = generator.curve() - n = generator.order() - r = self.r - s = self.s - e = hash - x = r - - # Compute the curve point with x as x-coordinate - alpha = ( - pow(x, 3, curve.p()) + (curve.a() * x) + curve.b() - ) % curve.p() - beta = numbertheory.square_root_mod_prime(alpha, curve.p()) - y = beta if beta % 2 == 0 else curve.p() - beta - - # Compute the public key - R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) - Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) - Pk1 = Public_key(generator, Q1) - - # And the second solution - R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) - Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) - Pk2 = Public_key(generator, Q2) - - return [Pk1, Pk2] - - -class Public_key(object): - """Public key for ECDSA.""" - - def __init__(self, generator, point, verify=True): - """Low level ECDSA public key object. - - :param generator: the Point that generates the group (the base point) - :param point: the Point that defines the public key - :param bool verify: if True check if point is valid point on curve - - :raises InvalidPointError: if the point parameters are invalid or - point does not lay on the curve - """ - - self.curve = generator.curve() - self.generator = generator - self.point = point - n = generator.order() - p = self.curve.p() - if not (0 <= point.x() < p) or not (0 <= point.y() < p): - raise InvalidPointError( - "The public point has x or y out of range." - ) - if verify and not self.curve.contains_point(point.x(), point.y()): - raise InvalidPointError("Point does not lay on the curve") - if not n: - raise InvalidPointError("Generator point must have order.") - # for curve parameters with base point with cofactor 1, all points - # that are on the curve are scalar multiples of the base point, so - # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 - if ( - verify - and self.curve.cofactor() != 1 - and not n * point == ellipticcurve.INFINITY - ): - raise InvalidPointError("Generator point order is bad.") - - def __eq__(self, other): - """Return True if the keys are identical, False otherwise. - - Note: for comparison, only placement on the same curve and point - equality is considered, use of the same generator point is not - considered. - """ - if isinstance(other, Public_key): - return self.curve == other.curve and self.point == other.point - return NotImplemented - - def __ne__(self, other): - """Return False if the keys are identical, True otherwise.""" - return not self == other - - def verifies(self, hash, signature): - """Verify that signature is a valid signature of hash. - Return True if the signature is valid. - """ - - # From X9.62 J.3.1. - - G = self.generator - n = G.order() - r = signature.r - s = signature.s - if r < 1 or r > n - 1: - return False - if s < 1 or s > n - 1: - return False - c = numbertheory.inverse_mod(s, n) - u1 = (hash * c) % n - u2 = (r * c) % n - if hasattr(G, "mul_add"): - xy = G.mul_add(u1, self.point, u2) - else: - xy = u1 * G + u2 * self.point - v = xy.x() % n - return v == r - - -class Private_key(object): - """Private key for ECDSA.""" - - def __init__(self, public_key, secret_multiplier): - """public_key is of class Public_key; - secret_multiplier is a large integer. - """ - - self.public_key = public_key - self.secret_multiplier = secret_multiplier - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, Private_key): - return ( - self.public_key == other.public_key - and self.secret_multiplier == other.secret_multiplier - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - def sign(self, hash, random_k): - """Return a signature for the provided hash, using the provided - random nonce. It is absolutely vital that random_k be an unpredictable - number in the range [1, self.public_key.point.order()-1]. If - an attacker can guess random_k, he can compute our private key from a - single signature. Also, if an attacker knows a few high-order - bits (or a few low-order bits) of random_k, he can compute our private - key from many signatures. The generation of nonces with adequate - cryptographic strength is very difficult and far beyond the scope - of this comment. - - May raise RuntimeError, in which case retrying with a new - random value k is in order. - """ - - G = self.public_key.generator - n = G.order() - k = random_k % n - # Fix the bit-length of the random nonce, - # so that it doesn't leak via timing. - # This does not change that ks = k mod n - ks = k + n - kt = ks + n - if bit_length(ks) == bit_length(n): - p1 = kt * G - else: - p1 = ks * G - r = p1.x() % n - if r == 0: - raise RSZeroError("amazingly unlucky random number r") - s = ( - numbertheory.inverse_mod(k, n) - * (hash + (self.secret_multiplier * r) % n) - ) % n - if s == 0: - raise RSZeroError("amazingly unlucky random number s") - return Signature(r, s) - - -def int_to_string(x): # pragma: no cover - """Convert integer x into a string of bytes, as per X9.62.""" - # deprecated in 0.19 - warnings.warn( - "Function is unused in library code. If you use this code, " - "change to util.string_to_number.", - DeprecationWarning, - ) - assert x >= 0 - if x == 0: - return b"\0" - result = [] - while x: - ordinal = x & 0xFF - result.append(int2byte(ordinal)) - x >>= 8 - - result.reverse() - return b"".join(result) - - -def string_to_int(s): # pragma: no cover - """Convert a string of bytes into an integer, as per X9.62.""" - # deprecated in 0.19 - warnings.warn( - "Function is unused in library code. If you use this code, " - "change to util.number_to_string.", - DeprecationWarning, - ) - result = 0 - for c in s: - if not isinstance(c, int): - c = ord(c) - result = 256 * result + c - return result - - -def digest_integer(m): # pragma: no cover - """Convert an integer into a string of bytes, compute - its SHA-1 hash, and convert the result to an integer.""" - # deprecated in 0.19 - warnings.warn( - "Function is unused in library code. If you use this code, " - "change to a one-liner with util.number_to_string and " - "util.string_to_number methods.", - DeprecationWarning, - ) - # - # I don't expect this function to be used much. I wrote - # it in order to be able to duplicate the examples - # in ECDSAVS. - # - from hashlib import sha1 - - return string_to_int(sha1(int_to_string(m)).digest()) - - -def point_is_valid(generator, x, y): - """Is (x,y) a valid public key based on the specified generator?""" - - # These are the tests specified in X9.62. - - n = generator.order() - curve = generator.curve() - p = curve.p() - if not (0 <= x < p) or not (0 <= y < p): - return False - if not curve.contains_point(x, y): - return False - if ( - curve.cofactor() != 1 - and not n * ellipticcurve.PointJacobi(curve, x, y, 1) - == ellipticcurve.INFINITY - ): - return False - return True - - -# secp112r1 curve -_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) -# s = 00F50B02 8E4D696E 67687561 51752904 72783FB1 -_a = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD2088"), 16) -_b = int(remove_whitespace("659E F8BA0439 16EEDE89 11702B22"), 16) -_Gx = int(remove_whitespace("09487239 995A5EE7 6B55F9C2 F098"), 16) -_Gy = int(remove_whitespace("A89C E5AF8724 C0A23E0E 0FF77500"), 16) -_r = int(remove_whitespace("DB7C 2ABF62E3 5E7628DF AC6561C5"), 16) -_h = 1 -curve_112r1 = ellipticcurve.CurveFp(_p, _a, _b, _h) -generator_112r1 = ellipticcurve.PointJacobi( - curve_112r1, _Gx, _Gy, 1, _r, generator=True -) - - -# secp112r2 curve -_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) -# s = 022757A1 114D69E 67687561 51755316 C05E0BD4 -_a = int(remove_whitespace("6127 C24C05F3 8A0AAAF6 5C0EF02C"), 16) -_b = int(remove_whitespace("51DE F1815DB5 ED74FCC3 4C85D709"), 16) -_Gx = int(remove_whitespace("4BA30AB5 E892B4E1 649DD092 8643"), 16) -_Gy = int(remove_whitespace("ADCD 46F5882E 3747DEF3 6E956E97"), 16) -_r = int(remove_whitespace("36DF 0AAFD8B8 D7597CA1 0520D04B"), 16) -_h = 4 -curve_112r2 = ellipticcurve.CurveFp(_p, _a, _b, _h) -generator_112r2 = ellipticcurve.PointJacobi( - curve_112r2, _Gx, _Gy, 1, _r, generator=True -) - - -# secp128r1 curve -_p = int(remove_whitespace("FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF"), 16) -# S = 000E0D4D 69E6768 75615175 0CC03A44 73D03679 -# a and b are mod p, so a is equal to p-3, or simply -3 -# _a = -3 -_b = int(remove_whitespace("E87579C1 1079F43D D824993C 2CEE5ED3"), 16) -_Gx = int(remove_whitespace("161FF752 8B899B2D 0C28607C A52C5B86"), 16) -_Gy = int(remove_whitespace("CF5AC839 5BAFEB13 C02DA292 DDED7A83"), 16) -_r = int(remove_whitespace("FFFFFFFE 00000000 75A30D1B 9038A115"), 16) -_h = 1 -curve_128r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) -generator_128r1 = ellipticcurve.PointJacobi( - curve_128r1, _Gx, _Gy, 1, _r, generator=True -) - - -# secp160r1 -_p = int(remove_whitespace("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7FFFFFFF"), 16) -# S = 1053CDE4 2C14D696 E6768756 1517533B F3F83345 -# a and b are mod p, so a is equal to p-3, or simply -3 -# _a = -3 -_b = int(remove_whitespace("1C97BEFC 54BD7A8B 65ACF89F 81D4D4AD C565FA45"), 16) -_Gx = int( - remove_whitespace("4A96B568 8EF57328 46646989 68C38BB9 13CBFC82"), - 16, -) -_Gy = int( - remove_whitespace("23A62855 3168947D 59DCC912 04235137 7AC5FB32"), - 16, -) -_r = int( - remove_whitespace("01 00000000 00000000 0001F4C8 F927AED3 CA752257"), - 16, -) -_h = 1 -curve_160r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) -generator_160r1 = ellipticcurve.PointJacobi( - curve_160r1, _Gx, _Gy, 1, _r, generator=True -) - - -# NIST Curve P-192: -_p = 6277101735386680763835789423207666416083908700390324961279 -_r = 6277101735386680763835789423176059013767194773182842284081 -# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L -# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L -_b = int( - remove_whitespace( - """ - 64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811""" - ), - 16, -) - -curve_192 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_192 = ellipticcurve.PointJacobi( - curve_192, _Gx, _Gy, 1, _r, generator=True -) - - -# NIST Curve P-224: -_p = int( - remove_whitespace( - """ - 2695994666715063979466701508701963067355791626002630814351 - 0066298881""" - ) -) -_r = int( - remove_whitespace( - """ - 2695994666715063979466701508701962594045780771442439172168 - 2722368061""" - ) -) -# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L -# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL -_b = int( - remove_whitespace( - """ - B4050A85 0C04B3AB F5413256 5044B0B7 D7BFD8BA 270B3943 - 2355FFB4""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 56C21122 343280D6 - 115C1D21""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - BD376388 B5F723FB 4C22DFE6 CD4375A0 5A074764 44D58199 - 85007E34""" - ), - 16, -) - -curve_224 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_224 = ellipticcurve.PointJacobi( - curve_224, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-256: -_p = int( - remove_whitespace( - """ - 1157920892103562487626974469494075735300861434152903141955 - 33631308867097853951""" - ) -) -_r = int( - remove_whitespace( - """ - 115792089210356248762697446949407573529996955224135760342 - 422259061068512044369""" - ) -) -# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L -# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL -_b = int( - remove_whitespace( - """ - 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 - 3BCE3C3E 27D2604B""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 - F4A13945 D898C296""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE - CBB64068 37BF51F5""" - ), - 16, -) - -curve_256 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_256 = ellipticcurve.PointJacobi( - curve_256, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-384: -_p = int( - remove_whitespace( - """ - 3940200619639447921227904010014361380507973927046544666794 - 8293404245721771496870329047266088258938001861606973112319""" - ) -) -_r = int( - remove_whitespace( - """ - 3940200619639447921227904010014361380507973927046544666794 - 6905279627659399113263569398956308152294913554433653942643""" - ) -) -# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L -# c = int(remove_whitespace( -# """ -# 79d1e655 f868f02f ff48dcde e14151dd b80643c1 406d0ca1 -# 0dfe6fc5 2009540a 495e8042 ea5f744f 6e184667 cc722483""" -# ), 16) -_b = int( - remove_whitespace( - """ - B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 - 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 - 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C - E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F""" - ), - 16, -) - -curve_384 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_384 = ellipticcurve.PointJacobi( - curve_384, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-521: -_p = int( - "686479766013060971498190079908139321726943530014330540939" - "446345918554318339765605212255964066145455497729631139148" - "0858037121987999716643812574028291115057151" -) -_r = int( - "686479766013060971498190079908139321726943530014330540939" - "446345918554318339765539424505774633321719753296399637136" - "3321113864768612440380340372808892707005449" -) -# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL -# c = int(remove_whitespace( -# """ -# 0b4 8bfa5f42 0a349495 39d2bdfc 264eeeeb 077688e4 -# 4fbf0ad8 f6d0edb3 7bd6b533 28100051 8e19f1b9 ffbe0fe9 -# ed8a3c22 00b8f875 e523868c 70c1e5bf 55bad637""" -# ), 16) -_b = int( - remove_whitespace( - """ - 051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B - 99B315F3 B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD - 3BB1BF07 3573DF88 3D2C34F1 EF451FD4 6B503F00""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - C6 858E06B7 0404E9CD 9E3ECB66 2395B442 9C648139 - 053FB521 F828AF60 6B4D3DBA A14B5E77 EFE75928 FE1DC127 - A2FFA8DE 3348B3C1 856A429B F97E7E31 C2E5BD66""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 98F54449 - 579B4468 17AFBD17 273E662C 97EE7299 5EF42640 C550B901 - 3FAD0761 353C7086 A272C240 88BE9476 9FD16650""" - ), - 16, -) - -curve_521 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_521 = ellipticcurve.PointJacobi( - curve_521, _Gx, _Gy, 1, _r, generator=True -) - -# Certicom secp256-k1 -_a = 0x0000000000000000000000000000000000000000000000000000000000000000 -_b = 0x0000000000000000000000000000000000000000000000000000000000000007 -_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 -_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 -_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - -curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_secp256k1 = ellipticcurve.PointJacobi( - curve_secp256k1, _Gx, _Gy, 1, _r, generator=True -) - -# Brainpool P-160-r1 -_a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 -_b = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 -_p = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F -_Gx = 0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3 -_Gy = 0x1667CB477A1A8EC338F94741669C976316DA6321 -_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 - -curve_brainpoolp160r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp160r1 = ellipticcurve.PointJacobi( - curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-160-t1 -_a = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620C -_b = 0x7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380 -# _z = 0x24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B -_Gx = 0xB199B13B9B34EFC1397E64BAEB05ACC265FF2378 -_Gy = 0xADD6718B7C7C1961F0991B842443772152C9E0AD -_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 -curve_brainpoolp160t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp160t1 = ellipticcurve.PointJacobi( - curve_brainpoolp160t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-192-r1 -_a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF -_b = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 -_p = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 -_Gx = 0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6 -_Gy = 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F -_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 - -curve_brainpoolp192r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp192r1 = ellipticcurve.PointJacobi( - curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-192-t1 -_a = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294 -_b = 0x13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79 -# _z = 0x1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB -_Gx = 0x3AE9E58C82F63C30282E1FE7BBF43FA72C446AF6F4618129 -_Gy = 0x097E2C5667C2223A902AB5CA449D0084B7E5B3DE7CCC01C9 -_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 - -curve_brainpoolp192t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp192t1 = ellipticcurve.PointJacobi( - curve_brainpoolp192t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-224-r1 -_a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 -_b = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B -_p = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF -_Gx = 0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D -_Gy = 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD -_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F - -curve_brainpoolp224r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp224r1 = ellipticcurve.PointJacobi( - curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-224-t1 -_a = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC -_b = 0x4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D -# _z = 0x2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F -_Gx = 0x6AB1E344CE25FF3896424E7FFE14762ECB49F8928AC0C76029B4D580 -_Gy = 0x0374E9F5143E568CD23F3F4D7C0D4B1E41C8CC0D1C6ABD5F1A46DB4C -_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F - -curve_brainpoolp224t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp224t1 = ellipticcurve.PointJacobi( - curve_brainpoolp224t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-256-r1 -_a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 -_b = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 -_p = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 -_Gx = 0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 -_Gy = 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 -_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - -curve_brainpoolp256r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp256r1 = ellipticcurve.PointJacobi( - curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-256-t1 -_a = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374 -_b = 0x662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04 -# _z = 0x3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0 -_Gx = 0xA3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4 -_Gy = 0x2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE -_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - -curve_brainpoolp256t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp256t1 = ellipticcurve.PointJacobi( - curve_brainpoolp256t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-320-r1 -_a = int( - remove_whitespace( - """ - 3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9 - F492F375A97D860EB4""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539 - 816F5EB4AC8FB1F1A6""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC - 28FCD412B1F1B32E27""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599 - C710AF8D0D39E20611""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6A - C7D35245D1692E8EE1""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 - E98691555B44C59311""" - ), - 16, -) - -curve_brainpoolp320r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp320r1 = ellipticcurve.PointJacobi( - curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-320-t1 -_a = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC - 28FCD412B1F1B32E24""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547 - CEB5B4FEF422340353""" - ), - 16, -) -# _z = int( -# remove_whitespace( -# """ -# 15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18F -# EFC3E5AB7496F3C7B1""" -# ), -# 16, -# ) -_Gx = int( - remove_whitespace( - """ - 925BE9FB01AFC6FB4D3E7D4990010F813408AB106C4F09CB7EE07868CC136F - FF3357F624A21BED52""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 63BA3A7A27483EBF6671DBEF7ABB30EBEE084E58A0B077AD42A5A0989D1EE7 - 1B1B9BC0455FB0D2C3""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 - E98691555B44C59311""" - ), - 16, -) - -curve_brainpoolp320t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp320t1 = ellipticcurve.PointJacobi( - curve_brainpoolp320t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-384-r1 -_a = int( - remove_whitespace( - """ - 7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F9 - 0F8AA5814A503AD4EB04A8C7DD22CE2826""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62 - D57CB4390295DBC9943AB78696FA504C11""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 - 23ACD3A729901D1A71874700133107EC53""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10 - E8E826E03436D646AAEF87B2E247D4AF1E""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF991292 - 80E4646217791811142820341263C5315""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 - A7CF3AB6AF6B7FC3103B883202E9046565""" - ), - 16, -) - -curve_brainpoolp384r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp384r1 = ellipticcurve.PointJacobi( - curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True -) - -_a = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 - 23ACD3A729901D1A71874700133107EC50""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE - 1D2074AA263B88805CED70355A33B471EE""" - ), - 16, -) -# _z = int( -# remove_whitespace( -# """ -# 41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE -# 97D2D63DBC87BCCDDCCC5DA39E8589291C""" -# ), -# 16, -# ) -_Gx = int( - remove_whitespace( - """ - 18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AAB - FFC4FF191B946A5F54D8D0AA2F418808CC""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CC - FE469408584DC2B2912675BF5B9E582928""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 - A7CF3AB6AF6B7FC3103B883202E9046565""" - ), - 16, -) - -curve_brainpoolp384t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp384t1 = ellipticcurve.PointJacobi( - curve_brainpoolp384t1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-512-r1 -_a = int( - remove_whitespace( - """ - 7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863 - BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117 - A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D009 - 8EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F81 - 11B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" - ), - 16, -) - -curve_brainpoolp512r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp512r1 = ellipticcurve.PointJacobi( - curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-512-t1 -_a = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36 - A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E""" - ), - 16, -) -# _z = int( -# remove_whitespace( -# """ -# 12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B -# 64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB""" -# ), -# 16, -# ) -_Gx = int( - remove_whitespace( - """ - 640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C031 - 3D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CE - E9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" - ), - 16, -) - -curve_brainpoolp512t1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp512t1 = ellipticcurve.PointJacobi( - curve_brainpoolp512t1, _Gx, _Gy, 1, _q, generator=True -) diff --git a/.venv/Lib/site-packages/ecdsa/eddsa.py b/.venv/Lib/site-packages/ecdsa/eddsa.py deleted file mode 100644 index 9769cfd..0000000 --- a/.venv/Lib/site-packages/ecdsa/eddsa.py +++ /dev/null @@ -1,252 +0,0 @@ -"""Implementation of Edwards Digital Signature Algorithm.""" - -import hashlib -from ._sha3 import shake_256 -from . import ellipticcurve -from ._compat import ( - remove_whitespace, - bit_length, - bytes_to_int, - int_to_bytes, - compat26_str, -) - -# edwards25519, defined in RFC7748 -_p = 2**255 - 19 -_a = -1 -_d = int( - remove_whitespace( - "370957059346694393431380835087545651895421138798432190163887855330" - "85940283555" - ) -) -_h = 8 - -_Gx = int( - remove_whitespace( - "151122213495354007725011514095885315114540126930418572060461132" - "83949847762202" - ) -) -_Gy = int( - remove_whitespace( - "463168356949264781694283940034751631413079938662562256157830336" - "03165251855960" - ) -) -_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED - - -def _sha512(data): - return hashlib.new("sha512", compat26_str(data)).digest() - - -curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) -generator_ed25519 = ellipticcurve.PointEdwards( - curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True -) - - -# edwards448, defined in RFC7748 -_p = 2**448 - 2**224 - 1 -_a = 1 -_d = -39081 % _p -_h = 4 - -_Gx = int( - remove_whitespace( - "224580040295924300187604334099896036246789641632564134246125461" - "686950415467406032909029192869357953282578032075146446173674602635" - "247710" - ) -) -_Gy = int( - remove_whitespace( - "298819210078481492676017930443930673437544040154080242095928241" - "372331506189835876003536878655418784733982303233503462500531545062" - "832660" - ) -) -_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D - - -def _shake256(data): - return shake_256(data, 114) - - -curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) -generator_ed448 = ellipticcurve.PointEdwards( - curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True -) - - -class PublicKey(object): - """Public key for the Edwards Digital Signature Algorithm.""" - - def __init__(self, generator, public_key, public_point=None): - self.generator = generator - self.curve = generator.curve() - self.__encoded = public_key - # plus one for the sign bit and round up - self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 - if len(public_key) != self.baselen: - raise ValueError( - "Incorrect size of the public key, expected: {0} bytes".format( - self.baselen - ) - ) - if public_point: - self.__point = public_point - else: - self.__point = ellipticcurve.PointEdwards.from_bytes( - self.curve, public_key - ) - - def __eq__(self, other): - if isinstance(other, PublicKey): - return ( - self.curve == other.curve and self.__encoded == other.__encoded - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - @property - def point(self): - return self.__point - - @point.setter - def point(self, other): - if self.__point != other: - raise ValueError("Can't change the coordinates of the point") - self.__point = other - - def public_point(self): - return self.__point - - def public_key(self): - return self.__encoded - - def verify(self, data, signature): - """Verify a Pure EdDSA signature over data.""" - data = compat26_str(data) - if len(signature) != 2 * self.baselen: - raise ValueError( - "Invalid signature length, expected: {0} bytes".format( - 2 * self.baselen - ) - ) - R = ellipticcurve.PointEdwards.from_bytes( - self.curve, signature[: self.baselen] - ) - S = bytes_to_int(signature[self.baselen :], "little") - if S >= self.generator.order(): - raise ValueError("Invalid signature") - - dom = bytearray() - if self.curve == curve_ed448: - dom = bytearray(b"SigEd448" + b"\x00\x00") - - k = bytes_to_int( - self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), - "little", - ) - - if self.generator * S != self.__point * k + R: - raise ValueError("Invalid signature") - - return True - - -class PrivateKey(object): - """Private key for the Edwards Digital Signature Algorithm.""" - - def __init__(self, generator, private_key): - self.generator = generator - self.curve = generator.curve() - # plus one for the sign bit and round up - self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 - if len(private_key) != self.baselen: - raise ValueError( - "Incorrect size of private key, expected: {0} bytes".format( - self.baselen - ) - ) - self.__private_key = bytes(private_key) - self.__h = bytearray(self.curve.hash_func(private_key)) - self.__public_key = None - - a = self.__h[: self.baselen] - a = self._key_prune(a) - scalar = bytes_to_int(a, "little") - self.__s = scalar - - @property - def private_key(self): - return self.__private_key - - def __eq__(self, other): - if isinstance(other, PrivateKey): - return ( - self.curve == other.curve - and self.__private_key == other.__private_key - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - def _key_prune(self, key): - # make sure the key is not in a small subgroup - h = self.curve.cofactor() - if h == 4: - h_log = 2 - elif h == 8: - h_log = 3 - else: - raise ValueError("Only cofactor 4 and 8 curves supported") - key[0] &= ~((1 << h_log) - 1) - - # ensure the highest bit is set but no higher - l = bit_length(self.curve.p()) - if l % 8 == 0: - key[-1] = 0 - key[-2] |= 0x80 - else: - key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 - return key - - def public_key(self): - """Generate the public key based on the included private key""" - if self.__public_key: - return self.__public_key - - public_point = self.generator * self.__s - - self.__public_key = PublicKey( - self.generator, public_point.to_bytes(), public_point - ) - - return self.__public_key - - def sign(self, data): - """Perform a Pure EdDSA signature over data.""" - data = compat26_str(data) - A = self.public_key().public_key() - - prefix = self.__h[self.baselen :] - - dom = bytearray() - if self.curve == curve_ed448: - dom = bytearray(b"SigEd448" + b"\x00\x00") - - r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") - R = (self.generator * r).to_bytes() - - k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") - k %= self.generator.order() - - S = (r + k * self.__s) % self.generator.order() - - return R + int_to_bytes(S, self.baselen, "little") diff --git a/.venv/Lib/site-packages/ecdsa/ellipticcurve.py b/.venv/Lib/site-packages/ecdsa/ellipticcurve.py deleted file mode 100644 index 18816a6..0000000 --- a/.venv/Lib/site-packages/ecdsa/ellipticcurve.py +++ /dev/null @@ -1,1598 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- -# -# Implementation of elliptic curves, for cryptographic applications. -# -# This module doesn't provide any way to choose a random elliptic -# curve, nor to verify that an elliptic curve was chosen randomly, -# because one can simply use NIST's standard curves. -# -# Notes from X9.62-1998 (draft): -# Nomenclature: -# - Q is a public key. -# The "Elliptic Curve Domain Parameters" include: -# - q is the "field size", which in our case equals p. -# - p is a big prime. -# - G is a point of prime order (5.1.1.1). -# - n is the order of G (5.1.1.1). -# Public-key validation (5.2.2): -# - Verify that Q is not the point at infinity. -# - Verify that X_Q and Y_Q are in [0,p-1]. -# - Verify that Q is on the curve. -# - Verify that nQ is the point at infinity. -# Signature generation (5.3): -# - Pick random k from [1,n-1]. -# Signature checking (5.4.2): -# - Verify that r and s are in [1,n-1]. -# -# Revision history: -# 2005.12.31 - Initial version. -# 2008.11.25 - Change CurveFp.is_on to contains_point. -# -# Written in 2005 by Peter Pearson and placed in the public domain. -# Modified extensively as part of python-ecdsa. - -from __future__ import division - -try: - from gmpy2 import mpz - - GMPY = True -except ImportError: # pragma: no branch - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - - -from six import python_2_unicode_compatible -from . import numbertheory -from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int -from .errors import MalformedPointError -from .util import orderlen, string_to_number, number_to_string - - -@python_2_unicode_compatible -class CurveFp(object): - """ - :term:`Short Weierstrass Elliptic Curve ` over a - prime field. - """ - - if GMPY: # pragma: no branch - - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). - - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic - curve equation divided by the order of the base point. It is used - for selection of efficient algorithm for public point verification. - """ - self.__p = mpz(p) - self.__a = mpz(a) - self.__b = mpz(b) - # h is not used in calculations and it can be None, so don't use - # gmpy with it - self.__h = h - - else: # pragma: no branch - - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). - - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic - curve equation divided by the order of the base point. It is used - for selection of efficient algorithm for public point verification. - """ - self.__p = p - self.__a = a - self.__b = b - self.__h = h - - def __eq__(self, other): - """Return True if other is an identical curve, False otherwise. - - Note: the value of the cofactor of the curve is not taken into account - when comparing curves, as it's derived from the base point and - intrinsic curve characteristic (but it's complex to compute), - only the prime and curve parameters are considered. - """ - if isinstance(other, CurveFp): - p = self.__p - return ( - self.__p == other.__p - and self.__a % p == other.__a % p - and self.__b % p == other.__b % p - ) - return NotImplemented - - def __ne__(self, other): - """Return False if other is an identical curve, True otherwise.""" - return not self == other - - def __hash__(self): - return hash((self.__p, self.__a, self.__b)) - - def p(self): - return self.__p - - def a(self): - return self.__a - - def b(self): - return self.__b - - def cofactor(self): - return self.__h - - def contains_point(self, x, y): - """Is the point (x,y) on this curve?""" - return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 - - def __str__(self): - if self.__h is not None: - return "CurveFp(p={0}, a={1}, b={2}, h={3})".format( - self.__p, - self.__a, - self.__b, - self.__h, - ) - return "CurveFp(p={0}, a={1}, b={2})".format( - self.__p, - self.__a, - self.__b, - ) - - -class CurveEdTw(object): - """Parameters for a Twisted Edwards Elliptic Curve""" - - if GMPY: # pragma: no branch - - def __init__(self, p, a, d, h=None, hash_func=None): - """ - The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). - - h is the cofactor of the curve. - hash_func is the hash function associated with the curve - (like SHA-512 for Ed25519) - """ - self.__p = mpz(p) - self.__a = mpz(a) - self.__d = mpz(d) - self.__h = h - self.__hash_func = hash_func - - else: - - def __init__(self, p, a, d, h=None, hash_func=None): - """ - The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). - - h is the cofactor of the curve. - hash_func is the hash function associated with the curve - (like SHA-512 for Ed25519) - """ - self.__p = p - self.__a = a - self.__d = d - self.__h = h - self.__hash_func = hash_func - - def __eq__(self, other): - """Returns True if other is an identical curve.""" - if isinstance(other, CurveEdTw): - p = self.__p - return ( - self.__p == other.__p - and self.__a % p == other.__a % p - and self.__d % p == other.__d % p - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the other is an identical curve, True otherwise.""" - return not self == other - - def __hash__(self): - return hash((self.__p, self.__a, self.__d)) - - def contains_point(self, x, y): - """Is the point (x, y) on this curve?""" - return ( - self.__a * x * x + y * y - 1 - self.__d * x * x * y * y - ) % self.__p == 0 - - def p(self): - return self.__p - - def a(self): - return self.__a - - def d(self): - return self.__d - - def hash_func(self, data): - return self.__hash_func(data) - - def cofactor(self): - return self.__h - - def __str__(self): - if self.__h is not None: - return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( - self.__p, - self.__a, - self.__d, - self.__h, - ) - return "CurveEdTw(p={0}, a={1}, d={2})".format( - self.__p, - self.__a, - self.__d, - ) - - -class AbstractPoint(object): - """Class for common methods of elliptic curve points.""" - - @staticmethod - def _from_raw_encoding(data, raw_encoding_length): - """ - Decode public point from :term:`raw encoding`. - - :term:`raw encoding` is the same as the :term:`uncompressed` encoding, - but without the 0x04 byte at the beginning. - """ - # real assert, from_bytes() should not call us with different length - assert len(data) == raw_encoding_length - xs = data[: raw_encoding_length // 2] - ys = data[raw_encoding_length // 2 :] - # real assert, raw_encoding_length is calculated by multiplying an - # integer by two so it will always be even - assert len(xs) == raw_encoding_length // 2 - assert len(ys) == raw_encoding_length // 2 - coord_x = string_to_number(xs) - coord_y = string_to_number(ys) - - return coord_x, coord_y - - @staticmethod - def _from_compressed(data, curve): - """Decode public point from compressed encoding.""" - if data[:1] not in (b"\x02", b"\x03"): - raise MalformedPointError("Malformed compressed point encoding") - - is_even = data[:1] == b"\x02" - x = string_to_number(data[1:]) - p = curve.p() - alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p - try: - beta = numbertheory.square_root_mod_prime(alpha, p) - except numbertheory.Error as e: - raise MalformedPointError( - "Encoding does not correspond to a point on curve", e - ) - if is_even == bool(beta & 1): - y = p - beta - else: - y = beta - return x, y - - @classmethod - def _from_hybrid(cls, data, raw_encoding_length, validate_encoding): - """Decode public point from hybrid encoding.""" - # real assert, from_bytes() should not call us with different types - assert data[:1] in (b"\x06", b"\x07") - - # primarily use the uncompressed as it's easiest to handle - x, y = cls._from_raw_encoding(data[1:], raw_encoding_length) - - # but validate if it's self-consistent if we're asked to do that - if validate_encoding and ( - y & 1 - and data[:1] != b"\x07" - or (not y & 1) - and data[:1] != b"\x06" - ): - raise MalformedPointError("Inconsistent hybrid point encoding") - - return x, y - - @classmethod - def _from_edwards(cls, curve, data): - """Decode a point on an Edwards curve.""" - data = bytearray(data) - p = curve.p() - # add 1 for the sign bit and then round up - exp_len = (bit_length(p) + 1 + 7) // 8 - if len(data) != exp_len: - raise MalformedPointError("Point length doesn't match the curve.") - x_0 = (data[-1] & 0x80) >> 7 - - data[-1] &= 0x80 - 1 - - y = bytes_to_int(data, "little") - if GMPY: - y = mpz(y) - - x2 = ( - (y * y - 1) - * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p) - % p - ) - - try: - x = numbertheory.square_root_mod_prime(x2, p) - except numbertheory.Error as e: - raise MalformedPointError( - "Encoding does not correspond to a point on curve", e - ) - - if x % 2 != x_0: - x = -x % p - - return x, y - - @classmethod - def from_bytes( - cls, curve, data, validate_encoding=True, valid_encodings=None - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - Note: generally you will want to call the ``from_bytes()`` method of - either a child class, PointJacobi or Point. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: x and y coordinates of the encoded point - :rtype: tuple(int, int) - """ - if not valid_encodings: - valid_encodings = set( - ["uncompressed", "compressed", "hybrid", "raw"] - ) - if not all( - i in set(("uncompressed", "compressed", "hybrid", "raw")) - for i in valid_encodings - ): - raise ValueError( - "Only uncompressed, compressed, hybrid or raw encoding " - "supported." - ) - data = normalise_bytes(data) - - if isinstance(curve, CurveEdTw): - return cls._from_edwards(curve, data) - - key_len = len(data) - raw_encoding_length = 2 * orderlen(curve.p()) - if key_len == raw_encoding_length and "raw" in valid_encodings: - coord_x, coord_y = cls._from_raw_encoding( - data, raw_encoding_length - ) - elif key_len == raw_encoding_length + 1 and ( - "hybrid" in valid_encodings or "uncompressed" in valid_encodings - ): - if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings: - coord_x, coord_y = cls._from_hybrid( - data, raw_encoding_length, validate_encoding - ) - elif data[:1] == b"\x04" and "uncompressed" in valid_encodings: - coord_x, coord_y = cls._from_raw_encoding( - data[1:], raw_encoding_length - ) - else: - raise MalformedPointError( - "Invalid X9.62 encoding of the public point" - ) - elif ( - key_len == raw_encoding_length // 2 + 1 - and "compressed" in valid_encodings - ): - coord_x, coord_y = cls._from_compressed(data, curve) - else: - raise MalformedPointError( - "Length of string does not match lengths of " - "any of the enabled ({0}) encodings of the " - "curve.".format(", ".join(valid_encodings)) - ) - return coord_x, coord_y - - def _raw_encode(self): - """Convert the point to the :term:`raw encoding`.""" - prime = self.curve().p() - x_str = number_to_string(self.x(), prime) - y_str = number_to_string(self.y(), prime) - return x_str + y_str - - def _compressed_encode(self): - """Encode the point into the compressed form.""" - prime = self.curve().p() - x_str = number_to_string(self.x(), prime) - if self.y() & 1: - return b"\x03" + x_str - return b"\x02" + x_str - - def _hybrid_encode(self): - """Encode the point into the hybrid form.""" - raw_enc = self._raw_encode() - if self.y() & 1: - return b"\x07" + raw_enc - return b"\x06" + raw_enc - - def _edwards_encode(self): - """Encode the point according to RFC8032 encoding.""" - self.scale() - x, y, p = self.x(), self.y(), self.curve().p() - - # add 1 for the sign bit and then round up - enc_len = (bit_length(p) + 1 + 7) // 8 - y_str = int_to_bytes(y, enc_len, "little") - if x % 2: - y_str[-1] |= 0x80 - return y_str - - def to_bytes(self, encoding="raw"): - """ - Convert the point to a byte string. - - The method by default uses the :term:`raw encoding` (specified - by `encoding="raw"`. It can also output points in :term:`uncompressed`, - :term:`compressed`, and :term:`hybrid` formats. - - For points on Edwards curves `encoding` is ignored and only the - encoding defined in RFC 8032 is supported. - - :return: :term:`raw encoding` of a public on the curve - :rtype: bytes - """ - assert encoding in ("raw", "uncompressed", "compressed", "hybrid") - curve = self.curve() - if isinstance(curve, CurveEdTw): - return self._edwards_encode() - elif encoding == "raw": - return self._raw_encode() - elif encoding == "uncompressed": - return b"\x04" + self._raw_encode() - elif encoding == "hybrid": - return self._hybrid_encode() - else: - return self._compressed_encode() - - @staticmethod - def _naf(mult): - """Calculate non-adjacent form of number.""" - ret = [] - while mult: - if mult % 2: - nd = mult % 4 - if nd >= 2: - nd -= 4 - ret.append(nd) - mult -= nd - else: - ret.append(0) - mult //= 2 - return ret - - -class PointJacobi(AbstractPoint): - """ - Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates. - - In Jacobian coordinates, there are three parameters, X, Y and Z. - They correspond to affine parameters 'x' and 'y' like so: - - x = X / Z² - y = Y / Z³ - """ - - def __init__(self, curve, x, y, z, order=None, generator=False): - """ - Initialise a point that uses Jacobi representation internally. - - :param CurveFp curve: curve on which the point resides - :param int x: the X parameter of Jacobi representation (equal to x when - converting from affine coordinates - :param int y: the Y parameter of Jacobi representation (equal to y when - converting from affine coordinates - :param int z: the Z parameter of Jacobi representation (equal to 1 when - converting from affine coordinates - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: the point provided is a curve generator, as - such, it will be commonly used with scalar multiplication. This will - cause to precompute multiplication table generation for it - """ - super(PointJacobi, self).__init__() - self.__curve = curve - if GMPY: # pragma: no branch - self.__coords = (mpz(x), mpz(y), mpz(z)) - self.__order = order and mpz(order) - else: # pragma: no branch - self.__coords = (x, y, z) - self.__order = order - self.__generator = generator - self.__precompute = [] - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=True, - valid_encodings=None, - order=None, - generator=False, - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: the point provided is a curve generator, as - such, it will be commonly used with scalar multiplication. This - will cause to precompute multiplication table generation for it - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Point on curve - :rtype: PointJacobi - """ - coord_x, coord_y = super(PointJacobi, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return PointJacobi(curve, coord_x, coord_y, 1, order, generator) - - def _maybe_precompute(self): - if not self.__generator or self.__precompute: - return - - # since this code will execute just once, and it's fully deterministic, - # depend on atomicity of the last assignment to switch from empty - # self.__precompute to filled one and just ignore the unlikely - # situation when two threads execute it at the same time (as it won't - # lead to inconsistent __precompute) - order = self.__order - assert order - precompute = [] - i = 1 - order *= 2 - coord_x, coord_y, coord_z = self.__coords - doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order) - order *= 2 - precompute.append((doubler.x(), doubler.y())) - - while i < order: - i *= 2 - doubler = doubler.double().scale() - precompute.append((doubler.x(), doubler.y())) - - self.__precompute = precompute - - def __getstate__(self): - # while this code can execute at the same time as _maybe_precompute() - # is updating the __precompute or scale() is updating the __coords, - # there is no requirement for consistency between __coords and - # __precompute - state = self.__dict__.copy() - return state - - def __setstate__(self, state): - self.__dict__.update(state) - - def __eq__(self, other): - """Compare for equality two points with each-other. - - Note: only points that lay on the same curve can be equal. - """ - x1, y1, z1 = self.__coords - if other is INFINITY: - return not y1 or not z1 - if isinstance(other, Point): - x2, y2, z2 = other.x(), other.y(), 1 - elif isinstance(other, PointJacobi): - x2, y2, z2 = other.__coords - else: - return NotImplemented - if self.__curve != other.curve(): - return False - p = self.__curve.p() - - zz1 = z1 * z1 % p - zz2 = z2 * z2 % p - - # compare the fractions by bringing them to the same denominator - # depend on short-circuit to save 4 multiplications in case of - # inequality - return (x1 * zz2 - x2 * zz1) % p == 0 and ( - y1 * zz2 * z2 - y2 * zz1 * z1 - ) % p == 0 - - def __ne__(self, other): - """Compare for inequality two points with each-other.""" - return not self == other - - def order(self): - """Return the order of the point. - - None if it is undefined. - """ - return self.__order - - def curve(self): - """Return curve over which the point is defined.""" - return self.__curve - - def x(self): - """ - Return affine x coordinate. - - This method should be used only when the 'y' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - x, _, z = self.__coords - if z == 1: - return x - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return x * z**2 % p - - def y(self): - """ - Return affine y coordinate. - - This method should be used only when the 'x' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - _, y, z = self.__coords - if z == 1: - return y - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return y * z**3 % p - - def scale(self): - """ - Return point scaled so that z == 1. - - Modifies point in place, returns self. - """ - x, y, z = self.__coords - if z == 1: - return self - - # scaling is deterministic, so even if two threads execute the below - # code at the same time, they will set __coords to the same value - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(z, p) - zz_inv = z_inv * z_inv % p - x = x * zz_inv % p - y = y * zz_inv * z_inv % p - self.__coords = (x, y, 1) - return self - - def to_affine(self): - """Return point in affine form.""" - _, y, z = self.__coords - if not y or not z: - return INFINITY - self.scale() - x, y, z = self.__coords - return Point(self.__curve, x, y, self.__order) - - @staticmethod - def from_affine(point, generator=False): - """Create from an affine point. - - :param bool generator: set to True to make the point to precalculate - multiplication table - useful for public point when verifying many - signatures (around 100 or so) or for generator points of a curve. - """ - return PointJacobi( - point.curve(), point.x(), point.y(), 1, point.order(), generator - ) - - # please note that all the methods that use the equations from - # hyperelliptic - # are formatted in a way to maximise performance. - # Things that make code faster: multiplying instead of taking to the power - # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and - # `pow(x, 4, p)`), - # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is - # faster than `x1 = self.x1; x2 = self.x2`), - # similarly, sometimes the `% p` is skipped if it makes the calculation - # faster and the result of calculation is later reduced modulo `p` - - def _double_with_z_1(self, X1, Y1, p, a): - """Add a point to itself with z == 1.""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p - M = 3 * XX + a - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = 2 * Y1 % p - return T, Y3, Z3 - - def _double(self, X1, Y1, Z1, p, a): - """Add a point to itself, arbitrary z.""" - if Z1 == 1: - return self._double_with_z_1(X1, Y1, p, a) - if not Y1 or not Z1: - return 0, 0, 1 - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - ZZ = Z1 * Z1 % p - S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p - M = (3 * XX + a * ZZ * ZZ) % p - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p - - return T, Y3, Z3 - - def double(self): - """Add a point to itself.""" - X1, Y1, Z1 = self.__coords - - if not Y1: - return INFINITY - - p, a = self.__curve.p(), self.__curve.a() - - X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def _add_with_z_1(self, X1, Y1, X2, Y2, p): - """add points when both Z1 and Z2 equal 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl - H = X2 - X1 - HH = H * H - I = 4 * HH % p - J = H * I - r = 2 * (Y2 - Y1) - if not H and not r: - return self._double_with_z_1(X1, Y1, p, self.__curve.a()) - V = X1 * I - X3 = (r**2 - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = 2 * H % p - return X3, Y3, Z3 - - def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z1 == Z2""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m - A = (X2 - X1) ** 2 % p - B = X1 * A % p - C = X2 * A - D = (Y2 - Y1) ** 2 % p - if not A and not D: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - X3 = (D - B - C) % p - Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p - Z3 = Z1 * (X2 - X1) % p - return X3, Y3, Z3 - - def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z2 == 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl - Z1Z1 = Z1 * Z1 % p - U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p - H = (U2 - X1) % p - HH = H * H % p - I = 4 * HH % p - J = H * I - r = 2 * (S2 - Y1) % p - if not r and not H: - return self._double_with_z_1(X2, Y2, p, self.__curve.a()) - V = X1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p - return X3, Y3, Z3 - - def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add points with arbitrary z""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl - Z1Z1 = Z1 * Z1 % p - Z2Z2 = Z2 * Z2 % p - U1 = X1 * Z2Z2 % p - U2 = X2 * Z1Z1 % p - S1 = Y1 * Z2 * Z2Z2 % p - S2 = Y2 * Z1 * Z1Z1 % p - H = U2 - U1 - I = 4 * H * H % p - J = H * I % p - r = 2 * (S2 - S1) % p - if not H and not r: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - V = U1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * S1 * J) % p - Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p - - return X3, Y3, Z3 - - def __radd__(self, other): - """Add other to self.""" - return self + other - - def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add two points, select fastest method.""" - if not Y1 or not Z1: - return X2, Y2, Z2 - if not Y2 or not Z2: - return X1, Y1, Z1 - if Z1 == Z2: - if Z1 == 1: - return self._add_with_z_1(X1, Y1, X2, Y2, p) - return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) - if Z1 == 1: - return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) - if Z2 == 1: - return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) - return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) - - def __add__(self, other): - """Add two points on elliptic curve.""" - if self == INFINITY: - return other - if other == INFINITY: - return self - if isinstance(other, Point): - other = PointJacobi.from_affine(other) - if self.__curve != other.__curve: - raise ValueError("The other point is on different curve") - - p = self.__curve.p() - X1, Y1, Z1 = self.__coords - X2, Y2, Z2 = other.__coords - - X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __rmul__(self, other): - """Multiply point by an integer.""" - return self * other - - def _mul_precompute(self, other): - """Multiply point by integer with precomputation table.""" - X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() - _add = self._add - for X2, Y2 in self.__precompute: - if other % 2: - if other % 4 >= 2: - other = (other + 1) // 2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - else: - other = (other - 1) // 2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - else: - other //= 2 - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __mul__(self, other): - """Multiply point by an integer.""" - if not self.__coords[1] or not other: - return INFINITY - if other == 1: - return self - if self.__order: - # order*2 as a protection for Minerva - other = other % (self.__order * 2) - self._maybe_precompute() - if self.__precompute: - return self._mul_precompute(other) - - self = self.scale() - X2, Y2, _ = self.__coords - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - _double = self._double - _add = self._add - # since adding points when at least one of them is scaled - # is quicker, reverse the NAF order - for i in reversed(self._naf(other)): - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - if i < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - elif i > 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def mul_add(self, self_mul, other, other_mul): - """ - Do two multiplications at the same time, add results. - - calculates self*self_mul + other*other_mul - """ - if other == INFINITY or other_mul == 0: - return self * self_mul - if self_mul == 0: - return other * other_mul - if not isinstance(other, PointJacobi): - other = PointJacobi.from_affine(other) - # when the points have precomputed answers, then multiplying them alone - # is faster (as it uses NAF and no point doublings) - self._maybe_precompute() - other._maybe_precompute() - if self.__precompute and other.__precompute: - return self * self_mul + other * other_mul - - if self.__order: - self_mul = self_mul % self.__order - other_mul = other_mul % self.__order - - # (X3, Y3, Z3) is the accumulator - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - - # as we have 6 unique points to work with, we can't scale all of them, - # but do scale the ones that are used most often - self.scale() - X1, Y1, Z1 = self.__coords - other.scale() - X2, Y2, Z2 = other.__coords - - _double = self._double - _add = self._add - - # with NAF we have 3 options: no add, subtract, add - # so with 2 points, we have 9 combinations: - # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B - # so we need 4 combined points: - mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p) - pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p) - mApB_X, mApB_Y, mApB_Z = pAmB_X, -pAmB_Y, pAmB_Z - pApB_X, pApB_Y, pApB_Z = mAmB_X, -mAmB_Y, mAmB_Z - # when the self and other sum to infinity, we need to add them - # one by one to get correct result but as that's very unlikely to - # happen in regular operation, we don't need to optimise this case - if not pApB_Y or not pApB_Z: - return self * self_mul + other * other_mul - - # gmp object creation has cumulatively higher overhead than the - # speedup we get from calculating the NAF using gmp so ensure use - # of int() - self_naf = list(reversed(self._naf(int(self_mul)))) - other_naf = list(reversed(self._naf(int(other_mul)))) - # ensure that the lists are the same length (zip() will truncate - # longer one otherwise) - if len(self_naf) < len(other_naf): - self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf - elif len(self_naf) > len(other_naf): - other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf - - for A, B in zip(self_naf, other_naf): - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - - # conditions ordered from most to least likely - if A == 0: - if B == 0: - pass - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p) - elif A < 0: - if B == 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p) - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p) - else: - assert A > 0 - if B == 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p) - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __neg__(self): - """Return negated point.""" - x, y, z = self.__coords - return PointJacobi(self.__curve, x, -y, z, self.__order) - - -class Point(AbstractPoint): - """A point on a short Weierstrass elliptic curve. Altering x and y is - forbidden, but they can be read by the x() and y() methods.""" - - def __init__(self, curve, x, y, order=None): - """curve, x, y, order; order (optional) is the order of this point.""" - super(Point, self).__init__() - self.__curve = curve - if GMPY: - self.__x = x and mpz(x) - self.__y = y and mpz(y) - self.__order = order and mpz(order) - else: - self.__x = x - self.__y = y - self.__order = order - # self.curve is allowed to be None only for INFINITY: - if self.__curve: - assert self.__curve.contains_point(x, y) - # for curves with cofactor 1, all points that are on the curve are - # scalar multiples of the base point, so performing multiplication is - # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 - if curve and curve.cofactor() != 1 and order: - assert self * order == INFINITY - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=True, - valid_encodings=None, - order=None, - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - :param int order: the point order, must be non zero when using - generator=True - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Point on curve - :rtype: Point - """ - coord_x, coord_y = super(Point, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return Point(curve, coord_x, coord_y, order) - - def __eq__(self, other): - """Return True if the points are identical, False otherwise. - - Note: only points that lay on the same curve can be equal. - """ - if isinstance(other, Point): - return ( - self.__curve == other.__curve - and self.__x == other.__x - and self.__y == other.__y - ) - return NotImplemented - - def __ne__(self, other): - """Returns False if points are identical, True otherwise.""" - return not self == other - - def __neg__(self): - return Point(self.__curve, self.__x, self.__curve.p() - self.__y) - - def __add__(self, other): - """Add one point to another point.""" - - # X9.62 B.3: - - if not isinstance(other, Point): - return NotImplemented - if other == INFINITY: - return self - if self == INFINITY: - return other - assert self.__curve == other.__curve - if self.__x == other.__x: - if (self.__y + other.__y) % self.__curve.p() == 0: - return INFINITY - else: - return self.double() - - p = self.__curve.p() - - l = ( - (other.__y - self.__y) - * numbertheory.inverse_mod(other.__x - self.__x, p) - ) % p - - x3 = (l * l - self.__x - other.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def __mul__(self, other): - """Multiply a point by an integer.""" - - def leftmost_bit(x): - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 - - e = other - if e == 0 or (self.__order and e % self.__order == 0): - return INFINITY - if self == INFINITY: - return INFINITY - if e < 0: - return (-self) * (-e) - - # From X9.62 D.3.2: - - e3 = 3 * e - negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) - i = leftmost_bit(e3) // 2 - result = self - # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) - while i > 1: - result = result.double() - if (e3 & i) != 0 and (e & i) == 0: - result = result + self - if (e3 & i) == 0 and (e & i) != 0: - result = result + negative_self - # print_(". . . i = %d, result = %s" % ( i, result )) - i = i // 2 - - return result - - def __rmul__(self, other): - """Multiply a point by an integer.""" - - return self * other - - def __str__(self): - if self == INFINITY: - return "infinity" - return "(%d,%d)" % (self.__x, self.__y) - - def double(self): - """Return a new point that is twice the old.""" - - if self == INFINITY: - return INFINITY - - # X9.62 B.3: - - p = self.__curve.p() - a = self.__curve.a() - - l = ( - (3 * self.__x * self.__x + a) - * numbertheory.inverse_mod(2 * self.__y, p) - ) % p - - x3 = (l * l - 2 * self.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def x(self): - return self.__x - - def y(self): - return self.__y - - def curve(self): - return self.__curve - - def order(self): - return self.__order - - -class PointEdwards(AbstractPoint): - """Point on Twisted Edwards curve. - - Internally represents the coordinates on the curve using four parameters, - X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so: - - x = X / Z - y = Y / Z - x*y = T / Z - """ - - def __init__(self, curve, x, y, z, t, order=None, generator=False): - """ - Initialise a point that uses the extended coordinates internally. - """ - super(PointEdwards, self).__init__() - self.__curve = curve - if GMPY: # pragma: no branch - self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t)) - self.__order = order and mpz(order) - else: # pragma: no branch - self.__coords = (x, y, z, t) - self.__order = order - self.__generator = generator - self.__precompute = [] - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=None, - valid_encodings=None, - order=None, - generator=False, - ): - """ - Initialise the object from byte encoding of a point. - - `validate_encoding` and `valid_encodings` are provided for - compatibility with Weierstrass curves, they are ignored for Edwards - points. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ecdsa.ellipticcurve.CurveEdTw - :param None validate_encoding: Ignored, encoding is always validated - :param None valid_encodings: Ignored, there is just one encoding - supported - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: Flag to mark the point as a curve generator, - this will cause the library to pre-compute some values to - make repeated usages of the point much faster - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Initialised point on an Edwards curve - :rtype: PointEdwards - """ - coord_x, coord_y = super(PointEdwards, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return PointEdwards( - curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator - ) - - def _maybe_precompute(self): - if not self.__generator or self.__precompute: - return self.__precompute - - # since this code will execute just once, and it's fully deterministic, - # depend on atomicity of the last assignment to switch from empty - # self.__precompute to filled one and just ignore the unlikely - # situation when two threads execute it at the same time (as it won't - # lead to inconsistent __precompute) - order = self.__order - assert order - precompute = [] - i = 1 - order *= 2 - coord_x, coord_y, coord_z, coord_t = self.__coords - prime = self.__curve.p() - - doubler = PointEdwards( - self.__curve, coord_x, coord_y, coord_z, coord_t, order - ) - # for "protection" against Minerva we need 1 or 2 more bits depending - # on order bit size, but it's easier to just calculate one - # point more always - order *= 4 - - while i < order: - doubler = doubler.scale() - coord_x, coord_y = doubler.x(), doubler.y() - coord_t = coord_x * coord_y % prime - precompute.append((coord_x, coord_y, coord_t)) - - i *= 2 - doubler = doubler.double() - - self.__precompute = precompute - return self.__precompute - - def x(self): - """Return affine x coordinate.""" - X1, _, Z1, _ = self.__coords - if Z1 == 1: - return X1 - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - return X1 * z_inv % p - - def y(self): - """Return affine y coordinate.""" - _, Y1, Z1, _ = self.__coords - if Z1 == 1: - return Y1 - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - return Y1 * z_inv % p - - def curve(self): - """Return the curve of the point.""" - return self.__curve - - def order(self): - return self.__order - - def scale(self): - """ - Return point scaled so that z == 1. - - Modifies point in place, returns self. - """ - X1, Y1, Z1, _ = self.__coords - if Z1 == 1: - return self - - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - x = X1 * z_inv % p - y = Y1 * z_inv % p - t = x * y % p - self.__coords = (x, y, 1, t) - return self - - def __eq__(self, other): - """Compare for equality two points with each-other. - - Note: only points on the same curve can be equal. - """ - x1, y1, z1, t1 = self.__coords - if other is INFINITY: - return not x1 or not t1 - if isinstance(other, PointEdwards): - x2, y2, z2, t2 = other.__coords - else: - return NotImplemented - if self.__curve != other.curve(): - return False - p = self.__curve.p() - - # cross multiply to eliminate divisions - xn1 = x1 * z2 % p - xn2 = x2 * z1 % p - yn1 = y1 * z2 % p - yn2 = y2 * z1 % p - return xn1 == xn2 and yn1 == yn2 - - def __ne__(self, other): - """Compare for inequality two points with each-other.""" - return not self == other - - def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a): - """add two points, assume sane parameters.""" - # after add-2008-hwcd-2 - # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html - # NOTE: there are more efficient formulas for Z1 or Z2 == 1 - A = X1 * X2 % p - B = Y1 * Y2 % p - C = Z1 * T2 % p - D = T1 * Z2 % p - E = D + C - F = ((X1 - Y1) * (X2 + Y2) + B - A) % p - G = B + a * A - H = D - C - if not H: - return self._double(X1, Y1, Z1, T1, p, a) - X3 = E * F % p - Y3 = G * H % p - T3 = E * H % p - Z3 = F * G % p - - return X3, Y3, Z3, T3 - - def __add__(self, other): - """Add point to another.""" - if other == INFINITY: - return self - if ( - not isinstance(other, PointEdwards) - or self.__curve != other.__curve - ): - raise ValueError("The other point is on a different curve.") - - p, a = self.__curve.p(), self.__curve.a() - X1, Y1, Z1, T1 = self.__coords - X2, Y2, Z2, T2 = other.__coords - - X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a) - - if not X3 or not T3: - return INFINITY - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __radd__(self, other): - """Add other to self.""" - return self + other - - def _double(self, X1, Y1, Z1, T1, p, a): - """Double the point, assume sane parameters.""" - # after "dbl-2008-hwcd" - # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html - # NOTE: there are more efficient formulas for Z1 == 1 - A = X1 * X1 % p - B = Y1 * Y1 % p - C = 2 * Z1 * Z1 % p - D = a * A % p - E = ((X1 + Y1) * (X1 + Y1) - A - B) % p - G = D + B - F = G - C - H = D - B - X3 = E * F % p - Y3 = G * H % p - T3 = E * H % p - Z3 = F * G % p - - return X3, Y3, Z3, T3 - - def double(self): - """Return point added to itself.""" - X1, Y1, Z1, T1 = self.__coords - - if not X1 or not T1: - return INFINITY - - p, a = self.__curve.p(), self.__curve.a() - - X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a) - - # both Ed25519 and Ed448 have prime order, so no point added to - # itself will equal zero - if not X3 or not T3: # pragma: no branch - return INFINITY - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __rmul__(self, other): - """Multiply point by an integer.""" - return self * other - - def _mul_precompute(self, other): - """Multiply point by integer with precomputation table.""" - X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a() - _add = self._add - for X2, Y2, T2 in self.__precompute: - rem = other % 4 - if rem == 0 or rem == 2: - other //= 2 - elif rem == 3: - other = (other + 1) // 2 - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a) - else: - assert rem == 1 - other = (other - 1) // 2 - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a) - - if not X3 or not T3: - return INFINITY - - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __mul__(self, other): - """Multiply point by an integer.""" - X2, Y2, Z2, T2 = self.__coords - if not X2 or not T2 or not other: - return INFINITY - if other == 1: - return self - if self.__order: - # order*2 as a "protection" for Minerva - other = other % (self.__order * 2) - if self._maybe_precompute(): - return self._mul_precompute(other) - - X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates - p, a = self.__curve.p(), self.__curve.a() - _double = self._double - _add = self._add - - for i in reversed(self._naf(other)): - X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a) - if i < 0: - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a) - elif i > 0: - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a) - - if not X3 or not T3: - return INFINITY - - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - -# This one point is the Point At Infinity for all purposes: -INFINITY = Point(None, None, None) diff --git a/.venv/Lib/site-packages/ecdsa/errors.py b/.venv/Lib/site-packages/ecdsa/errors.py deleted file mode 100644 index 0184c05..0000000 --- a/.venv/Lib/site-packages/ecdsa/errors.py +++ /dev/null @@ -1,4 +0,0 @@ -class MalformedPointError(AssertionError): - """Raised in case the encoding of private or public key is malformed.""" - - pass diff --git a/.venv/Lib/site-packages/ecdsa/keys.py b/.venv/Lib/site-packages/ecdsa/keys.py deleted file mode 100644 index f74252c..0000000 --- a/.venv/Lib/site-packages/ecdsa/keys.py +++ /dev/null @@ -1,1631 +0,0 @@ -""" -Primary classes for performing signing and verification operations. -""" - -import binascii -from hashlib import sha1 -import os -from six import PY2 -from . import ecdsa, eddsa -from . import der, ssh -from . import rfc6979 -from . import ellipticcurve -from .curves import NIST192p, Curve, Ed25519, Ed448 -from .ecdsa import RSZeroError -from .util import string_to_number, number_to_string, randrange -from .util import sigencode_string, sigdecode_string, bit_length -from .util import ( - oid_ecPublicKey, - encoded_oid_ecPublicKey, - oid_ecDH, - oid_ecMQV, - MalformedSignature, -) -from ._compat import normalise_bytes -from .errors import MalformedPointError -from .ellipticcurve import PointJacobi, CurveEdTw - - -__all__ = [ - "BadSignatureError", - "BadDigestError", - "VerifyingKey", - "SigningKey", - "MalformedPointError", -] - - -class BadSignatureError(Exception): - """ - Raised when verification of signature failed. - - Will be raised irrespective of reason of the failure: - - * the calculated or provided hash does not match the signature - * the signature does not match the curve/public key - * the encoding of the signature is malformed - * the size of the signature does not match the curve of the VerifyingKey - """ - - pass - - -class BadDigestError(Exception): - """Raised in case the selected hash is too large for the curve.""" - - pass - - -def _truncate_and_convert_digest(digest, curve, allow_truncate): - """Truncates and converts digest to an integer.""" - if not allow_truncate: - if len(digest) > curve.baselen: - raise BadDigestError( - "this curve ({0}) is too short " - "for the length of your digest ({1})".format( - curve.name, 8 * len(digest) - ) - ) - else: - digest = digest[: curve.baselen] - number = string_to_number(digest) - if allow_truncate: - max_length = bit_length(curve.order) - # we don't use bit_length(number) as that truncates leading zeros - length = len(digest) * 8 - - # See NIST FIPS 186-4: - # - # When the length of the output of the hash function is greater - # than N (i.e., the bit length of q), then the leftmost N bits of - # the hash function output block shall be used in any calculation - # using the hash function output during the generation or - # verification of a digital signature. - # - # as such, we need to shift-out the low-order bits: - number >>= max(0, length - max_length) - - return number - - -class VerifyingKey(object): - """ - Class for handling keys that can verify signatures (public keys). - - :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the - cryptographic operations will take place - :ivar default_hashfunc: the function that will be used for hashing the - data. Should implement the same API as hashlib.sha1 - :vartype default_hashfunc: callable - :ivar pubkey: the actual public key - :vartype pubkey: ~ecdsa.ecdsa.Public_key - """ - - def __init__(self, _error__please_use_generate=None): - """Unsupported, please use one of the classmethods to initialise.""" - if not _error__please_use_generate: - raise TypeError( - "Please use VerifyingKey.generate() to construct me" - ) - self.curve = None - self.default_hashfunc = None - self.pubkey = None - - def __repr__(self): - pub_key = self.to_string("compressed") - if self.default_hashfunc: - hash_name = self.default_hashfunc().name - else: - hash_name = "None" - return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( - pub_key, self.curve, hash_name - ) - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, VerifyingKey): - return self.curve == other.curve and self.pubkey == other.pubkey - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - @classmethod - def from_public_point( - cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True - ): - """ - Initialise the object from a Point object. - - This is a low-level method, generally you will not want to use it. - - :param point: The point to wrap around, the actual public key - :type point: ~ecdsa.ellipticcurve.AbstractPoint - :param curve: The curve on which the point needs to reside, defaults - to NIST192p - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface - as :py:class:`hashlib.sha1` - :type hashfunc: callable - :type bool validate_point: whether to check if the point lays on curve - should always be used if the public point is not a result - of our own calculation - - :raises MalformedPointError: if the public point does not lay on the - curve - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - self = cls(_error__please_use_generate=True) - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method incompatible with Edwards curves") - if not isinstance(point, ellipticcurve.PointJacobi): - point = ellipticcurve.PointJacobi.from_affine(point) - self.curve = curve - self.default_hashfunc = hashfunc - try: - self.pubkey = ecdsa.Public_key( - curve.generator, point, validate_point - ) - except ecdsa.InvalidPointError: - raise MalformedPointError("Point does not lay on the curve") - self.pubkey.order = curve.order - return self - - def precompute(self, lazy=False): - """ - Precompute multiplication tables for faster signature verification. - - Calling this method will cause the library to precompute the - scalar multiplication tables, used in signature verification. - While it's an expensive operation (comparable to performing - as many signatures as the bit size of the curve, i.e. 256 for NIST256p) - it speeds up verification 2 times. You should call this method - if you expect to verify hundreds of signatures (or more) using the same - VerifyingKey object. - - Note: You should call this method only once, this method generates a - new precomputation table every time it's called. - - :param bool lazy: whether to calculate the precomputation table now - (if set to False) or if it should be delayed to the time of first - use (when set to True) - """ - if isinstance(self.curve.curve, CurveEdTw): - pt = self.pubkey.point - self.pubkey.point = ellipticcurve.PointEdwards( - pt.curve(), - pt.x(), - pt.y(), - 1, - pt.x() * pt.y(), - self.curve.order, - generator=True, - ) - else: - self.pubkey.point = ellipticcurve.PointJacobi.from_affine( - self.pubkey.point, True - ) - # as precomputation in now delayed to the time of first use of the - # point and we were asked specifically to precompute now, make - # sure the precomputation is performed now to preserve the behaviour - if not lazy: - self.pubkey.point * 2 - - @classmethod - def from_string( - cls, - string, - curve=NIST192p, - hashfunc=sha1, - validate_point=True, - valid_encodings=None, - ): - """ - Initialise the object from byte encoding of public key. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - It also works with the native encoding of Ed25519 and Ed448 public - keys (technically those are compressed, but encoded differently than - in other signature systems). - - Note, while the method is named "from_string" it's a misnomer from - Python 2 days when there were no binary strings. In Python 3 the - input needs to be a bytes-like object. - - :param string: single point encoding of the public key - :type string: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as - hashlib.sha1. Ignored for EdDSA. - :type hashfunc: callable - :param validate_point: whether to verify that the point lays on the - provided curve or not, defaults to True. Ignored for EdDSA. - :type validate_point: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - Ignored for EdDSA. - :type valid_encodings: :term:`set-like object` - - :raises MalformedPointError: if the public point does not lay on the - curve or the encoding is invalid - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None # ignored for EdDSA - try: - self.pubkey = eddsa.PublicKey(curve.generator, string) - except ValueError: - raise MalformedPointError("Malformed point for the curve") - return self - - point = PointJacobi.from_bytes( - curve.curve, - string, - validate_encoding=validate_point, - valid_encodings=valid_encodings, - ) - return cls.from_public_point(point, curve, hashfunc, validate_point) - - @classmethod - def from_pem( - cls, - string, - hashfunc=sha1, - valid_encodings=None, - valid_curve_encodings=None, - ): - """ - Initialise from public key stored in :term:`PEM` format. - - The PEM header of the key should be ``BEGIN PUBLIC KEY``. - - See the :func:`~VerifyingKey.from_der()` method for details of the - format supported. - - Note: only a single PEM object decoding is supported in provided - string. - - :param string: text with PEM-encoded public ECDSA key - :type string: str - :param valid_encodings: list of allowed point encodings. - By default :term:`uncompressed`, :term:`compressed`, and - :term:`hybrid`. To read malformed files, include - :term:`raw encoding` with ``raw`` in the list. - :type valid_encodings: :term:`set-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - return cls.from_der( - der.unpem(string), - hashfunc=hashfunc, - valid_encodings=valid_encodings, - valid_curve_encodings=valid_curve_encodings, - ) - - @classmethod - def from_der( - cls, - string, - hashfunc=sha1, - valid_encodings=None, - valid_curve_encodings=None, - ): - """ - Initialise the key stored in :term:`DER` format. - - The expected format of the key is the SubjectPublicKeyInfo structure - from RFC5912 (for RSA keys, it's known as the PKCS#1 format):: - - SubjectPublicKeyInfo {PUBLIC-KEY: IOSet} ::= SEQUENCE { - algorithm AlgorithmIdentifier {PUBLIC-KEY, {IOSet}}, - subjectPublicKey BIT STRING - } - - Note: only public EC keys are supported by this method. The - SubjectPublicKeyInfo.algorithm.algorithm field must specify - id-ecPublicKey (see RFC3279). - - Only the named curve encoding is supported, thus the - SubjectPublicKeyInfo.algorithm.parameters field needs to be an - object identifier. A sequence in that field indicates an explicit - parameter curve encoding, this format is not supported. A NULL object - in that field indicates an "implicitlyCA" encoding, where the curve - parameters come from CA certificate, those, again, are not supported. - - :param string: binary string with the DER encoding of public ECDSA key - :type string: bytes-like object - :param valid_encodings: list of allowed point encodings. - By default :term:`uncompressed`, :term:`compressed`, and - :term:`hybrid`. To read malformed files, include - :term:`raw encoding` with ``raw`` in the list. - :type valid_encodings: :term:`set-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if valid_encodings is None: - valid_encodings = set(["uncompressed", "compressed", "hybrid"]) - string = normalise_bytes(string) - # [[oid_ecPublicKey,oid_curve], point_str_bitstring] - s1, empty = der.remove_sequence(string) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER pubkey: %s" % binascii.hexlify(empty) - ) - s2, point_str_bitstring = der.remove_sequence(s1) - # s2 = oid_ecPublicKey,oid_curve - oid_pk, rest = der.remove_object(s2) - if oid_pk in (Ed25519.oid, Ed448.oid): - if oid_pk == Ed25519.oid: - curve = Ed25519 - else: - assert oid_pk == Ed448.oid - curve = Ed448 - point_str, empty = der.remove_bitstring(point_str_bitstring, 0) - if empty: - raise der.UnexpectedDER("trailing junk after public key") - return cls.from_string(point_str, curve, None) - if not oid_pk == oid_ecPublicKey: - raise der.UnexpectedDER( - "Unexpected object identifier in DER " - "encoding: {0!r}".format(oid_pk) - ) - curve = Curve.from_der(rest, valid_curve_encodings) - point_str, empty = der.remove_bitstring(point_str_bitstring, 0) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after pubkey pointstring: %s" - % binascii.hexlify(empty) - ) - # raw encoding of point is invalid in DER files - if len(point_str) == curve.verifying_key_length: - raise der.UnexpectedDER("Malformed encoding of public point") - return cls.from_string( - point_str, - curve, - hashfunc=hashfunc, - valid_encodings=valid_encodings, - ) - - @classmethod - def from_public_key_recovery( - cls, - signature, - data, - curve, - hashfunc=sha1, - sigdecode=sigdecode_string, - allow_truncate=True, - ): - """ - Return keys that can be used as verifiers of the provided signature. - - Tries to recover the public key that can be used to verify the - signature, usually returns two keys like that. - - :param signature: the byte string with the encoded signature - :type signature: bytes-like object - :param data: the data to be hashed for signature verification - :type data: bytes-like object - :param curve: the curve over which the signature was performed - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :param bool allow_truncate: if True, the provided hashfunc can generate - values larger than the bit size of the order of the curve, the - extra bits (at the end of the digest) will be truncated. - :type sigdecode: callable - - :return: Initialised VerifyingKey objects - :rtype: list of VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - data = normalise_bytes(data) - digest = hashfunc(data).digest() - return cls.from_public_key_recovery_with_digest( - signature, - digest, - curve, - hashfunc=hashfunc, - sigdecode=sigdecode, - allow_truncate=allow_truncate, - ) - - @classmethod - def from_public_key_recovery_with_digest( - cls, - signature, - digest, - curve, - hashfunc=sha1, - sigdecode=sigdecode_string, - allow_truncate=False, - ): - """ - Return keys that can be used as verifiers of the provided signature. - - Tries to recover the public key that can be used to verify the - signature, usually returns two keys like that. - - :param signature: the byte string with the encoded signature - :type signature: bytes-like object - :param digest: the hash value of the message signed by the signature - :type digest: bytes-like object - :param curve: the curve over which the signature was performed - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided hashfunc can generate - values larger than the bit size of the order of the curve (and - the length of provided `digest`), the extra bits (at the end of the - digest) will be truncated. - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - generator = curve.generator - r, s = sigdecode(signature, generator.order()) - sig = ecdsa.Signature(r, s) - - digest = normalise_bytes(digest) - digest_as_number = _truncate_and_convert_digest( - digest, curve, allow_truncate - ) - pks = sig.recover_public_keys(digest_as_number, generator) - - # Transforms the ecdsa.Public_key object into a VerifyingKey - verifying_keys = [ - cls.from_public_point(pk.point, curve, hashfunc) for pk in pks - ] - return verifying_keys - - def to_string(self, encoding="raw"): - """ - Convert the public key to a byte string. - - The method by default uses the :term:`raw encoding` (specified - by `encoding="raw"`. It can also output keys in :term:`uncompressed`, - :term:`compressed` and :term:`hybrid` formats. - - Remember that the curve identification is not part of the encoding - so to decode the point using :func:`~VerifyingKey.from_string`, curve - needs to be specified. - - Note: while the method is called "to_string", it's a misnomer from - Python 2 days when character strings and byte strings shared type. - On Python 3 the returned type will be `bytes`. - - :return: :term:`raw encoding` of the public key (public point) on the - curve - :rtype: bytes - """ - assert encoding in ("raw", "uncompressed", "compressed", "hybrid") - return self.pubkey.point.to_bytes(encoding) - - def to_pem( - self, point_encoding="uncompressed", curve_parameters_encoding=None - ): - """ - Convert the public key to the :term:`PEM` format. - - The PEM header of the key will be ``BEGIN PUBLIC KEY``. - - The format of the key is described in the - :func:`~VerifyingKey.from_der()` method. - This method supports only "named curve" encoding of keys. - - :param str point_encoding: specification of the encoding format - of public keys. "uncompressed" is most portable, "compressed" is - smallest. "hybrid" is uncommon and unsupported by most - implementations, it is as big as "uncompressed". - :param str curve_parameters_encoding: the encoding for curve parameters - to use, by default tries to use ``named_curve`` encoding, - if that is not possible, falls back to ``explicit`` encoding. - - :return: portable encoding of the public key - :rtype: bytes - - .. warning:: The PEM is encoded to US-ASCII, it needs to be - re-encoded if the system is incompatible (e.g. uses UTF-16) - """ - return der.topem( - self.to_der(point_encoding, curve_parameters_encoding), - "PUBLIC KEY", - ) - - def to_der( - self, point_encoding="uncompressed", curve_parameters_encoding=None - ): - """ - Convert the public key to the :term:`DER` format. - - The format of the key is described in the - :func:`~VerifyingKey.from_der()` method. - This method supports only "named curve" encoding of keys. - - :param str point_encoding: specification of the encoding format - of public keys. "uncompressed" is most portable, "compressed" is - smallest. "hybrid" is uncommon and unsupported by most - implementations, it is as big as "uncompressed". - :param str curve_parameters_encoding: the encoding for curve parameters - to use, by default tries to use ``named_curve`` encoding, - if that is not possible, falls back to ``explicit`` encoding. - - :return: DER encoding of the public key - :rtype: bytes - """ - if point_encoding == "raw": - raise ValueError("raw point_encoding not allowed in DER") - point_str = self.to_string(point_encoding) - if isinstance(self.curve.curve, CurveEdTw): - return der.encode_sequence( - der.encode_sequence(der.encode_oid(*self.curve.oid)), - der.encode_bitstring(bytes(point_str), 0), - ) - return der.encode_sequence( - der.encode_sequence( - encoded_oid_ecPublicKey, - self.curve.to_der(curve_parameters_encoding, point_encoding), - ), - # 0 is the number of unused bits in the - # bit string - der.encode_bitstring(point_str, 0), - ) - - def to_ssh(self): - """ - Convert the public key to the SSH format. - - :return: SSH encoding of the public key - :rtype: bytes - """ - return ssh.serialize_public( - self.curve.name, - self.to_string(), - ) - - def verify( - self, - signature, - data, - hashfunc=None, - sigdecode=sigdecode_string, - allow_truncate=True, - ): - """ - Verify a signature made over provided data. - - Will hash `data` to verify the signature. - - By default expects signature in :term:`raw encoding`. Can also be used - to verify signatures in ASN.1 DER encoding by using - :func:`ecdsa.util.sigdecode_der` - as the `sigdecode` parameter. - - :param signature: encoding of the signature - :type signature: sigdecode method dependent - :param data: data signed by the `signature`, will be hashed using - `hashfunc`, if specified, or default hash function - :type data: :term:`bytes-like object` - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when verifying - SHA-384 output using NIST256p or in similar situations. Defaults to - True. - - :raises BadSignatureError: if the signature is invalid or malformed - - :return: True if the verification was successful - :rtype: bool - """ - # signature doesn't have to be a bytes-like-object so don't normalise - # it, the decoders will do that - data = normalise_bytes(data) - if isinstance(self.curve.curve, CurveEdTw): - signature = normalise_bytes(signature) - try: - return self.pubkey.verify(data, signature) - except (ValueError, MalformedPointError) as e: - raise BadSignatureError("Signature verification failed", e) - - hashfunc = hashfunc or self.default_hashfunc - digest = hashfunc(data).digest() - return self.verify_digest(signature, digest, sigdecode, allow_truncate) - - def verify_digest( - self, - signature, - digest, - sigdecode=sigdecode_string, - allow_truncate=False, - ): - """ - Verify a signature made over provided hash value. - - By default expects signature in :term:`raw encoding`. Can also be used - to verify signatures in ASN.1 DER encoding by using - :func:`ecdsa.util.sigdecode_der` - as the `sigdecode` parameter. - - :param signature: encoding of the signature - :type signature: sigdecode method dependent - :param digest: raw hash value that the signature authenticates. - :type digest: :term:`bytes-like object` - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when verifying - SHA-384 output using NIST256p or in similar situations. - - :raises BadSignatureError: if the signature is invalid or malformed - :raises BadDigestError: if the provided digest is too big for the curve - associated with this VerifyingKey and allow_truncate was not set - - :return: True if the verification was successful - :rtype: bool - """ - # signature doesn't have to be a bytes-like-object so don't normalise - # it, the decoders will do that - digest = normalise_bytes(digest) - number = _truncate_and_convert_digest( - digest, - self.curve, - allow_truncate, - ) - - try: - r, s = sigdecode(signature, self.pubkey.order) - except (der.UnexpectedDER, MalformedSignature) as e: - raise BadSignatureError("Malformed formatting of signature", e) - sig = ecdsa.Signature(r, s) - if self.pubkey.verifies(number, sig): - return True - raise BadSignatureError("Signature verification failed") - - -class SigningKey(object): - """ - Class for handling keys that can create signatures (private keys). - - :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the - cryptographic operations will take place - :ivar default_hashfunc: the function that will be used for hashing the - data. Should implement the same API as :py:class:`hashlib.sha1` - :ivar int baselen: the length of a :term:`raw encoding` of private key - :ivar `~ecdsa.keys.VerifyingKey` verifying_key: the public key - associated with this private key - :ivar `~ecdsa.ecdsa.Private_key` privkey: the actual private key - """ - - def __init__(self, _error__please_use_generate=None): - """Unsupported, please use one of the classmethods to initialise.""" - if not _error__please_use_generate: - raise TypeError("Please use SigningKey.generate() to construct me") - self.curve = None - self.default_hashfunc = None - self.baselen = None - self.verifying_key = None - self.privkey = None - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, SigningKey): - return ( - self.curve == other.curve - and self.verifying_key == other.verifying_key - and self.privkey == other.privkey - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - @classmethod - def _twisted_edwards_keygen(cls, curve, entropy): - """Generate a private key on a Twisted Edwards curve.""" - if not entropy: - entropy = os.urandom - random = entropy(curve.baselen) - private_key = eddsa.PrivateKey(curve.generator, random) - public_key = private_key.public_key() - - verifying_key = VerifyingKey.from_string( - public_key.public_key(), curve - ) - - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None - self.baselen = curve.baselen - self.privkey = private_key - self.verifying_key = verifying_key - return self - - @classmethod - def _weierstrass_keygen(cls, curve, entropy, hashfunc): - """Generate a private key on a Weierstrass curve.""" - secexp = randrange(curve.order, entropy) - return cls.from_secret_exponent(secexp, curve, hashfunc) - - @classmethod - def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): - """ - Generate a random private key. - - :param curve: The curve on which the point needs to reside, defaults - to NIST192p - :type curve: ~ecdsa.curves.Curve - :param entropy: Source of randomness for generating the private keys, - should provide cryptographically secure random numbers if the keys - need to be secure. Uses os.urandom() by default. - :type entropy: callable - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if isinstance(curve.curve, CurveEdTw): - return cls._twisted_edwards_keygen(curve, entropy) - return cls._weierstrass_keygen(curve, entropy, hashfunc) - - @classmethod - def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): - """ - Create a private key from a random integer. - - Note: it's a low level method, it's recommended to use the - :func:`~SigningKey.generate` method to create private keys. - - :param int secexp: secret multiplier (the actual private key in ECDSA). - Needs to be an integer between 1 and the curve order. - :param curve: The curve on which the point needs to reside - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :raises MalformedPointError: when the provided secexp is too large - or too small for the curve selected - :raises RuntimeError: if the generation of public key from private - key failed - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError( - "Edwards keys don't support setting the secret scalar " - "(exponent) directly" - ) - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = hashfunc - self.baselen = curve.baselen - n = curve.order - if not 1 <= secexp < n: - raise MalformedPointError( - "Invalid value for secexp, expected integer " - "between 1 and {0}".format(n) - ) - pubkey_point = curve.generator * secexp - if hasattr(pubkey_point, "scale"): - pubkey_point = pubkey_point.scale() - self.verifying_key = VerifyingKey.from_public_point( - pubkey_point, curve, hashfunc, False - ) - pubkey = self.verifying_key.pubkey - self.privkey = ecdsa.Private_key(pubkey, secexp) - self.privkey.order = n - return self - - @classmethod - def from_string(cls, string, curve=NIST192p, hashfunc=sha1): - """ - Decode the private key from :term:`raw encoding`. - - Note: the name of this method is a misnomer coming from days of - Python 2, when binary strings and character strings shared a type. - In Python 3, the expected type is `bytes`. - - :param string: the raw encoding of the private key - :type string: :term:`bytes-like object` - :param curve: The curve on which the point needs to reside - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - string = normalise_bytes(string) - - if len(string) != curve.baselen: - raise MalformedPointError( - "Invalid length of private key, received {0}, " - "expected {1}".format(len(string), curve.baselen) - ) - if isinstance(curve.curve, CurveEdTw): - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None # Ignored for EdDSA - self.baselen = curve.baselen - self.privkey = eddsa.PrivateKey(curve.generator, string) - self.verifying_key = VerifyingKey.from_string( - self.privkey.public_key().public_key(), curve - ) - return self - secexp = string_to_number(string) - return cls.from_secret_exponent(secexp, curve, hashfunc) - - @classmethod - def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None): - """ - Initialise from key stored in :term:`PEM` format. - - The PEM formats supported are the un-encrypted RFC5915 - (the ssleay format) supported by OpenSSL, and the more common - un-encrypted RFC5958 (the PKCS #8 format). - - The legacy format files have the header with the string - ``BEGIN EC PRIVATE KEY``. - PKCS#8 files have the header ``BEGIN PRIVATE KEY``. - Encrypted files (ones that include the string - ``Proc-Type: 4,ENCRYPTED`` - right after the PEM header) are not supported. - - See :func:`~SigningKey.from_der` for ASN.1 syntax of the objects in - this files. - - :param string: text with PEM-encoded private ECDSA key - :type string: str - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - :raises UnexpectedDER: if the encoding of the PEM file is incorrect - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if not PY2 and isinstance(string, str): # pragma: no branch - string = string.encode() - - # The privkey pem may have multiple sections, commonly it also has - # "EC PARAMETERS", we need just "EC PRIVATE KEY". PKCS#8 should not - # have the "EC PARAMETERS" section; it's just "PRIVATE KEY". - private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----") - if private_key_index == -1: - private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----") - - return cls.from_der( - der.unpem(string[private_key_index:]), - hashfunc, - valid_curve_encodings, - ) - - @classmethod - def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None): - """ - Initialise from key stored in :term:`DER` format. - - The DER formats supported are the un-encrypted RFC5915 - (the ssleay format) supported by OpenSSL, and the more common - un-encrypted RFC5958 (the PKCS #8 format). - - Both formats contain an ASN.1 object following the syntax specified - in RFC5915:: - - ECPrivateKey ::= SEQUENCE { - version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1), - privateKey OCTET STRING, - parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, - publicKey [1] BIT STRING OPTIONAL - } - - `publicKey` field is ignored completely (errors, if any, in it will - be undetected). - - Two formats are supported for the `parameters` field: the named - curve and the explicit encoding of curve parameters. - In the legacy ssleay format, this implementation requires the optional - `parameters` field to get the curve name. In PKCS #8 format, the curve - is part of the PrivateKeyAlgorithmIdentifier. - - The PKCS #8 format includes an ECPrivateKey object as the `privateKey` - field within a larger structure:: - - OneAsymmetricKey ::= SEQUENCE { - version Version, - privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - privateKey PrivateKey, - attributes [0] Attributes OPTIONAL, - ..., - [[2: publicKey [1] PublicKey OPTIONAL ]], - ... - } - - The `attributes` and `publicKey` fields are completely ignored; errors - in them will not be detected. - - :param string: binary string with DER-encoded private ECDSA key - :type string: :term:`bytes-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - Ignored for EdDSA. - :type valid_curve_encodings: :term:`set-like object` - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - :raises UnexpectedDER: if the encoding of the DER file is incorrect - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - s = normalise_bytes(string) - curve = None - - s, empty = der.remove_sequence(s) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER privkey: %s" % binascii.hexlify(empty) - ) - - version, s = der.remove_integer(s) - - # At this point, PKCS #8 has a sequence containing the algorithm - # identifier and the curve identifier. The ssleay format instead has - # an octet string containing the key data, so this is how we can - # distinguish the two formats. - if der.is_sequence(s): - if version not in (0, 1): - raise der.UnexpectedDER( - "expected version '0' or '1' at start of privkey, got %d" - % version - ) - - sequence, s = der.remove_sequence(s) - algorithm_oid, algorithm_identifier = der.remove_object(sequence) - - if algorithm_oid in (Ed25519.oid, Ed448.oid): - if algorithm_identifier: - raise der.UnexpectedDER( - "Non NULL parameters for a EdDSA key" - ) - key_str_der, s = der.remove_octet_string(s) - - # As RFC5958 describe, there are may be optional Attributes - # and Publickey. Don't raise error if something after - # Privatekey - - # TODO parse attributes or validate publickey - # if s: - # raise der.UnexpectedDER( - # "trailing junk inside the privateKey" - # ) - key_str, s = der.remove_octet_string(key_str_der) - if s: - raise der.UnexpectedDER( - "trailing junk after the encoded private key" - ) - - if algorithm_oid == Ed25519.oid: - curve = Ed25519 - else: - assert algorithm_oid == Ed448.oid - curve = Ed448 - - return cls.from_string(key_str, curve, None) - - if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV): - raise der.UnexpectedDER( - "unexpected algorithm identifier '%s'" % (algorithm_oid,) - ) - - curve = Curve.from_der(algorithm_identifier, valid_curve_encodings) - - # Up next is an octet string containing an ECPrivateKey. Ignore - # the optional "attributes" and "publicKey" fields that come after. - s, _ = der.remove_octet_string(s) - - # Unpack the ECPrivateKey to get to the key data octet string, - # and rejoin the ssleay parsing path. - s, empty = der.remove_sequence(s) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER privkey: %s" - % binascii.hexlify(empty) - ) - - version, s = der.remove_integer(s) - - # The version of the ECPrivateKey must be 1. - if version != 1: - raise der.UnexpectedDER( - "expected version '1' at start of DER privkey, got %d" - % version - ) - - privkey_str, s = der.remove_octet_string(s) - - if not curve: - tag, curve_oid_str, s = der.remove_constructed(s) - if tag != 0: - raise der.UnexpectedDER( - "expected tag 0 in DER privkey, got %d" % tag - ) - curve = Curve.from_der(curve_oid_str, valid_curve_encodings) - - # we don't actually care about the following fields - # - # tag, pubkey_bitstring, s = der.remove_constructed(s) - # if tag != 1: - # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" - # % tag) - # pubkey_str = der.remove_bitstring(pubkey_bitstring, 0) - # if empty != "": - # raise der.UnexpectedDER("trailing junk after DER privkey " - # "pubkeystr: %s" - # % binascii.hexlify(empty)) - - # our from_string method likes fixed-length privkey strings - if len(privkey_str) < curve.baselen: - privkey_str = ( - b"\x00" * (curve.baselen - len(privkey_str)) + privkey_str - ) - return cls.from_string(privkey_str, curve, hashfunc) - - def to_string(self): - """ - Convert the private key to :term:`raw encoding`. - - Note: while the method is named "to_string", its name comes from - Python 2 days, when binary and character strings used the same type. - The type used in Python 3 is `bytes`. - - :return: raw encoding of private key - :rtype: bytes - """ - if isinstance(self.curve.curve, CurveEdTw): - return bytes(self.privkey.private_key) - secexp = self.privkey.secret_multiplier - s = number_to_string(secexp, self.privkey.order) - return s - - def to_pem( - self, - point_encoding="uncompressed", - format="ssleay", - curve_parameters_encoding=None, - ): - """ - Convert the private key to the :term:`PEM` format. - - See :func:`~SigningKey.from_pem` method for format description. - - Only the named curve format is supported. - The public key will be included in generated string. - - The PEM header will specify ``BEGIN EC PRIVATE KEY`` or - ``BEGIN PRIVATE KEY``, depending on the desired format. - - :param str point_encoding: format to use for encoding public point - :param str format: either ``ssleay`` (default) or ``pkcs8`` - :param str curve_parameters_encoding: format of encoded curve - parameters, default depends on the curve, if the curve has - an associated OID, ``named_curve`` format will be used, - if no OID is associated with the curve, the fallback of - ``explicit`` parameters will be used. - - :return: PEM encoded private key - :rtype: bytes - - .. warning:: The PEM is encoded to US-ASCII, it needs to be - re-encoded if the system is incompatible (e.g. uses UTF-16) - """ - # TODO: "BEGIN ECPARAMETERS" - assert format in ("ssleay", "pkcs8") - header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY" - return der.topem( - self.to_der(point_encoding, format, curve_parameters_encoding), - header, - ) - - def _encode_eddsa(self): - """Create a PKCS#8 encoding of EdDSA keys.""" - ec_private_key = der.encode_octet_string(self.to_string()) - return der.encode_sequence( - der.encode_integer(0), - der.encode_sequence(der.encode_oid(*self.curve.oid)), - der.encode_octet_string(ec_private_key), - ) - - def to_der( - self, - point_encoding="uncompressed", - format="ssleay", - curve_parameters_encoding=None, - ): - """ - Convert the private key to the :term:`DER` format. - - See :func:`~SigningKey.from_der` method for format specification. - - Only the named curve format is supported. - The public key will be included in the generated string. - - :param str point_encoding: format to use for encoding public point - Ignored for EdDSA - :param str format: either ``ssleay`` (default) or ``pkcs8``. - EdDSA keys require ``pkcs8``. - :param str curve_parameters_encoding: format of encoded curve - parameters, default depends on the curve, if the curve has - an associated OID, ``named_curve`` format will be used, - if no OID is associated with the curve, the fallback of - ``explicit`` parameters will be used. - Ignored for EdDSA. - - :return: DER encoded private key - :rtype: bytes - """ - # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), - # cont[1],bitstring]) - if point_encoding == "raw": - raise ValueError("raw encoding not allowed in DER") - assert format in ("ssleay", "pkcs8") - if isinstance(self.curve.curve, CurveEdTw): - if format != "pkcs8": - raise ValueError("Only PKCS#8 format supported for EdDSA keys") - return self._encode_eddsa() - encoded_vk = self.get_verifying_key().to_string(point_encoding) - priv_key_elems = [ - der.encode_integer(1), - der.encode_octet_string(self.to_string()), - ] - if format == "ssleay": - priv_key_elems.append( - der.encode_constructed( - 0, self.curve.to_der(curve_parameters_encoding) - ) - ) - # the 0 in encode_bitstring specifies the number of unused bits - # in the `encoded_vk` string - priv_key_elems.append( - der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)) - ) - ec_private_key = der.encode_sequence(*priv_key_elems) - - if format == "ssleay": - return ec_private_key - else: - return der.encode_sequence( - # version = 1 means the public key is not present in the - # top-level structure. - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(*oid_ecPublicKey), - self.curve.to_der(curve_parameters_encoding), - ), - der.encode_octet_string(ec_private_key), - ) - - def to_ssh(self): - """ - Convert the private key to the SSH format. - - :return: SSH encoded private key - :rtype: bytes - """ - return ssh.serialize_private( - self.curve.name, - self.verifying_key.to_string(), - self.to_string(), - ) - - def get_verifying_key(self): - """ - Return the VerifyingKey associated with this private key. - - Equivalent to reading the `verifying_key` field of an instance. - - :return: a public key that can be used to verify the signatures made - with this SigningKey - :rtype: VerifyingKey - """ - return self.verifying_key - - def sign_deterministic( - self, - data, - hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b"", - ): - """ - Create signature over data. - - For Weierstrass curves it uses the deterministic RFC6979 algorithm. - For Edwards curves it uses the standard EdDSA algorithm. - - For ECDSA the data will be hashed using the `hashfunc` function before - signing. - For EdDSA the data will be hashed with the hash associated with the - curve (SHA-512 for Ed25519 and SHAKE-256 for Ed448). - - This is the recommended method for performing signatures when hashing - of data is necessary. - - :param data: data to be hashed and computed signature over - :type data: :term:`bytes-like object` - :param hashfunc: hash function to use for computing the signature, - if unspecified, the default hash function selected during - object initialisation will be used (see - `VerifyingKey.default_hashfunc`). The object needs to implement - the same interface as hashlib.sha1. - Ignored with EdDSA. - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` - as examples of such functions. - Ignored with EdDSA. - :type sigencode: callable - :param extra_entropy: additional data that will be fed into the random - number generator used in the RFC6979 process. Entirely optional. - Ignored with EdDSA. - :type extra_entropy: :term:`bytes-like object` - - :return: encoded signature over `data` - :rtype: bytes or sigencode function dependent type - """ - hashfunc = hashfunc or self.default_hashfunc - data = normalise_bytes(data) - - if isinstance(self.curve.curve, CurveEdTw): - return self.privkey.sign(data) - - extra_entropy = normalise_bytes(extra_entropy) - digest = hashfunc(data).digest() - - return self.sign_digest_deterministic( - digest, - hashfunc=hashfunc, - sigencode=sigencode, - extra_entropy=extra_entropy, - allow_truncate=True, - ) - - def sign_digest_deterministic( - self, - digest, - hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b"", - allow_truncate=False, - ): - """ - Create signature for digest using the deterministic RFC6979 algorithm. - - `digest` should be the output of cryptographically secure hash function - like SHA256 or SHA-3-256. - - This is the recommended method for performing signatures when no - hashing of data is necessary. - - :param digest: hash of data that will be signed - :type digest: :term:`bytes-like object` - :param hashfunc: hash function to use for computing the random "k" - value from RFC6979 process, - if unspecified, the default hash function selected during - object initialisation will be used (see - :attr:`.VerifyingKey.default_hashfunc`). The object needs to - implement - the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`. - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See :func:`~ecdsa.util.sigencode_string` and - :func:`~ecdsa.util.sigencode_der` - as examples of such functions. - :type sigencode: callable - :param extra_entropy: additional data that will be fed into the random - number generator used in the RFC6979 process. Entirely optional. - :type extra_entropy: :term:`bytes-like object` - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. - - :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependent type - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - secexp = self.privkey.secret_multiplier - hashfunc = hashfunc or self.default_hashfunc - digest = normalise_bytes(digest) - extra_entropy = normalise_bytes(extra_entropy) - - def simple_r_s(r, s, order): - return r, s, order - - retry_gen = 0 - while True: - k = rfc6979.generate_k( - self.curve.generator.order(), - secexp, - hashfunc, - digest, - retry_gen=retry_gen, - extra_entropy=extra_entropy, - ) - try: - r, s, order = self.sign_digest( - digest, - sigencode=simple_r_s, - k=k, - allow_truncate=allow_truncate, - ) - break - except RSZeroError: - retry_gen += 1 - - return sigencode(r, s, order) - - def sign( - self, - data, - entropy=None, - hashfunc=None, - sigencode=sigencode_string, - k=None, - allow_truncate=True, - ): - """ - Create signature over data. - - Uses the probabilistic ECDSA algorithm for Weierstrass curves - (NIST256p, etc.) and the deterministic EdDSA algorithm for the - Edwards curves (Ed25519, Ed448). - - This method uses the standard ECDSA algorithm that requires a - cryptographically secure random number generator. - - It's recommended to use the :func:`~SigningKey.sign_deterministic` - method instead of this one. - - :param data: data that will be hashed for signing - :type data: :term:`bytes-like object` - :param callable entropy: randomness source, :func:`os.urandom` by - default. Ignored with EdDSA. - :param hashfunc: hash function to use for hashing the provided - ``data``. - If unspecified the default hash function selected during - object initialisation will be used (see - :attr:`.VerifyingKey.default_hashfunc`). - Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`. - The output length of the - hash (in bytes) must not be longer than the length of the curve - order (rounded up to the nearest byte), so using SHA256 with - NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish - unlikely event of a hash output larger than the curve order, the - hash will effectively be wrapped mod n). - If you want to explicitly allow use of large hashes with small - curves set the ``allow_truncate`` to ``True``. - Use ``hashfunc=hashlib.sha1`` to match openssl's - ``-ecdsa-with-SHA1`` mode, - or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's - ``-ecdsa-with-SHA256``. - Ignored for EdDSA - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See :func:`~ecdsa.util.sigencode_string` and - :func:`~ecdsa.util.sigencode_der` - as examples of such functions. - Ignored for EdDSA - :type sigencode: callable - :param int k: a pre-selected nonce for calculating the signature. - In typical use cases, it should be set to None (the default) to - allow its generation from an entropy source. - Ignored for EdDSA. - :param bool allow_truncate: if ``True``, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. True by - default. - Ignored for EdDSA. - - :raises RSZeroError: in the unlikely event when *r* parameter or - *s* parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different ``k``, or use the - :func:`~SigningKey.sign_deterministic` in such case. - - :return: encoded signature of the hash of `data` - :rtype: bytes or sigencode function dependent type - """ - hashfunc = hashfunc or self.default_hashfunc - data = normalise_bytes(data) - if isinstance(self.curve.curve, CurveEdTw): - return self.sign_deterministic(data) - h = hashfunc(data).digest() - return self.sign_digest(h, entropy, sigencode, k, allow_truncate) - - def sign_digest( - self, - digest, - entropy=None, - sigencode=sigencode_string, - k=None, - allow_truncate=False, - ): - """ - Create signature over digest using the probabilistic ECDSA algorithm. - - This method uses the standard ECDSA algorithm that requires a - cryptographically secure random number generator. - - This method does not hash the input. - - It's recommended to use the - :func:`~SigningKey.sign_digest_deterministic` method - instead of this one. - - :param digest: hash value that will be signed - :type digest: :term:`bytes-like object` - :param callable entropy: randomness source, os.urandom by default - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` - as examples of such functions. - :type sigencode: callable - :param int k: a pre-selected nonce for calculating the signature. - In typical use cases, it should be set to None (the default) to - allow its generation from an entropy source. - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. - - :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different 'k', or use the - :func:`~SigningKey.sign_digest_deterministic` in such case. - - :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependent type - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - digest = normalise_bytes(digest) - number = _truncate_and_convert_digest( - digest, - self.curve, - allow_truncate, - ) - r, s = self.sign_number(number, entropy, k) - return sigencode(r, s, self.privkey.order) - - def sign_number(self, number, entropy=None, k=None): - """ - Sign an integer directly. - - Note, this is a low level method, usually you will want to use - :func:`~SigningKey.sign_deterministic` or - :func:`~SigningKey.sign_digest_deterministic`. - - :param int number: number to sign using the probabilistic ECDSA - algorithm. - :param callable entropy: entropy source, os.urandom by default - :param int k: pre-selected nonce for signature operation. If unset - it will be selected at random using the entropy source. - - :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different 'k', or use the - :func:`~SigningKey.sign_digest_deterministic` in such case. - - :return: the "r" and "s" parameters of the signature - :rtype: tuple of ints - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - order = self.privkey.order - - if k is not None: - _k = k - else: - _k = randrange(order, entropy) - - assert 1 <= _k < order - sig = self.privkey.sign(number, _k) - return sig.r, sig.s diff --git a/.venv/Lib/site-packages/ecdsa/numbertheory.py b/.venv/Lib/site-packages/ecdsa/numbertheory.py deleted file mode 100644 index fe974f8..0000000 --- a/.venv/Lib/site-packages/ecdsa/numbertheory.py +++ /dev/null @@ -1,835 +0,0 @@ -#! /usr/bin/env python -# -# Provide some simple capabilities from number theory. -# -# Version of 2008.11.14. -# -# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. -# Revision history: -# 2008.11.14: Use pow(base, exponent, modulus) for modular_exp. -# Make gcd and lcm accept arbitrarily many arguments. - -from __future__ import division - -import sys -from six import integer_types, PY2 -from six.moves import reduce - -try: - xrange -except NameError: - xrange = range -try: - from gmpy2 import powmod, mpz - - GMPY2 = True - GMPY = False -except ImportError: # pragma: no branch - GMPY2 = False - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - - -if GMPY2 or GMPY: # pragma: no branch - integer_types = tuple(integer_types + (type(mpz(1)),)) - - -import math -import warnings -import random -from .util import bit_length - - -class Error(Exception): - """Base class for exceptions in this module.""" - - pass - - -class JacobiError(Error): - pass - - -class SquareRootError(Error): - pass - - -class NegativeExponentError(Error): - pass - - -def modular_exp(base, exponent, modulus): # pragma: no cover - """Raise base to exponent, reducing by modulus""" - # deprecated in 0.14 - warnings.warn( - "Function is unused in library code. If you use this code, " - "change to pow() builtin.", - DeprecationWarning, - ) - if exponent < 0: - raise NegativeExponentError( - "Negative exponents (%d) not allowed" % exponent - ) - return pow(base, exponent, modulus) - - -def polynomial_reduce_mod(poly, polymod, p): - """Reduce poly by polymod, integer arithmetic modulo p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # This module has been tested only by extensive use - # in calculating modular square roots. - - # Just to make this easy, require a monic polynomial: - assert polymod[-1] == 1 - - assert len(polymod) > 1 - - while len(poly) >= len(polymod): - if poly[-1] != 0: - for i in xrange(2, len(polymod) + 1): - poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p - poly = poly[0:-1] - - return poly - - -def polynomial_multiply_mod(m1, m2, polymod, p): - """Polynomial multiplication modulo a polynomial over ints mod p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # This is just a seat-of-the-pants implementation. - - # This module has been tested only by extensive use - # in calculating modular square roots. - - # Initialize the product to zero: - - prod = (len(m1) + len(m2) - 1) * [0] - - # Add together all the cross-terms: - - for i in xrange(len(m1)): - for j in xrange(len(m2)): - prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p - - return polynomial_reduce_mod(prod, polymod, p) - - -def polynomial_exp_mod(base, exponent, polymod, p): - """Polynomial exponentiation modulo a polynomial over ints mod p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # Based on the Handbook of Applied Cryptography, algorithm 2.227. - - # This module has been tested only by extensive use - # in calculating modular square roots. - - assert exponent < p - - if exponent == 0: - return [1] - - G = base - k = exponent - if k % 2 == 1: - s = G - else: - s = [1] - - while k > 1: - k = k // 2 - G = polynomial_multiply_mod(G, G, polymod, p) - if k % 2 == 1: - s = polynomial_multiply_mod(G, s, polymod, p) - - return s - - -def jacobi(a, n): - """Jacobi symbol""" - - # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. - - # This function has been tested by comparison with a small - # table printed in HAC, and by extensive use in calculating - # modular square roots. - - if not n >= 3: - raise JacobiError("n must be larger than 2") - if not n % 2 == 1: - raise JacobiError("n must be odd") - a = a % n - if a == 0: - return 0 - if a == 1: - return 1 - a1, e = a, 0 - while a1 % 2 == 0: - a1, e = a1 // 2, e + 1 - if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: - s = 1 - else: - s = -1 - if a1 == 1: - return s - if n % 4 == 3 and a1 % 4 == 3: - s = -s - return s * jacobi(n % a1, a1) - - -def square_root_mod_prime(a, p): - """Modular square root of a, mod p, p prime.""" - - # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. - - # This module has been tested for all values in [0,p-1] for - # every prime p from 3 to 1229. - - assert 0 <= a < p - assert 1 < p - - if a == 0: - return 0 - if p == 2: - return a - - jac = jacobi(a, p) - if jac == -1: - raise SquareRootError("%d has no square root modulo %d" % (a, p)) - - if p % 4 == 3: - return pow(a, (p + 1) // 4, p) - - if p % 8 == 5: - d = pow(a, (p - 1) // 4, p) - if d == 1: - return pow(a, (p + 3) // 8, p) - assert d == p - 1 - return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p - - if PY2: - # xrange on python2 can take integers representable as C long only - range_top = min(0x7FFFFFFF, p) - else: - range_top = p - for b in xrange(2, range_top): # pragma: no branch - if jacobi(b * b - 4 * a, p) == -1: - f = (a, -b, 1) - ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) - if ff[1]: - raise SquareRootError("p is not prime") - return ff[0] - # just an assertion - raise RuntimeError("No b found.") # pragma: no cover - - -# because all the inverse_mod code is arch/environment specific, and coveralls -# expects it to execute equal number of times, we need to waive it by -# adding the "no branch" pragma to all branches -if GMPY2: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - if a == 0: # pragma: no branch - return 0 - return powmod(a, -1, m) - -elif GMPY: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - # while libgmp does support inverses modulo, it is accessible - # only using the native `pow()` function, and `pow()` in gmpy sanity - # checks the parameters before passing them on to underlying - # implementation - if a == 0: # pragma: no branch - return 0 - a = mpz(a) - m = mpz(m) - - lm, hm = mpz(1), mpz(0) - low, high = a % m, m - while low > 1: # pragma: no branch - r = high // low - lm, low, hm, high = hm - lm * r, high - low * r, lm, low - - return lm % m - -elif sys.version_info >= (3, 8): # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - if a == 0: # pragma: no branch - return 0 - return pow(a, -1, m) - -else: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - - if a == 0: # pragma: no branch - return 0 - - lm, hm = 1, 0 - low, high = a % m, m - while low > 1: # pragma: no branch - r = high // low - lm, low, hm, high = hm - lm * r, high - low * r, lm, low - - return lm % m - - -try: - gcd2 = math.gcd -except AttributeError: - - def gcd2(a, b): - """Greatest common divisor using Euclid's algorithm.""" - while a: - a, b = b % a, a - return b - - -def gcd(*a): - """Greatest common divisor. - - Usage: gcd([ 2, 4, 6 ]) - or: gcd(2, 4, 6) - """ - - if len(a) > 1: - return reduce(gcd2, a) - if hasattr(a[0], "__iter__"): - return reduce(gcd2, a[0]) - return a[0] - - -def lcm2(a, b): - """Least common multiple of two integers.""" - - return (a * b) // gcd(a, b) - - -def lcm(*a): - """Least common multiple. - - Usage: lcm([ 3, 4, 5 ]) - or: lcm(3, 4, 5) - """ - - if len(a) > 1: - return reduce(lcm2, a) - if hasattr(a[0], "__iter__"): - return reduce(lcm2, a[0]) - return a[0] - - -def factorization(n): - """Decompose n into a list of (prime,exponent) pairs.""" - - assert isinstance(n, integer_types) - - if n < 2: - return [] - - result = [] - - # Test the small primes: - - for d in smallprimes: - if d > n: - break - q, r = divmod(n, d) - if r == 0: - count = 1 - while d <= n: # pragma: no branch - n = q - q, r = divmod(n, d) - if r != 0: - break - count = count + 1 - result.append((d, count)) - - # If n is still greater than the last of our small primes, - # it may require further work: - - if n > smallprimes[-1]: - if is_prime(n): # If what's left is prime, it's easy: - result.append((n, 1)) - else: # Ugh. Search stupidly for a divisor: - d = smallprimes[-1] - while 1: - d = d + 2 # Try the next divisor. - q, r = divmod(n, d) - if q < d: # n < d*d means we're done, n = 1 or prime. - break - if r == 0: # d divides n. How many times? - count = 1 - n = q - # As long as d might still divide n, - while d <= n: # pragma: no branch - q, r = divmod(n, d) # see if it does. - if r != 0: - break - n = q # It does. Reduce n, increase count. - count = count + 1 - result.append((d, count)) - if n > 1: - result.append((n, 1)) - - return result - - -def phi(n): # pragma: no cover - """Return the Euler totient function of n.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - assert isinstance(n, integer_types) - - if n < 3: - return 1 - - result = 1 - ff = factorization(n) - for f in ff: - e = f[1] - if e > 1: - result = result * f[0] ** (e - 1) * (f[0] - 1) - else: - result = result * (f[0] - 1) - return result - - -def carmichael(n): # pragma: no cover - """Return Carmichael function of n. - - Carmichael(n) is the smallest integer x such that - m**x = 1 mod n for all m relatively prime to n. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - return carmichael_of_factorized(factorization(n)) - - -def carmichael_of_factorized(f_list): # pragma: no cover - """Return the Carmichael function of a number that is - represented as a list of (prime,exponent) pairs. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - if len(f_list) < 1: - return 1 - - result = carmichael_of_ppower(f_list[0]) - for i in xrange(1, len(f_list)): - result = lcm(result, carmichael_of_ppower(f_list[i])) - - return result - - -def carmichael_of_ppower(pp): # pragma: no cover - """Carmichael function of the given power of the given prime.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - p, a = pp - if p == 2 and a > 2: - return 2 ** (a - 2) - else: - return (p - 1) * p ** (a - 1) - - -def order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - # Warning: this implementation is not very clever, and will - # take a long time if m is very large. - - if m <= 1: - return 0 - - assert gcd(x, m) == 1 - - z = x - result = 1 - while z != 1: - z = (z * x) % m - result = result + 1 - return result - - -def largest_factor_relatively_prime(a, b): # pragma: no cover - """Return the largest factor of a relatively prime to b.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - while 1: - d = gcd(a, b) - if d <= 1: - break - b = d - while 1: - q, r = divmod(a, d) - if r > 0: - break - a = q - return a - - -def kinda_order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m', - where m' is the largest factor of m relatively prime to x. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - return order_mod(x, largest_factor_relatively_prime(m, x)) - - -def is_prime(n): - """Return True if x is prime, False otherwise. - - We use the Miller-Rabin test, as given in Menezes et al. p. 138. - This test is not exact: there are composite values n for which - it returns True. - - In testing the odd numbers from 10000001 to 19999999, - about 66 composites got past the first test, - 5 got past the second test, and none got past the third. - Since factors of 2, 3, 5, 7, and 11 were detected during - preliminary screening, the number of numbers tested by - Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) - = 4.57 million. - """ - - # (This is used to study the risk of false positives:) - global miller_rabin_test_count - - miller_rabin_test_count = 0 - - if n <= smallprimes[-1]: - if n in smallprimes: - return True - else: - return False - # 2310 = 2 * 3 * 5 * 7 * 11 - if gcd(n, 2310) != 1: - return False - - # Choose a number of iterations sufficient to reduce the - # probability of accepting a composite below 2**-80 - # (from Menezes et al. Table 4.4): - - t = 40 - n_bits = 1 + bit_length(n) - assert 11 <= n_bits <= 16384 - for k, tt in ( - (100, 27), - (150, 18), - (200, 15), - (250, 12), - (300, 9), - (350, 8), - (400, 7), - (450, 6), - (550, 5), - (650, 4), - (850, 3), - (1300, 2), - ): - if n_bits < k: - break - t = tt - - # Run the test t times: - - s = 0 - r = n - 1 - while (r % 2) == 0: - s = s + 1 - r = r // 2 - for i in xrange(t): - a = random.choice(smallprimes) - y = pow(a, r, n) - if y != 1 and y != n - 1: - j = 1 - while j <= s - 1 and y != n - 1: - y = pow(y, 2, n) - if y == 1: - miller_rabin_test_count = i + 1 - return False - j = j + 1 - if y != n - 1: - miller_rabin_test_count = i + 1 - return False - return True - - -def next_prime(starting_value): - """Return the smallest prime larger than the starting value.""" - - if starting_value < 2: - return 2 - result = (starting_value + 1) | 1 - while not is_prime(result): - result = result + 2 - return result - - -smallprimes = [ - 2, - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - 131, - 137, - 139, - 149, - 151, - 157, - 163, - 167, - 173, - 179, - 181, - 191, - 193, - 197, - 199, - 211, - 223, - 227, - 229, - 233, - 239, - 241, - 251, - 257, - 263, - 269, - 271, - 277, - 281, - 283, - 293, - 307, - 311, - 313, - 317, - 331, - 337, - 347, - 349, - 353, - 359, - 367, - 373, - 379, - 383, - 389, - 397, - 401, - 409, - 419, - 421, - 431, - 433, - 439, - 443, - 449, - 457, - 461, - 463, - 467, - 479, - 487, - 491, - 499, - 503, - 509, - 521, - 523, - 541, - 547, - 557, - 563, - 569, - 571, - 577, - 587, - 593, - 599, - 601, - 607, - 613, - 617, - 619, - 631, - 641, - 643, - 647, - 653, - 659, - 661, - 673, - 677, - 683, - 691, - 701, - 709, - 719, - 727, - 733, - 739, - 743, - 751, - 757, - 761, - 769, - 773, - 787, - 797, - 809, - 811, - 821, - 823, - 827, - 829, - 839, - 853, - 857, - 859, - 863, - 877, - 881, - 883, - 887, - 907, - 911, - 919, - 929, - 937, - 941, - 947, - 953, - 967, - 971, - 977, - 983, - 991, - 997, - 1009, - 1013, - 1019, - 1021, - 1031, - 1033, - 1039, - 1049, - 1051, - 1061, - 1063, - 1069, - 1087, - 1091, - 1093, - 1097, - 1103, - 1109, - 1117, - 1123, - 1129, - 1151, - 1153, - 1163, - 1171, - 1181, - 1187, - 1193, - 1201, - 1213, - 1217, - 1223, - 1229, -] - -miller_rabin_test_count = 0 diff --git a/.venv/Lib/site-packages/ecdsa/rfc6979.py b/.venv/Lib/site-packages/ecdsa/rfc6979.py deleted file mode 100644 index 0728b5a..0000000 --- a/.venv/Lib/site-packages/ecdsa/rfc6979.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -RFC 6979: - Deterministic Usage of the Digital Signature Algorithm (DSA) and - Elliptic Curve Digital Signature Algorithm (ECDSA) - - http://tools.ietf.org/html/rfc6979 - -Many thanks to Coda Hale for his implementation in Go language: - https://github.com/codahale/rfc6979 -""" - -import hmac -from binascii import hexlify -from .util import number_to_string, number_to_string_crop, bit_length -from ._compat import hmac_compat - - -# bit_length was defined in this module previously so keep it for backwards -# compatibility, will need to deprecate and remove it later -__all__ = ["bit_length", "bits2int", "bits2octets", "generate_k"] - - -def bits2int(data, qlen): - x = int(hexlify(data), 16) - l = len(data) * 8 - - if l > qlen: - return x >> (l - qlen) - return x - - -def bits2octets(data, order): - z1 = bits2int(data, bit_length(order)) - z2 = z1 - order - - if z2 < 0: - z2 = z1 - - return number_to_string_crop(z2, order) - - -# https://tools.ietf.org/html/rfc6979#section-3.2 -def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): - """ - Generate the ``k`` value - the nonce for DSA. - - :param int order: order of the DSA generator used in the signature - :param int secexp: secure exponent (private key) in numeric form - :param hash_func: reference to the same hash function used for generating - hash, like :py:class:`hashlib.sha1` - :param bytes data: hash in binary form of the signing data - :param int retry_gen: how many good 'k' values to skip before returning - :param bytes extra_entropy: additional added data in binary form as per - section-3.6 of rfc6979 - :rtype: int - """ - - qlen = bit_length(order) - holen = hash_func().digest_size - rolen = (qlen + 7) // 8 - bx = ( - hmac_compat(number_to_string(secexp, order)), - hmac_compat(bits2octets(data, order)), - hmac_compat(extra_entropy), - ) - - # Step B - v = b"\x01" * holen - - # Step C - k = b"\x00" * holen - - # Step D - - k = hmac.new(k, digestmod=hash_func) - k.update(v + b"\x00") - for i in bx: - k.update(i) - k = k.digest() - - # Step E - v = hmac.new(k, v, hash_func).digest() - - # Step F - k = hmac.new(k, digestmod=hash_func) - k.update(v + b"\x01") - for i in bx: - k.update(i) - k = k.digest() - - # Step G - v = hmac.new(k, v, hash_func).digest() - - # Step H - while True: - # Step H1 - t = b"" - - # Step H2 - while len(t) < rolen: - v = hmac.new(k, v, hash_func).digest() - t += v - - # Step H3 - secret = bits2int(t, qlen) - - if 1 <= secret < order: - if retry_gen <= 0: - return secret - retry_gen -= 1 - - k = hmac.new(k, v + b"\x00", hash_func).digest() - v = hmac.new(k, v, hash_func).digest() diff --git a/.venv/Lib/site-packages/ecdsa/ssh.py b/.venv/Lib/site-packages/ecdsa/ssh.py deleted file mode 100644 index 64e9403..0000000 --- a/.venv/Lib/site-packages/ecdsa/ssh.py +++ /dev/null @@ -1,83 +0,0 @@ -import binascii -from . import der -from ._compat import compat26_str, int_to_bytes - -_SSH_ED25519 = b"ssh-ed25519" -_SK_MAGIC = b"openssh-key-v1\0" -_NONE = b"none" - - -def _get_key_type(name): - if name == "Ed25519": - return _SSH_ED25519 - else: - raise ValueError("Unsupported key type") - - -class _Serializer: - def __init__(self): - self.bytes = b"" - - def put_raw(self, val): - self.bytes += val - - def put_u32(self, val): - self.bytes += int_to_bytes(val, length=4, byteorder="big") - - def put_str(self, val): - self.put_u32(len(val)) - self.bytes += val - - def put_pad(self, blklen=8): - padlen = blklen - (len(self.bytes) % blklen) - self.put_raw(bytearray(range(1, 1 + padlen))) - - def encode(self): - return binascii.b2a_base64(compat26_str(self.bytes)) - - def tobytes(self): - return self.bytes - - def topem(self): - return der.topem(self.bytes, "OPENSSH PRIVATE KEY") - - -def serialize_public(name, pub): - serial = _Serializer() - ktype = _get_key_type(name) - serial.put_str(ktype) - serial.put_str(pub) - return b" ".join([ktype, serial.encode()]) - - -def serialize_private(name, pub, priv): - # encode public part - spub = _Serializer() - ktype = _get_key_type(name) - spub.put_str(ktype) - spub.put_str(pub) - - # encode private part - spriv = _Serializer() - checksum = 0 - spriv.put_u32(checksum) - spriv.put_u32(checksum) - spriv.put_raw(spub.tobytes()) - spriv.put_str(priv + pub) - comment = b"" - spriv.put_str(comment) - spriv.put_pad() - - # top-level structure - main = _Serializer() - main.put_raw(_SK_MAGIC) - ciphername = kdfname = _NONE - main.put_str(ciphername) - main.put_str(kdfname) - nokdf = 0 - main.put_u32(nokdf) - nkeys = 1 - main.put_u32(nkeys) - main.put_str(spub.tobytes()) - main.put_str(spriv.tobytes()) - return main.topem() diff --git a/.venv/Lib/site-packages/ecdsa/test_curves.py b/.venv/Lib/site-packages/ecdsa/test_curves.py deleted file mode 100644 index 93b6c9b..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_curves.py +++ /dev/null @@ -1,361 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest - -import base64 -import pytest -from .curves import ( - Curve, - NIST256p, - curves, - UnknownCurveError, - PRIME_FIELD_OID, - curve_by_name, -) -from .ellipticcurve import CurveFp, PointJacobi, CurveEdTw -from . import der -from .util import number_to_string - - -class TestParameterEncoding(unittest.TestCase): - @classmethod - def setUpClass(cls): - # minimal, but with cofactor (excludes seed when compared to - # OpenSSL output) - cls.base64_params = ( - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=" - ) - - def test_from_pem(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END EC PARAMETERS-----\n" - ) - curve = Curve.from_pem(pem_params) - - self.assertIs(curve, NIST256p) - - def test_from_pem_with_explicit_when_explicit_disabled(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END EC PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params, ["named_curve"]) - - self.assertIn("explicit curve parameters not", str(e.exception)) - - def test_from_pem_with_named_curve_with_named_curve_disabled(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "BggqhkjOPQMBBw==\n" - "-----END EC PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params, ["explicit"]) - - self.assertIn("named_curve curve parameters not", str(e.exception)) - - def test_from_pem_with_wrong_header(self): - pem_params = ( - "-----BEGIN PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params) - - self.assertIn("PARAMETERS PEM header", str(e.exception)) - - def test_to_pem(self): - pem_params = ( - b"-----BEGIN EC PARAMETERS-----\n" - b"BggqhkjOPQMBBw==\n" - b"-----END EC PARAMETERS-----\n" - ) - encoding = NIST256p.to_pem() - - self.assertEqual(pem_params, encoding) - - def test_compare_with_different_object(self): - self.assertNotEqual(NIST256p, 256) - - def test_named_curve_params_der(self): - encoded = NIST256p.to_der() - - # just the encoding of the NIST256p OID (prime256v1) - self.assertEqual(b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07", encoded) - - def test_verify_that_default_is_named_curve_der(self): - encoded_default = NIST256p.to_der() - encoded_named = NIST256p.to_der("named_curve") - - self.assertEqual(encoded_default, encoded_named) - - def test_encoding_to_explicit_params(self): - encoded = NIST256p.to_der("explicit") - - self.assertEqual(encoded, bytes(base64.b64decode(self.base64_params))) - - def test_encoding_to_unsupported_type(self): - with self.assertRaises(ValueError) as e: - NIST256p.to_der("unsupported") - - self.assertIn("Only 'named_curve'", str(e.exception)) - - def test_encoding_to_explicit_compressed_params(self): - encoded = NIST256p.to_der("explicit", "compressed") - - compressed_base_point = ( - "MIHAAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" - "/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" - "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEIQNrF9Hy4SxCR/i85uVjpEDydwN9" - "gS3rM6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVR" - "AgEB" - ) - - self.assertEqual( - encoded, bytes(base64.b64decode(compressed_base_point)) - ) - - def test_decoding_explicit_from_openssl(self): - # generated with openssl 1.1.1k using - # openssl ecparam -name P-256 -param_enc explicit -out /tmp/file.pem - p256_explicit = ( - "MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" - "/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" - "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+" - "kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK" - "fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz" - "ucrC/GMlUQIBAQ==" - ) - - decoded = Curve.from_der(bytes(base64.b64decode(p256_explicit))) - - self.assertEqual(NIST256p, decoded) - - def test_decoding_well_known_from_explicit_params(self): - curve = Curve.from_der(bytes(base64.b64decode(self.base64_params))) - - self.assertIs(curve, NIST256p) - - def test_decoding_with_incorrect_valid_encodings(self): - with self.assertRaises(ValueError) as e: - Curve.from_der(b"", ["explicitCA"]) - - self.assertIn("Only named_curve", str(e.exception)) - - def test_compare_curves_with_different_generators(self): - curve_fp = CurveFp(23, 1, 7) - base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - base_b = PointJacobi(curve_fp, 1, 20, 1, 9, generator=True) - - curve_a = Curve("unknown", curve_fp, base_a, None) - curve_b = Curve("unknown", curve_fp, base_b, None) - - self.assertNotEqual(curve_a, curve_b) - - def test_default_encode_for_custom_curve(self): - curve_fp = CurveFp(23, 1, 7) - base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - - curve = Curve("unknown", curve_fp, base_point, None) - - encoded = curve.to_der() - - decoded = Curve.from_der(encoded) - - self.assertEqual(curve, decoded) - - expected = "MCECAQEwDAYHKoZIzj0BAQIBFzAGBAEBBAEHBAMEDQMCAQk=" - - self.assertEqual(encoded, bytes(base64.b64decode(expected))) - - def test_named_curve_encode_for_custom_curve(self): - curve_fp = CurveFp(23, 1, 7) - base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - - curve = Curve("unknown", curve_fp, base_point, None) - - with self.assertRaises(UnknownCurveError) as e: - curve.to_der("named_curve") - - self.assertIn("Can't encode curve", str(e.exception)) - - def test_try_decoding_binary_explicit(self): - sect113r1_explicit = ( - "MIGRAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0BAgMCAgEJMDkEDwAwiCUMpufH" - "/mSc6Fgg9wQPAOi+5NPiJgdEGIvg6ccjAxUAEOcjqxTWluZ2h1YVF1b+v4/LSakE" - "HwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY7oTRMV7TGIYCDwEAAAAAAAAA2czsijnl" - "bwIBAg==" - ) - - with self.assertRaises(UnknownCurveError) as e: - Curve.from_der(base64.b64decode(sect113r1_explicit)) - - self.assertIn("Characteristic 2 curves unsupported", str(e.exception)) - - def test_decode_malformed_named_curve(self): - bad_der = der.encode_oid(*NIST256p.oid) + der.encode_integer(1) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unexpected data after OID", str(e.exception)) - - def test_decode_malformed_explicit_garbage_after_ECParam(self): - bad_der = bytes( - base64.b64decode(self.base64_params) - ) + der.encode_integer(1) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unexpected data after ECParameters", str(e.exception)) - - def test_decode_malformed_unknown_version_number(self): - bad_der = der.encode_sequence(der.encode_integer(2)) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unknown parameter encoding format", str(e.exception)) - - def test_decode_malformed_unknown_field_type(self): - curve_p = NIST256p.curve.p() - bad_der = der.encode_sequence( - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(1, 2, 3), der.encode_integer(curve_p) - ), - der.encode_sequence( - der.encode_octet_string( - number_to_string(NIST256p.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(NIST256p.curve.b(), curve_p) - ), - ), - der.encode_octet_string( - NIST256p.generator.to_bytes("uncompressed") - ), - der.encode_integer(NIST256p.generator.order()), - ) - - with self.assertRaises(UnknownCurveError) as e: - Curve.from_der(bad_der) - - self.assertIn("Unknown field type: (1, 2, 3)", str(e.exception)) - - def test_decode_malformed_garbage_after_prime(self): - curve_p = NIST256p.curve.p() - bad_der = der.encode_sequence( - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(*PRIME_FIELD_OID), - der.encode_integer(curve_p), - der.encode_integer(1), - ), - der.encode_sequence( - der.encode_octet_string( - number_to_string(NIST256p.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(NIST256p.curve.b(), curve_p) - ), - ), - der.encode_octet_string( - NIST256p.generator.to_bytes("uncompressed") - ), - der.encode_integer(NIST256p.generator.order()), - ) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Prime-p element", str(e.exception)) - - -class TestCurveSearching(unittest.TestCase): - def test_correct_name(self): - c = curve_by_name("NIST256p") - self.assertIs(c, NIST256p) - - def test_openssl_name(self): - c = curve_by_name("prime256v1") - self.assertIs(c, NIST256p) - - def test_unknown_curve(self): - with self.assertRaises(UnknownCurveError) as e: - curve_by_name("foo bar") - - self.assertIn( - "name 'foo bar' unknown, only curves supported: " - "['NIST192p', 'NIST224p'", - str(e.exception), - ) - - def test_with_None_as_parameter(self): - with self.assertRaises(UnknownCurveError) as e: - curve_by_name(None) - - self.assertIn( - "name None unknown, only curves supported: " - "['NIST192p', 'NIST224p'", - str(e.exception), - ) - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_named(curve): - ret = Curve.from_der(curve.to_der("named_curve")) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_explicit(curve): - if isinstance(curve.curve, CurveEdTw): - with pytest.raises(UnknownCurveError): - curve.to_der("explicit") - else: - ret = Curve.from_der(curve.to_der("explicit")) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_default(curve): - ret = Curve.from_der(curve.to_der()) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_explicit_compressed(curve): - if isinstance(curve.curve, CurveEdTw): - with pytest.raises(UnknownCurveError): - curve.to_der("explicit", "compressed") - else: - ret = Curve.from_der(curve.to_der("explicit", "compressed")) - - assert curve == ret diff --git a/.venv/Lib/site-packages/ecdsa/test_der.py b/.venv/Lib/site-packages/ecdsa/test_der.py deleted file mode 100644 index 0c2dc4d..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_der.py +++ /dev/null @@ -1,478 +0,0 @@ -# compatibility with Python 2.6, for that we need unittest2 package, -# which is not available on 3.3 or 3.4 -import warnings -from binascii import hexlify - -try: - import unittest2 as unittest -except ImportError: - import unittest -import sys -import hypothesis.strategies as st -from hypothesis import given, settings -import pytest -from ._compat import str_idx_as_int -from .curves import NIST256p, NIST224p -from .der import ( - remove_integer, - UnexpectedDER, - read_length, - encode_bitstring, - remove_bitstring, - remove_object, - encode_oid, - remove_constructed, - remove_octet_string, - remove_sequence, -) - - -class TestRemoveInteger(unittest.TestCase): - # DER requires the integers to be 0-padded only if they would be - # interpreted as negative, check if those errors are detected - def test_non_minimal_encoding(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b"\x02\x02\x00\x01") - - def test_negative_with_high_bit_set(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b"\x02\x01\x80") - - def test_minimal_with_high_bit_set(self): - val, rem = remove_integer(b"\x02\x02\x00\x80") - - self.assertEqual(val, 0x80) - self.assertEqual(rem, b"") - - def test_two_zero_bytes_with_high_bit_set(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b"\x02\x03\x00\x00\xff") - - def test_zero_length_integer(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b"\x02\x00") - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b"") - - def test_encoding_of_zero(self): - val, rem = remove_integer(b"\x02\x01\x00") - - self.assertEqual(val, 0) - self.assertEqual(rem, b"") - - def test_encoding_of_127(self): - val, rem = remove_integer(b"\x02\x01\x7f") - - self.assertEqual(val, 127) - self.assertEqual(rem, b"") - - def test_encoding_of_128(self): - val, rem = remove_integer(b"\x02\x02\x00\x80") - - self.assertEqual(val, 128) - self.assertEqual(rem, b"") - - def test_wrong_tag(self): - with self.assertRaises(UnexpectedDER) as e: - remove_integer(b"\x01\x02\x00\x80") - - self.assertIn("wanted type 'integer'", str(e.exception)) - - def test_wrong_length(self): - with self.assertRaises(UnexpectedDER) as e: - remove_integer(b"\x02\x03\x00\x80") - - self.assertIn("Length longer", str(e.exception)) - - -class TestReadLength(unittest.TestCase): - # DER requires the lengths between 0 and 127 to be encoded using the short - # form and lengths above that encoded with minimal number of bytes - # necessary - def test_zero_length(self): - self.assertEqual((0, 1), read_length(b"\x00")) - - def test_two_byte_zero_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b"\x81\x00") - - def test_two_byte_small_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b"\x81\x7f") - - def test_long_form_with_zero_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b"\x80") - - def test_smallest_two_byte_length(self): - self.assertEqual((128, 2), read_length(b"\x81\x80")) - - def test_zero_padded_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b"\x82\x00\x80") - - def test_two_three_byte_length(self): - self.assertEqual((256, 3), read_length(b"\x82\x01\x00")) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - read_length(b"") - - def test_length_overflow(self): - with self.assertRaises(UnexpectedDER): - read_length(b"\x83\x01\x00") - - -class TestEncodeBitstring(unittest.TestCase): - # DER requires BIT STRINGS to include a number of padding bits in the - # encoded byte string, that padding must be between 0 and 7 - - def test_old_call_convention(self): - """This is the old way to use the function.""" - warnings.simplefilter("always") - with pytest.warns(DeprecationWarning) as warns: - der = encode_bitstring(b"\x00\xff") - - self.assertEqual(len(warns), 1) - self.assertIn( - "unused= needs to be specified", warns[0].message.args[0] - ) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_new_call_convention(self): - """This is how it should be called now.""" - # make sure no warnings are raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - der = encode_bitstring(b"\xff", 0) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_implicit_unused_bits(self): - """ - Writing bit string with already included the number of unused bits. - """ - # make sure no warnings are raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - der = encode_bitstring(b"\x00\xff", None) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_explicit_unused_bits(self): - der = encode_bitstring(b"\xff\xf0", 4) - - self.assertEqual(der, b"\x03\x03\x04\xff\xf0") - - def test_empty_string(self): - self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00") - - def test_invalid_unused_count(self): - with self.assertRaises(ValueError): - encode_bitstring(b"\xff\x00", 8) - - def test_invalid_unused_with_empty_string(self): - with self.assertRaises(ValueError): - encode_bitstring(b"", 1) - - def test_non_zero_padding_bits(self): - with self.assertRaises(ValueError): - encode_bitstring(b"\xff", 2) - - -class TestRemoveBitstring(unittest.TestCase): - def test_old_call_convention(self): - """This is the old way to call the function.""" - warnings.simplefilter("always") - with pytest.warns(DeprecationWarning) as warns: - bits, rest = remove_bitstring(b"\x03\x02\x00\xff") - - self.assertEqual(len(warns), 1) - self.assertIn( - "expect_unused= needs to be specified", warns[0].message.args[0] - ) - - self.assertEqual(bits, b"\x00\xff") - self.assertEqual(rest, b"") - - def test_new_call_convention(self): - # make sure no warnings are raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0) - - self.assertEqual(bits, b"\xff") - self.assertEqual(rest, b"") - - def test_implicit_unexpected_unused(self): - # make sure no warnings are raised - with warnings.catch_warnings(): - warnings.simplefilter("error") - bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None) - - self.assertEqual(bits, (b"\xff", 0)) - self.assertEqual(rest, b"") - - def test_with_padding(self): - ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None) - - self.assertEqual(ret, (b"\xf0", 4)) - self.assertEqual(rest, b"") - - def test_not_a_bitstring(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x02\x02\x00\xff", None) - - def test_empty_encoding(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x00", None) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"", None) - - def test_no_length(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03", None) - - def test_unexpected_number_of_unused_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x02\x00\xff", 1) - - def test_invalid_encoding_of_unused_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x03\x08\xff\x00", None) - - def test_invalid_encoding_of_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x01\x01", None) - - def test_invalid_padding_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x02\x01\xff", None) - - -class TestStrIdxAsInt(unittest.TestCase): - def test_str(self): - self.assertEqual(115, str_idx_as_int("str", 0)) - - def test_bytes(self): - self.assertEqual(115, str_idx_as_int(b"str", 0)) - - def test_bytearray(self): - self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0)) - - -class TestEncodeOid(unittest.TestCase): - def test_pub_key_oid(self): - oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) - self.assertEqual(hexlify(oid_ecPublicKey), b"06072a8648ce3d0201") - - def test_nist224p_oid(self): - self.assertEqual(hexlify(NIST224p.encoded_oid), b"06052b81040021") - - def test_nist256p_oid(self): - self.assertEqual( - hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107" - ) - - def test_large_second_subid(self): - # from X.690, section 8.19.5 - oid = encode_oid(2, 999, 3) - self.assertEqual(oid, b"\x06\x03\x88\x37\x03") - - def test_with_two_subids(self): - oid = encode_oid(2, 999) - self.assertEqual(oid, b"\x06\x02\x88\x37") - - def test_zero_zero(self): - oid = encode_oid(0, 0) - self.assertEqual(oid, b"\x06\x01\x00") - - def test_with_wrong_types(self): - with self.assertRaises((TypeError, AssertionError)): - encode_oid(0, None) - - def test_with_small_first_large_second(self): - with self.assertRaises(AssertionError): - encode_oid(1, 40) - - def test_small_first_max_second(self): - oid = encode_oid(1, 39) - self.assertEqual(oid, b"\x06\x01\x4f") - - def test_with_invalid_first(self): - with self.assertRaises(AssertionError): - encode_oid(3, 39) - - -class TestRemoveObject(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) - - def test_pub_key_oid(self): - oid, rest = remove_object(self.oid_ecPublicKey) - self.assertEqual(rest, b"") - self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) - - def test_with_extra_bytes(self): - oid, rest = remove_object(self.oid_ecPublicKey + b"more") - self.assertEqual(rest, b"more") - self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) - - def test_with_large_second_subid(self): - # from X.690, section 8.19.5 - oid, rest = remove_object(b"\x06\x03\x88\x37\x03") - self.assertEqual(rest, b"") - self.assertEqual(oid, (2, 999, 3)) - - def test_with_padded_first_subid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x02\x80\x00") - - def test_with_padded_second_subid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x04\x88\x37\x80\x01") - - def test_with_missing_last_byte_of_multi_byte(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x03\x88\x37\x83") - - def test_with_two_subids(self): - oid, rest = remove_object(b"\x06\x02\x88\x37") - self.assertEqual(rest, b"") - self.assertEqual(oid, (2, 999)) - - def test_zero_zero(self): - oid, rest = remove_object(b"\x06\x01\x00") - self.assertEqual(rest, b"") - self.assertEqual(oid, (0, 0)) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"") - - def test_missing_length(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06") - - def test_empty_oid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x00") - - def test_empty_oid_overflow(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x01") - - def test_with_wrong_type(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x04\x02\x88\x37") - - def test_with_too_long_length(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x03\x88\x37") - - -class TestRemoveConstructed(unittest.TestCase): - def test_simple(self): - data = b"\xa1\x02\xff\xaa" - - tag, body, rest = remove_constructed(data) - - self.assertEqual(tag, 0x01) - self.assertEqual(body, b"\xff\xaa") - self.assertEqual(rest, b"") - - def test_with_malformed_tag(self): - data = b"\x01\x02\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_constructed(data) - - self.assertIn("constructed tag", str(e.exception)) - - -class TestRemoveOctetString(unittest.TestCase): - def test_simple(self): - data = b"\x04\x03\xaa\xbb\xcc" - body, rest = remove_octet_string(data) - self.assertEqual(body, b"\xaa\xbb\xcc") - self.assertEqual(rest, b"") - - def test_with_malformed_tag(self): - data = b"\x03\x03\xaa\xbb\xcc" - with self.assertRaises(UnexpectedDER) as e: - remove_octet_string(data) - - self.assertIn("octetstring", str(e.exception)) - - -class TestRemoveSequence(unittest.TestCase): - def test_simple(self): - data = b"\x30\x02\xff\xaa" - body, rest = remove_sequence(data) - self.assertEqual(body, b"\xff\xaa") - self.assertEqual(rest, b"") - - def test_with_empty_string(self): - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(b"") - - self.assertIn("Empty string", str(e.exception)) - - def test_with_wrong_tag(self): - data = b"\x20\x02\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(data) - - self.assertIn("wanted type 'sequence'", str(e.exception)) - - def test_with_wrong_length(self): - data = b"\x30\x03\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(data) - - self.assertIn("Length longer", str(e.exception)) - - -@st.composite -def st_oid(draw, max_value=2**512, max_size=50): - """ - Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples - - :param max_value: maximum value of any single sub-identifier - :param max_size: maximum length of the generated OID - """ - first = draw(st.integers(min_value=0, max_value=2)) - if first < 2: - second = draw(st.integers(min_value=0, max_value=39)) - else: - second = draw(st.integers(min_value=0, max_value=max_value)) - rest = draw( - st.lists( - st.integers(min_value=0, max_value=max_value), max_size=max_size - ) - ) - return (first, second) + tuple(rest) - - -HYP_SETTINGS = {} - - -if "--fast" in sys.argv: # pragma: no cover - HYP_SETTINGS["max_examples"] = 2 - - -@settings(**HYP_SETTINGS) -@given(st_oid()) -def test_oids(ids): - encoded_oid = encode_oid(*ids) - decoded_oid, rest = remove_object(encoded_oid) - assert rest == b"" - assert decoded_oid == ids diff --git a/.venv/Lib/site-packages/ecdsa/test_ecdh.py b/.venv/Lib/site-packages/ecdsa/test_ecdh.py deleted file mode 100644 index cb22580..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_ecdh.py +++ /dev/null @@ -1,449 +0,0 @@ -import os -import sys -import shutil -import subprocess -import pytest -from binascii import unhexlify - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from .curves import ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - BRAINPOOLP160r1, - SECP112r2, - SECP128r1, -) -from .curves import curves -from .ecdh import ( - ECDH, - InvalidCurveError, - InvalidSharedSecretError, - NoKeyError, - NoCurveError, -) -from .keys import SigningKey, VerifyingKey -from .ellipticcurve import CurveEdTw - - -if "--fast" in sys.argv: # pragma: no cover - curves = [SECP112r2, SECP128r1] - - -@pytest.mark.parametrize( - "vcurve", - curves, - ids=[curve.name for curve in curves], -) -def test_ecdh_each(vcurve): - if isinstance(vcurve.curve, CurveEdTw): - pytest.skip("ECDH is not supported for Edwards curves") - ecdh1 = ECDH(curve=vcurve) - ecdh2 = ECDH(curve=vcurve) - - ecdh2.generate_private_key() - ecdh1.load_received_public_key(ecdh2.get_public_key()) - ecdh2.load_received_public_key(ecdh1.generate_private_key()) - - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - assert secret1 == secret2 - - -def test_ecdh_both_keys_present(): - key1 = SigningKey.generate(BRAINPOOLP160r1) - key2 = SigningKey.generate(BRAINPOOLP160r1) - - ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key) - ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key) - - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - - assert secret1 == secret2 - - -def test_ecdh_no_public_key(): - ecdh1 = ECDH(curve=NIST192p) - - with pytest.raises(NoKeyError): - ecdh1.generate_sharedsecret_bytes() - - ecdh1.generate_private_key() - - with pytest.raises(NoKeyError): - ecdh1.generate_sharedsecret_bytes() - - -class TestECDH(unittest.TestCase): - def test_load_key_from_wrong_curve(self): - ecdh1 = ECDH() - ecdh1.set_curve(NIST192p) - - key1 = SigningKey.generate(BRAINPOOLP160r1) - - with self.assertRaises(InvalidCurveError) as e: - ecdh1.load_private_key(key1) - - self.assertIn("Curve mismatch", str(e.exception)) - - def test_generate_without_curve(self): - ecdh1 = ECDH() - - with self.assertRaises(NoCurveError) as e: - ecdh1.generate_private_key() - - self.assertIn("Curve must be set", str(e.exception)) - - def test_load_bytes_without_curve_set(self): - ecdh1 = ECDH() - - with self.assertRaises(NoCurveError) as e: - ecdh1.load_private_key_bytes(b"\x01" * 32) - - self.assertIn("Curve must be set", str(e.exception)) - - def test_set_curve_from_received_public_key(self): - ecdh1 = ECDH() - - key1 = SigningKey.generate(BRAINPOOLP160r1) - - ecdh1.load_received_public_key(key1.verifying_key) - - self.assertEqual(ecdh1.curve, BRAINPOOLP160r1) - - -def test_ecdh_wrong_public_key_curve(): - ecdh1 = ECDH(curve=NIST192p) - ecdh1.generate_private_key() - ecdh2 = ECDH(curve=NIST256p) - ecdh2.generate_private_key() - - with pytest.raises(InvalidCurveError): - ecdh1.load_received_public_key(ecdh2.get_public_key()) - - with pytest.raises(InvalidCurveError): - ecdh2.load_received_public_key(ecdh1.get_public_key()) - - ecdh1.public_key = ecdh2.get_public_key() - ecdh2.public_key = ecdh1.get_public_key() - - with pytest.raises(InvalidCurveError): - ecdh1.generate_sharedsecret_bytes() - - with pytest.raises(InvalidCurveError): - ecdh2.generate_sharedsecret_bytes() - - -def test_ecdh_invalid_shared_secret_curve(): - ecdh1 = ECDH(curve=NIST256p) - ecdh1.generate_private_key() - - ecdh1.load_received_public_key( - SigningKey.generate(NIST256p).get_verifying_key() - ) - - ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order - - with pytest.raises(InvalidSharedSecretError): - ecdh1.generate_sharedsecret_bytes() - - -# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt -# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt -# https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt -@pytest.mark.parametrize( - "curve,privatekey,pubkey,secret", - [ - pytest.param( - NIST192p, - "f17d3fea367b74d340851ca4270dcb24c271f445bed9d527", - "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0" - "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523", - "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0", - id="NIST192p-1", - ), - pytest.param( - NIST192p, - "56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5", - "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7" - "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125", - "c208847568b98835d7312cef1f97f7aa298283152313c29d", - id="NIST192p-2", - ), - pytest.param( - NIST192p, - "c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949", - "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f" - "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279", - "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd", - id="NIST192p-3", - ), - pytest.param( - NIST192p, - "e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda", - "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88" - "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5", - "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c", - id="NIST192p-4", - ), - pytest.param( - NIST192p, - "beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d", - "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594" - "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73", - "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4", - id="NIST192p-5", - ), - pytest.param( - NIST192p, - "cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699", - "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516" - "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7", - "f67053b934459985a315cb017bf0302891798d45d0e19508", - id="NIST192p-6", - ), - pytest.param( - NIST224p, - "8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd", - "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280" - "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7", - "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8", - id="NIST224p", - ), - pytest.param( - NIST256p, - "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", - "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" - "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", - "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", - id="NIST256p-1", - ), - pytest.param( - NIST256p, - "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", - "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" - "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", - "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", - id="NIST256p-2", - ), - pytest.param( - NIST256p, - "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8", - "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3" - "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", - "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", - id="NIST256p-3", - ), - pytest.param( - NIST256p, - "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d", - "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" - "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", - "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", - id="NIST256p-4", - ), - pytest.param( - NIST256p, - "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf", - "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922" - "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", - "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", - id="NIST256p-5", - ), - pytest.param( - NIST256p, - "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef", - "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792" - "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", - "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", - id="NIST256p-6", - ), - pytest.param( - NIST384p, - "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1" - "5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1", - "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459" - "2efda27fe7513272734466b400091adbf2d68c58e0c50066" - "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66" - "1efedf243451915ed0905a32b060992b468c64766fc8437a", - "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4" - "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", - id="NIST384p", - ), - pytest.param( - NIST521p, - "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea" - "c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47", - "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433" - "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d" - "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83" - "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676", - "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366" - "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831", - id="NIST521p", - ), - ], -) -def test_ecdh_NIST(curve, privatekey, pubkey, secret): - ecdh = ECDH(curve=curve) - ecdh.load_private_key_bytes(unhexlify(privatekey)) - ecdh.load_received_public_key_bytes(unhexlify(pubkey)) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(secret) - - -pem_local_private_key = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -der_local_private_key = ( - "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864" - "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06" - "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c" -) -pem_remote_public_key = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" - "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n" -) -der_remote_public_key = ( - "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563" - "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c" -) -gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea" - - -def test_ecdh_pem(): - ecdh = ECDH() - ecdh.load_private_key_pem(pem_local_private_key) - ecdh.load_received_public_key_pem(pem_remote_public_key) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(gshared_secret) - - -def test_ecdh_der(): - ecdh = ECDH() - ecdh.load_private_key_der(unhexlify(der_local_private_key)) - ecdh.load_received_public_key_der(unhexlify(der_remote_public_key)) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(gshared_secret) - - -# Exception classes used by run_openssl. -class RunOpenSslError(Exception): - pass - - -def run_openssl(cmd): - OPENSSL = "openssl" - p = subprocess.Popen( - [OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - stdout, ignored = p.communicate() - if p.returncode != 0: - raise RunOpenSslError( - "cmd '%s %s' failed: rc=%s, stdout/err was %s" - % (OPENSSL, cmd, p.returncode, stdout) - ) - return stdout.decode() - - -OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") -) - - -@pytest.mark.slow -@pytest.mark.parametrize( - "vcurve", - curves, - ids=[curve.name for curve in curves], -) -def test_ecdh_with_openssl(vcurve): - if isinstance(vcurve.curve, CurveEdTw): - pytest.skip("Edwards curves are not supported for ECDH") - - assert vcurve.openssl_name - - if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES: - pytest.skip("system openssl does not support " + vcurve.openssl_name) - - try: - hlp = run_openssl("pkeyutl -help") - if hlp.find("-derive") == 0: # pragma: no cover - pytest.skip("system openssl does not support `pkeyutl -derive`") - except RunOpenSslError: # pragma: no cover - pytest.skip("system openssl could not be executed") - - if os.path.isdir("t"): # pragma: no branch - shutil.rmtree("t") - os.mkdir("t") - run_openssl( - "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name - ) - run_openssl( - "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name - ) - run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem") - - ecdh1 = ECDH(curve=vcurve) - ecdh2 = ECDH(curve=vcurve) - with open("t/privkey1.pem") as e: - key = e.read() - ecdh1.load_private_key_pem(key) - with open("t/privkey2.pem") as e: - key = e.read() - ecdh2.load_private_key_pem(key) - - with open("t/pubkey1.pem") as e: - key = e.read() - vk1 = VerifyingKey.from_pem(key) - assert vk1.to_string() == ecdh1.get_public_key().to_string() - vk2 = ecdh2.get_public_key() - with open("t/pubkey2.pem", "wb") as e: - e.write(vk2.to_pem()) - - ecdh1.load_received_public_key(vk2) - ecdh2.load_received_public_key(vk1) - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - - assert secret1 == secret2 - - run_openssl( - "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" - ) - run_openssl( - "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" - ) - - with open("t/secret1", "rb") as e: - ssl_secret1 = e.read() - with open("t/secret1", "rb") as e: - ssl_secret2 = e.read() - - assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2 - assert len(secret1) == vk1.curve.verifying_key_length // 2 - - assert ssl_secret1 == ssl_secret2 - assert secret1 == ssl_secret1 diff --git a/.venv/Lib/site-packages/ecdsa/test_ecdsa.py b/.venv/Lib/site-packages/ecdsa/test_ecdsa.py deleted file mode 100644 index c1e2582..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_ecdsa.py +++ /dev/null @@ -1,694 +0,0 @@ -from __future__ import print_function -import sys -import hypothesis.strategies as st -from hypothesis import given, settings, note, example - -try: - import unittest2 as unittest -except ImportError: - import unittest -import pytest -from .ecdsa import ( - Private_key, - Public_key, - Signature, - generator_192, - digest_integer, - ellipticcurve, - point_is_valid, - generator_224, - generator_256, - generator_384, - generator_521, - generator_secp256k1, - curve_192, - InvalidPointError, - curve_112r2, - generator_112r2, - int_to_string, -) -from .ellipticcurve import Point - - -HYP_SETTINGS = {} -# old hypothesis doesn't have the "deadline" setting -if sys.version_info > (2, 7): # pragma: no branch - # SEC521p is slow, allow long execution for it - HYP_SETTINGS["deadline"] = 5000 - - -class TestP192FromX9_62(unittest.TestCase): - """Check test vectors from X9.62""" - - @classmethod - def setUpClass(cls): - cls.d = 651056770906015076056810763456358567190100156695615665659 - cls.Q = cls.d * generator_192 - cls.k = 6140507067065001063065065565667405560006161556565665656654 - cls.R = cls.k * generator_192 - - cls.msg = 968236873715988614170569073515315707566766479517 - cls.pubk = Public_key(generator_192, generator_192 * cls.d) - cls.privk = Private_key(cls.pubk, cls.d) - cls.sig = cls.privk.sign(cls.msg, cls.k) - - def test_point_multiplication(self): - assert self.Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 - - def test_point_multiplication_2(self): - assert self.R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - assert self.R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - - def test_mult_and_addition(self): - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * generator_192 + u2 * self.Q - assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - assert temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - - def test_signature(self): - r, s = self.sig.r, self.sig.s - assert r == 3342403536405981729393488334694600415596881826869351677613 - assert s == 5735822328888155254683894997897571951568553642892029982342 - - def test_verification(self): - assert self.pubk.verifies(self.msg, self.sig) - - def test_rejection(self): - assert not self.pubk.verifies(self.msg - 1, self.sig) - - def test_verification_with_regular_point(self): - pubk = Public_key( - Point( - generator_192.curve(), - generator_192.x(), - generator_192.y(), - generator_192.order(), - ), - self.pubk.point, - ) - - assert pubk.verifies(self.msg, self.sig) - - -class TestPublicKey(unittest.TestCase): - def test_equality_public_keys(self): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - pub_key1 = Public_key(gen, point) - pub_key2 = Public_key(gen, point) - self.assertEqual(pub_key1, pub_key2) - - def test_inequality_public_key(self): - gen = generator_192 - x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point1 = ellipticcurve.Point(gen.curve(), x1, y1) - - x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15 - y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF - point2 = ellipticcurve.Point(gen.curve(), x2, y2) - - pub_key1 = Public_key(gen, point1) - pub_key2 = Public_key(gen, point2) - self.assertNotEqual(pub_key1, pub_key2) - - def test_inequality_different_curves(self): - gen = generator_192 - x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point1 = ellipticcurve.Point(gen.curve(), x1, y1) - - x2 = 0x722BA0FB6B8FC8898A4C6AB49E66 - y2 = 0x2B7344BB57A7ABC8CA0F1A398C7D - point2 = ellipticcurve.Point(generator_112r2.curve(), x2, y2) - - pub_key1 = Public_key(gen, point1) - pub_key2 = Public_key(generator_112r2, point2) - self.assertNotEqual(pub_key1, pub_key2) - - def test_inequality_public_key_not_implemented(self): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - pub_key = Public_key(gen, point) - self.assertNotEqual(pub_key, None) - - def test_public_key_with_generator_without_order(self): - gen = ellipticcurve.PointJacobi( - generator_192.curve(), generator_192.x(), generator_192.y(), 1 - ) - - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - - with self.assertRaises(InvalidPointError) as e: - Public_key(gen, point) - - self.assertIn("Generator point must have order", str(e.exception)) - - def test_public_point_on_curve_not_scalar_multiple_of_base_point(self): - x = 2 - y = 0xBE6AA4938EF7CFE6FE29595B6B00 - # we need a curve with cofactor != 1 - point = ellipticcurve.PointJacobi(curve_112r2, x, y, 1) - - self.assertTrue(curve_112r2.contains_point(x, y)) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_112r2, point) - - self.assertIn("Generator point order", str(e.exception)) - - def test_point_is_valid_with_not_scalar_multiple_of_base_point(self): - x = 2 - y = 0xBE6AA4938EF7CFE6FE29595B6B00 - - self.assertFalse(point_is_valid(generator_112r2, x, y)) - - # the tests to verify the extensiveness of tests in ecdsa.ecdsa - # if PointJacobi gets modified to calculate the x and y mod p the tests - # below will need to use a fake/mock object - def test_invalid_point_x_negative(self): - pt = ellipticcurve.PointJacobi(curve_192, -1, 0, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_x_equal_p(self): - pt = ellipticcurve.PointJacobi(curve_192, curve_192.p(), 0, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_y_negative(self): - pt = ellipticcurve.PointJacobi(curve_192, 0, -1, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_y_equal_p(self): - pt = ellipticcurve.PointJacobi(curve_192, 0, curve_192.p(), 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - -class TestPublicKeyVerifies(unittest.TestCase): - # test all the different ways that a signature can be publicly invalid - @classmethod - def setUpClass(cls): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - - cls.pub_key = Public_key(gen, point) - - def test_sig_with_r_zero(self): - sig = Signature(0, 1) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_r_order(self): - sig = Signature(generator_192.order(), 1) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_s_zero(self): - sig = Signature(1, 0) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_s_order(self): - sig = Signature(1, generator_192.order()) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - -class TestPrivateKey(unittest.TestCase): - @classmethod - def setUpClass(cls): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - cls.pub_key = Public_key(gen, point) - - def test_equality_private_keys(self): - pr_key1 = Private_key(self.pub_key, 100) - pr_key2 = Private_key(self.pub_key, 100) - self.assertEqual(pr_key1, pr_key2) - - def test_inequality_private_keys(self): - pr_key1 = Private_key(self.pub_key, 100) - pr_key2 = Private_key(self.pub_key, 200) - self.assertNotEqual(pr_key1, pr_key2) - - def test_inequality_private_keys_not_implemented(self): - pr_key = Private_key(self.pub_key, 100) - self.assertNotEqual(pr_key, None) - - -# Testing point validity, as per ECDSAVS.pdf B.2.2: -P192_POINTS = [ - ( - generator_192, - 0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A, - 0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC, - False, - ), - ( - generator_192, - 0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B, - 0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB, - False, - ), - ( - generator_192, - 0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792, - 0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6, - False, - ), - ( - generator_192, - 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6, - 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F, - True, - ), - ( - generator_192, - 0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70, - 0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E, - True, - ), - ( - generator_192, - 0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED, - 0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9, - True, - ), - ( - generator_192, - 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15, - 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF, - True, - ), - ( - generator_192, - 0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA, - 0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835, - False, - ), - ( - generator_192, - 0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12, - 0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2, - False, - ), - ( - generator_192, - 0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43, - 0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA, - False, - ), - ( - generator_192, - 0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC, - 0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6, - False, - ), - ( - generator_192, - 0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253, - 0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923, - False, - ), -] - - -@pytest.mark.parametrize("generator,x,y,expected", P192_POINTS) -def test_point_validity(generator, x, y, expected): - """ - `generator` defines the curve; is `(x, y)` a point on - this curve? `expected` is True if the right answer is Yes. - """ - assert point_is_valid(generator, x, y) == expected - - -# Trying signature-verification tests from ECDSAVS.pdf B.2.4: -CURVE_192_KATS = [ - ( - generator_192, - int( - "0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" - "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" - "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" - "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" - "58", - 16, - ), - 0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC, - 0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4, - 0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916, - 0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479, - True, - ), - ( - generator_192, - int( - "0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" - "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" - "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" - "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" - "f4", - 16, - ), - 0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7, - 0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7, - 0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF, - 0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C, - True, - ), - ( - generator_192, - int( - "0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" - "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" - "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" - "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" - "dd", - 16, - ), - 0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7, - 0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336, - 0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91, - 0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09, - False, - ), - ( - generator_192, - int( - "0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" - "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" - "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" - "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" - "8a", - 16, - ), - 0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B, - 0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4, - 0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1, - 0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520, - False, - ), - ( - generator_192, - int( - "0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" - "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" - "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" - "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" - "fb", - 16, - ), - 0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828, - 0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF, - 0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796, - 0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0, - False, - ), - ( - generator_192, - int( - "0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" - "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" - "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" - "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" - "6d", - 16, - ), - 0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F, - 0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686, - 0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325, - 0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633, - False, - ), - ( - generator_192, - int( - "0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" - "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" - "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" - "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" - "e1", - 16, - ), - 0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04, - 0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1, - 0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C, - 0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50, - False, - ), - ( - generator_192, - int( - "0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" - "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" - "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" - "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" - "a2", - 16, - ), - 0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA, - 0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E, - 0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955, - 0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95, - False, - ), - ( - generator_192, - int( - "0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" - "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" - "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" - "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" - "58", - 16, - ), - 0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F, - 0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC, - 0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62, - 0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289, - False, - ), - ( - generator_192, - int( - "0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" - "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" - "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" - "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" - "7a", - 16, - ), - 0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A, - 0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905, - 0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B, - 0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5, - True, - ), - ( - generator_192, - int( - "0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" - "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" - "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" - "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" - "6d", - 16, - ), - 0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF, - 0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1, - 0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06, - 0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC, - False, - ), - ( - generator_192, - int( - "0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" - "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" - "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" - "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" - "d7", - 16, - ), - 0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753, - 0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520, - 0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668, - 0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599, - False, - ), - ( - generator_192, - int( - "0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" - "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" - "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" - "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" - "f3", - 16, - ), - 0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835, - 0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B, - 0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF, - 0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068, - False, - ), - ( - generator_192, - int( - "0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" - "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" - "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" - "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", - 16, - ), - 0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0, - 0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA, - 0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23, - 0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9, - False, - ), - ( - generator_192, - int( - "0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" - "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" - "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" - "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", - 16, - ), - 0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77, - 0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22, - 0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1, - 0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9, - False, - ), -] - - -@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS) -def test_signature_validity(gen, msg, qx, qy, r, s, expected): - """ - `msg` = message, `qx` and `qy` represent the base point on - elliptic curve of `gen`, `r` and `s` are the signature, and - `expected` is True iff the signature is expected to be valid.""" - pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) - with pytest.warns(DeprecationWarning) as warns: - msg_dgst = digest_integer(msg) - assert len(warns) == 3 - assert "unused" in warns[0].message.args[0] - assert "unused" in warns[1].message.args[0] - assert "unused" in warns[2].message.args[0] - assert expected == pubk.verifies(msg_dgst, Signature(r, s)) - - -@pytest.mark.parametrize( - "gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]] -) -def test_pk_recovery(gen, msg, r, s, qx, qy, expected): - del expected - sign = Signature(r, s) - with pytest.warns(DeprecationWarning) as warns: - msg_dgst = digest_integer(msg) - assert len(warns) == 3 - assert "unused" in warns[0].message.args[0] - assert "unused" in warns[1].message.args[0] - assert "unused" in warns[2].message.args[0] - pks = sign.recover_public_keys(msg_dgst, gen) - - assert pks - - # Test if the signature is valid for all found public keys - for pk in pks: - q = pk.point - test_signature_validity(gen, msg, q.x(), q.y(), r, s, True) - - # Test if the original public key is in the set of found keys - original_q = ellipticcurve.Point(gen.curve(), qx, qy) - points = [pk.point for pk in pks] - assert original_q in points - - -@st.composite -def st_random_gen_key_msg_nonce(draw): - """Hypothesis strategy for test_sig_verify().""" - name_gen = { - "generator_192": generator_192, - "generator_224": generator_224, - "generator_256": generator_256, - "generator_secp256k1": generator_secp256k1, - "generator_384": generator_384, - "generator_521": generator_521, - } - name = draw(st.sampled_from(sorted(name_gen.keys()))) - note("Generator used: {0}".format(name)) - generator = name_gen[name] - order = int(generator.order()) - 1 - - key = draw(st.integers(min_value=1, max_value=order)) - msg = draw(st.integers(min_value=1, max_value=order)) - nonce = draw( - st.integers(min_value=1, max_value=order) - | st.integers(min_value=order >> 1, max_value=order) - ) - return generator, key, msg, nonce - - -SIG_VER_SETTINGS = dict(HYP_SETTINGS) -if "--fast" in sys.argv: # pragma: no cover - SIG_VER_SETTINGS["max_examples"] = 1 -else: - SIG_VER_SETTINGS["max_examples"] = 10 - - -@settings(**SIG_VER_SETTINGS) -@example((generator_224, 4, 1, 1)) -@given(st_random_gen_key_msg_nonce()) -def test_sig_verify(args): - """ - Check if signing and verification works for arbitrary messages and - that signatures for other messages are rejected. - """ - generator, sec_mult, msg, nonce = args - - pubkey = Public_key(generator, generator * sec_mult) - privkey = Private_key(pubkey, sec_mult) - - signature = privkey.sign(msg, nonce) - - assert pubkey.verifies(msg, signature) - - assert not pubkey.verifies(msg - 1, signature) - - -def test_int_to_string_with_zero(): - with pytest.warns(DeprecationWarning) as warns: - assert int_to_string(0) == b"\x00" - - assert len(warns) == 1 - assert "unused" in warns[0].message.args[0] diff --git a/.venv/Lib/site-packages/ecdsa/test_eddsa.py b/.venv/Lib/site-packages/ecdsa/test_eddsa.py deleted file mode 100644 index 6821b3b..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_eddsa.py +++ /dev/null @@ -1,1124 +0,0 @@ -import sys -import pickle -import hashlib -import pytest - -try: - import unittest2 as unittest -except ImportError: - import unittest -from hypothesis import given, settings, example -import hypothesis.strategies as st -from .ellipticcurve import PointEdwards, INFINITY, CurveEdTw -from .eddsa import ( - generator_ed25519, - curve_ed25519, - generator_ed448, - curve_ed448, - PrivateKey, - PublicKey, -) -from .ecdsa import generator_256, curve_256 -from .errors import MalformedPointError -from ._compat import a2b_hex, compat26_str - - -class TestA2B_Hex(unittest.TestCase): - def test_invalid_input(self): - with self.assertRaises(ValueError): - a2b_hex("abcdefghi") - - -def test_ed25519_curve_compare(): - assert curve_ed25519 != curve_256 - - -def test_ed25519_and_ed448_compare(): - assert curve_ed448 != curve_ed25519 - - -def test_ed25519_and_custom_curve_compare(): - a = CurveEdTw(curve_ed25519.p(), -curve_ed25519.a(), 1) - - assert curve_ed25519 != a - - -def test_ed25519_and_almost_exact_curve_compare(): - a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), 1) - - assert curve_ed25519 != a - - -def test_ed25519_and_same_curve_params(): - a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), curve_ed25519.d()) - - assert curve_ed25519 == a - assert not (curve_ed25519 != a) - - -def test_ed25519_contains_point(): - g = generator_ed25519 - assert curve_ed25519.contains_point(g.x(), g.y()) - - -def test_ed25519_contains_point_bad(): - assert not curve_ed25519.contains_point(1, 1) - - -def test_ed25519_double(): - a = generator_ed25519 - - z = a.double() - - assert isinstance(z, PointEdwards) - - x2 = int( - "24727413235106541002554574571675588834622768167397638456726423" - "682521233608206" - ) - y2 = int( - "15549675580280190176352668710449542251549572066445060580507079" - "593062643049417" - ) - - b = PointEdwards(curve_ed25519, x2, y2, 1, x2 * y2) - - assert z == b - assert a != b - - -def test_ed25519_add_as_double(): - a = generator_ed25519 - - z = a + a - - assert isinstance(z, PointEdwards) - - b = generator_ed25519.double() - - assert z == b - - -def test_ed25519_double_infinity(): - a = PointEdwards(curve_ed25519, 0, 1, 1, 0) - - z = a.double() - - assert z is INFINITY - - -def test_ed25519_double_badly_encoded_infinity(): - # invalid point, mostly to make instrumental happy - a = PointEdwards(curve_ed25519, 1, 1, 1, 0) - - z = a.double() - - assert z is INFINITY - - -def test_ed25519_eq_with_different_z(): - x = generator_ed25519.x() - y = generator_ed25519.y() - p = curve_ed25519.p() - - a = PointEdwards(curve_ed25519, x * 2 % p, y * 2 % p, 2, x * y * 2 % p) - b = PointEdwards(curve_ed25519, x * 3 % p, y * 3 % p, 3, x * y * 3 % p) - - assert a == b - - assert not (a != b) - - -def test_ed25519_eq_against_infinity(): - assert generator_ed25519 != INFINITY - - -def test_ed25519_eq_encoded_infinity_against_infinity(): - a = PointEdwards(curve_ed25519, 0, 1, 1, 0) - assert a == INFINITY - - -def test_ed25519_eq_bad_encode_of_infinity_against_infinity(): - # technically incorrect encoding of the point at infinity, but we check - # both X and T, so verify that just T==0 works - a = PointEdwards(curve_ed25519, 1, 1, 1, 0) - assert a == INFINITY - - -def test_ed25519_eq_against_non_Edwards_point(): - assert generator_ed25519 != generator_256 - - -def test_ed25519_eq_against_negated_point(): - g = generator_ed25519 - neg = PointEdwards(curve_ed25519, -g.x(), g.y(), 1, -g.x() * g.y()) - assert g != neg - - -def test_ed25519_eq_x_different_y(): - # not points on the curve, but __eq__ doesn't care - a = PointEdwards(curve_ed25519, 1, 1, 1, 1) - b = PointEdwards(curve_ed25519, 1, 2, 1, 2) - - assert a != b - - -def test_ed25519_mul_by_order(): - g = PointEdwards( - curve_ed25519, - generator_ed25519.x(), - generator_ed25519.y(), - 1, - generator_ed25519.x() * generator_ed25519.y(), - ) - - assert g * generator_ed25519.order() == INFINITY - - -def test_radd(): - - a = PointEdwards(curve_ed25519, 1, 1, 1, 1) - - p = INFINITY + a - - assert p == a - - -def test_ed25519_test_normalisation_and_scaling(): - x = generator_ed25519.x() - y = generator_ed25519.y() - p = curve_ed25519.p() - - a = PointEdwards(curve_ed25519, x * 11 % p, y * 11 % p, 11, x * y * 11 % p) - - assert a.x() == x - assert a.y() == y - - a.scale() - - assert a.x() == x - assert a.y() == y - - a.scale() # second execution should be a noop - - assert a.x() == x - assert a.y() == y - - -def test_ed25519_add_three_times(): - a = generator_ed25519 - - z = a + a + a - - x3 = int( - "468967334644549386571235445953867877890461982801326656862413" - "21779790909858396" - ) - y3 = int( - "832484377853344397649037712036920113830141722629755531674120" - "2210403726505172" - ) - - b = PointEdwards(curve_ed25519, x3, y3, 1, x3 * y3) - - assert z == b - - -def test_ed25519_add_to_infinity(): - # generator * (order-1) - x1 = int( - "427838232691226969392843410947554224151809796397784248136826" - "78720006717057747" - ) - y1 = int( - "463168356949264781694283940034751631413079938662562256157830" - "33603165251855960" - ) - inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) - - inf = inf_m_1 + generator_ed25519 - - assert inf is INFINITY - - -def test_ed25519_add_and_mul_equivalence(): - g = generator_ed25519 - - assert g + g == g * 2 - assert g + g + g == g * 3 - - -def test_ed25519_add_literal_infinity(): - g = generator_ed25519 - z = g + INFINITY - - assert z == g - - -def test_ed25519_add_infinity(): - inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) - g = generator_ed25519 - z = g + inf - - assert z == g - - z = inf + g - - assert z == g - - -class TestEd25519(unittest.TestCase): - def test_add_wrong_curves(self): - with self.assertRaises(ValueError) as e: - generator_ed25519 + generator_ed448 - - self.assertIn("different curve", str(e.exception)) - - def test_add_wrong_point_type(self): - with self.assertRaises(ValueError) as e: - generator_ed25519 + generator_256 - - self.assertIn("different curve", str(e.exception)) - - -def test_generate_with_point(): - x1 = int( - "427838232691226969392843410947554224151809796397784248136826" - "78720006717057747" - ) - y1 = int( - "463168356949264781694283940034751631413079938662562256157830" - "33603165251855960" - ) - p = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) - - pk = PublicKey(generator_ed25519, b"0" * 32, public_point=p) - - assert pk.public_point() == p - - -def test_ed25519_mul_to_order_min_1(): - x1 = int( - "427838232691226969392843410947554224151809796397784248136826" - "78720006717057747" - ) - y1 = int( - "463168356949264781694283940034751631413079938662562256157830" - "33603165251855960" - ) - inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) - - assert generator_ed25519 * (generator_ed25519.order() - 1) == inf_m_1 - - -def test_ed25519_mul_to_infinity(): - assert generator_ed25519 * generator_ed25519.order() == INFINITY - - -def test_ed25519_mul_to_infinity_plus_1(): - g = generator_ed25519 - assert g * (g.order() + 1) == g - - -def test_ed25519_mul_and_add(): - g = generator_ed25519 - a = g * 128 - b = g * 64 + g * 64 - - assert a == b - - -def test_ed25519_mul_and_add_2(): - g = generator_ed25519 - - a = g * 123 - b = g * 120 + g * 3 - - assert a == b - - -def test_ed25519_mul_infinity(): - inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) - - z = inf * 11 - - assert z == INFINITY - - -def test_ed25519_mul_by_zero(): - z = generator_ed25519 * 0 - - assert z == INFINITY - - -def test_ed25519_mul_by_one(): - z = generator_ed25519 * 1 - - assert z == generator_ed25519 - - -def test_ed25519_mul_custom_point(): - # verify that multiplication without order set works - - g = generator_ed25519 - - a = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - z = a * 11 - - assert z == g * 11 - - -def test_ed25519_pickle(): - g = generator_ed25519 - assert pickle.loads(pickle.dumps(g)) == g - - -def test_ed448_eq_against_different_curve(): - assert generator_ed25519 != generator_ed448 - - -def test_ed448_double(): - g = generator_ed448 - z = g.double() - - assert isinstance(z, PointEdwards) - - x2 = int( - "4845591495304045936995492052586696895690942404582120401876" - "6013278705691214670908136440114445572635086627683154494739" - "7859048262938744149" - ) - y2 = int( - "4940887598674337276743026725267350893505445523037277237461" - "2648447308771911703729389009346215770388834286503647778745" - "3078312060500281069" - ) - - b = PointEdwards(curve_ed448, x2, y2, 1, x2 * y2) - - assert z == b - assert g != b - - -def test_ed448_add_as_double(): - g = generator_ed448 - z = g + g - - b = g.double() - - assert z == b - - -def test_ed448_mul_as_double(): - g = generator_ed448 - z = g * 2 - b = g.double() - - assert z == b - - -def test_ed448_add_to_infinity(): - # generator * (order - 1) - x1 = int( - "5022586839996825903617194737881084981068517190547539260353" - "6473749366191269932473977736719082931859264751085238669719" - "1187378895383117729" - ) - y1 = int( - "2988192100784814926760179304439306734375440401540802420959" - "2824137233150618983587600353687865541878473398230323350346" - "2500531545062832660" - ) - inf_m_1 = PointEdwards(curve_ed448, x1, y1, 1, x1 * y1) - - inf = inf_m_1 + generator_ed448 - - assert inf is INFINITY - - -def test_ed448_mul_to_infinity(): - g = generator_ed448 - inf = g * g.order() - - assert inf is INFINITY - - -def test_ed448_mul_to_infinity_plus_1(): - g = generator_ed448 - - z = g * (g.order() + 1) - - assert z == g - - -def test_ed448_add_and_mul_equivalence(): - g = generator_ed448 - - assert g + g == g * 2 - assert g + g + g == g * 3 - - -def test_ed25519_encode(): - g = generator_ed25519 - g_bytes = g.to_bytes() - assert len(g_bytes) == 32 - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - assert g_bytes == exp_bytes - - -def test_ed25519_decode(): - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - a = PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - assert a == generator_ed25519 - - -class TestEdwardsMalformed(unittest.TestCase): - def test_invalid_point(self): - exp_bytes = ( - b"\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - with self.assertRaises(MalformedPointError): - PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - def test_invalid_length(self): - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66" - ) - with self.assertRaises(MalformedPointError) as e: - PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - self.assertIn("length", str(e.exception)) - - def test_ed448_invalid(self): - exp_bytes = b"\xff" * 57 - with self.assertRaises(MalformedPointError): - PointEdwards.from_bytes(curve_ed448, exp_bytes) - - -def test_ed448_encode(): - g = generator_ed448 - g_bytes = g.to_bytes() - assert len(g_bytes) == 57 - exp_bytes = ( - b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" - b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" - b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" - b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" - ) - assert g_bytes == exp_bytes - - -def test_ed448_decode(): - exp_bytes = ( - b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" - b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" - b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" - b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" - ) - - a = PointEdwards.from_bytes(curve_ed448, exp_bytes) - - assert a == generator_ed448 - - -class TestEdDSAEquality(unittest.TestCase): - def test_equal_public_points(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed25519, b"\x01" * 32) - - self.assertEqual(key1, key2) - # verify that `__ne__` works as expected - self.assertFalse(key1 != key2) - - def test_unequal_public_points(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed25519, b"\x03" * 32) - - self.assertNotEqual(key1, key2) - - def test_unequal_to_string(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = b"\x01" * 32 - - self.assertNotEqual(key1, key2) - - def test_unequal_publickey_curves(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed448, b"\x03" * 56 + b"\x00") - - self.assertNotEqual(key1, key2) - # verify that `__ne__` works as expected - self.assertTrue(key1 != key2) - - def test_equal_private_keys(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed25519, b"\x01" * 32) - - self.assertEqual(key1, key2) - # verify that `__ne__` works as expected - self.assertFalse(key1 != key2) - - def test_unequal_private_keys(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed25519, b"\x02" * 32) - - self.assertNotEqual(key1, key2) - # verify that `__ne__` works as expected - self.assertTrue(key1 != key2) - - def test_unequal_privatekey_to_string(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = b"\x01" * 32 - - self.assertNotEqual(key1, key2) - - def test_unequal_privatekey_curves(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed448, b"\x01" * 57) - - self.assertNotEqual(key1, key2) - - -class TestInvalidEdDSAInputs(unittest.TestCase): - def test_wrong_length_of_private_key(self): - with self.assertRaises(ValueError): - PrivateKey(generator_ed25519, b"\x01" * 31) - - def test_wrong_length_of_public_key(self): - with self.assertRaises(ValueError): - PublicKey(generator_ed25519, b"\x01" * 33) - - def test_wrong_cofactor_curve(self): - ed_c = curve_ed25519 - - def _hash(data): - return hashlib.new("sha512", compat26_str(data)).digest() - - curve = CurveEdTw(ed_c.p(), ed_c.a(), ed_c.d(), 1, _hash) - g = generator_ed25519 - fake_gen = PointEdwards(curve, g.x(), g.y(), 1, g.x() * g.y()) - - with self.assertRaises(ValueError) as e: - PrivateKey(fake_gen, g.to_bytes()) - - self.assertIn("cofactor", str(e.exception)) - - def test_invalid_signature_length(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - with self.assertRaises(ValueError) as e: - key.verify(b"", b"\x01" * 65) - - self.assertIn("length", str(e.exception)) - - def test_changing_public_key(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - g = key.point - - new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - key.point = new_g - - self.assertEqual(g, key.point) - - def test_changing_public_key_to_different_point(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - with self.assertRaises(ValueError) as e: - key.point = generator_ed25519 - - self.assertIn("coordinates", str(e.exception)) - - def test_invalid_s_value(self): - key = PublicKey( - generator_ed25519, - b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" - b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", - ) - sig_valid = bytearray( - b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" - b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" - b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" - b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" - ) - - self.assertTrue(key.verify(b"", sig_valid)) - - sig_invalid = bytearray(sig_valid) - sig_invalid[-1] = 0xFF - - with self.assertRaises(ValueError): - key.verify(b"", sig_invalid) - - def test_invalid_r_value(self): - key = PublicKey( - generator_ed25519, - b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" - b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", - ) - sig_valid = bytearray( - b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" - b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" - b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" - b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" - ) - - self.assertTrue(key.verify(b"", sig_valid)) - - sig_invalid = bytearray(sig_valid) - sig_invalid[0] = 0xE0 - - with self.assertRaises(ValueError): - key.verify(b"", sig_invalid) - - -HYP_SETTINGS = dict() -if "--fast" in sys.argv: # pragma: no cover - HYP_SETTINGS["max_examples"] = 2 -else: - HYP_SETTINGS["max_examples"] = 10 - - -@settings(**HYP_SETTINGS) -@example(1) -@example(5) # smallest multiple that requires changing sign of x -@given(st.integers(min_value=1, max_value=int(generator_ed25519.order() - 1))) -def test_ed25519_encode_decode(multiple): - a = generator_ed25519 * multiple - - b = PointEdwards.from_bytes(curve_ed25519, a.to_bytes()) - - assert a == b - - -@settings(**HYP_SETTINGS) -@example(1) -@example(2) # smallest multiple that requires changing the sign of x -@given(st.integers(min_value=1, max_value=int(generator_ed448.order() - 1))) -def test_ed448_encode_decode(multiple): - a = generator_ed448 * multiple - - b = PointEdwards.from_bytes(curve_ed448, a.to_bytes()) - - assert a == b - - -@settings(**HYP_SETTINGS) -@example(1) -@example(2) -@given(st.integers(min_value=1, max_value=int(generator_ed25519.order()) - 1)) -def test_ed25519_mul_precompute_vs_naf(multiple): - """Compare multiplication with and without precomputation.""" - g = generator_ed25519 - new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - assert g * multiple == multiple * new_g - - -# Test vectors from RFC 8032 -TEST_VECTORS = [ - # TEST 1 - ( - generator_ed25519, - "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60", - "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a", - "", - "e5564300c360ac729086e2cc806e828a" - "84877f1eb8e5d974d873e06522490155" - "5fb8821590a33bacc61e39701cf9b46b" - "d25bf5f0595bbe24655141438e7a100b", - ), - # TEST 2 - ( - generator_ed25519, - "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb", - "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c", - "72", - "92a009a9f0d4cab8720e820b5f642540" - "a2b27b5416503f8fb3762223ebdb69da" - "085ac1e43e15996e458f3613d0f11d8c" - "387b2eaeb4302aeeb00d291612bb0c00", - ), - # TEST 3 - ( - generator_ed25519, - "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7", - "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025", - "af82", - "6291d657deec24024827e69c3abe01a3" - "0ce548a284743a445e3680d7db5ac3ac" - "18ff9b538d16f290ae67f760984dc659" - "4a7c15e9716ed28dc027beceea1ec40a", - ), - # TEST 1024 - ( - generator_ed25519, - "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5", - "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e", - "08b8b2b733424243760fe426a4b54908" - "632110a66c2f6591eabd3345e3e4eb98" - "fa6e264bf09efe12ee50f8f54e9f77b1" - "e355f6c50544e23fb1433ddf73be84d8" - "79de7c0046dc4996d9e773f4bc9efe57" - "38829adb26c81b37c93a1b270b20329d" - "658675fc6ea534e0810a4432826bf58c" - "941efb65d57a338bbd2e26640f89ffbc" - "1a858efcb8550ee3a5e1998bd177e93a" - "7363c344fe6b199ee5d02e82d522c4fe" - "ba15452f80288a821a579116ec6dad2b" - "3b310da903401aa62100ab5d1a36553e" - "06203b33890cc9b832f79ef80560ccb9" - "a39ce767967ed628c6ad573cb116dbef" - "efd75499da96bd68a8a97b928a8bbc10" - "3b6621fcde2beca1231d206be6cd9ec7" - "aff6f6c94fcd7204ed3455c68c83f4a4" - "1da4af2b74ef5c53f1d8ac70bdcb7ed1" - "85ce81bd84359d44254d95629e9855a9" - "4a7c1958d1f8ada5d0532ed8a5aa3fb2" - "d17ba70eb6248e594e1a2297acbbb39d" - "502f1a8c6eb6f1ce22b3de1a1f40cc24" - "554119a831a9aad6079cad88425de6bd" - "e1a9187ebb6092cf67bf2b13fd65f270" - "88d78b7e883c8759d2c4f5c65adb7553" - "878ad575f9fad878e80a0c9ba63bcbcc" - "2732e69485bbc9c90bfbd62481d9089b" - "eccf80cfe2df16a2cf65bd92dd597b07" - "07e0917af48bbb75fed413d238f5555a" - "7a569d80c3414a8d0859dc65a46128ba" - "b27af87a71314f318c782b23ebfe808b" - "82b0ce26401d2e22f04d83d1255dc51a" - "ddd3b75a2b1ae0784504df543af8969b" - "e3ea7082ff7fc9888c144da2af58429e" - "c96031dbcad3dad9af0dcbaaaf268cb8" - "fcffead94f3c7ca495e056a9b47acdb7" - "51fb73e666c6c655ade8297297d07ad1" - "ba5e43f1bca32301651339e22904cc8c" - "42f58c30c04aafdb038dda0847dd988d" - "cda6f3bfd15c4b4c4525004aa06eeff8" - "ca61783aacec57fb3d1f92b0fe2fd1a8" - "5f6724517b65e614ad6808d6f6ee34df" - "f7310fdc82aebfd904b01e1dc54b2927" - "094b2db68d6f903b68401adebf5a7e08" - "d78ff4ef5d63653a65040cf9bfd4aca7" - "984a74d37145986780fc0b16ac451649" - "de6188a7dbdf191f64b5fc5e2ab47b57" - "f7f7276cd419c17a3ca8e1b939ae49e4" - "88acba6b965610b5480109c8b17b80e1" - "b7b750dfc7598d5d5011fd2dcc5600a3" - "2ef5b52a1ecc820e308aa342721aac09" - "43bf6686b64b2579376504ccc493d97e" - "6aed3fb0f9cd71a43dd497f01f17c0e2" - "cb3797aa2a2f256656168e6c496afc5f" - "b93246f6b1116398a346f1a641f3b041" - "e989f7914f90cc2c7fff357876e506b5" - "0d334ba77c225bc307ba537152f3f161" - "0e4eafe595f6d9d90d11faa933a15ef1" - "369546868a7f3a45a96768d40fd9d034" - "12c091c6315cf4fde7cb68606937380d" - "b2eaaa707b4c4185c32eddcdd306705e" - "4dc1ffc872eeee475a64dfac86aba41c" - "0618983f8741c5ef68d3a101e8a3b8ca" - "c60c905c15fc910840b94c00a0b9d0", - "0aab4c900501b3e24d7cdf4663326a3a" - "87df5e4843b2cbdb67cbf6e460fec350" - "aa5371b1508f9f4528ecea23c436d94b" - "5e8fcd4f681e30a6ac00a9704a188a03", - ), - # TEST SHA(abc) - ( - generator_ed25519, - "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42", - "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf", - "ddaf35a193617abacc417349ae204131" - "12e6fa4e89a97ea20a9eeee64b55d39a" - "2192992a274fc1a836ba3c23a3feebbd" - "454d4423643ce80e2a9ac94fa54ca49f", - "dc2a4459e7369633a52b1bf277839a00" - "201009a3efbf3ecb69bea2186c26b589" - "09351fc9ac90b3ecfdfbc7c66431e030" - "3dca179c138ac17ad9bef1177331a704", - ), - # Blank - ( - generator_ed448, - "6c82a562cb808d10d632be89c8513ebf" - "6c929f34ddfa8c9f63c9960ef6e348a3" - "528c8a3fcc2f044e39a3fc5b94492f8f" - "032e7549a20098f95b", - "5fd7449b59b461fd2ce787ec616ad46a" - "1da1342485a70e1f8a0ea75d80e96778" - "edf124769b46c7061bd6783df1e50f6c" - "d1fa1abeafe8256180", - "", - "533a37f6bbe457251f023c0d88f976ae" - "2dfb504a843e34d2074fd823d41a591f" - "2b233f034f628281f2fd7a22ddd47d78" - "28c59bd0a21bfd3980ff0d2028d4b18a" - "9df63e006c5d1c2d345b925d8dc00b41" - "04852db99ac5c7cdda8530a113a0f4db" - "b61149f05a7363268c71d95808ff2e65" - "2600", - ), - # 1 octet - ( - generator_ed448, - "c4eab05d357007c632f3dbb48489924d" - "552b08fe0c353a0d4a1f00acda2c463a" - "fbea67c5e8d2877c5e3bc397a659949e" - "f8021e954e0a12274e", - "43ba28f430cdff456ae531545f7ecd0a" - "c834a55d9358c0372bfa0c6c6798c086" - "6aea01eb00742802b8438ea4cb82169c" - "235160627b4c3a9480", - "03", - "26b8f91727bd62897af15e41eb43c377" - "efb9c610d48f2335cb0bd0087810f435" - "2541b143c4b981b7e18f62de8ccdf633" - "fc1bf037ab7cd779805e0dbcc0aae1cb" - "cee1afb2e027df36bc04dcecbf154336" - "c19f0af7e0a6472905e799f1953d2a0f" - "f3348ab21aa4adafd1d234441cf807c0" - "3a00", - ), - # 11 octets - ( - generator_ed448, - "cd23d24f714274e744343237b93290f5" - "11f6425f98e64459ff203e8985083ffd" - "f60500553abc0e05cd02184bdb89c4cc" - "d67e187951267eb328", - "dcea9e78f35a1bf3499a831b10b86c90" - "aac01cd84b67a0109b55a36e9328b1e3" - "65fce161d71ce7131a543ea4cb5f7e9f" - "1d8b00696447001400", - "0c3e544074ec63b0265e0c", - "1f0a8888ce25e8d458a21130879b840a" - "9089d999aaba039eaf3e3afa090a09d3" - "89dba82c4ff2ae8ac5cdfb7c55e94d5d" - "961a29fe0109941e00b8dbdeea6d3b05" - "1068df7254c0cdc129cbe62db2dc957d" - "bb47b51fd3f213fb8698f064774250a5" - "028961c9bf8ffd973fe5d5c206492b14" - "0e00", - ), - # 12 octets - ( - generator_ed448, - "258cdd4ada32ed9c9ff54e63756ae582" - "fb8fab2ac721f2c8e676a72768513d93" - "9f63dddb55609133f29adf86ec9929dc" - "cb52c1c5fd2ff7e21b", - "3ba16da0c6f2cc1f30187740756f5e79" - "8d6bc5fc015d7c63cc9510ee3fd44adc" - "24d8e968b6e46e6f94d19b945361726b" - "d75e149ef09817f580", - "64a65f3cdedcdd66811e2915", - "7eeeab7c4e50fb799b418ee5e3197ff6" - "bf15d43a14c34389b59dd1a7b1b85b4a" - "e90438aca634bea45e3a2695f1270f07" - "fdcdf7c62b8efeaf00b45c2c96ba457e" - "b1a8bf075a3db28e5c24f6b923ed4ad7" - "47c3c9e03c7079efb87cb110d3a99861" - "e72003cbae6d6b8b827e4e6c143064ff" - "3c00", - ), - # 13 octets - ( - generator_ed448, - "7ef4e84544236752fbb56b8f31a23a10" - "e42814f5f55ca037cdcc11c64c9a3b29" - "49c1bb60700314611732a6c2fea98eeb" - "c0266a11a93970100e", - "b3da079b0aa493a5772029f0467baebe" - "e5a8112d9d3a22532361da294f7bb381" - "5c5dc59e176b4d9f381ca0938e13c6c0" - "7b174be65dfa578e80", - "64a65f3cdedcdd66811e2915e7", - "6a12066f55331b6c22acd5d5bfc5d712" - "28fbda80ae8dec26bdd306743c5027cb" - "4890810c162c027468675ecf645a8317" - "6c0d7323a2ccde2d80efe5a1268e8aca" - "1d6fbc194d3f77c44986eb4ab4177919" - "ad8bec33eb47bbb5fc6e28196fd1caf5" - "6b4e7e0ba5519234d047155ac727a105" - "3100", - ), - # 64 octets - ( - generator_ed448, - "d65df341ad13e008567688baedda8e9d" - "cdc17dc024974ea5b4227b6530e339bf" - "f21f99e68ca6968f3cca6dfe0fb9f4fa" - "b4fa135d5542ea3f01", - "df9705f58edbab802c7f8363cfe5560a" - "b1c6132c20a9f1dd163483a26f8ac53a" - "39d6808bf4a1dfbd261b099bb03b3fb5" - "0906cb28bd8a081f00", - "bd0f6a3747cd561bdddf4640a332461a" - "4a30a12a434cd0bf40d766d9c6d458e5" - "512204a30c17d1f50b5079631f64eb31" - "12182da3005835461113718d1a5ef944", - "554bc2480860b49eab8532d2a533b7d5" - "78ef473eeb58c98bb2d0e1ce488a98b1" - "8dfde9b9b90775e67f47d4a1c3482058" - "efc9f40d2ca033a0801b63d45b3b722e" - "f552bad3b4ccb667da350192b61c508c" - "f7b6b5adadc2c8d9a446ef003fb05cba" - "5f30e88e36ec2703b349ca229c267083" - "3900", - ), - # 256 octets - ( - generator_ed448, - "2ec5fe3c17045abdb136a5e6a913e32a" - "b75ae68b53d2fc149b77e504132d3756" - "9b7e766ba74a19bd6162343a21c8590a" - "a9cebca9014c636df5", - "79756f014dcfe2079f5dd9e718be4171" - "e2ef2486a08f25186f6bff43a9936b9b" - "fe12402b08ae65798a3d81e22e9ec80e" - "7690862ef3d4ed3a00", - "15777532b0bdd0d1389f636c5f6b9ba7" - "34c90af572877e2d272dd078aa1e567c" - "fa80e12928bb542330e8409f31745041" - "07ecd5efac61ae7504dabe2a602ede89" - "e5cca6257a7c77e27a702b3ae39fc769" - "fc54f2395ae6a1178cab4738e543072f" - "c1c177fe71e92e25bf03e4ecb72f47b6" - "4d0465aaea4c7fad372536c8ba516a60" - "39c3c2a39f0e4d832be432dfa9a706a6" - "e5c7e19f397964ca4258002f7c0541b5" - "90316dbc5622b6b2a6fe7a4abffd9610" - "5eca76ea7b98816af0748c10df048ce0" - "12d901015a51f189f3888145c03650aa" - "23ce894c3bd889e030d565071c59f409" - "a9981b51878fd6fc110624dcbcde0bf7" - "a69ccce38fabdf86f3bef6044819de11", - "c650ddbb0601c19ca11439e1640dd931" - "f43c518ea5bea70d3dcde5f4191fe53f" - "00cf966546b72bcc7d58be2b9badef28" - "743954e3a44a23f880e8d4f1cfce2d7a" - "61452d26da05896f0a50da66a239a8a1" - "88b6d825b3305ad77b73fbac0836ecc6" - "0987fd08527c1a8e80d5823e65cafe2a" - "3d00", - ), - # 1023 octets - ( - generator_ed448, - "872d093780f5d3730df7c212664b37b8" - "a0f24f56810daa8382cd4fa3f77634ec" - "44dc54f1c2ed9bea86fafb7632d8be19" - "9ea165f5ad55dd9ce8", - "a81b2e8a70a5ac94ffdbcc9badfc3feb" - "0801f258578bb114ad44ece1ec0e799d" - "a08effb81c5d685c0c56f64eecaef8cd" - "f11cc38737838cf400", - "6ddf802e1aae4986935f7f981ba3f035" - "1d6273c0a0c22c9c0e8339168e675412" - "a3debfaf435ed651558007db4384b650" - "fcc07e3b586a27a4f7a00ac8a6fec2cd" - "86ae4bf1570c41e6a40c931db27b2faa" - "15a8cedd52cff7362c4e6e23daec0fbc" - "3a79b6806e316efcc7b68119bf46bc76" - "a26067a53f296dafdbdc11c77f7777e9" - "72660cf4b6a9b369a6665f02e0cc9b6e" - "dfad136b4fabe723d2813db3136cfde9" - "b6d044322fee2947952e031b73ab5c60" - "3349b307bdc27bc6cb8b8bbd7bd32321" - "9b8033a581b59eadebb09b3c4f3d2277" - "d4f0343624acc817804728b25ab79717" - "2b4c5c21a22f9c7839d64300232eb66e" - "53f31c723fa37fe387c7d3e50bdf9813" - "a30e5bb12cf4cd930c40cfb4e1fc6225" - "92a49588794494d56d24ea4b40c89fc0" - "596cc9ebb961c8cb10adde976a5d602b" - "1c3f85b9b9a001ed3c6a4d3b1437f520" - "96cd1956d042a597d561a596ecd3d173" - "5a8d570ea0ec27225a2c4aaff26306d1" - "526c1af3ca6d9cf5a2c98f47e1c46db9" - "a33234cfd4d81f2c98538a09ebe76998" - "d0d8fd25997c7d255c6d66ece6fa56f1" - "1144950f027795e653008f4bd7ca2dee" - "85d8e90f3dc315130ce2a00375a318c7" - "c3d97be2c8ce5b6db41a6254ff264fa6" - "155baee3b0773c0f497c573f19bb4f42" - "40281f0b1f4f7be857a4e59d416c06b4" - "c50fa09e1810ddc6b1467baeac5a3668" - "d11b6ecaa901440016f389f80acc4db9" - "77025e7f5924388c7e340a732e554440" - "e76570f8dd71b7d640b3450d1fd5f041" - "0a18f9a3494f707c717b79b4bf75c984" - "00b096b21653b5d217cf3565c9597456" - "f70703497a078763829bc01bb1cbc8fa" - "04eadc9a6e3f6699587a9e75c94e5bab" - "0036e0b2e711392cff0047d0d6b05bd2" - "a588bc109718954259f1d86678a579a3" - "120f19cfb2963f177aeb70f2d4844826" - "262e51b80271272068ef5b3856fa8535" - "aa2a88b2d41f2a0e2fda7624c2850272" - "ac4a2f561f8f2f7a318bfd5caf969614" - "9e4ac824ad3460538fdc25421beec2cc" - "6818162d06bbed0c40a387192349db67" - "a118bada6cd5ab0140ee273204f628aa" - "d1c135f770279a651e24d8c14d75a605" - "9d76b96a6fd857def5e0b354b27ab937" - "a5815d16b5fae407ff18222c6d1ed263" - "be68c95f32d908bd895cd76207ae7264" - "87567f9a67dad79abec316f683b17f2d" - "02bf07e0ac8b5bc6162cf94697b3c27c" - "d1fea49b27f23ba2901871962506520c" - "392da8b6ad0d99f7013fbc06c2c17a56" - "9500c8a7696481c1cd33e9b14e40b82e" - "79a5f5db82571ba97bae3ad3e0479515" - "bb0e2b0f3bfcd1fd33034efc6245eddd" - "7ee2086ddae2600d8ca73e214e8c2b0b" - "db2b047c6a464a562ed77b73d2d841c4" - "b34973551257713b753632efba348169" - "abc90a68f42611a40126d7cb21b58695" - "568186f7e569d2ff0f9e745d0487dd2e" - "b997cafc5abf9dd102e62ff66cba87", - "e301345a41a39a4d72fff8df69c98075" - "a0cc082b802fc9b2b6bc503f926b65bd" - "df7f4c8f1cb49f6396afc8a70abe6d8a" - "ef0db478d4c6b2970076c6a0484fe76d" - "76b3a97625d79f1ce240e7c576750d29" - "5528286f719b413de9ada3e8eb78ed57" - "3603ce30d8bb761785dc30dbc320869e" - "1a00", - ), -] - - -@pytest.mark.parametrize( - "generator,private_key,public_key,message,signature", - TEST_VECTORS, -) -def test_vectors(generator, private_key, public_key, message, signature): - private_key = a2b_hex(private_key) - public_key = a2b_hex(public_key) - message = a2b_hex(message) - signature = a2b_hex(signature) - - sig_key = PrivateKey(generator, private_key) - ver_key = PublicKey(generator, public_key) - - assert sig_key.public_key().public_key() == ver_key.public_key() - - gen_sig = sig_key.sign(message) - - assert gen_sig == signature - - assert ver_key.verify(message, signature) diff --git a/.venv/Lib/site-packages/ecdsa/test_ellipticcurve.py b/.venv/Lib/site-packages/ecdsa/test_ellipticcurve.py deleted file mode 100644 index 9bf0951..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_ellipticcurve.py +++ /dev/null @@ -1,256 +0,0 @@ -import pytest - -try: - import unittest2 as unittest -except ImportError: - import unittest -from hypothesis import given, settings -import hypothesis.strategies as st - -try: - from hypothesis import HealthCheck - - HC_PRESENT = True -except ImportError: # pragma: no cover - HC_PRESENT = False -from .numbertheory import inverse_mod -from .ellipticcurve import CurveFp, INFINITY, Point, CurveEdTw - - -HYP_SETTINGS = {} -if HC_PRESENT: # pragma: no branch - HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow] - HYP_SETTINGS["deadline"] = 5000 - - -# NIST Curve P-192: -p = 6277101735386680763835789423207666416083908700390324961279 -r = 6277101735386680763835789423176059013767194773182842284081 -# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 -# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 -b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 -Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 -Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 - -c192 = CurveFp(p, -3, b) -p192 = Point(c192, Gx, Gy, r) - -c_23 = CurveFp(23, 1, 1) -g_23 = Point(c_23, 13, 7, 7) - - -HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"] = 2 - - -@settings(**HYP_SLOW_SETTINGS) -@given(st.integers(min_value=1, max_value=r - 1)) -def test_p192_mult_tests(multiple): - inv_m = inverse_mod(multiple, r) - - p1 = p192 * multiple - assert p1 * inv_m == p192 - - -def add_n_times(point, n): - ret = INFINITY - i = 0 - while i <= n: - yield ret - ret = ret + point - i += 1 - - -# From X9.62 I.1 (p. 96): -@pytest.mark.parametrize( - "p, m, check", - [(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))], - ids=["g_23 test with mult {0}".format(i) for i in range(9)], -) -def test_add_and_mult_equivalence(p, m, check): - assert p * m == check - - -class TestCurve(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.c_23 = CurveFp(23, 1, 1) - - def test_equality_curves(self): - self.assertEqual(self.c_23, CurveFp(23, 1, 1)) - - def test_inequality_curves(self): - c192 = CurveFp(p, -3, b) - self.assertNotEqual(self.c_23, c192) - - def test_usability_in_a_hashed_collection_curves(self): - {self.c_23: None} - - def test_hashability_curves(self): - hash(self.c_23) - - def test_conflation_curves(self): - ne1, ne2, ne3 = CurveFp(24, 1, 1), CurveFp(23, 2, 1), CurveFp(23, 1, 2) - eq1, eq2, eq3 = CurveFp(23, 1, 1), CurveFp(23, 1, 1), self.c_23 - self.assertEqual(len(set((c_23, eq1, eq2, eq3))), 1) - self.assertEqual(len(set((c_23, ne1, ne2, ne3))), 4) - self.assertDictEqual({c_23: None}, {eq1: None}) - self.assertIn(eq2, {eq3: None}) - - def test___str__(self): - self.assertEqual(str(self.c_23), "CurveFp(p=23, a=1, b=1)") - - def test___str___with_cofactor(self): - c = CurveFp(23, 1, 1, 4) - self.assertEqual(str(c), "CurveFp(p=23, a=1, b=1, h=4)") - - -class TestCurveEdTw(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.c_23 = CurveEdTw(23, 1, 1) - - def test___str__(self): - self.assertEqual(str(self.c_23), "CurveEdTw(p=23, a=1, d=1)") - - def test___str___with_cofactor(self): - c = CurveEdTw(23, 1, 1, 4) - self.assertEqual(str(c), "CurveEdTw(p=23, a=1, d=1, h=4)") - - def test_usability_in_a_hashed_collection_curves(self): - {self.c_23: None} - - def test_hashability_curves(self): - hash(self.c_23) - - -class TestPoint(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.c_23 = CurveFp(23, 1, 1) - cls.g_23 = Point(cls.c_23, 13, 7, 7) - - p = 6277101735386680763835789423207666416083908700390324961279 - r = 6277101735386680763835789423176059013767194773182842284081 - # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 - # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 - b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 - Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 - Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 - - cls.c192 = CurveFp(p, -3, b) - cls.p192 = Point(cls.c192, Gx, Gy, r) - - def test_p192(self): - # Checking against some sample computations presented - # in X9.62: - d = 651056770906015076056810763456358567190100156695615665659 - Q = d * self.p192 - self.assertEqual( - Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 - ) - - k = 6140507067065001063065065565667405560006161556565665656654 - R = k * self.p192 - self.assertEqual( - R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - ) - self.assertEqual( - R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - ) - - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * self.p192 + u2 * Q - self.assertEqual( - temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - ) - self.assertEqual( - temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - ) - - def test_double_infinity(self): - p1 = INFINITY - p3 = p1.double() - self.assertEqual(p1, p3) - self.assertEqual(p3.x(), p1.x()) - self.assertEqual(p3.y(), p3.y()) - - def test_double(self): - x1, y1, x3, y3 = (3, 10, 7, 12) - - p1 = Point(self.c_23, x1, y1) - p3 = p1.double() - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_multiply(self): - x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) - p1 = Point(self.c_23, x1, y1) - p3 = p1 * m - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - # Trivial tests from X9.62 B.3: - def test_add(self): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - - x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) - p1 = Point(self.c_23, x1, y1) - p2 = Point(self.c_23, x2, y2) - p3 = p1 + p2 - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_add_as_double(self): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - - x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) - p1 = Point(self.c_23, x1, y1) - p2 = Point(self.c_23, x2, y2) - p3 = p1 + p2 - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_equality_points(self): - self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) - - def test_inequality_points(self): - c = CurveFp(100, -3, 100) - p = Point(c, 100, 100, 100) - self.assertNotEqual(self.g_23, p) - - def test_inequality_points_diff_types(self): - c = CurveFp(100, -3, 100) - self.assertNotEqual(self.g_23, c) - - def test_to_bytes_from_bytes(self): - p = Point(self.c_23, 3, 10) - - self.assertEqual(p, Point.from_bytes(self.c_23, p.to_bytes())) - - def test_add_to_neg_self(self): - p = Point(self.c_23, 3, 10) - - self.assertEqual(INFINITY, p + (-p)) - - def test_add_to_infinity(self): - p = Point(self.c_23, 3, 10) - - self.assertIs(p, p + INFINITY) - - def test_mul_infinity_by_scalar(self): - self.assertIs(INFINITY, INFINITY * 10) - - def test_mul_by_negative(self): - p = Point(self.c_23, 3, 10) - - self.assertEqual(p * -5, (-p) * 5) - - def test_str_infinity(self): - self.assertEqual(str(INFINITY), "infinity") - - def test_str_point(self): - p = Point(self.c_23, 3, 10) - - self.assertEqual(str(p), "(3,10)") diff --git a/.venv/Lib/site-packages/ecdsa/test_jacobi.py b/.venv/Lib/site-packages/ecdsa/test_jacobi.py deleted file mode 100644 index 9a46afe..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_jacobi.py +++ /dev/null @@ -1,753 +0,0 @@ -import pickle -import sys - -try: - import unittest2 as unittest -except ImportError: - import unittest - -import os -import signal -import pytest -import threading -import platform -import hypothesis.strategies as st -from hypothesis import given, assume, settings, example - -from .ellipticcurve import CurveFp, PointJacobi, INFINITY -from .ecdsa import ( - generator_256, - curve_256, - generator_224, - generator_brainpoolp160r1, - curve_brainpoolp160r1, - generator_112r2, - curve_112r2, -) -from .numbertheory import inverse_mod -from .util import randrange - - -NO_OLD_SETTINGS = {} -if sys.version_info > (2, 7): # pragma: no branch - NO_OLD_SETTINGS["deadline"] = 5000 - - -SLOW_SETTINGS = {} -if "--fast" in sys.argv: # pragma: no cover - SLOW_SETTINGS["max_examples"] = 2 -else: - SLOW_SETTINGS["max_examples"] = 10 - - -class TestJacobi(unittest.TestCase): - def test___init__(self): - curve = object() - x = 2 - y = 3 - z = 1 - order = 4 - pj = PointJacobi(curve, x, y, z, order) - - self.assertEqual(pj.order(), order) - self.assertIs(pj.curve(), curve) - self.assertEqual(pj.x(), x) - self.assertEqual(pj.y(), y) - - def test_add_with_different_curves(self): - p_a = PointJacobi.from_affine(generator_256) - p_b = PointJacobi.from_affine(generator_224) - - with self.assertRaises(ValueError): # pragma: no branch - p_a + p_b - - def test_compare_different_curves(self): - self.assertNotEqual(generator_256, generator_224) - - def test_equality_with_non_point(self): - pj = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(pj, "value") - - def test_conversion(self): - pj = PointJacobi.from_affine(generator_256) - pw = pj.to_affine() - - self.assertEqual(generator_256, pw) - - def test_single_double(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256.double() - - pj = pj.double() - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_double_with_zero_point(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_double_with_zero_equivalent_point(self): - pj = PointJacobi(curve_256, 0, curve_256.p(), 1) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_double_with_zero_equivalent_point_non_1_z(self): - pj = PointJacobi(curve_256, 0, curve_256.p(), 2) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_compare_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - self.assertEqual(pj, pa) - self.assertEqual(pa, pj) - - def test_to_affine_with_zero_point(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pa = pj.to_affine() - - self.assertIs(pa, INFINITY) - - def test_add_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - s = pj + pa - - self.assertEqual(s, pj.double()) - - def test_radd_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - s = pa + pj - - self.assertEqual(s, pj.double()) - - def test_add_with_infinity(self): - pj = PointJacobi.from_affine(generator_256) - - s = pj + INFINITY - - self.assertEqual(s, pj) - - def test_add_zero_point_to_affine(self): - pa = PointJacobi.from_affine(generator_256).to_affine() - pj = PointJacobi(curve_256, 0, 0, 1) - - s = pj + pa - - self.assertIs(s, pa) - - def test_multiply_by_zero(self): - pj = PointJacobi.from_affine(generator_256) - - pj = pj * 0 - - self.assertIs(pj, INFINITY) - - def test_zero_point_multiply_by_one(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pj = pj * 1 - - self.assertIs(pj, INFINITY) - - def test_multiply_by_one(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 1 - - pj = pj * 1 - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_multiply_by_two(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 2 - - pj = pj * 2 - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_rmul_by_two(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 2 - - pj = 2 * pj - - self.assertEqual(pj, pw) - - def test_compare_non_zero_with_infinity(self): - pj = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(pj, INFINITY) - - def test_compare_zero_point_with_infinity(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - self.assertEqual(pj, INFINITY) - - def test_compare_double_with_multiply(self): - pj = PointJacobi.from_affine(generator_256) - dbl = pj.double() - mlpl = pj * 2 - - self.assertEqual(dbl, mlpl) - - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) - ) - ) - def test_multiplications(self, mul): - pj = PointJacobi.from_affine(generator_brainpoolp160r1) - pw = pj.to_affine() * mul - - pj = pj * mul - - self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y())) - self.assertEqual(pj, pw) - - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=0, max_value=int(generator_brainpoolp160r1.order() - 1) - ) - ) - @example(0) - @example(int(generator_brainpoolp160r1.order())) - def test_precompute(self, mul): - precomp = generator_brainpoolp160r1 - self.assertTrue(precomp._PointJacobi__precompute) - pj = PointJacobi.from_affine(generator_brainpoolp160r1) - - a = precomp * mul - b = pj * mul - - self.assertEqual(a, b) - - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - ) - @example(3, 3) - def test_add_scaled_points(self, a_mul, b_mul): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), - ) - def test_add_one_scaled_point(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z, p)) - - new_zz = new_z * new_z % p - - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz % p, - b.y() * new_zz * new_z % p, - new_z, - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - @pytest.mark.slow - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), - ) - @example(1, 1, 1) - @example(3, 3, 3) - @example(2, int(generator_brainpoolp160r1.order() - 2), 1) - @example(2, int(generator_brainpoolp160r1.order() - 2), 3) - def test_add_same_scale_points(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z, p)) - - new_zz = new_z * new_z % p - - a = PointJacobi( - curve_brainpoolp160r1, - a.x() * new_zz % p, - a.y() * new_zz * new_z % p, - new_z, - ) - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz % p, - b.y() * new_zz * new_z % p, - new_z, - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - def test_add_same_scale_points_static(self): - j_g = generator_brainpoolp160r1 - p = curve_brainpoolp160r1.p() - a = j_g * 11 - a.scale() - z1 = 13 - x = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - y = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - - c = a + a - - self.assertEqual(c, x + y) - - @pytest.mark.slow - @settings(**SLOW_SETTINGS) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order() - 1) - ), - st.lists( - st.integers( - min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1) - ), - min_size=2, - max_size=2, - unique=True, - ), - ) - @example(2, 2, [2, 1]) - @example(2, 2, [2, 3]) - @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 3]) - @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 1]) - def test_add_different_scale_points(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z[0], p)) - assume(inverse_mod(new_z[1], p)) - - new_zz0 = new_z[0] * new_z[0] % p - new_zz1 = new_z[1] * new_z[1] % p - - a = PointJacobi( - curve_brainpoolp160r1, - a.x() * new_zz0 % p, - a.y() * new_zz0 * new_z[0] % p, - new_z[0], - ) - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz1 % p, - b.y() * new_zz1 * new_z[1] % p, - new_z[1], - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - def test_add_different_scale_points_static(self): - j_g = generator_brainpoolp160r1 - p = curve_brainpoolp160r1.p() - a = j_g * 11 - a.scale() - z1 = 13 - x = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - z2 = 29 - y = PointJacobi( - curve_brainpoolp160r1, - a.x() * z2**2 % p, - a.y() * z2**3 % p, - z2, - ) - - c = a + a - - self.assertEqual(c, x + y) - - def test_add_different_points_same_scale_static(self): - j_g = generator_brainpoolp160r1 - p = curve_brainpoolp160r1.p() - a = j_g * 11 - a.scale() - b = j_g * 12 - z = 13 - x = PointJacobi( - curve_brainpoolp160r1, - a.x() * z**2 % p, - a.y() * z**3 % p, - z, - ) - y = PointJacobi( - curve_brainpoolp160r1, - b.x() * z**2 % p, - b.y() * z**3 % p, - z, - ) - - c = a + b - - self.assertEqual(c, x + y) - - def test_add_same_point_different_scale_second_z_1_static(self): - j_g = generator_112r2 - p = curve_112r2.p() - z = 11 - a = j_g * z - a.scale() - - x = PointJacobi( - curve_112r2, - a.x() * z**2 % p, - a.y() * z**3 % p, - z, - ) - y = PointJacobi( - curve_112r2, - a.x(), - a.y(), - 1, - ) - - c = a + a - - self.assertEqual(c, x + y) - - def test_add_to_infinity_static(self): - j_g = generator_112r2 - - z = 11 - a = j_g * z - a.scale() - - b = -a - - x = PointJacobi( - curve_112r2, - a.x(), - a.y(), - 1, - ) - y = PointJacobi( - curve_112r2, - b.x(), - b.y(), - 1, - ) - - self.assertEqual(INFINITY, x + y) - - def test_add_point_3_times(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g * 3, j_g + j_g + j_g) - - def test_mul_without_order(self): - j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) - - self.assertEqual(j_g * generator_256.order(), INFINITY) - - def test_mul_add_inf(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g, j_g.mul_add(1, INFINITY, 1)) - - def test_mul_add_same(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g * 2, j_g.mul_add(1, j_g, 1)) - - def test_mul_add_precompute(self): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) - b = PointJacobi.from_affine(j_g * 255, True) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual(j_g * (5 + 255 * 7), j_g * 5 + b * 7) - self.assertEqual(j_g * (5 + 255 * 7), j_g.mul_add(5, b, 7)) - - def test_mul_add_precompute_large(self): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) - b = PointJacobi.from_affine(j_g * 255, True) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 - ) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) - ) - - def test_mul_add_to_mul(self): - j_g = PointJacobi.from_affine(generator_256) - - a = j_g * 3 - b = j_g.mul_add(2, j_g, 1) - - self.assertEqual(a, b) - - def test_mul_add_differnt(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = j_g * 2 - - self.assertEqual(j_g.mul_add(1, w_a, 1), j_g * 3) - - def test_mul_add_slightly_different(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = j_g * 2 - w_b = j_g * 3 - - self.assertEqual(w_a.mul_add(1, w_b, 3), w_a * 1 + w_b * 3) - - def test_mul_add(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = generator_256 * 255 - w_b = generator_256 * (0xA8 * 0xF0) - j_b = j_g * 0xA8 - - ret = j_g.mul_add(255, j_b, 0xF0) - - self.assertEqual(ret.to_affine(), w_a + w_b) - - def test_mul_add_large(self): - j_g = PointJacobi.from_affine(generator_256) - b = PointJacobi.from_affine(j_g * 255) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 - ) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) - ) - - def test_mul_add_with_infinity_as_result(self): - j_g = PointJacobi.from_affine(generator_256) - - order = generator_256.order() - - b = PointJacobi.from_affine(generator_256 * 256) - - self.assertEqual(j_g.mul_add(order % 256, b, order // 256), INFINITY) - - def test_mul_add_without_order(self): - j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) - - order = generator_256.order() - - w_b = generator_256 * 34 - w_b.scale() - - b = PointJacobi(curve_256, w_b.x(), w_b.y(), 1) - - self.assertEqual(j_g.mul_add(order % 34, b, order // 34), INFINITY) - - def test_mul_add_with_doubled_negation_of_itself(self): - j_g = PointJacobi.from_affine(generator_256 * 17) - - dbl_neg = 2 * (-j_g) - - self.assertEqual(j_g.mul_add(4, dbl_neg, 2), INFINITY) - - def test_equality(self): - pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - pj2 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - self.assertEqual(pj1, pj2) - - def test_equality_with_invalid_object(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(j_g, 12) - - def test_equality_with_wrong_curves(self): - p_a = PointJacobi.from_affine(generator_256) - p_b = PointJacobi.from_affine(generator_224) - - self.assertNotEqual(p_a, p_b) - - def test_add_with_point_at_infinity(self): - pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - x, y, z = pj1._add(2, 3, 1, 5, 5, 0, 23) - - self.assertEqual((x, y, z), (2, 3, 1)) - - def test_pickle(self): - pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) - - @pytest.mark.slow - @settings(**NO_OLD_SETTINGS) - @pytest.mark.skipif( - platform.python_implementation() == "PyPy", - reason="threading on PyPy breaks coverage", - ) - @given(st.integers(min_value=1, max_value=10)) - def test_multithreading(self, thread_num): # pragma: no cover - # ensure that generator's precomputation table is filled - generator_112r2 * 2 - - # create a fresh point that doesn't have a filled precomputation table - gen = generator_112r2 - gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) - - self.assertEqual(gen._PointJacobi__precompute, []) - - def runner(generator): - order = generator.order() - for _ in range(10): - generator * randrange(order) - - threads = [] - for _ in range(thread_num): - threads.append(threading.Thread(target=runner, args=(gen,))) - - for t in threads: - t.start() - - runner(gen) - - for t in threads: - t.join() - - self.assertEqual( - gen._PointJacobi__precompute, - generator_112r2._PointJacobi__precompute, - ) - - @pytest.mark.slow - @pytest.mark.skipif( - platform.system() == "Windows" - or platform.python_implementation() == "PyPy", - reason="there are no signals on Windows, and threading breaks coverage" - " on PyPy", - ) - def test_multithreading_with_interrupts(self): # pragma: no cover - thread_num = 10 - # ensure that generator's precomputation table is filled - generator_112r2 * 2 - - # create a fresh point that doesn't have a filled precomputation table - gen = generator_112r2 - gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) - - self.assertEqual(gen._PointJacobi__precompute, []) - - def runner(generator): - order = generator.order() - for _ in range(50): - generator * randrange(order) - - def interrupter(barrier_start, barrier_end, lock_exit): - # wait until MainThread can handle KeyboardInterrupt - barrier_start.release() - barrier_end.acquire() - os.kill(os.getpid(), signal.SIGINT) - lock_exit.release() - - threads = [] - for _ in range(thread_num): - threads.append(threading.Thread(target=runner, args=(gen,))) - - barrier_start = threading.Lock() - barrier_start.acquire() - barrier_end = threading.Lock() - barrier_end.acquire() - lock_exit = threading.Lock() - lock_exit.acquire() - - threads.append( - threading.Thread( - target=interrupter, - args=(barrier_start, barrier_end, lock_exit), - ) - ) - - for t in threads: - t.start() - - with self.assertRaises(KeyboardInterrupt): - # signal to interrupter that we can now handle the signal - barrier_start.acquire() - barrier_end.release() - runner(gen) - # use the lock to ensure we never go past the scope of - # assertRaises before the os.kill is called - lock_exit.acquire() - - for t in threads: - t.join() - - self.assertEqual( - gen._PointJacobi__precompute, - generator_112r2._PointJacobi__precompute, - ) diff --git a/.venv/Lib/site-packages/ecdsa/test_keys.py b/.venv/Lib/site-packages/ecdsa/test_keys.py deleted file mode 100644 index 348475e..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_keys.py +++ /dev/null @@ -1,1138 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest - -try: - buffer -except NameError: - buffer = memoryview - -import os -import array -import pytest -import hashlib - -from .keys import ( - VerifyingKey, - SigningKey, - MalformedPointError, - BadSignatureError, -) -from .der import ( - unpem, - UnexpectedDER, - encode_sequence, - encode_oid, - encode_bitstring, - encode_integer, - encode_octet_string, -) -from .util import ( - sigencode_string, - sigencode_der, - sigencode_strings, - sigdecode_string, - sigdecode_der, - sigdecode_strings, -) -from .curves import NIST256p, Curve, BRAINPOOLP160r1, Ed25519, Ed448 -from .ellipticcurve import Point, PointJacobi, CurveFp, INFINITY -from .ecdsa import generator_brainpoolp160r1 - - -class TestVerifyingKeyFromString(unittest.TestCase): - """ - Verify that ecdsa.keys.VerifyingKey.from_string() can be used with - bytes-like objects - """ - - @classmethod - def setUpClass(cls): - cls.key_bytes = ( - b"\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf" - b"\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1" - b"Y\xfb\x92xa\xebo+\x9cG\xfav\xca" - ) - cls.vk = VerifyingKey.from_string(cls.key_bytes) - - def test_bytes(self): - self.assertIsNotNone(self.vk) - self.assertIsInstance(self.vk, VerifyingKey) - self.assertEqual( - self.vk.pubkey.point.x(), - 105419898848891948935835657980914000059957975659675736097, - ) - self.assertEqual( - self.vk.pubkey.point.y(), - 4286866841217412202667522375431381222214611213481632495306, - ) - - def test_bytes_memoryview(self): - vk = VerifyingKey.from_string(buffer(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray(self): - vk = VerifyingKey.from_string(bytearray(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytesarray_memoryview(self): - vk = VerifyingKey.from_string(buffer(bytearray(self.key_bytes))) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_string(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes_memoryview(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_string(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_ints(self): - arr = array.array("I", self.key_bytes) - vk = VerifyingKey.from_string(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_ints_memoryview(self): - arr = array.array("I", self.key_bytes) - vk = VerifyingKey.from_string(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_uncompressed(self): - vk = VerifyingKey.from_string(b"\x04" + self.key_bytes) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray_uncompressed(self): - vk = VerifyingKey.from_string(bytearray(b"\x04" + self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_compressed(self): - vk = VerifyingKey.from_string(b"\x02" + self.key_bytes[:24]) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray_compressed(self): - vk = VerifyingKey.from_string(bytearray(b"\x02" + self.key_bytes[:24])) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_ed25519_VerifyingKey_from_string_imported(self): - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"AAA", Ed25519) - - -class TestVerifyingKeyFromDer(unittest.TestCase): - """ - Verify that ecdsa.keys.VerifyingKey.from_der() can be used with - bytes-like objects. - """ - - @classmethod - def setUpClass(cls): - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" - "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n" - ) - cls.key_pem = key_str - - cls.key_bytes = unpem(key_str) - assert isinstance(cls.key_bytes, bytes) - cls.vk = VerifyingKey.from_pem(key_str) - cls.sk = SigningKey.from_pem(prv_key_str) - - key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8\n" - "Poqzgjau4kfxBPyZimeRfuY/9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" - "-----END PUBLIC KEY-----\n" - ) - cls.vk2 = VerifyingKey.from_pem(key_str) - - cls.sk2 = SigningKey.generate(vk.curve) - - def test_load_key_with_explicit_parameters(self): - pub_key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" - "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" - "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" - "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" - "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" - "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" - "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" - "-----END PUBLIC KEY-----" - ) - pk = VerifyingKey.from_pem(pub_key_str) - - pk_exp = VerifyingKey.from_string( - b"\x04\x8a\xf5\x52\x48\x18\xb3\x98\xe6\x6c\x57\x3b\x8a\xdd\x7f" - b"\x60\x8d\x97\x4f\xff\xc8\x95\xa1\x23\x30\xd6\x5f\xb7\x5a\x30" - b"\x8e\xaa\x41\x60\x7d\x84\xac\x91\xe4\xe1\x4e\x4f\xa7\x85\xaf" - b"\x5a\xad\x71\x98\x7c\x08\x66\x5b\x07\xec\x88\x38\x2a\x67\x9f" - b"\x09\xf4\x7a\x4a\x65", - curve=NIST256p, - ) - self.assertEqual(pk, pk_exp) - - def test_load_key_with_explicit_with_explicit_disabled(self): - pub_key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" - "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" - "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" - "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" - "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" - "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" - "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" - "-----END PUBLIC KEY-----" - ) - with self.assertRaises(UnexpectedDER): - VerifyingKey.from_pem( - pub_key_str, valid_curve_encodings=["named_curve"] - ) - - def test_load_key_with_disabled_format(self): - with self.assertRaises(MalformedPointError) as e: - VerifyingKey.from_der(self.key_bytes, valid_encodings=["raw"]) - - self.assertIn("enabled (raw) encodings", str(e.exception)) - - def test_custom_hashfunc(self): - vk = VerifyingKey.from_der(self.key_bytes, hashlib.sha256) - - self.assertIs(vk.default_hashfunc, hashlib.sha256) - - def test_from_pem_with_custom_hashfunc(self): - vk = VerifyingKey.from_pem(self.key_pem, hashlib.sha256) - - self.assertIs(vk.default_hashfunc, hashlib.sha256) - - def test_bytes(self): - vk = VerifyingKey.from_der(self.key_bytes) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_memoryview(self): - vk = VerifyingKey.from_der(buffer(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray(self): - vk = VerifyingKey.from_der(bytearray(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytesarray_memoryview(self): - vk = VerifyingKey.from_der(buffer(bytearray(self.key_bytes))) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_der(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes_memoryview(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_der(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_equality_on_verifying_keys(self): - self.assertTrue(self.vk == self.sk.get_verifying_key()) - - def test_inequality_on_verifying_keys(self): - self.assertFalse(self.vk == self.vk2) - - def test_inequality_on_verifying_keys_not_implemented(self): - self.assertFalse(self.vk == None) - - def test_VerifyingKey_inequality_on_same_curve(self): - self.assertNotEqual(self.vk, self.sk2.verifying_key) - - def test_SigningKey_inequality_on_same_curve(self): - self.assertNotEqual(self.sk, self.sk2) - - def test_inequality_on_wrong_types(self): - self.assertFalse(self.vk == self.sk) - - def test_from_public_point_old(self): - pj = self.vk.pubkey.point - point = Point(pj.curve(), pj.x(), pj.y()) - - vk = VerifyingKey.from_public_point(point, self.vk.curve) - - self.assertTrue(vk == self.vk) - - def test_ed25519_VerifyingKey_repr__(self): - sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) - string = repr(sk.verifying_key) - - self.assertEqual( - "VerifyingKey.from_string(" - "bytearray(b'K\\x0c\\xfbZH\\x8e\\x8c\\x8c\\x07\\xee\\xda\\xfb" - "\\xe1\\x97\\xcd\\x90\\x18\\x02\\x15h]\\xfe\\xbe\\xcbB\\xba\\xe6r" - "\\x10\\xae\\xf1P'), Ed25519, None)", - string, - ) - - def test_edwards_from_public_point(self): - point = Ed25519.generator - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_point(point, Ed25519) - - self.assertIn("incompatible with Edwards", str(e.exception)) - - def test_edwards_precompute_no_side_effect(self): - sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) - vk = sk.verifying_key - vk2 = VerifyingKey.from_string(vk.to_string(), Ed25519) - vk.precompute() - - self.assertEqual(vk, vk2) - - def test_parse_malfomed_eddsa_der_pubkey(self): - der_str = encode_sequence( - encode_sequence(encode_oid(*Ed25519.oid)), - encode_bitstring(bytes(Ed25519.generator.to_bytes()), 0), - encode_bitstring(b"\x00", 0), - ) - - with self.assertRaises(UnexpectedDER) as e: - VerifyingKey.from_der(der_str) - - self.assertIn("trailing junk after public key", str(e.exception)) - - def test_edwards_from_public_key_recovery(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_key_recovery(b"", b"", Ed25519) - - self.assertIn("unsupported for Edwards", str(e.exception)) - - def test_edwards_from_public_key_recovery_with_digest(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_key_recovery_with_digest( - b"", b"", Ed25519 - ) - - self.assertIn("unsupported for Edwards", str(e.exception)) - - def test_load_ed25519_from_pem(self): - vk_pem = ( - "-----BEGIN PUBLIC KEY-----\n" - "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(vk_pem) - - self.assertIsInstance(vk.curve, Curve) - self.assertIs(vk.curve, Ed25519) - - vk_str = ( - b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" - b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" - ) - - vk_2 = VerifyingKey.from_string(vk_str, Ed25519) - - self.assertEqual(vk, vk_2) - - def test_export_ed255_to_pem(self): - vk_str = ( - b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" - b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" - ) - - vk = VerifyingKey.from_string(vk_str, Ed25519) - - vk_pem = ( - b"-----BEGIN PUBLIC KEY-----\n" - b"MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - b"-----END PUBLIC KEY-----\n" - ) - - self.assertEqual(vk_pem, vk.to_pem()) - - def test_export_ed255_to_ssh(self): - vk_str = ( - b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" - b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" - ) - - vk = VerifyingKey.from_string(vk_str, Ed25519) - - vk_ssh = b"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICMAUNDWZCIojuNViX5uQVeNrt5EJu5WJ7yF5gsvKstl\n" - - self.assertEqual(vk_ssh, vk.to_ssh()) - - def test_ed25519_export_import(self): - sk = SigningKey.generate(Ed25519) - vk = sk.verifying_key - - vk2 = VerifyingKey.from_pem(vk.to_pem()) - - self.assertEqual(vk, vk2) - - def test_ed25519_sig_verify(self): - vk_pem = ( - "-----BEGIN PUBLIC KEY-----\n" - "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(vk_pem) - - data = b"data\n" - - # signature created by OpenSSL 3.0.0 beta1 - sig = ( - b"\x64\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" - b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" - b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" - b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" - ) - - self.assertTrue(vk.verify(sig, data)) - - def test_ed25519_sig_verify_malformed(self): - vk_pem = ( - "-----BEGIN PUBLIC KEY-----\n" - "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(vk_pem) - - data = b"data\n" - - # modified signature from test_ed25519_sig_verify - sig = ( - b"\xAA\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" - b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" - b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" - b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" - ) - - with self.assertRaises(BadSignatureError): - vk.verify(sig, data) - - def test_ed448_from_pem(self): - pem_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" - "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(pem_str) - - self.assertIsInstance(vk.curve, Curve) - self.assertIs(vk.curve, Ed448) - - vk_str = ( - b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" - b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" - b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" - b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" - ) - - vk2 = VerifyingKey.from_string(vk_str, Ed448) - - self.assertEqual(vk, vk2) - - def test_ed448_to_pem(self): - vk_str = ( - b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" - b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" - b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" - b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" - ) - vk = VerifyingKey.from_string(vk_str, Ed448) - - vk_pem = ( - b"-----BEGIN PUBLIC KEY-----\n" - b"MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0dTdYD2ll94g5\n" - b"8MhSnBiBQB9A1MMA\n" - b"-----END PUBLIC KEY-----\n" - ) - - self.assertEqual(vk_pem, vk.to_pem()) - - def test_ed448_export_import(self): - sk = SigningKey.generate(Ed448) - vk = sk.verifying_key - - vk2 = VerifyingKey.from_pem(vk.to_pem()) - - self.assertEqual(vk, vk2) - - def test_ed448_sig_verify(self): - pem_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" - "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(pem_str) - - data = b"data\n" - - # signature created by OpenSSL 3.0.0 beta1 - sig = ( - b"\x68\xed\x2c\x70\x35\x22\xca\x1c\x35\x03\xf3\xaa\x51\x33\x3d\x00" - b"\xc0\xae\xb0\x54\xc5\xdc\x7f\x6f\x30\x57\xb4\x1d\xcb\xe9\xec\xfa" - b"\xc8\x45\x3e\x51\xc1\xcb\x60\x02\x6a\xd0\x43\x11\x0b\x5f\x9b\xfa" - b"\x32\x88\xb2\x38\x6b\xed\xac\x09\x00\x78\xb1\x7b\x5d\x7e\xf8\x16" - b"\x31\xdd\x1b\x3f\x98\xa0\xce\x19\xe7\xd8\x1c\x9f\x30\xac\x2f\xd4" - b"\x1e\x55\xbf\x21\x98\xf6\x4c\x8c\xbe\x81\xa5\x2d\x80\x4c\x62\x53" - b"\x91\xd5\xee\x03\x30\xc6\x17\x66\x4b\x9e\x0c\x8d\x40\xd0\xad\xae" - b"\x0a\x00" - ) - - self.assertTrue(vk.verify(sig, data)) - - -class TestSigningKey(unittest.TestCase): - """ - Verify that ecdsa.keys.SigningKey.from_der() can be used with - bytes-like objects. - """ - - @classmethod - def setUpClass(cls): - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - cls.sk1 = SigningKey.from_pem(prv_key_str) - - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBheyEIL1u+SUqlC6YkE\n" - "PKKfVh+lJXcOscWhNAMyAAS4gXfQhO8X9eRWOUCAKDYPn1m0pNcmTmLaBlHc5Ho1\n" - "pMW0XPUVk0I6i1V7nCCZ82w=\n" - "-----END PRIVATE KEY-----\n" - ) - cls.sk1_pkcs8 = SigningKey.from_pem(prv_key_str) - - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" - "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" - "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - cls.sk2 = SigningKey.from_pem(prv_key_str) - - def test_to_der_pkcs8(self): - self.assertEqual( - self.sk1.to_der(format="pkcs8"), - b"0o\x02\x01\x010\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H" - b"\xce=\x03\x01\x01\x04U0S\x02\x01\x01\x04\x18^\xc8B\x0b\xd6\xef" - b"\x92R\xa9B\xe9\x89\x04<\xa2\x9fV\x1f\xa5%w\x0e\xb1\xc5\xa14\x03" - b"2\x00\x04\xb8\x81w\xd0\x84\xef\x17\xf5\xe4V9@\x80(6\x0f\x9fY" - b"\xb4\xa4\xd7&Nb\xda\x06Q\xdc\xe4z5\xa4\xc5\xb4\\\xf5\x15\x93B:" - b"\x8bU{\x9c \x99\xf3l", - ) - - def test_decoding_explicit_curve_parameters(self): - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" - "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" - "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" - "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" - "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" - "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" - "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" - "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" - "-----END PRIVATE KEY-----\n" - ) - - sk = SigningKey.from_pem(prv_key_str) - - sk2 = SigningKey.from_string( - b"\x21\x7b\x51\x11\xf5\x26\x47\x5e\xab\x65\xb9\xaf\x0c\x60\xf6" - b"\x94\x01\x05\x3d\x96\xb6\x0c\xb3\xf2\xcf\x47\x33\x4a\x36\xb9" - b"\xf3\x20", - curve=NIST256p, - ) - - self.assertEqual(sk, sk2) - - def test_decoding_explicit_curve_parameters_with_explicit_disabled(self): - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" - "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" - "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" - "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" - "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" - "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" - "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" - "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" - "-----END PRIVATE KEY-----\n" - ) - - with self.assertRaises(UnexpectedDER): - SigningKey.from_pem( - prv_key_str, valid_curve_encodings=["named_curve"] - ) - - def test_equality_on_signing_keys(self): - sk = SigningKey.from_secret_exponent( - self.sk1.privkey.secret_multiplier, self.sk1.curve - ) - self.assertEqual(self.sk1, sk) - self.assertEqual(self.sk1_pkcs8, sk) - - def test_verify_with_empty_message(self): - sig = self.sk1.sign(b"") - - self.assertTrue(sig) - - vk = self.sk1.verifying_key - - self.assertTrue(vk.verify(sig, b"")) - - def test_verify_with_precompute(self): - sig = self.sk1.sign(b"message") - - vk = self.sk1.verifying_key - - vk.precompute() - - self.assertTrue(vk.verify(sig, b"message")) - - def test_compare_verifying_key_with_precompute(self): - vk1 = self.sk1.verifying_key - vk1.precompute() - - vk2 = self.sk1_pkcs8.verifying_key - - self.assertEqual(vk1, vk2) - - def test_verify_with_lazy_precompute(self): - sig = self.sk2.sign(b"other message") - - vk = self.sk2.verifying_key - - vk.precompute(lazy=True) - - self.assertTrue(vk.verify(sig, b"other message")) - - def test_inequality_on_signing_keys(self): - self.assertNotEqual(self.sk1, self.sk2) - - def test_inequality_on_signing_keys_not_implemented(self): - self.assertNotEqual(self.sk1, None) - - def test_ed25519_from_pem(self): - pem_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" - "-----END PRIVATE KEY-----\n" - ) - - sk = SigningKey.from_pem(pem_str) - - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - self.assertEqual(sk, sk_str) - - def test_ed25519_from_der_bad_alg_id_params(self): - der_str = encode_sequence( - encode_integer(1), - encode_sequence(encode_oid(*Ed25519.oid), encode_integer(1)), - encode_octet_string(encode_octet_string(b"A" * 32)), - ) - - with self.assertRaises(UnexpectedDER) as e: - SigningKey.from_der(der_str) - - self.assertIn("Non NULL parameters", str(e.exception)) - - def test_ed25519_from_der_junk_after_priv_key(self): - der_str = encode_sequence( - encode_integer(1), - encode_sequence( - encode_oid(*Ed25519.oid), - ), - encode_octet_string(encode_octet_string(b"A" * 32) + b"B"), - ) - - with self.assertRaises(UnexpectedDER) as e: - SigningKey.from_der(der_str) - - self.assertIn( - "trailing junk after the encoded private key", str(e.exception) - ) - - def test_ed25519_sign(self): - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - msg = b"message" - - sig = sk_str.sign(msg, sigencode=sigencode_der) - - self.assertEqual( - sig, - b"\xe1,v\xc9>%\xda\xd2~>\xc3&\na\xf4@|\x9e`X\x11\x13@<\x987\xd4" - b"\r\xb1\xf5\xb3\x15\x7f%i{\xdf}\xdd\xb1\xf3\x02\x7f\x80\x02\xc2" - b'|\xe5\xd6\x06\xc4\n\xa3\xb0\xf6}\xc0\xed)"+E\xaf\x00', - ) - - def test_ed25519_sign_digest_deterministic(self): - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - with self.assertRaises(ValueError) as e: - sk_str.sign_digest_deterministic(b"a" * 20) - - self.assertIn("Method unsupported for Edwards", str(e.exception)) - - def test_ed25519_sign_digest(self): - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - with self.assertRaises(ValueError) as e: - sk_str.sign_digest(b"a" * 20) - - self.assertIn("Method unsupported for Edwards", str(e.exception)) - - def test_ed25519_sign_number(self): - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - with self.assertRaises(ValueError) as e: - sk_str.sign_number(20) - - self.assertIn("Method unsupported for Edwards", str(e.exception)) - - def test_ed25519_to_der_ssleay(self): - pem_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" - "-----END PRIVATE KEY-----\n" - ) - - sk = SigningKey.from_pem(pem_str) - - with self.assertRaises(ValueError) as e: - sk.to_der(format="ssleay") - - self.assertIn("Only PKCS#8 format", str(e.exception)) - - def test_ed25519_to_pem(self): - sk = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - pem_str = ( - b"-----BEGIN PRIVATE KEY-----\n" - b"MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" - b"-----END PRIVATE KEY-----\n" - ) - - self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) - - def test_ed25519_to_ssh(self): - sk = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - ssh_str = ( - b"-----BEGIN OPENSSH PRIVATE KEY-----\n" - b"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZWQyNTUx\n" - b"OQAAACAjAFDQ1mQiKI7jVYl+bkFXja7eRCbuVie8heYLLyrLZQAAAIgAAAAAAAAAAAAAAAtzc2gt\n" - b"ZWQyNTUxOQAAACAjAFDQ1mQiKI7jVYl+bkFXja7eRCbuVie8heYLLyrLZQAAAEA0usfRTtTxvE+M\n" - b"SD4PGXdM/Li+rFRmRRGa19e4Bwv11CMAUNDWZCIojuNViX5uQVeNrt5EJu5WJ7yF5gsvKstlAAAA\n" - b"AAECAwQF\n" - b"-----END OPENSSH PRIVATE KEY-----\n" - ) - - self.assertEqual(sk.to_ssh(), ssh_str) - - def test_ed25519_to_and_from_pem(self): - sk = SigningKey.generate(Ed25519) - - decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) - - self.assertEqual(sk, decoded) - - def test_ed25519_custom_entropy(self): - sk = SigningKey.generate(Ed25519, entropy=os.urandom) - - self.assertIsNotNone(sk) - - def test_ed25519_from_secret_exponent(self): - with self.assertRaises(ValueError) as e: - SigningKey.from_secret_exponent(1234567890, curve=Ed25519) - - self.assertIn("don't support setting the secret", str(e.exception)) - - def test_ed448_from_pem(self): - pem_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" - "OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" - "-----END PRIVATE KEY-----\n" - ) - sk = SigningKey.from_pem(pem_str) - - sk_str = SigningKey.from_string( - b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" - b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" - b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" - b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", - Ed448, - ) - - self.assertEqual(sk, sk_str) - - def test_ed448_to_pem(self): - sk = SigningKey.from_string( - b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" - b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" - b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" - b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", - Ed448, - ) - pem_str = ( - b"-----BEGIN PRIVATE KEY-----\n" - b"MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmPOP0JMYaLGlTz\n" - b"wovmvCDJ2zLaezu9NLz9aQ==\n" - b"-----END PRIVATE KEY-----\n" - ) - - self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) - - def test_ed448_encode_decode(self): - sk = SigningKey.generate(Ed448) - - decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) - - self.assertEqual(decoded, sk) - - -class TestTrivialCurve(unittest.TestCase): - @classmethod - def setUpClass(cls): - # To test what happens with r or s in signing happens to be zero we - # need to find a scalar that creates one of the points on a curve that - # has x coordinate equal to zero. - # Even for secp112r2 curve that's non trivial so use this toy - # curve, for which we can iterate over all points quickly - curve = CurveFp(163, 84, 58) - gen = PointJacobi(curve, 2, 87, 1, 167, generator=True) - - cls.toy_curve = Curve("toy_p8", curve, gen, (1, 2, 0)) - - cls.sk = SigningKey.from_secret_exponent( - 140, - cls.toy_curve, - hashfunc=hashlib.sha1, - ) - - def test_generator_sanity(self): - gen = self.toy_curve.generator - - self.assertEqual(gen * gen.order(), INFINITY) - - def test_public_key_sanity(self): - self.assertEqual(self.sk.verifying_key.to_string(), b"\x98\x1e") - - def test_deterministic_sign(self): - sig = self.sk.sign_deterministic(b"message") - - self.assertEqual(sig, b"-.") - - self.assertTrue(self.sk.verifying_key.verify(sig, b"message")) - - def test_deterministic_sign_random_message(self): - msg = os.urandom(32) - sig = self.sk.sign_deterministic(msg) - self.assertEqual(len(sig), 2) - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - def test_deterministic_sign_that_rises_R_zero_error(self): - # the raised RSZeroError is caught and handled internally by - # sign_deterministic methods - msg = b"\x00\x4f" - sig = self.sk.sign_deterministic(msg) - self.assertEqual(sig, b"\x36\x9e") - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - def test_deterministic_sign_that_rises_S_zero_error(self): - msg = b"\x01\x6d" - sig = self.sk.sign_deterministic(msg) - self.assertEqual(sig, b"\x49\x6c") - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - -# test VerifyingKey.verify() -prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -key_bytes = unpem(prv_key_str) -assert isinstance(key_bytes, bytes) -sk = SigningKey.from_der(key_bytes) -vk = sk.verifying_key - -data = ( - b"some string for signing" - b"contents don't really matter" - b"but do include also some crazy values: " - b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0" -) -assert len(data) % 4 == 0 -sha1 = hashlib.sha1() -sha1.update(data) -data_hash = sha1.digest() -assert isinstance(data_hash, bytes) -sig_raw = sk.sign(data, sigencode=sigencode_string) -assert isinstance(sig_raw, bytes) -sig_der = sk.sign(data, sigencode=sigencode_der) -assert isinstance(sig_der, bytes) -sig_strings = sk.sign(data, sigencode=sigencode_strings) -assert isinstance(sig_strings[0], bytes) - -verifiers = [] -for modifier, fun in [ - ("bytes", lambda x: x), - ("bytes memoryview", buffer), - ("bytearray", bytearray), - ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array("B", x)), - ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), - ("array.array of ints", lambda x: array.array("I", x)), - ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), -]: - if "ints" in modifier: - conv = lambda x: x - else: - conv = fun - for sig_format, signature, decoder, mod_apply in [ - ("raw", sig_raw, sigdecode_string, lambda x: conv(x)), - ("der", sig_der, sigdecode_der, lambda x: conv(x)), - ( - "strings", - sig_strings, - sigdecode_strings, - lambda x: tuple(conv(i) for i in x), - ), - ]: - for method_name, vrf_mthd, vrf_data in [ - ("verify", vk.verify, data), - ("verify_digest", vk.verify_digest, data_hash), - ]: - verifiers.append( - pytest.param( - signature, - decoder, - mod_apply, - fun, - vrf_mthd, - vrf_data, - id="{2}-{0}-{1}".format(modifier, sig_format, method_name), - ) - ) - - -@pytest.mark.parametrize( - "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", verifiers -) -def test_VerifyingKey_verify( - signature, decoder, mod_apply, fun, vrf_mthd, vrf_data -): - sig = mod_apply(signature) - - assert vrf_mthd(sig, fun(vrf_data), sigdecode=decoder) - - -# test SigningKey.from_string() -prv_key_bytes = ( - b"^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2" - b"\x9fV\x1f\xa5%w\x0e\xb1\xc5" -) -assert len(prv_key_bytes) == 24 -converters = [] -for modifier, convert in [ - ("bytes", lambda x: x), - ("bytes memoryview", buffer), - ("bytearray", bytearray), - ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array("B", x)), - ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), - ("array.array of ints", lambda x: array.array("I", x)), - ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), -]: - converters.append(pytest.param(convert, id=modifier)) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_from_string(convert): - key = convert(prv_key_bytes) - sk = SigningKey.from_string(key) - - assert sk.to_string() == prv_key_bytes - - -# test SigningKey.from_der() -prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -key_bytes = unpem(prv_key_str) -assert isinstance(key_bytes, bytes) - -# last two converters are for array.array of ints, those require input -# that's multiple of 4, which no curve we support produces -@pytest.mark.parametrize("convert", converters[:-2]) -def test_SigningKey_from_der(convert): - key = convert(key_bytes) - sk = SigningKey.from_der(key) - - assert sk.to_string() == prv_key_bytes - - -# test SigningKey.sign_deterministic() -extra_entropy = b"\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_deterministic(convert): - sig = sk.sign_deterministic( - convert(data), extra_entropy=convert(extra_entropy) - ) - - vk.verify(sig, data) - - -# test SigningKey.sign_digest_deterministic() -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_digest_deterministic(convert): - sig = sk.sign_digest_deterministic( - convert(data_hash), extra_entropy=convert(extra_entropy) - ) - - vk.verify(sig, data) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign(convert): - sig = sk.sign(convert(data)) - - vk.verify(sig, data) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_digest(convert): - sig = sk.sign_digest(convert(data_hash)) - - vk.verify(sig, data) - - -def test_SigningKey_with_unlikely_value(): - sk = SigningKey.from_secret_exponent(NIST256p.order - 1, curve=NIST256p) - vk = sk.verifying_key - sig = sk.sign(b"hello") - assert vk.verify(sig, b"hello") - - -def test_SigningKey_with_custom_curve_old_point(): - generator = generator_brainpoolp160r1 - generator = Point( - generator.curve(), - generator.x(), - generator.y(), - generator.order(), - ) - - curve = Curve( - "BRAINPOOLP160r1", - generator.curve(), - generator, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), - ) - - sk = SigningKey.from_secret_exponent(12, curve) - - sk2 = SigningKey.from_secret_exponent(12, BRAINPOOLP160r1) - - assert sk.privkey == sk2.privkey - - -def test_VerifyingKey_inequality_with_different_curves(): - sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) - sk2 = SigningKey.from_secret_exponent(2, NIST256p) - - assert not (sk1.verifying_key == sk2.verifying_key) - - -def test_VerifyingKey_inequality_with_different_secret_points(): - sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) - sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) - - assert not (sk1.verifying_key == sk2.verifying_key) - - -def test_SigningKey_from_pem_pkcs8v2_EdDSA(): - pem = """-----BEGIN PRIVATE KEY----- - MFMCAQEwBQYDK2VwBCIEICc2F2ag1n1QP0jY+g9qWx5sDkx0s/HdNi3cSRHw+zsI - oSMDIQA+HQ2xCif8a/LMWR2m5HaCm5I2pKe/cc8OiRANMHxjKQ== - -----END PRIVATE KEY-----""" - - sk = SigningKey.from_pem(pem) - assert sk.curve == Ed25519 diff --git a/.venv/Lib/site-packages/ecdsa/test_malformed_sigs.py b/.venv/Lib/site-packages/ecdsa/test_malformed_sigs.py deleted file mode 100644 index e5a87c2..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_malformed_sigs.py +++ /dev/null @@ -1,378 +0,0 @@ -from __future__ import with_statement, division - -import hashlib - -try: - from hashlib import algorithms_available -except ImportError: # pragma: no cover - algorithms_available = [ - "md5", - "sha1", - "sha224", - "sha256", - "sha384", - "sha512", - ] -# skip algorithms broken by change to OpenSSL 3.0 and early versions -# of hashlib that list algorithms that require the legacy provider to work -# https://bugs.python.org/issue38820 -algorithms_available = [ - i - for i in algorithms_available - if i not in ("mdc2", "md2", "md4", "whirlpool", "ripemd160") -] -from functools import partial -import pytest -import sys -import hypothesis.strategies as st -from hypothesis import note, assume, given, settings, example - -from .keys import SigningKey -from .keys import BadSignatureError -from .util import sigencode_der, sigencode_string -from .util import sigdecode_der, sigdecode_string -from .curves import curves, SECP112r2, SECP128r1 -from .der import ( - encode_integer, - encode_bitstring, - encode_octet_string, - encode_oid, - encode_sequence, - encode_constructed, -) -from .ellipticcurve import CurveEdTw - - -example_data = b"some data to sign" -"""Since the data is hashed for processing, really any string will do.""" - - -hash_and_size = [ - (name, hashlib.new(name).digest_size) for name in algorithms_available -] -"""Pairs of hash names and their output sizes. -Needed for pairing with curves as we don't support hashes -bigger than order sizes of curves.""" - - -if "--fast" in sys.argv: # pragma: no cover - curves = [SECP112r2, SECP128r1] - - -keys_and_sigs = [] -"""Name of the curve+hash combination, VerifyingKey and DER signature.""" - - -# for hypothesis strategy shrinking we want smallest curves and hashes first -for curve in sorted(curves, key=lambda x: x.baselen): - for hash_alg in [ - name - for name, size in sorted(hash_and_size, key=lambda x: x[1]) - if 0 < size <= curve.baselen - ]: - sk = SigningKey.generate( - curve, hashfunc=partial(hashlib.new, hash_alg) - ) - - keys_and_sigs.append( - ( - "{0} {1}".format(curve, hash_alg), - sk.verifying_key, - sk.sign(example_data, sigencode=sigencode_der), - ) - ) - - -# first make sure that the signatures can be verified -@pytest.mark.parametrize( - "verifying_key,signature", - [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs], -) -def test_signatures(verifying_key, signature): - assert verifying_key.verify( - signature, example_data, sigdecode=sigdecode_der - ) - - -@st.composite -def st_fuzzed_sig(draw, keys_and_sigs): # pragma: no cover - """ - Hypothesis strategy that generates pairs of VerifyingKey and malformed - signatures created by fuzzing of a valid signature. - """ - name, verifying_key, old_sig = draw(st.sampled_from(keys_and_sigs)) - note("Configuration: {0}".format(name)) - - sig = bytearray(old_sig) - - # decide which bytes should be removed - to_remove = draw( - st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True) - ) - to_remove.sort() - for i in reversed(to_remove): - del sig[i] - note("Remove bytes: {0}".format(to_remove)) - - # decide which bytes of the original signature should be changed - xors = None - if sig: # pragma: no branch - xors = draw( - st.dictionaries( - st.integers(min_value=0, max_value=len(sig) - 1), - st.integers(min_value=1, max_value=255), - ) - ) - for i, val in xors.items(): - sig[i] ^= val - note("xors: {0}".format(xors)) - - # decide where new data should be inserted - insert_pos = draw(st.integers(min_value=0, max_value=len(sig))) - # NIST521p signature is about 140 bytes long, test slightly longer - insert_data = draw(st.binary(max_size=256)) - - sig = sig[:insert_pos] + insert_data + sig[insert_pos:] - note( - "Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data) - ) - - sig = bytes(sig) - # make sure that there was performed at least one mutation on the data - assume(to_remove or xors or insert_data) - # and that the mutations didn't cancel each-other out - assume(sig != old_sig) - - return verifying_key, sig - - -params = {} -# not supported in hypothesis 2.0.0 -if sys.version_info >= (2, 7): # pragma: no branch - from hypothesis import HealthCheck - - # deadline=5s because NIST521p are slow to verify - params["deadline"] = 5000 - params["suppress_health_check"] = [ - HealthCheck.data_too_large, - HealthCheck.filter_too_much, - HealthCheck.too_slow, - ] -if "--fast" in sys.argv: # pragma: no cover - params["max_examples"] = 20 - -slow_params = dict(params) -if "--fast" in sys.argv: # pragma: no cover - slow_params["max_examples"] = 1 -else: - slow_params["max_examples"] = 10 - - -@settings(**slow_params) -@given(st_fuzzed_sig(keys_and_sigs)) -def test_fuzzed_der_signatures(args): - verifying_key, sig = args - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) - - -@st.composite -def st_random_der_ecdsa_sig_value(draw): # pragma: no cover - """ - Hypothesis strategy for selecting random values and encoding them - to ECDSA-Sig-Value object:: - - ECDSA-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - """ - name, verifying_key, _ = draw(st.sampled_from(keys_and_sigs)) - note("Configuration: {0}".format(name)) - order = int(verifying_key.curve.order) - - # the encode_integer doesn't support negative numbers, would be nice - # to generate them too, but we have coverage for remove_integer() - # verifying that it doesn't accept them, so meh. - # Test all numbers around the ones that can show up (around order) - # way smaller and slightly bigger - r = draw( - st.integers(min_value=0, max_value=order << 4) - | st.integers(min_value=order >> 2, max_value=order + 1) - ) - s = draw( - st.integers(min_value=0, max_value=order << 4) - | st.integers(min_value=order >> 2, max_value=order + 1) - ) - - sig = encode_sequence(encode_integer(r), encode_integer(s)) - - return verifying_key, sig - - -@settings(**slow_params) -@given(st_random_der_ecdsa_sig_value()) -def test_random_der_ecdsa_sig_value(params): - """ - Check if random values encoded in ECDSA-Sig-Value structure are rejected - as signature. - """ - verifying_key, sig = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) - - -def st_der_integer(*args, **kwargs): # pragma: no cover - """ - Hypothesis strategy that returns a random positive integer as DER - INTEGER. - Parameters are passed to hypothesis.strategy.integer. - """ - if "min_value" not in kwargs: # pragma: no branch - kwargs["min_value"] = 0 - return st.builds(encode_integer, st.integers(*args, **kwargs)) - - -@st.composite -def st_der_bit_string(draw, *args, **kwargs): # pragma: no cover - """ - Hypothesis strategy that returns a random DER BIT STRING. - Parameters are passed to hypothesis.strategy.binary. - """ - data = draw(st.binary(*args, **kwargs)) - if data: - unused = draw(st.integers(min_value=0, max_value=7)) - data = bytearray(data) - data[-1] &= -(2**unused) - data = bytes(data) - else: - unused = 0 - return encode_bitstring(data, unused) - - -def st_der_octet_string(*args, **kwargs): # pragma: no cover - """ - Hypothesis strategy that returns a random DER OCTET STRING object. - Parameters are passed to hypothesis.strategy.binary - """ - return st.builds(encode_octet_string, st.binary(*args, **kwargs)) - - -def st_der_null(): # pragma: no cover - """ - Hypothesis strategy that returns DER NULL object. - """ - return st.just(b"\x05\x00") - - -@st.composite -def st_der_oid(draw): # pragma: no cover - """ - Hypothesis strategy that returns DER OBJECT IDENTIFIER objects. - """ - first = draw(st.integers(min_value=0, max_value=2)) - if first < 2: - second = draw(st.integers(min_value=0, max_value=39)) - else: - second = draw(st.integers(min_value=0, max_value=2**512)) - rest = draw( - st.lists(st.integers(min_value=0, max_value=2**512), max_size=50) - ) - return encode_oid(first, second, *rest) - - -def st_der(): # pragma: no cover - """ - Hypothesis strategy that returns random DER structures. - - A valid DER structure is any primitive object, an octet encoding - of a valid DER structure, sequence of valid DER objects or a constructed - encoding of any of the above. - """ - return st.recursive( # pragma: no branch - st.just(b"") - | st_der_integer(max_value=2**4096) - | st_der_bit_string(max_size=1024**2) - | st_der_octet_string(max_size=1024**2) - | st_der_null() - | st_der_oid(), - lambda children: st.builds(encode_octet_string, st.one_of(children)) - | st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) - | st.builds( - lambda x: encode_sequence(*x), st.lists(children, max_size=200) - ) - | st.builds( - encode_constructed, - st.integers(min_value=0, max_value=0x3F), - st.one_of(children), - ), - max_leaves=40, - ) - - -@settings(**slow_params) -@given(st.sampled_from(keys_and_sigs), st_der()) -def test_random_der_as_signature(params, der): - """Check if random DER structures are rejected as signature""" - name, verifying_key, _ = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(der, example_data, sigdecode=sigdecode_der) - - -@settings(**slow_params) -@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) -@example( - keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) -) -@example( - keys_and_sigs[0], - encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00", -) -@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3)) -def test_random_bytes_as_signature(params, der): - """Check if random bytes are rejected as signature""" - name, verifying_key, _ = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(der, example_data, sigdecode=sigdecode_der) - - -keys_and_string_sigs = [ - ( - name, - verifying_key, - sigencode_string( - *sigdecode_der(sig, verifying_key.curve.order), - order=verifying_key.curve.order - ), - ) - for name, verifying_key, sig in keys_and_sigs - if not isinstance(verifying_key.curve.curve, CurveEdTw) -] -""" -Name of the curve+hash combination, VerifyingKey and signature as a -byte string. -""" - - -keys_and_string_sigs += [ - ( - name, - verifying_key, - sig, - ) - for name, verifying_key, sig in keys_and_sigs - if isinstance(verifying_key.curve.curve, CurveEdTw) -] - - -@settings(**slow_params) -@given(st_fuzzed_sig(keys_and_string_sigs)) -def test_fuzzed_string_signatures(params): - verifying_key, sig = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_string) diff --git a/.venv/Lib/site-packages/ecdsa/test_numbertheory.py b/.venv/Lib/site-packages/ecdsa/test_numbertheory.py deleted file mode 100644 index 966eca2..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_numbertheory.py +++ /dev/null @@ -1,483 +0,0 @@ -import operator -from functools import reduce -import sys - -try: - import unittest2 as unittest -except ImportError: - import unittest -import hypothesis.strategies as st -import pytest -from hypothesis import given, settings, example - -try: - from hypothesis import HealthCheck - - HC_PRESENT = True -except ImportError: # pragma: no cover - HC_PRESENT = False -from .numbertheory import ( - SquareRootError, - JacobiError, - factorization, - gcd, - lcm, - jacobi, - inverse_mod, - is_prime, - next_prime, - smallprimes, - square_root_mod_prime, -) - -try: - from gmpy2 import mpz -except ImportError: - try: - from gmpy import mpz - except ImportError: - - def mpz(x): - return x - - -BIGPRIMES = ( - 999671, - 999683, - 999721, - 999727, - 999749, - 999763, - 999769, - 999773, - 999809, - 999853, - 999863, - 999883, - 999907, - 999917, - 999931, - 999953, - 999959, - 999961, - 999979, - 999983, -) - - -@pytest.mark.parametrize( - "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])] -) -def test_next_prime(prime, next_p): - assert next_prime(prime) == next_p - - -@pytest.mark.parametrize("val", [-1, 0, 1]) -def test_next_prime_with_nums_less_2(val): - assert next_prime(val) == 2 - - -@pytest.mark.slow -@pytest.mark.parametrize("prime", smallprimes) -def test_square_root_mod_prime_for_small_primes(prime): - squares = set() - for num in range(0, 1 + prime // 2): - sq = num * num % prime - squares.add(sq) - root = square_root_mod_prime(sq, prime) - # tested for real with TestNumbertheory.test_square_root_mod_prime - assert root * root % prime == sq - - for nonsquare in range(0, prime): - if nonsquare in squares: - continue - with pytest.raises(SquareRootError): - square_root_mod_prime(nonsquare, prime) - - -def test_square_root_mod_prime_for_2(): - a = square_root_mod_prime(1, 2) - assert a == 1 - - -def test_square_root_mod_prime_for_small_prime(): - root = square_root_mod_prime(98**2 % 101, 101) - assert root * root % 101 == 9 - - -def test_square_root_mod_prime_for_p_congruent_5(): - p = 13 - assert p % 8 == 5 - - root = square_root_mod_prime(3, p) - assert root * root % p == 3 - - -def test_square_root_mod_prime_for_p_congruent_5_large_d(): - p = 29 - assert p % 8 == 5 - - root = square_root_mod_prime(4, p) - assert root * root % p == 4 - - -class TestSquareRootModPrime(unittest.TestCase): - def test_power_of_2_p(self): - with self.assertRaises(JacobiError): - square_root_mod_prime(12, 32) - - def test_no_square(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(12, 31) - - self.assertIn("no square root", str(e.exception)) - - def test_non_prime(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(12, 33) - - self.assertIn("p is not prime", str(e.exception)) - - def test_non_prime_with_negative(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(697 - 1, 697) - - self.assertIn("p is not prime", str(e.exception)) - - -@st.composite -def st_two_nums_rel_prime(draw): - # 521-bit is the biggest curve we operate on, use 1024 for a bit - # of breathing space - mod = draw(st.integers(min_value=2, max_value=2**1024)) - num = draw( - st.integers(min_value=1, max_value=mod - 1).filter( - lambda x: gcd(x, mod) == 1 - ) - ) - return num, mod - - -@st.composite -def st_primes(draw, *args, **kwargs): - if "min_value" not in kwargs: # pragma: no branch - kwargs["min_value"] = 1 - prime = draw( - st.sampled_from(smallprimes) - | st.integers(*args, **kwargs).filter(is_prime) - ) - return prime - - -@st.composite -def st_num_square_prime(draw): - prime = draw(st_primes(max_value=2**1024)) - num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) - sq = num * num % prime - return sq, prime - - -@st.composite -def st_comp_with_com_fac(draw): - """ - Strategy that returns lists of numbers, all having a common factor. - """ - primes = draw( - st.lists(st_primes(max_value=2**512), min_size=1, max_size=10) - ) - # select random prime(s) that will make the common factor of composites - com_fac_primes = draw( - st.lists(st.sampled_from(primes), min_size=1, max_size=20) - ) - com_fac = reduce(operator.mul, com_fac_primes, 1) - - # select at most 20 lists (returned numbers), - # each having at most 30 primes (factors) including none (then the number - # will be 1) - comp_primes = draw( # pragma: no branch - st.integers(min_value=1, max_value=20).flatmap( - lambda n: st.lists( - st.lists(st.sampled_from(primes), max_size=30), - min_size=1, - max_size=n, - ) - ) - ) - - return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes] - - -@st.composite -def st_comp_no_com_fac(draw): - """ - Strategy that returns lists of numbers that don't have a common factor. - """ - primes = draw( - st.lists( - st_primes(max_value=2**512), min_size=2, max_size=10, unique=True - ) - ) - # first select the primes that will create the uncommon factor - # between returned numbers - uncom_fac_primes = draw( - st.lists( - st.sampled_from(primes), - min_size=1, - max_size=len(primes) - 1, - unique=True, - ) - ) - uncom_fac = reduce(operator.mul, uncom_fac_primes, 1) - - # then build composites from leftover primes - leftover_primes = [i for i in primes if i not in uncom_fac_primes] - - assert leftover_primes - assert uncom_fac_primes - - # select at most 20 lists, each having at most 30 primes - # selected from the leftover_primes list - number_primes = draw( # pragma: no branch - st.integers(min_value=1, max_value=20).flatmap( - lambda n: st.lists( - st.lists(st.sampled_from(leftover_primes), max_size=30), - min_size=1, - max_size=n, - ) - ) - ) - - numbers = [reduce(operator.mul, nums, 1) for nums in number_primes] - - insert_at = draw(st.integers(min_value=0, max_value=len(numbers))) - numbers.insert(insert_at, uncom_fac) - return numbers - - -HYP_SETTINGS = {} -if HC_PRESENT: # pragma: no branch - HYP_SETTINGS["suppress_health_check"] = [ - HealthCheck.filter_too_much, - HealthCheck.too_slow, - ] - # the factorization() sometimes takes a long time to finish - HYP_SETTINGS["deadline"] = 5000 - -if "--fast" in sys.argv: # pragma: no cover - HYP_SETTINGS["max_examples"] = 20 - - -HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -if "--fast" in sys.argv: # pragma: no cover - HYP_SLOW_SETTINGS["max_examples"] = 1 -else: - HYP_SLOW_SETTINGS["max_examples"] = 20 - - -class TestIsPrime(unittest.TestCase): - def test_very_small_prime(self): - assert is_prime(23) - - def test_very_small_composite(self): - assert not is_prime(22) - - def test_small_prime(self): - assert is_prime(123456791) - - def test_special_composite(self): - assert not is_prime(10261) - - def test_medium_prime_1(self): - # nextPrime[2^256] - assert is_prime(2**256 + 0x129) - - def test_medium_prime_2(self): - # nextPrime(2^256+0x129) - assert is_prime(2**256 + 0x12D) - - def test_medium_trivial_composite(self): - assert not is_prime(2**256 + 0x130) - - def test_medium_non_trivial_composite(self): - assert not is_prime(2**256 + 0x12F) - - def test_large_prime(self): - # nextPrime[2^2048] - assert is_prime(mpz(2) ** 2048 + 0x3D5) - - def test_pseudoprime_base_19(self): - assert not is_prime(1543267864443420616877677640751301) - - def test_pseudoprime_base_300(self): - # F. Arnault "Constructing Carmichael Numbers Which Are Strong - # Pseudoprimes to Several Bases". Journal of Symbolic - # Computation. 20 (2): 151-161. doi:10.1006/jsco.1995.1042. - # Section 4.4 Large Example (a pseudoprime to all bases up to - # 300) - p = int( - "29 674 495 668 685 510 550 154 174 642 905 332 730 " - "771 991 799 853 043 350 995 075 531 276 838 753 171 " - "770 199 594 238 596 428 121 188 033 664 754 218 345 " - "562 493 168 782 883".replace(" ", "") - ) - - assert is_prime(p) - for _ in range(10): - if not is_prime(p * (313 * (p - 1) + 1) * (353 * (p - 1) + 1)): - break - else: - assert False, "composite not detected" - - -class TestNumbertheory(unittest.TestCase): - def test_gcd(self): - assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5 - assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5 - assert gcd(3) == 3 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SLOW_SETTINGS) - @example([877 * 1151, 877 * 1009]) - @given(st_comp_with_com_fac()) - def test_gcd_with_com_factor(self, numbers): - n = gcd(numbers) - assert 1 in numbers or n != 1 - for i in numbers: - assert i % n == 0 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SLOW_SETTINGS) - @example([1151, 1069, 1009]) - @given(st_comp_no_com_fac()) - def test_gcd_with_uncom_factor(self, numbers): - n = gcd(numbers) - assert n == 1 - - @settings(**HYP_SLOW_SETTINGS) - @given( - st.lists( - st.integers(min_value=1, max_value=2**8192), - min_size=1, - max_size=20, - ) - ) - def test_gcd_with_random_numbers(self, numbers): - n = gcd(numbers) - for i in numbers: - # check that at least it's a divider - assert i % n == 0 - - def test_lcm(self): - assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7 - assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 - assert lcm(3) == 3 - - @settings(**HYP_SLOW_SETTINGS) - @given( - st.lists( - st.integers(min_value=1, max_value=2**8192), - min_size=1, - max_size=20, - ) - ) - def test_lcm_with_random_numbers(self, numbers): - n = lcm(numbers) - for i in numbers: - assert n % i == 0 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SLOW_SETTINGS) - @given(st_num_square_prime()) - def test_square_root_mod_prime(self, vals): - square, prime = vals - - calc = square_root_mod_prime(square, prime) - assert calc * calc % prime == square - - @pytest.mark.slow - @settings(**HYP_SLOW_SETTINGS) - @given(st.integers(min_value=1, max_value=10**12)) - @example(265399 * 1526929) - @example(373297**2 * 553991) - def test_factorization(self, num): - factors = factorization(num) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == num - - def test_factorisation_smallprimes(self): - exp = 101 * 103 - assert 101 in smallprimes - assert 103 in smallprimes - factors = factorization(exp) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == exp - - def test_factorisation_not_smallprimes(self): - exp = 1231 * 1237 - assert 1231 not in smallprimes - assert 1237 not in smallprimes - factors = factorization(exp) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == exp - - def test_jacobi_with_zero(self): - assert jacobi(0, 3) == 0 - - def test_jacobi_with_one(self): - assert jacobi(1, 3) == 1 - - @settings(**HYP_SLOW_SETTINGS) - @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) - def test_jacobi(self, mod): - mod = mpz(mod) - if is_prime(mod): - squares = set() - for root in range(1, mod): - root = mpz(root) - assert jacobi(root * root, mod) == 1 - squares.add(root * root % mod) - for i in range(1, mod): - if i not in squares: - i = mpz(i) - assert jacobi(i, mod) == -1 - else: - factors = factorization(mod) - for a in range(1, mod): - c = 1 - for i in factors: - c *= jacobi(a, i[0]) ** i[1] - assert c == jacobi(a, mod) - - @settings(**HYP_SLOW_SETTINGS) - @given(st_two_nums_rel_prime()) - def test_inverse_mod(self, nums): - num, mod = nums - - inv = inverse_mod(num, mod) - - assert 0 < inv < mod - assert num * inv % mod == 1 - - def test_inverse_mod_with_zero(self): - assert 0 == inverse_mod(0, 11) diff --git a/.venv/Lib/site-packages/ecdsa/test_pyecdsa.py b/.venv/Lib/site-packages/ecdsa/test_pyecdsa.py deleted file mode 100644 index 20201ba..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_pyecdsa.py +++ /dev/null @@ -1,2522 +0,0 @@ -from __future__ import with_statement, division - -try: - import unittest2 as unittest -except ImportError: - import unittest -import os -import shutil -import subprocess -import pytest -import sys -from binascii import hexlify, unhexlify -import hashlib -from functools import partial - -from hypothesis import given, settings -import hypothesis.strategies as st - -from six import b, print_, binary_type -from .keys import SigningKey, VerifyingKey -from .keys import BadSignatureError, MalformedPointError, BadDigestError -from . import util -from .util import ( - sigencode_der, - sigencode_strings, - sigencode_strings_canonize, - sigencode_string_canonize, - sigencode_der_canonize, -) -from .util import sigdecode_der, sigdecode_strings, sigdecode_string -from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature -from .curves import Curve, UnknownCurveError -from .curves import ( - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - BRAINPOOLP160t1, - BRAINPOOLP192t1, - BRAINPOOLP224t1, - BRAINPOOLP256t1, - BRAINPOOLP320t1, - BRAINPOOLP384t1, - BRAINPOOLP512t1, - Ed25519, - Ed448, - curves, -) -from .ecdsa import ( - curve_brainpoolp224r1, - curve_brainpoolp256r1, - curve_brainpoolp384r1, - curve_brainpoolp512r1, -) -from .ellipticcurve import Point -from . import der -from . import rfc6979 -from . import ecdsa - - -class SubprocessError(Exception): - pass - - -HYP_SETTINGS = {} - - -if "--fast" in sys.argv: # pragma: no cover - HYP_SETTINGS["max_examples"] = 2 - - -def run_openssl(cmd): - OPENSSL = "openssl" - p = subprocess.Popen( - [OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - stdout, ignored = p.communicate() - if p.returncode != 0: - raise SubprocessError( - "cmd '%s %s' failed: rc=%s, stdout/err was %s" - % (OPENSSL, cmd, p.returncode, stdout) - ) - return stdout.decode() - - -class ECDSA(unittest.TestCase): - def test_basic(self): - priv = SigningKey.generate() - pub = priv.get_verifying_key() - - data = b"blahblah" - sig = priv.sign(data) - - self.assertTrue(pub.verify(sig, data)) - self.assertRaises(BadSignatureError, pub.verify, sig, data + b"bad") - - pub2 = VerifyingKey.from_string(pub.to_string()) - self.assertTrue(pub2.verify(sig, data)) - - def test_deterministic(self): - data = b"blahblah" - secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) - - priv = SigningKey.from_secret_exponent( - secexp, SECP256k1, hashlib.sha256 - ) - pub = priv.get_verifying_key() - - k = rfc6979.generate_k( - SECP256k1.generator.order(), - secexp, - hashlib.sha256, - hashlib.sha256(data).digest(), - ) - - sig1 = priv.sign(data, k=k) - self.assertTrue(pub.verify(sig1, data)) - - sig2 = priv.sign(data, k=k) - self.assertTrue(pub.verify(sig2, data)) - - sig3 = priv.sign_deterministic(data, hashlib.sha256) - self.assertTrue(pub.verify(sig3, data)) - - self.assertEqual(sig1, sig2) - self.assertEqual(sig1, sig3) - - def test_bad_usage(self): - # sk=SigningKey() is wrong - self.assertRaises(TypeError, SigningKey) - self.assertRaises(TypeError, VerifyingKey) - - def test_lengths_default(self): - default = NIST192p - priv = SigningKey.generate() - pub = priv.get_verifying_key() - self.assertEqual(len(pub.to_string()), default.verifying_key_length) - sig = priv.sign(b"data") - self.assertEqual(len(sig), default.signature_length) - - def test_serialize(self): - seed = b"secret" - curve = NIST192p - secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) - secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) - self.assertEqual(secexp1, secexp2) - priv1 = SigningKey.from_secret_exponent(secexp1, curve) - priv2 = SigningKey.from_secret_exponent(secexp2, curve) - self.assertEqual( - hexlify(priv1.to_string()), hexlify(priv2.to_string()) - ) - self.assertEqual(priv1.to_pem(), priv2.to_pem()) - pub1 = priv1.get_verifying_key() - pub2 = priv2.get_verifying_key() - data = b"data" - sig1 = priv1.sign(data) - sig2 = priv2.sign(data) - self.assertTrue(pub1.verify(sig1, data)) - self.assertTrue(pub2.verify(sig1, data)) - self.assertTrue(pub1.verify(sig2, data)) - self.assertTrue(pub2.verify(sig2, data)) - self.assertEqual(hexlify(pub1.to_string()), hexlify(pub2.to_string())) - - def test_nonrandom(self): - s = b"all the entropy in the entire world, compressed into one line" - - def not_much_entropy(numbytes): - return s[:numbytes] - - # we control the entropy source, these two keys should be identical: - priv1 = SigningKey.generate(entropy=not_much_entropy) - priv2 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual( - hexlify(priv1.get_verifying_key().to_string()), - hexlify(priv2.get_verifying_key().to_string()), - ) - # likewise, signatures should be identical. Obviously you'd never - # want to do this with keys you care about, because the secrecy of - # the private key depends upon using different random numbers for - # each signature - sig1 = priv1.sign(b"data", entropy=not_much_entropy) - sig2 = priv2.sign(b"data", entropy=not_much_entropy) - self.assertEqual(hexlify(sig1), hexlify(sig2)) - - def assertTruePrivkeysEqual(self, priv1, priv2): - self.assertEqual( - priv1.privkey.secret_multiplier, priv2.privkey.secret_multiplier - ) - self.assertEqual( - priv1.privkey.public_key.generator, - priv2.privkey.public_key.generator, - ) - - def test_privkey_creation(self): - s = b"all the entropy in the entire world, compressed into one line" - - def not_much_entropy(numbytes): - return s[:numbytes] - - priv1 = SigningKey.generate() - self.assertEqual(priv1.baselen, NIST192p.baselen) - - priv1 = SigningKey.generate(curve=NIST224p) - self.assertEqual(priv1.baselen, NIST224p.baselen) - - priv1 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual(priv1.baselen, NIST192p.baselen) - priv2 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual(priv2.baselen, NIST192p.baselen) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.from_secret_exponent(secexp=3) - self.assertEqual(priv1.baselen, NIST192p.baselen) - priv2 = SigningKey.from_secret_exponent(secexp=3) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) - self.assertEqual(priv1.baselen, NIST224p.baselen) - - def test_privkey_strings(self): - priv1 = SigningKey.generate() - s1 = priv1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST192p.baselen) - priv2 = SigningKey.from_string(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) - self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.generate(curve=NIST256p) - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) - self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - def test_privkey_strings_brainpool(self): - priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b"-----BEGIN EC PRIVATE KEY-----")) - self.assertTrue(s1.strip().endswith(b"-----END EC PRIVATE KEY-----")) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - def assertTruePubkeysEqual(self, pub1, pub2): - self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) - self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) - self.assertEqual(pub1.curve, pub2.curve) - - def test_pubkey_strings(self): - priv1 = SigningKey.generate() - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST192p.verifying_key_length) - pub2 = VerifyingKey.from_string(s1) - self.assertTruePubkeysEqual(pub1, pub2) - - priv1 = SigningKey.generate(curve=NIST256p) - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST256p.verifying_key_length) - pub2 = VerifyingKey.from_string(s1, curve=NIST256p) - self.assertTruePubkeysEqual(pub1, pub2) - - pub1_der = pub1.to_der() - self.assertEqual(type(pub1_der), binary_type) - pub2 = VerifyingKey.from_der(pub1_der) - self.assertTruePubkeysEqual(pub1, pub2) - - self.assertRaises( - der.UnexpectedDER, VerifyingKey.from_der, pub1_der + b"junk" - ) - badpub = VerifyingKey.from_der(pub1_der) - - class FakeGenerator: - def order(self): - return 123456789 - - class FakeCurveFp: - def p(self): - return int( - "6525534529039240705020950546962731340" - "4541085228058844382513856749047873406763" - ) - - badcurve = Curve( - "unknown", FakeCurveFp(), FakeGenerator(), (1, 2, 3, 4, 5, 6), None - ) - badpub.curve = badcurve - badder = badpub.to_der() - self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) - - pem = pub1.to_pem() - self.assertEqual(type(pem), binary_type) - self.assertTrue(pem.startswith(b"-----BEGIN PUBLIC KEY-----"), pem) - self.assertTrue(pem.strip().endswith(b"-----END PUBLIC KEY-----"), pem) - pub2 = VerifyingKey.from_pem(pem) - self.assertTruePubkeysEqual(pub1, pub2) - - def test_pubkey_strings_brainpool(self): - priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), BRAINPOOLP512r1.verifying_key_length) - pub2 = VerifyingKey.from_string(s1, curve=BRAINPOOLP512r1) - self.assertTruePubkeysEqual(pub1, pub2) - - pub1_der = pub1.to_der() - self.assertEqual(type(pub1_der), binary_type) - pub2 = VerifyingKey.from_der(pub1_der) - self.assertTruePubkeysEqual(pub1, pub2) - - def test_vk_to_der_with_invalid_point_encoding(self): - sk = SigningKey.generate() - vk = sk.verifying_key - - with self.assertRaises(ValueError): - vk.to_der("raw") - - def test_sk_to_der_with_invalid_point_encoding(self): - sk = SigningKey.generate() - - with self.assertRaises(ValueError): - sk.to_der("raw") - - def test_vk_from_der_garbage_after_curve_oid(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + b( - "garbage" - ) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_invalid_key_type(self): - type_oid_der = der.encode_oid(*(1, 2, 3)) - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_garbage_after_point_string(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) + b"garbage" - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_invalid_bitstring(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x08\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_with_invalid_length_of_encoding(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\xff" * 64, 0) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_with_raw_encoding(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\xff" * 48, 0) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_signature_strings(self): - priv1 = SigningKey.generate() - pub1 = priv1.get_verifying_key() - data = b"data" - - sig = priv1.sign(data) - self.assertEqual(type(sig), binary_type) - self.assertEqual(len(sig), NIST192p.signature_length) - self.assertTrue(pub1.verify(sig, data)) - - sig = priv1.sign(data, sigencode=sigencode_strings) - self.assertEqual(type(sig), tuple) - self.assertEqual(len(sig), 2) - self.assertEqual(type(sig[0]), binary_type) - self.assertEqual(type(sig[1]), binary_type) - self.assertEqual(len(sig[0]), NIST192p.baselen) - self.assertEqual(len(sig[1]), NIST192p.baselen) - self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) - - sig_der = priv1.sign(data, sigencode=sigencode_der) - self.assertEqual(type(sig_der), binary_type) - self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) - - def test_sigencode_string_canonize_no_change(self): - r = 12 - s = 400 - order = SECP112r1.order - - new_r, new_s = sigdecode_string( - sigencode_string_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(s, new_s) - - def test_sigencode_string_canonize(self): - r = 12 - order = SECP112r1.order - s = order - 10 - - new_r, new_s = sigdecode_string( - sigencode_string_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(order - s, new_s) - - def test_sigencode_strings_canonize_no_change(self): - r = 12 - s = 400 - order = SECP112r1.order - - new_r, new_s = sigdecode_strings( - sigencode_strings_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(s, new_s) - - def test_sigencode_strings_canonize(self): - r = 12 - order = SECP112r1.order - s = order - 10 - - new_r, new_s = sigdecode_strings( - sigencode_strings_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(order - s, new_s) - - def test_sigencode_der_canonize_no_change(self): - r = 13 - s = 200 - order = SECP112r1.order - - new_r, new_s = sigdecode_der( - sigencode_der_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(s, new_s) - - def test_sigencode_der_canonize(self): - r = 13 - order = SECP112r1.order - s = order - 14 - - new_r, new_s = sigdecode_der( - sigencode_der_canonize(r, s, order), order - ) - - self.assertEqual(r, new_r) - self.assertEqual(order - s, new_s) - - def test_sig_decode_strings_with_invalid_count(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b"one", b"two", b"three"], 0xFF) - - def test_sig_decode_strings_with_wrong_r_len(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b"one", b"two"], 0xFF) - - def test_sig_decode_strings_with_wrong_s_len(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b"\xa0", b"\xb0\xff"], 0xFF) - - def test_verify_with_too_long_input(self): - sk = SigningKey.generate() - vk = sk.verifying_key - - with self.assertRaises(BadDigestError): - vk.verify_digest(None, b"\x00" * 128) - - def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): - with self.assertRaises(MalformedPointError): - SigningKey.from_secret_exponent(0) - - def test_sk_from_string_with_wrong_len_string(self): - with self.assertRaises(MalformedPointError): - SigningKey.from_string(b"\x01") - - def test_sk_from_der_with_junk_after_sequence(self): - ver_der = der.encode_integer(1) - to_decode = der.encode_sequence(ver_der) + b"garbage" - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_with_wrong_version(self): - ver_der = der.encode_integer(0) - to_decode = der.encode_sequence(ver_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_invalid_const_tag(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b"\x00\xff") - curve_oid_der = der.encode_oid(*(1, 2, 3)) - const_der = der.encode_constructed(1, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_garbage_after_privkey_oid(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b"\x00\xff") - curve_oid_der = der.encode_oid(*(1, 2, 3)) + b"garbage" - const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_with_short_privkey(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b"\x00\xff") - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - sk = SigningKey.from_der(to_decode) - self.assertEqual(sk.privkey.secret_multiplier, 255) - - def test_sk_from_p8_der_with_wrong_version(self): - ver_der = der.encode_integer(2) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_wrong_algorithm(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 3), der.encode_oid(1, 2, 840, 10045, 3, 1, 1) - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_trailing_junk_after_algorithm(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - der.encode_octet_string(b"junk"), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_trailing_junk_after_key(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - + der.encode_integer(999) - ) - to_decode = der.encode_sequence( - ver_der, - algorithm_der, - privkey_der, - der.encode_octet_string(b"junk"), - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sign_with_too_long_hash(self): - sk = SigningKey.from_secret_exponent(12) - - with self.assertRaises(BadDigestError): - sk.sign_digest(b"\xff" * 64) - - def test_hashfunc(self): - sk = SigningKey.generate(curve=NIST256p, hashfunc=hashlib.sha256) - data = b"security level is 128 bits" - sig = sk.sign(data) - vk = VerifyingKey.from_string( - sk.get_verifying_key().to_string(), - curve=NIST256p, - hashfunc=hashlib.sha256, - ) - self.assertTrue(vk.verify(sig, data)) - - sk2 = SigningKey.generate(curve=NIST256p) - sig2 = sk2.sign(data, hashfunc=hashlib.sha256) - vk2 = VerifyingKey.from_string( - sk2.get_verifying_key().to_string(), - curve=NIST256p, - hashfunc=hashlib.sha256, - ) - self.assertTrue(vk2.verify(sig2, data)) - - vk3 = VerifyingKey.from_string( - sk.get_verifying_key().to_string(), curve=NIST256p - ) - self.assertTrue(vk3.verify(sig, data, hashfunc=hashlib.sha256)) - - def test_public_key_recovery(self): - # Create keys - curve = BRAINPOOLP160r1 - - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - - # Sign a message - data = b"blahblah" - signature = sk.sign(data) - - # Recover verifying keys - recovered_vks = VerifyingKey.from_public_key_recovery( - signature, data, curve - ) - - # Test if each pk is valid - for recovered_vk in recovered_vks: - # Test if recovered vk is valid for the data - self.assertTrue(recovered_vk.verify(signature, data)) - - # Test if properties are equal - self.assertEqual(vk.curve, recovered_vk.curve) - self.assertEqual( - vk.default_hashfunc, recovered_vk.default_hashfunc - ) - - # Test if original vk is the list of recovered keys - self.assertIn( - vk.pubkey.point, - [recovered_vk.pubkey.point for recovered_vk in recovered_vks], - ) - - def test_public_key_recovery_with_custom_hash(self): - # Create keys - curve = BRAINPOOLP160r1 - - sk = SigningKey.generate(curve=curve, hashfunc=hashlib.sha256) - vk = sk.get_verifying_key() - - # Sign a message - data = b"blahblah" - signature = sk.sign(data) - - # Recover verifying keys - recovered_vks = VerifyingKey.from_public_key_recovery( - signature, - data, - curve, - hashfunc=hashlib.sha256, - allow_truncate=True, - ) - - # Test if each pk is valid - for recovered_vk in recovered_vks: - # Test if recovered vk is valid for the data - self.assertTrue(recovered_vk.verify(signature, data)) - - # Test if properties are equal - self.assertEqual(vk.curve, recovered_vk.curve) - self.assertEqual(hashlib.sha256, recovered_vk.default_hashfunc) - - # Test if original vk is the list of recovered keys - self.assertIn( - vk.pubkey.point, - [recovered_vk.pubkey.point for recovered_vk in recovered_vks], - ) - - def test_encoding(self): - sk = SigningKey.from_secret_exponent(123456789) - vk = sk.verifying_key - - exp = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - self.assertEqual(vk.to_string(), exp) - self.assertEqual(vk.to_string("raw"), exp) - self.assertEqual(vk.to_string("uncompressed"), b"\x04" + exp) - self.assertEqual(vk.to_string("compressed"), b"\x02" + exp[:24]) - self.assertEqual(vk.to_string("hybrid"), b"\x06" + exp) - - def test_decoding(self): - sk = SigningKey.from_secret_exponent(123456789) - vk = sk.verifying_key - - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - from_raw = VerifyingKey.from_string(enc) - self.assertEqual(from_raw.pubkey.point, vk.pubkey.point) - - from_uncompressed = VerifyingKey.from_string(b"\x04" + enc) - self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) - - from_compressed = VerifyingKey.from_string(b"\x02" + enc[:24]) - self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point) - - from_uncompressed = VerifyingKey.from_string(b"\x06" + enc) - self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) - - def test_uncompressed_decoding_as_only_alowed(self): - enc = b( - "\x04" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) - sk = SigningKey.from_secret_exponent(123456789) - - self.assertEqual(vk, sk.verifying_key) - - def test_raw_decoding_with_blocked_format(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) - - self.assertIn("hybrid", str(exp.exception)) - - def test_decoding_with_unknown_format(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar")) - - self.assertIn("Only uncompressed, compressed", str(e.exception)) - - def test_uncompressed_decoding_with_blocked_format(self): - enc = b( - "\x04" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) - - self.assertIn("Invalid X9.62 encoding", str(exp.exception)) - - def test_hybrid_decoding_with_blocked_format(self): - enc = b( - "\x06" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) - - self.assertIn("Invalid X9.62 encoding", str(exp.exception)) - - def test_compressed_decoding_with_blocked_format(self): - enc = b( - "\x02" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - )[:25] - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw")) - - self.assertIn("(hybrid, raw)", str(exp.exception)) - - def test_decoding_with_malformed_uncompressed(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x02" + enc) - - def test_decoding_with_malformed_compressed(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x01" + enc[:24]) - - def test_decoding_with_inconsistent_hybrid(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x07" + enc) - - def test_decoding_with_point_not_on_curve(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(enc[:47] + b"\x00") - - def test_decoding_with_point_at_infinity(self): - # decoding it is unsupported, as it's not necessary to encode it - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x00") - - def test_not_lying_on_curve(self): - enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p() + 1) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x02" + enc) - - def test_from_string_with_invalid_curve_too_short_ver_key_len(self): - # both verifying_key_length and baselen are calculated internally - # by the Curve constructor, but since we depend on them verify - # that inconsistent values are detected - curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) - curve.verifying_key_length = 16 - curve.baselen = 32 - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x00" * 16, curve) - - def test_from_string_with_invalid_curve_too_long_ver_key_len(self): - # both verifying_key_length and baselen are calculated internally - # by the Curve constructor, but since we depend on them verify - # that inconsistent values are detected - curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) - curve.verifying_key_length = 16 - curve.baselen = 16 - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b"\x00" * 16, curve) - - -@pytest.mark.parametrize( - "val,even", [(i, j) for i in range(256) for j in [True, False]] -) -def test_VerifyingKey_decode_with_small_values(val, even): - enc = number_to_string(val, NIST192p.order) - - if even: - enc = b"\x02" + enc - else: - enc = b"\x03" + enc - - # small values can both be actual valid public keys and not, verify that - # only expected exceptions are raised if they are not - try: - vk = VerifyingKey.from_string(enc) - assert isinstance(vk, VerifyingKey) - except MalformedPointError: - assert True - - -params = [] -for curve in curves: - for enc in ["raw", "uncompressed", "compressed", "hybrid"]: - params.append( - pytest.param(curve, enc, id="{0}-{1}".format(curve.name, enc)) - ) - - -@pytest.mark.parametrize("curve,encoding", params) -def test_VerifyingKey_encode_decode(curve, encoding): - sk = SigningKey.generate(curve=curve) - vk = sk.verifying_key - - encoded = vk.to_string(encoding) - - from_enc = VerifyingKey.from_string(encoded, curve=curve) - - assert vk.pubkey.point == from_enc.pubkey.point - - -if "--fast" in sys.argv: # pragma: no cover - params = [NIST192p, BRAINPOOLP160r1] -else: - params = curves - - -@pytest.mark.parametrize("curve", params) -def test_lengths(curve): - priv = SigningKey.generate(curve=curve) - pub1 = priv.get_verifying_key() - pub2 = VerifyingKey.from_string(pub1.to_string(), curve) - assert pub1.to_string() == pub2.to_string() - assert len(pub1.to_string()) == curve.verifying_key_length - sig = priv.sign(b"data") - assert len(sig) == curve.signature_length - - -@pytest.mark.slow -class OpenSSL(unittest.TestCase): - # test interoperability with OpenSSL tools. Note that openssl's ECDSA - # sign/verify arguments changed between 0.9.8 and 1.0.0: the early - # versions require "-ecdsa-with-SHA1", the later versions want just - # "-SHA1" (or to leave out that argument entirely, which means the - # signature will use some default digest algorithm, probably determined - # by the key, probably always SHA1). - # - # openssl ecparam -name secp224r1 -genkey -out privkey.pem - # openssl ec -in privkey.pem -text -noout # get the priv/pub keys - # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt - # openssl asn1parse -in data.sig -inform DER - # data.sig is 64 bytes, probably 56b plus ASN1 overhead - # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? - # openssl ec -in privkey.pem -pubout -out pubkey.pem - # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der - - OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") - ) - - def get_openssl_messagedigest_arg(self, hash_name): - v = run_openssl("version") - # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", - # or "OpenSSL 0.9.8o 01 Jun 2010" - vs = v.split()[1].split(".") - if vs >= ["1", "0", "0"]: # pragma: no cover - return "-{0}".format(hash_name) - else: # pragma: no cover - return "-ecdsa-with-{0}".format(hash_name) - - # sk: 1:OpenSSL->python 2:python->OpenSSL - # vk: 3:OpenSSL->python 4:python->OpenSSL - # sig: 5:OpenSSL->python 6:python->OpenSSL - - @pytest.mark.slow - @pytest.mark.skipif( - "secp112r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r1", - ) - def test_from_openssl_secp112r1(self): - return self.do_test_from_openssl(SECP112r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp112r2" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r2", - ) - def test_from_openssl_secp112r2(self): - return self.do_test_from_openssl(SECP112r2) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp128r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp128r1", - ) - def test_from_openssl_secp128r1(self): - return self.do_test_from_openssl(SECP128r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp160r1", - ) - def test_from_openssl_secp160r1(self): - return self.do_test_from_openssl(SECP160r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_from_openssl_nist192p(self): - return self.do_test_from_openssl(NIST192p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_from_openssl_nist192p_sha256(self): - return self.do_test_from_openssl(NIST192p, "SHA256") - - @pytest.mark.slow - @pytest.mark.skipif( - "secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1", - ) - def test_from_openssl_nist224p(self): - return self.do_test_from_openssl(NIST224p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p(self): - return self.do_test_from_openssl(NIST256p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p_sha384(self): - return self.do_test_from_openssl(NIST256p, "SHA384") - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p_sha512(self): - return self.do_test_from_openssl(NIST256p, "SHA512") - - @pytest.mark.slow - @pytest.mark.skipif( - "secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1", - ) - def test_from_openssl_nist384p(self): - return self.do_test_from_openssl(NIST384p) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1", - ) - def test_from_openssl_nist521p(self): - return self.do_test_from_openssl(NIST521p) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1", - ) - def test_from_openssl_secp256k1(self): - return self.do_test_from_openssl(SECP256k1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1", - ) - def test_from_openssl_brainpoolp160r1(self): - return self.do_test_from_openssl(BRAINPOOLP160r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1", - ) - def test_from_openssl_brainpoolp192r1(self): - return self.do_test_from_openssl(BRAINPOOLP192r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1", - ) - def test_from_openssl_brainpoolp224r1(self): - return self.do_test_from_openssl(BRAINPOOLP224r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1", - ) - def test_from_openssl_brainpoolp256r1(self): - return self.do_test_from_openssl(BRAINPOOLP256r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1", - ) - def test_from_openssl_brainpoolp320r1(self): - return self.do_test_from_openssl(BRAINPOOLP320r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1", - ) - def test_from_openssl_brainpoolp384r1(self): - return self.do_test_from_openssl(BRAINPOOLP384r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1", - ) - def test_from_openssl_brainpoolp512r1(self): - return self.do_test_from_openssl(BRAINPOOLP512r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160t1", - ) - def test_from_openssl_brainpoolp160t1(self): - return self.do_test_from_openssl(BRAINPOOLP160t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192t1", - ) - def test_from_openssl_brainpoolp192t1(self): - return self.do_test_from_openssl(BRAINPOOLP192t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224t1", - ) - def test_from_openssl_brainpoolp224t1(self): - return self.do_test_from_openssl(BRAINPOOLP224t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256t1", - ) - def test_from_openssl_brainpoolp256t1(self): - return self.do_test_from_openssl(BRAINPOOLP256t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320t1", - ) - def test_from_openssl_brainpoolp320t1(self): - return self.do_test_from_openssl(BRAINPOOLP320t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384t1", - ) - def test_from_openssl_brainpoolp384t1(self): - return self.do_test_from_openssl(BRAINPOOLP384t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512t1", - ) - def test_from_openssl_brainpoolp512t1(self): - return self.do_test_from_openssl(BRAINPOOLP512t1) - - def do_test_from_openssl(self, curve, hash_name="SHA1"): - curvename = curve.openssl_name - assert curvename - # OpenSSL: create sk, vk, sign. - # Python: read vk(3), checksig(5), read sk(1), sign, check - mdarg = self.get_openssl_messagedigest_arg(hash_name) - if os.path.isdir("t"): # pragma: no cover - shutil.rmtree("t") - os.mkdir("t") - run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) - run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") - data = b"data" - with open("t/data.txt", "wb") as e: - e.write(data) - run_openssl( - "dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" - % mdarg - ) - with open("t/pubkey.pem", "rb") as e: - pubkey_pem = e.read() - vk = VerifyingKey.from_pem(pubkey_pem) # 3 - with open("t/data.sig", "rb") as e: - sig_der = e.read() - self.assertTrue( - vk.verify( - sig_der, - data, # 5 - hashfunc=partial(hashlib.new, hash_name), - sigdecode=sigdecode_der, - ) - ) - - with open("t/privkey.pem") as e: - fp = e.read() - sk = SigningKey.from_pem(fp) # 1 - sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name)) - self.assertTrue( - vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name)) - ) - - run_openssl( - "pkcs8 -topk8 -nocrypt " - "-in t/privkey.pem -outform pem -out t/privkey-p8.pem" - ) - with open("t/privkey-p8.pem", "rb") as e: - privkey_p8_pem = e.read() - sk_from_p8 = SigningKey.from_pem(privkey_p8_pem) - self.assertEqual(sk, sk_from_p8) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp112r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r1", - ) - def test_to_openssl_secp112r1(self): - self.do_test_to_openssl(SECP112r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp112r2" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r2", - ) - def test_to_openssl_secp112r2(self): - self.do_test_to_openssl(SECP112r2) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp128r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp128r1", - ) - def test_to_openssl_secp128r1(self): - self.do_test_to_openssl(SECP128r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp160r1", - ) - def test_to_openssl_secp160r1(self): - self.do_test_to_openssl(SECP160r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_to_openssl_nist192p(self): - self.do_test_to_openssl(NIST192p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_to_openssl_nist192p_sha256(self): - self.do_test_to_openssl(NIST192p, "SHA256") - - @pytest.mark.slow - @pytest.mark.skipif( - "secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1", - ) - def test_to_openssl_nist224p(self): - self.do_test_to_openssl(NIST224p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p(self): - self.do_test_to_openssl(NIST256p) - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p_sha384(self): - self.do_test_to_openssl(NIST256p, "SHA384") - - @pytest.mark.slow - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p_sha512(self): - self.do_test_to_openssl(NIST256p, "SHA512") - - @pytest.mark.slow - @pytest.mark.skipif( - "secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1", - ) - def test_to_openssl_nist384p(self): - self.do_test_to_openssl(NIST384p) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1", - ) - def test_to_openssl_nist521p(self): - self.do_test_to_openssl(NIST521p) - - @pytest.mark.slow - @pytest.mark.skipif( - "secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1", - ) - def test_to_openssl_secp256k1(self): - self.do_test_to_openssl(SECP256k1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1", - ) - def test_to_openssl_brainpoolp160r1(self): - self.do_test_to_openssl(BRAINPOOLP160r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1", - ) - def test_to_openssl_brainpoolp192r1(self): - self.do_test_to_openssl(BRAINPOOLP192r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1", - ) - def test_to_openssl_brainpoolp224r1(self): - self.do_test_to_openssl(BRAINPOOLP224r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1", - ) - def test_to_openssl_brainpoolp256r1(self): - self.do_test_to_openssl(BRAINPOOLP256r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1", - ) - def test_to_openssl_brainpoolp320r1(self): - self.do_test_to_openssl(BRAINPOOLP320r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1", - ) - def test_to_openssl_brainpoolp384r1(self): - self.do_test_to_openssl(BRAINPOOLP384r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1", - ) - def test_to_openssl_brainpoolp512r1(self): - self.do_test_to_openssl(BRAINPOOLP512r1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP160t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160t1", - ) - def test_to_openssl_brainpoolp160t1(self): - self.do_test_to_openssl(BRAINPOOLP160t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP192t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192t1", - ) - def test_to_openssl_brainpoolp192t1(self): - self.do_test_to_openssl(BRAINPOOLP192t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP224t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224t1", - ) - def test_to_openssl_brainpoolp224t1(self): - self.do_test_to_openssl(BRAINPOOLP224t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP256t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256t1", - ) - def test_to_openssl_brainpoolp256t1(self): - self.do_test_to_openssl(BRAINPOOLP256t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP320t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320t1", - ) - def test_to_openssl_brainpoolp320t1(self): - self.do_test_to_openssl(BRAINPOOLP320t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP384t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384t1", - ) - def test_to_openssl_brainpoolp384t1(self): - self.do_test_to_openssl(BRAINPOOLP384t1) - - @pytest.mark.slow - @pytest.mark.skipif( - "brainpoolP512t1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512t1", - ) - def test_to_openssl_brainpoolp512t1(self): - self.do_test_to_openssl(BRAINPOOLP512t1) - - def do_test_to_openssl(self, curve, hash_name="SHA1"): - curvename = curve.openssl_name - assert curvename - # Python: create sk, vk, sign. - # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check - mdarg = self.get_openssl_messagedigest_arg(hash_name) - if os.path.isdir("t"): # pragma: no cover - shutil.rmtree("t") - os.mkdir("t") - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - data = b"data" - with open("t/pubkey.der", "wb") as e: - e.write(vk.to_der()) # 4 - with open("t/pubkey.pem", "wb") as e: - e.write(vk.to_pem()) # 4 - sig_der = sk.sign( - data, - hashfunc=partial(hashlib.new, hash_name), - sigencode=sigencode_der, - ) - - with open("t/data.sig", "wb") as e: - e.write(sig_der) # 6 - with open("t/data.txt", "wb") as e: - e.write(data) - with open("t/baddata.txt", "wb") as e: - e.write(data + b"corrupt") - - self.assertRaises( - SubprocessError, - run_openssl, - "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" - % mdarg, - ) - run_openssl( - "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" - % mdarg - ) - - with open("t/privkey.pem", "wb") as e: - e.write(sk.to_pem()) # 2 - run_openssl( - "dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" - % mdarg - ) - - with open("t/privkey-explicit.pem", "wb") as e: - e.write(sk.to_pem(curve_parameters_encoding="explicit")) - run_openssl( - "dgst %s -sign t/privkey-explicit.pem -out t/data.sig2 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" - % mdarg - ) - - with open("t/privkey-p8.pem", "wb") as e: - e.write(sk.to_pem(format="pkcs8")) - run_openssl( - "dgst %s -sign t/privkey-p8.pem -out t/data.sig3 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" - % mdarg - ) - - with open("t/privkey-p8-explicit.pem", "wb") as e: - e.write( - sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit") - ) - run_openssl( - "dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" - % mdarg - ) - - OPENSSL_SUPPORTED_TYPES = set() - try: - if "-rawin" in run_openssl("pkeyutl -help"): - OPENSSL_SUPPORTED_TYPES = set( # pragma: no branch - c.lower() - for c in ("ED25519", "ED448") - if c in run_openssl("list -public-key-methods") - ) - except SubprocessError: # pragma: no cover - pass - - def do_eddsa_test_to_openssl(self, curve): - if os.path.isdir("t"): - shutil.rmtree("t") - os.mkdir("t") - - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - - data = b"data" - with open("t/pubkey.der", "wb") as e: - e.write(vk.to_der()) - with open("t/pubkey.pem", "wb") as e: - e.write(vk.to_pem()) - - sig = sk.sign(data) - - with open("t/data.sig", "wb") as e: - e.write(sig) - with open("t/data.txt", "wb") as e: - e.write(data) - with open("t/baddata.txt", "wb") as e: - e.write(data + b"corrupt") - - with self.assertRaises(SubprocessError): - run_openssl( - "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " - "-in t/baddata.txt -sigfile t/data.sig" - ) - run_openssl( - "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " - "-in t/data.txt -sigfile t/data.sig" - ) - - shutil.rmtree("t") - - # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures - # earlier versions support EdDSA only in X.509 certificates - @pytest.mark.slow - @pytest.mark.skipif( - "ed25519" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed25519", - ) - def test_to_openssl_ed25519(self): - return self.do_eddsa_test_to_openssl(Ed25519) - - @pytest.mark.slow - @pytest.mark.skipif( - "ed448" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed448", - ) - def test_to_openssl_ed448(self): - return self.do_eddsa_test_to_openssl(Ed448) - - def do_eddsa_test_from_openssl(self, curve): - curvename = curve.name - - if os.path.isdir("t"): - shutil.rmtree("t") - os.mkdir("t") - - data = b"data" - - run_openssl( - "genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format( - curvename - ) - ) - run_openssl( - "pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem" - ) - - with open("t/data.txt", "wb") as e: - e.write(data) - run_openssl( - "pkeyutl -sign -inkey t/privkey.pem " - "-rawin -in t/data.txt -out t/data.sig" - ) - - with open("t/data.sig", "rb") as e: - sig = e.read() - with open("t/pubkey.pem", "rb") as e: - vk = VerifyingKey.from_pem(e.read()) - - self.assertIs(vk.curve, curve) - - vk.verify(sig, data) - - shutil.rmtree("t") - - @pytest.mark.slow - @pytest.mark.skipif( - "ed25519" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed25519", - ) - def test_from_openssl_ed25519(self): - return self.do_eddsa_test_from_openssl(Ed25519) - - @pytest.mark.slow - @pytest.mark.skipif( - "ed448" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed448", - ) - def test_from_openssl_ed448(self): - return self.do_eddsa_test_from_openssl(Ed448) - - -class TooSmallCurve(unittest.TestCase): - OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") - ) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_sign_too_small_curve_dont_allow_truncate_raises(self): - sk = SigningKey.generate(curve=NIST192p) - data = b"data" - with self.assertRaises(BadDigestError): - sk.sign( - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigencode=sigencode_der, - allow_truncate=False, - ) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_verify_too_small_curve_dont_allow_truncate_raises(self): - sk = SigningKey.generate(curve=NIST192p) - vk = sk.get_verifying_key() - data = b"data" - sig_der = sk.sign( - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigencode=sigencode_der, - allow_truncate=True, - ) - with self.assertRaises(BadDigestError): - vk.verify( - sig_der, - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigdecode=sigdecode_der, - allow_truncate=False, - ) - - -class DER(unittest.TestCase): - def test_integer(self): - self.assertEqual(der.encode_integer(0), b"\x02\x01\x00") - self.assertEqual(der.encode_integer(1), b"\x02\x01\x01") - self.assertEqual(der.encode_integer(127), b"\x02\x01\x7f") - self.assertEqual(der.encode_integer(128), b"\x02\x02\x00\x80") - self.assertEqual(der.encode_integer(256), b"\x02\x02\x01\x00") - # self.assertEqual(der.encode_integer(-1), b"\x02\x01\xff") - - def s(n): - return der.remove_integer(der.encode_integer(n) + b"junk") - - self.assertEqual(s(0), (0, b"junk")) - self.assertEqual(s(1), (1, b"junk")) - self.assertEqual(s(127), (127, b"junk")) - self.assertEqual(s(128), (128, b"junk")) - self.assertEqual(s(256), (256, b"junk")) - self.assertEqual( - s(1234567890123456789012345678901234567890), - (1234567890123456789012345678901234567890, b"junk"), - ) - - def test_number(self): - self.assertEqual(der.encode_number(0), b"\x00") - self.assertEqual(der.encode_number(127), b"\x7f") - self.assertEqual(der.encode_number(128), b"\x81\x00") - self.assertEqual(der.encode_number(3 * 128 + 7), b"\x83\x07") - # self.assertEqual(der.read_number("\x81\x9b" + "more"), (155, 2)) - # self.assertEqual(der.encode_number(155), b"\x81\x9b") - for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): # , 155): - x = der.encode_number(n) + b"more" - n1, llen = der.read_number(x) - self.assertEqual(n1, n) - self.assertEqual(x[llen:], b"more") - - def test_length(self): - self.assertEqual(der.encode_length(0), b"\x00") - self.assertEqual(der.encode_length(127), b"\x7f") - self.assertEqual(der.encode_length(128), b"\x81\x80") - self.assertEqual(der.encode_length(255), b"\x81\xff") - self.assertEqual(der.encode_length(256), b"\x82\x01\x00") - self.assertEqual(der.encode_length(3 * 256 + 7), b"\x82\x03\x07") - self.assertEqual(der.read_length(b"\x81\x9b" + b"more"), (155, 2)) - self.assertEqual(der.encode_length(155), b"\x81\x9b") - for n in (0, 1, 2, 127, 128, 255, 256, 3 * 256 + 7, 155): - x = der.encode_length(n) + b"more" - n1, llen = der.read_length(x) - self.assertEqual(n1, n) - self.assertEqual(x[llen:], b"more") - - def test_sequence(self): - x = der.encode_sequence(b"ABC", b"DEF") + b"GHI" - self.assertEqual(x, b"\x30\x06ABCDEFGHI") - x1, rest = der.remove_sequence(x) - self.assertEqual(x1, b"ABCDEF") - self.assertEqual(rest, b"GHI") - - def test_constructed(self): - x = der.encode_constructed(0, NIST224p.encoded_oid) - self.assertEqual(hexlify(x), b"a007" + b"06052b81040021") - x = der.encode_constructed(1, unhexlify(b"0102030a0b0c")) - self.assertEqual(hexlify(x), b"a106" + b"0102030a0b0c") - - -class Util(unittest.TestCase): - @pytest.mark.slow - def test_trytryagain(self): - tta = util.randrange_from_seed__trytryagain - for i in range(1000): - seed = "seed-%d" % i - for order in ( - 2**8 - 2, - 2**8 - 1, - 2**8, - 2**8 + 1, - 2**8 + 2, - 2**16 - 1, - 2**16 + 1, - ): - n = tta(seed, order) - self.assertTrue(1 <= n < order, (1, n, order)) - # this trytryagain *does* provide long-term stability - self.assertEqual( - ("%x" % (tta("seed", NIST224p.order))).encode(), - b"6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc", - ) - - def test_trytryagain_single(self): - tta = util.randrange_from_seed__trytryagain - order = 2**8 - 2 - seed = b"text" - n = tta(seed, order) - # known issue: https://github.com/warner/python-ecdsa/issues/221 - if sys.version_info < (3, 0): # pragma: no branch - self.assertEqual(n, 228) - else: # pragma: no branch - self.assertEqual(n, 18) - - @settings(**HYP_SETTINGS) - @given(st.integers(min_value=0, max_value=10**200)) - def test_randrange(self, i): - # util.randrange does not provide long-term stability: we might - # change the algorithm in the future. - entropy = util.PRNG("seed-%d" % i) - for order in ( - 2**8 - 2, - 2**8 - 1, - 2**8, - 2**16 - 1, - 2**16 + 1, - ): - # that oddball 2**16+1 takes half our runtime - n = util.randrange(order, entropy=entropy) - self.assertTrue(1 <= n < order, (1, n, order)) - - def OFF_test_prove_uniformity(self): # pragma: no cover - order = 2**8 - 2 - counts = dict([(i, 0) for i in range(1, order)]) - assert 0 not in counts - assert order not in counts - for i in range(1000000): - seed = "seed-%d" % i - n = util.randrange_from_seed__trytryagain(seed, order) - counts[n] += 1 - # this technique should use the full range - self.assertTrue(counts[order - 1]) - for i in range(1, order): - print_("%3d: %s" % (i, "*" * (counts[i] // 100))) - - -class RFC6979(unittest.TestCase): - # https://tools.ietf.org/html/rfc6979#appendix-A.1 - def _do(self, generator, secexp, hsh, hash_func, expected): - actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) - self.assertEqual(expected, actual) - - def test_SECP256k1(self): - """RFC doesn't contain test vectors for SECP256k1 used in bitcoin. - This vector has been computed by Golang reference implementation instead.""" - self._do( - generator=SECP256k1.generator, - secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), - hsh=hashlib.sha256(b"sample").digest(), - hash_func=hashlib.sha256, - expected=int( - "8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", - 16, - ), - ) - - def test_SECP256k1_2(self): - self._do( - generator=SECP256k1.generator, - secexp=int( - "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", - 16, - ), - hsh=hashlib.sha256(b"sample").digest(), - hash_func=hashlib.sha256, - expected=int( - "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", - 16, - ), - ) - - def test_SECP256k1_3(self): - self._do( - generator=SECP256k1.generator, - secexp=0x1, - hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(), - hash_func=hashlib.sha256, - expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, - ) - - def test_SECP256k1_4(self): - self._do( - generator=SECP256k1.generator, - secexp=0x1, - hsh=hashlib.sha256( - b"All those moments will be lost in time, like tears in rain. Time to die..." - ).digest(), - hash_func=hashlib.sha256, - expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, - ) - - def test_SECP256k1_5(self): - self._do( - generator=SECP256k1.generator, - secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, - hsh=hashlib.sha256(b"Satoshi Nakamoto").digest(), - hash_func=hashlib.sha256, - expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, - ) - - def test_SECP256k1_6(self): - self._do( - generator=SECP256k1.generator, - secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181, - hsh=hashlib.sha256(b"Alan Turing").digest(), - hash_func=hashlib.sha256, - expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, - ) - - def test_1(self): - # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 - self._do( - generator=Point( - None, - 0, - 0, - int("4000000000000000000020108A2E0CC0D99F8A5EF", 16), - ), - secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), - hsh=unhexlify( - b( - "AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF" - ) - ), - hash_func=hashlib.sha256, - expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16), - ) - - def test_2(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha1(b"sample").digest(), - hash_func=hashlib.sha1, - expected=int( - "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16 - ), - ) - - def test_3(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha256(b"sample").digest(), - hash_func=hashlib.sha256, - expected=int( - "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16 - ), - ) - - def test_4(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha512(b"sample").digest(), - hash_func=hashlib.sha512, - expected=int( - "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16 - ), - ) - - def test_5(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha1(b"test").digest(), - hash_func=hashlib.sha1, - expected=int( - "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16 - ), - ) - - def test_6(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha256(b"test").digest(), - hash_func=hashlib.sha256, - expected=int( - "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16 - ), - ) - - def test_7(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=hashlib.sha512(b"test").digest(), - hash_func=hashlib.sha512, - expected=int( - "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16 - ), - ) - - def test_8(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=hashlib.sha1(b"sample").digest(), - hash_func=hashlib.sha1, - expected=int( - "089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", - 16, - ), - ) - - def test_9(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=hashlib.sha256(b"sample").digest(), - hash_func=hashlib.sha256, - expected=int( - "0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", - 16, - ), - ) - - def test_10(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=hashlib.sha512(b"test").digest(), - hash_func=hashlib.sha512, - expected=int( - "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", - 16, - ), - ) - - -class ECDH(unittest.TestCase): - def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): - qA = dA * generator - qB = dB * generator - Z = dA * qB - self.assertEqual(Point(curve, x_qA, y_qA), qA) - self.assertEqual(Point(curve, x_qB, y_qB), qB) - self.assertTrue( - (dA * qB) - == (dA * dB * generator) - == (dB * dA * generator) - == (dB * qA) - ) - self.assertEqual(Point(curve, x_Z, y_Z), Z) - - -class RFC6932(ECDH): - # https://tools.ietf.org/html/rfc6932#appendix-A.1 - - def test_brainpoolP224r1(self): - self._do( - curve=curve_brainpoolp224r1, - generator=BRAINPOOLP224r1.generator, - dA=int( - "7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16 - ), - x_qA=int( - "B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16 - ), - y_qA=int( - "46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16 - ), - dB=int( - "63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16 - ), - x_qB=int( - "2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16 - ), - y_qB=int( - "9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16 - ), - x_Z=int( - "312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16 - ), - y_Z=int( - "6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16 - ), - ) - - def test_brainpoolP256r1(self): - self._do( - curve=curve_brainpoolp256r1, - generator=BRAINPOOLP256r1.generator, - dA=int( - "041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" - "49699", - 16, - ), - x_qA=int( - "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" - "CC0D206", - 16, - ), - y_qA=int( - "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" - "B05F92B", - 16, - ), - dB=int( - "06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" - "804D3", - 16, - ), - x_qB=int( - "8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" - "341F0DB", - 16, - ), - y_qB=int( - "148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" - "4FDE40D", - 16, - ), - x_Z=int( - "05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" - "E23708", - 16, - ), - y_Z=int( - "6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" - "FD134C", - 16, - ), - ) - - @pytest.mark.slow - def test_brainpoolP384r1(self): - self._do( - curve=curve_brainpoolp384r1, - generator=BRAINPOOLP384r1.generator, - dA=int( - "014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" - "828D97E095884CA72B73FDABD5910DF0FA76A", - 16, - ), - x_qA=int( - "45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" - "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", - 16, - ), - y_qA=int( - "8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" - "2DC530A2CD89C859BB4B4B768497F49AB8CC859", - 16, - ), - dB=int( - "6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" - "5C90095B976F3529957506E1224A861711D54", - 16, - ), - x_qB=int( - "01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" - "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", - 16, - ), - y_qB=int( - "3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" - "8EE47487ED8044858D31D848F7A95C635A347AC", - 16, - ), - x_Z=int( - "04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" - "2F370A1F98D3FA73FD0C0747C632E12F1423EC", - 16, - ), - y_Z=int( - "7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" - "5D05085F5A01A9382D05BF72D96698FE3FF64E", - 16, - ), - ) - - @pytest.mark.slow - def test_brainpoolP512r1(self): - self._do( - curve=curve_brainpoolp512r1, - generator=BRAINPOOLP512r1.generator, - dA=int( - "636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" - "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" - "4CCE6FE333", - 16, - ), - x_qA=int( - "0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" - "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" - "AFC3EF0971CCDA", - 16, - ), - y_qA=int( - "A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" - "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" - "8EFC7343EA642E", - 16, - ), - dB=int( - "0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" - "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" - "99B86E5C8B", - 16, - ), - x_qB=int( - "5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" - "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" - "E739E11720D323", - 16, - ), - y_qB=int( - "96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" - "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" - "54EE1BA35E04BE", - 16, - ), - x_Z=int( - "1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" - "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" - "10E5811B191E", - 16, - ), - y_Z=int( - "2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" - "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" - "4F9FE9EAABA6", - 16, - ), - ) - - -class RFC7027(ECDH): - # https://tools.ietf.org/html/rfc7027#appendix-A - - def test_brainpoolP256r1(self): - self._do( - curve=curve_brainpoolp256r1, - generator=BRAINPOOLP256r1.generator, - dA=int( - "81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" - "04F1D", - 16, - ), - x_qA=int( - "44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" - "3100BE5", - 16, - ), - y_qA=int( - "8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" - "B089BDC", - 16, - ), - dB=int( - "55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" - "76BD3", - 16, - ), - x_qB=int( - "8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" - "1B39F7B", - 16, - ), - y_qB=int( - "990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" - "47CEC6A", - 16, - ), - x_Z=int( - "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" - "18BF2B", - 16, - ), - y_Z=int( - "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" - "2504DE", - 16, - ), - ) - - @pytest.mark.slow - def test_brainpoolP384r1(self): - self._do( - curve=curve_brainpoolp384r1, - generator=BRAINPOOLP384r1.generator, - dA=int( - "1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" - "D65D6F15EB5D1EE1610DF870795143627D042", - 16, - ), - x_qA=int( - "68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" - "588F885AB698C852D4A6E77A252D6380FCAF068", - 16, - ), - y_qA=int( - "55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" - "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", - 16, - ), - dB=int( - "032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" - "4E01F8BA5E0324309DB6A9831497ABAC96670", - 16, - ), - x_qB=int( - "4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" - "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", - 16, - ), - y_qB=int( - "62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" - "9185329B5B275903D192F8D4E1F32FE9CC78C48", - 16, - ), - x_Z=int( - "0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" - "E239BBADF6403715C35D4FB2A5444F575D4F42", - 16, - ), - y_Z=int( - "0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" - "E9E598157290F8756066975F1DB34B2324B7BD", - 16, - ), - ) - - @pytest.mark.slow - def test_brainpoolP512r1(self): - self._do( - curve=curve_brainpoolp512r1, - generator=BRAINPOOLP512r1.generator, - dA=int( - "16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" - "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" - "AD57665422", - 16, - ), - x_qA=int( - "0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" - "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" - "36D11640FD09FD", - 16, - ), - y_qA=int( - "72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" - "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" - "E82A6AD147FDE7", - 16, - ), - dB=int( - "230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" - "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" - "3666B25429", - 16, - ), - x_qB=int( - "9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" - "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" - "EDA55A5473199F", - 16, - ), - y_qB=int( - "2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" - "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" - "512B71876285FA", - 16, - ), - x_Z=int( - "A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" - "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" - "454B21C4CD1F", - 16, - ), - y_Z=int( - "7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" - "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" - "2BE6A26680A2", - 16, - ), - ) - - -# https://tools.ietf.org/html/rfc4754#page-5 -@pytest.mark.parametrize( - "w, gwx, gwy, k, msg, md, r, s, curve", - [ - pytest.param( - "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", - "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", - "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", - "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", - b"abc", - hashlib.sha256, - "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", - "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", - NIST256p, - id="ECDSA-256", - ), - pytest.param( - "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" - "62E528C38B2A81B35309668D73524D9F", - "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" - "5CE10C9DFEC21797415710721F437922", - "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" - "3D383B91C5E7EDAA2B714CC99D5743CA", - "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" - "854D7FA992F934D927376285E63414FA", - b"abc", - hashlib.sha384, - "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" - "08E07C9C63F2D21A07DCB56A6AF56EB3", - "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" - "CBB9F516CE0FA7D2FF630863A00E8B9F", - NIST384p, - id="ECDSA-384", - ), - pytest.param( - "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" - "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" - "5FA1", - "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" - "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" - "52A8", - "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" - "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" - "E643", - "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" - "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" - "6C2F", - b"abc", - hashlib.sha512, - "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" - "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" - "2251", - "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" - "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" - "2660", - NIST521p, - id="ECDSA-521", - ), - ], -) -def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): - sk = SigningKey.from_string(unhexlify(w), curve) - vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) - assert sk.verifying_key == vk - sig = sk.sign(msg, hashfunc=md, sigencode=sigencode_strings, k=int(k, 16)) - - assert sig == (unhexlify(r), unhexlify(s)) - - assert vk.verify(sig, msg, md, sigdecode_strings) diff --git a/.venv/Lib/site-packages/ecdsa/test_rw_lock.py b/.venv/Lib/site-packages/ecdsa/test_rw_lock.py deleted file mode 100644 index 0a84b9c..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_rw_lock.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright Mateusz Kobos, (c) 2011 -# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ -# released under the MIT licence - -try: - import unittest2 as unittest -except ImportError: - import unittest -import threading -import time -import copy -from ._rwlock import RWLock - - -class Writer(threading.Thread): - def __init__( - self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write - ): - """ - @param buffer_: common buffer_ shared by the readers and writers - @type buffer_: list - @type rw_lock: L{RWLock} - @param init_sleep_time: sleep time before doing any action - @type init_sleep_time: C{float} - @param sleep_time: sleep time while in critical section - @type sleep_time: C{float} - @param to_write: data that will be appended to the buffer - """ - threading.Thread.__init__(self) - self.__buffer = buffer_ - self.__rw_lock = rw_lock - self.__init_sleep_time = init_sleep_time - self.__sleep_time = sleep_time - self.__to_write = to_write - self.entry_time = None - """Time of entry to the critical section""" - self.exit_time = None - """Time of exit from the critical section""" - - def run(self): - time.sleep(self.__init_sleep_time) - self.__rw_lock.writer_acquire() - self.entry_time = time.time() - time.sleep(self.__sleep_time) - self.__buffer.append(self.__to_write) - self.exit_time = time.time() - self.__rw_lock.writer_release() - - -class Reader(threading.Thread): - def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time): - """ - @param buffer_: common buffer shared by the readers and writers - @type buffer_: list - @type rw_lock: L{RWLock} - @param init_sleep_time: sleep time before doing any action - @type init_sleep_time: C{float} - @param sleep_time: sleep time while in critical section - @type sleep_time: C{float} - """ - threading.Thread.__init__(self) - self.__buffer = buffer_ - self.__rw_lock = rw_lock - self.__init_sleep_time = init_sleep_time - self.__sleep_time = sleep_time - self.buffer_read = None - """a copy of a the buffer read while in critical section""" - self.entry_time = None - """Time of entry to the critical section""" - self.exit_time = None - """Time of exit from the critical section""" - - def run(self): - time.sleep(self.__init_sleep_time) - self.__rw_lock.reader_acquire() - self.entry_time = time.time() - time.sleep(self.__sleep_time) - self.buffer_read = copy.deepcopy(self.__buffer) - self.exit_time = time.time() - self.__rw_lock.reader_release() - - -class RWLockTestCase(unittest.TestCase): - def test_readers_nonexclusive_access(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Reader(buffer_, rw_lock, 0, 0)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0.3)) - threads.append(Reader(buffer_, rw_lock, 0.5, 0)) - - self.__start_and_join_threads(threads) - - ## The third reader should enter after the second one but it should - ## exit before the second one exits - ## (i.e. the readers should be in the critical section - ## at the same time) - - self.assertEqual([], threads[0].buffer_read) - self.assertEqual([1], threads[2].buffer_read) - self.assertEqual([1], threads[3].buffer_read) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[2].entry_time <= threads[3].entry_time) - self.assertTrue(threads[3].exit_time < threads[2].exit_time) - - def test_writers_exclusive_access(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1)) - threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2)) - threads.append(Reader(buffer_, rw_lock, 0.2, 0)) - - self.__start_and_join_threads(threads) - - ## The second writer should wait for the first one to exit - - self.assertEqual([1, 2], threads[2].buffer_read) - self.assertTrue(threads[0].exit_time <= threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].exit_time) - - def test_writer_priority(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) - threads.append(Reader(buffer_, rw_lock, 0.1, 0.4)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - - self.__start_and_join_threads(threads) - - ## The second writer should go before the second and the third reader - - self.assertEqual([1], threads[1].buffer_read) - self.assertEqual([1, 2], threads[3].buffer_read) - self.assertEqual([1, 2], threads[4].buffer_read) - self.assertTrue(threads[0].exit_time < threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[2].exit_time <= threads[3].entry_time) - self.assertTrue(threads[2].exit_time <= threads[4].entry_time) - - def test_many_writers_priority(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) - threads.append(Reader(buffer_, rw_lock, 0.1, 0.6)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - threads.append(Reader(buffer_, rw_lock, 0.4, 0)) - threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3)) - - self.__start_and_join_threads(threads) - - ## The two last writers should go first -- after the first reader and - ## before the second and the third reader - - self.assertEqual([1], threads[1].buffer_read) - self.assertEqual([1, 2, 3], threads[3].buffer_read) - self.assertEqual([1, 2, 3], threads[4].buffer_read) - self.assertTrue(threads[0].exit_time < threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[1].exit_time <= threads[5].entry_time) - self.assertTrue(threads[2].exit_time <= threads[3].entry_time) - self.assertTrue(threads[2].exit_time <= threads[4].entry_time) - self.assertTrue(threads[5].exit_time <= threads[3].entry_time) - self.assertTrue(threads[5].exit_time <= threads[4].entry_time) - - @staticmethod - def __init_variables(): - buffer_ = [] - rw_lock = RWLock() - threads = [] - return (buffer_, rw_lock, threads) - - @staticmethod - def __start_and_join_threads(threads): - for t in threads: - t.start() - for t in threads: - t.join() diff --git a/.venv/Lib/site-packages/ecdsa/test_sha3.py b/.venv/Lib/site-packages/ecdsa/test_sha3.py deleted file mode 100644 index d30381d..0000000 --- a/.venv/Lib/site-packages/ecdsa/test_sha3.py +++ /dev/null @@ -1,111 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest -import pytest - -try: - from gmpy2 import mpz - - GMPY = True -except ImportError: # pragma: no cover - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - -from ._sha3 import shake_256 -from ._compat import bytes_to_int, int_to_bytes - -B2I_VECTORS = [ - (b"\x00\x01", "big", 1), - (b"\x00\x01", "little", 0x0100), - (b"", "big", 0), - (b"\x00", "little", 0), -] - - -@pytest.mark.parametrize("bytes_in,endian,int_out", B2I_VECTORS) -def test_bytes_to_int(bytes_in, endian, int_out): - out = bytes_to_int(bytes_in, endian) - assert out == int_out - - -class TestBytesToInt(unittest.TestCase): - def test_bytes_to_int_wrong_endian(self): - with self.assertRaises(ValueError): - bytes_to_int(b"\x00", "middle") - - def test_int_to_bytes_wrong_endian(self): - with self.assertRaises(ValueError): - int_to_bytes(0, byteorder="middle") - - -@pytest.mark.skipif(GMPY == False, reason="requires gmpy or gmpy2") -def test_int_to_bytes_with_gmpy(): - assert int_to_bytes(mpz(1)) == b"\x01" - - -I2B_VECTORS = [ - (0, None, "big", b""), - (0, 1, "big", b"\x00"), - (1, None, "big", b"\x01"), - (0x0100, None, "little", b"\x00\x01"), - (0x0100, 4, "little", b"\x00\x01\x00\x00"), - (1, 4, "big", b"\x00\x00\x00\x01"), -] - - -@pytest.mark.parametrize("int_in,length,endian,bytes_out", I2B_VECTORS) -def test_int_to_bytes(int_in, length, endian, bytes_out): - out = int_to_bytes(int_in, length, endian) - assert out == bytes_out - - -SHAKE_256_VECTORS = [ - ( - b"Message.", - 32, - b"\x78\xa1\x37\xbb\x33\xae\xe2\x72\xb1\x02\x4f\x39\x43\xe5\xcf\x0c" - b"\x4e\x9c\x72\x76\x2e\x34\x4c\xf8\xf9\xc3\x25\x9d\x4f\x91\x2c\x3a", - ), - ( - b"", - 32, - b"\x46\xb9\xdd\x2b\x0b\xa8\x8d\x13\x23\x3b\x3f\xeb\x74\x3e\xeb\x24" - b"\x3f\xcd\x52\xea\x62\xb8\x1b\x82\xb5\x0c\x27\x64\x6e\xd5\x76\x2f", - ), - ( - b"message", - 32, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" - b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75", - ), - ( - b"message", - 16, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51", - ), - ( - b"message", - 64, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" - b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75" - b"\x30\xd6\xba\x2a\x46\x65\xf1\x9d\xf0\x62\x25\xb1\x26\xd1\x3e\xed" - b"\x91\xd5\x0d\xe7\xb9\xcb\x65\xf3\x3a\x46\xae\xd3\x6c\x7d\xc5\xe8", - ), - ( - b"A" * 1024, - 32, - b"\xa5\xef\x7e\x30\x8b\xe8\x33\x64\xe5\x9c\xf3\xb5\xf3\xba\x20\xa3" - b"\x5a\xe7\x30\xfd\xbc\x33\x11\xbf\x83\x89\x50\x82\xb4\x41\xe9\xb3", - ), -] - - -@pytest.mark.parametrize("msg,olen,ohash", SHAKE_256_VECTORS) -def test_shake_256(msg, olen, ohash): - out = shake_256(msg, olen) - assert out == bytearray(ohash) diff --git a/.venv/Lib/site-packages/ecdsa/util.py b/.venv/Lib/site-packages/ecdsa/util.py deleted file mode 100644 index 639bc0c..0000000 --- a/.venv/Lib/site-packages/ecdsa/util.py +++ /dev/null @@ -1,519 +0,0 @@ -""" -This module includes some utility functions. - -The methods most typically used are the sigencode and sigdecode functions -to be used with :func:`~ecdsa.keys.SigningKey.sign` and -:func:`~ecdsa.keys.VerifyingKey.verify` -respectively. See the :func:`sigencode_strings`, :func:`sigdecode_string`, -:func:`sigencode_der`, :func:`sigencode_strings_canonize`, -:func:`sigencode_string_canonize`, :func:`sigencode_der_canonize`, -:func:`sigdecode_strings`, :func:`sigdecode_string`, and -:func:`sigdecode_der` functions. -""" - -from __future__ import division - -import os -import math -import binascii -import sys -from hashlib import sha256 -from six import PY2, int2byte, next -from . import der -from ._compat import normalise_bytes - - -# RFC5480: -# The "unrestricted" algorithm identifier is: -# id-ecPublicKey OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } - -oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) -encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) - -# RFC5480: -# The ECDH algorithm uses the following object identifier: -# id-ecDH OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) schemes(1) -# ecdh(12) } - -oid_ecDH = (1, 3, 132, 1, 12) - -# RFC5480: -# The ECMQV algorithm uses the following object identifier: -# id-ecMQV OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) schemes(1) -# ecmqv(13) } - -oid_ecMQV = (1, 3, 132, 1, 13) - -if sys.version_info >= (3,): # pragma: no branch - - def entropy_to_bits(ent_256): - """Convert a bytestring to string of 0's and 1's""" - return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) - -else: - - def entropy_to_bits(ent_256): - """Convert a bytestring to string of 0's and 1's""" - return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) - - -if sys.version_info < (2, 7): # pragma: no branch - # Can't add a method to a built-in type so we are stuck with this - def bit_length(x): - return len(bin(x)) - 2 - -else: - - def bit_length(x): - return x.bit_length() or 1 - - -def orderlen(order): - return (1 + len("%x" % order)) // 2 # bytes - - -def randrange(order, entropy=None): - """Return a random integer k such that 1 <= k < order, uniformly - distributed across that range. Worst case should be a mean of 2 loops at - (2**k)+2. - - Note that this function is not declared to be forwards-compatible: we may - change the behavior in future releases. The entropy= argument (which - should get a callable that behaves like os.urandom) can be used to - achieve stability within a given release (for repeatable unit tests), but - should not be used as a long-term-compatible key generation algorithm. - """ - assert order > 1 - if entropy is None: - entropy = os.urandom - upper_2 = bit_length(order - 2) - upper_256 = upper_2 // 8 + 1 - while True: # I don't think this needs a counter with bit-wise randrange - ent_256 = entropy(upper_256) - ent_2 = entropy_to_bits(ent_256) - rand_num = int(ent_2[:upper_2], base=2) + 1 - if 0 < rand_num < order: - return rand_num - - -class PRNG: - # this returns a callable which, when invoked with an integer N, will - # return N pseudorandom bytes. Note: this is a short-term PRNG, meant - # primarily for the needs of randrange_from_seed__trytryagain(), which - # only needs to run it a few times per seed. It does not provide - # protection against state compromise (forward security). - def __init__(self, seed): - self.generator = self.block_generator(seed) - - def __call__(self, numbytes): - a = [next(self.generator) for i in range(numbytes)] - - if PY2: # pragma: no branch - return "".join(a) - else: - return bytes(a) - - def block_generator(self, seed): - counter = 0 - while True: - for byte in sha256( - ("prng-%d-%s" % (counter, seed)).encode() - ).digest(): - yield byte - counter += 1 - - -def randrange_from_seed__overshoot_modulo(seed, order): - # hash the data, then turn the digest into a number in [1,order). - # - # We use David-Sarah Hopwood's suggestion: turn it into a number that's - # sufficiently larger than the group order, then modulo it down to fit. - # This should give adequate (but not perfect) uniformity, and simple - # code. There are other choices: try-try-again is the main one. - base = PRNG(seed)(2 * orderlen(order)) - number = (int(binascii.hexlify(base), 16) % (order - 1)) + 1 - assert 1 <= number < order, (1, number, order) - return number - - -def lsb_of_ones(numbits): - return (1 << numbits) - 1 - - -def bits_and_bytes(order): - bits = int(math.log(order - 1, 2) + 1) - bytes = bits // 8 - extrabits = bits % 8 - return bits, bytes, extrabits - - -# the following randrange_from_seed__METHOD() functions take an -# arbitrarily-sized secret seed and turn it into a number that obeys the same -# range limits as randrange() above. They are meant for deriving consistent -# signing keys from a secret rather than generating them randomly, for -# example a protocol in which three signing keys are derived from a master -# secret. You should use a uniformly-distributed unguessable seed with about -# curve.baselen bytes of entropy. To use one, do this: -# seed = os.urandom(curve.baselen) # or other starting point -# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) -# sk = SigningKey.from_secret_exponent(secexp, curve) - - -def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): - # hash the seed, then turn the digest into a number in [1,order), but - # don't worry about trying to uniformly fill the range. This will lose, - # on average, four bits of entropy. - bits, _bytes, extrabits = bits_and_bytes(order) - if extrabits: - _bytes += 1 - base = hashmod(seed).digest()[:_bytes] - base = "\x00" * (_bytes - len(base)) + base - number = 1 + int(binascii.hexlify(base), 16) - assert 1 <= number < order - return number - - -def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): - # like string_to_randrange_truncate_bytes, but only lose an average of - # half a bit - bits = int(math.log(order - 1, 2) + 1) - maxbytes = (bits + 7) // 8 - base = hashmod(seed).digest()[:maxbytes] - base = "\x00" * (maxbytes - len(base)) + base - topbits = 8 * maxbytes - bits - if topbits: - base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] - number = 1 + int(binascii.hexlify(base), 16) - assert 1 <= number < order - return number - - -def randrange_from_seed__trytryagain(seed, order): - # figure out exactly how many bits we need (rounded up to the nearest - # bit), so we can reduce the chance of looping to less than 0.5 . This is - # specified to feed from a byte-oriented PRNG, and discards the - # high-order bits of the first byte as necessary to get the right number - # of bits. The average number of loops will range from 1.0 (when - # order=2**k-1) to 2.0 (when order=2**k+1). - assert order > 1 - bits, bytes, extrabits = bits_and_bytes(order) - generate = PRNG(seed) - while True: - extrabyte = b"" - if extrabits: - extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) - guess = string_to_number(extrabyte + generate(bytes)) + 1 - if 1 <= guess < order: - return guess - - -def number_to_string(num, order): - l = orderlen(order) - fmt_str = "%0" + str(2 * l) + "x" - string = binascii.unhexlify((fmt_str % num).encode()) - assert len(string) == l, (len(string), l) - return string - - -def number_to_string_crop(num, order): - l = orderlen(order) - fmt_str = "%0" + str(2 * l) + "x" - string = binascii.unhexlify((fmt_str % num).encode()) - return string[:l] - - -def string_to_number(string): - return int(binascii.hexlify(string), 16) - - -def string_to_number_fixedlen(string, order): - l = orderlen(order) - assert len(string) == l, (len(string), l) - return int(binascii.hexlify(string), 16) - - -def sigencode_strings(r, s, order): - """ - Encode the signature to a pair of strings in a tuple - - Encodes signature into raw encoding (:term:`raw encoding`) with the - ``r`` and ``s`` parts of the signature encoded separately. - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: raw encoding of ECDSA signature - :rtype: tuple(bytes, bytes) - """ - r_str = number_to_string(r, order) - s_str = number_to_string(s, order) - return (r_str, s_str) - - -def sigencode_string(r, s, order): - """ - Encode the signature to raw format (:term:`raw encoding`) - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: raw encoding of ECDSA signature - :rtype: bytes - """ - # for any given curve, the size of the signature numbers is - # fixed, so just use simple concatenation - r_str, s_str = sigencode_strings(r, s, order) - return r_str + s_str - - -def sigencode_der(r, s, order): - """ - Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. - - Encodes the signature to the following :term:`ASN.1` structure:: - - Ecdsa-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: DER encoding of ECDSA signature - :rtype: bytes - """ - return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) - - -def sigencode_strings_canonize(r, s, order): - """ - Encode the signature to a pair of strings in a tuple - - Encodes signature into raw encoding (:term:`raw encoding`) with the - ``r`` and ``s`` parts of the signature encoded separately. - - Makes sure that the signature is encoded in the canonical format, where - the ``s`` parameter is always smaller than ``order / 2``. - Most commonly used in bitcoin. - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: raw encoding of ECDSA signature - :rtype: tuple(bytes, bytes) - """ - if s > order / 2: - s = order - s - return sigencode_strings(r, s, order) - - -def sigencode_string_canonize(r, s, order): - """ - Encode the signature to raw format (:term:`raw encoding`) - - Makes sure that the signature is encoded in the canonical format, where - the ``s`` parameter is always smaller than ``order / 2``. - Most commonly used in bitcoin. - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: raw encoding of ECDSA signature - :rtype: bytes - """ - if s > order / 2: - s = order - s - return sigencode_string(r, s, order) - - -def sigencode_der_canonize(r, s, order): - """ - Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. - - Makes sure that the signature is encoded in the canonical format, where - the ``s`` parameter is always smaller than ``order / 2``. - Most commonly used in bitcoin. - - Encodes the signature to the following :term:`ASN.1` structure:: - - Ecdsa-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - - It's expected that this function will be used as a ``sigencode=`` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: DER encoding of ECDSA signature - :rtype: bytes - """ - if s > order / 2: - s = order - s - return sigencode_der(r, s, order) - - -class MalformedSignature(Exception): - """ - Raised by decoding functions when the signature is malformed. - - Malformed in this context means that the relevant strings or integers - do not match what a signature over provided curve would create. Either - because the byte strings have incorrect lengths or because the encoded - values are too large. - """ - - pass - - -def sigdecode_string(signature, order): - """ - Decoder for :term:`raw encoding` of ECDSA signatures. - - raw encoding is a simple concatenation of the two integers that comprise - the signature, with each encoded using the same amount of bytes depending - on curve size/order. - - It's expected that this function will be used as the ``sigdecode=`` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param signature: encoded signature - :type signature: bytes like object - :param order: order of the curve over which the signature was computed - :type order: int - - :raises MalformedSignature: when the encoding of the signature is invalid - - :return: tuple with decoded ``r`` and ``s`` values of signature - :rtype: tuple of ints - """ - signature = normalise_bytes(signature) - l = orderlen(order) - if not len(signature) == 2 * l: - raise MalformedSignature( - "Invalid length of signature, expected {0} bytes long, " - "provided string is {1} bytes long".format(2 * l, len(signature)) - ) - r = string_to_number_fixedlen(signature[:l], order) - s = string_to_number_fixedlen(signature[l:], order) - return r, s - - -def sigdecode_strings(rs_strings, order): - """ - Decode the signature from two strings. - - First string needs to be a big endian encoding of ``r``, second needs to - be a big endian encoding of the ``s`` parameter of an ECDSA signature. - - It's expected that this function will be used as the ``sigdecode=`` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param list rs_strings: list of two bytes-like objects, each encoding one - parameter of signature - :param int order: order of the curve over which the signature was computed - - :raises MalformedSignature: when the encoding of the signature is invalid - - :return: tuple with decoded ``r`` and ``s`` values of signature - :rtype: tuple of ints - """ - if not len(rs_strings) == 2: - raise MalformedSignature( - "Invalid number of strings provided: {0}, expected 2".format( - len(rs_strings) - ) - ) - (r_str, s_str) = rs_strings - r_str = normalise_bytes(r_str) - s_str = normalise_bytes(s_str) - l = orderlen(order) - if not len(r_str) == l: - raise MalformedSignature( - "Invalid length of first string ('r' parameter), " - "expected {0} bytes long, provided string is {1} " - "bytes long".format(l, len(r_str)) - ) - if not len(s_str) == l: - raise MalformedSignature( - "Invalid length of second string ('s' parameter), " - "expected {0} bytes long, provided string is {1} " - "bytes long".format(l, len(s_str)) - ) - r = string_to_number_fixedlen(r_str, order) - s = string_to_number_fixedlen(s_str, order) - return r, s - - -def sigdecode_der(sig_der, order): - """ - Decoder for DER format of ECDSA signatures. - - DER format of signature is one that uses the :term:`ASN.1` :term:`DER` - rules to encode it as a sequence of two integers:: - - Ecdsa-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - - It's expected that this function will be used as as the ``sigdecode=`` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param sig_der: encoded signature - :type sig_der: bytes like object - :param order: order of the curve over which the signature was computed - :type order: int - - :raises UnexpectedDER: when the encoding of signature is invalid - - :return: tuple with decoded ``r`` and ``s`` values of signature - :rtype: tuple of ints - """ - sig_der = normalise_bytes(sig_der) - # return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) - rs_strings, empty = der.remove_sequence(sig_der) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER sig: %s" % binascii.hexlify(empty) - ) - r, rest = der.remove_integer(rs_strings) - s, empty = der.remove_integer(rest) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER numbers: %s" % binascii.hexlify(empty) - ) - return r, s diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/INSTALLER b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/METADATA b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/METADATA deleted file mode 100644 index e94ba78..0000000 --- a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/METADATA +++ /dev/null @@ -1,533 +0,0 @@ -Metadata-Version: 2.1 -Name: fastapi -Version: 0.110.0 -Summary: FastAPI framework, high performance, easy to learn, fast to code, ready for production -Project-URL: Homepage, https://github.com/tiangolo/fastapi -Project-URL: Documentation, https://fastapi.tiangolo.com/ -Project-URL: Repository, https://github.com/tiangolo/fastapi -Author-email: Sebastián Ramírez -License-Expression: MIT -License-File: LICENSE -Classifier: Development Status :: 4 - Beta -Classifier: Environment :: Web Environment -Classifier: Framework :: AsyncIO -Classifier: Framework :: FastAPI -Classifier: Framework :: Pydantic -Classifier: Framework :: Pydantic :: 1 -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Information Technology -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Internet -Classifier: Topic :: Internet :: WWW/HTTP -Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers -Classifier: Topic :: Software Development -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Typing :: Typed -Requires-Python: >=3.8 -Requires-Dist: pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4 -Requires-Dist: starlette<0.37.0,>=0.36.3 -Requires-Dist: typing-extensions>=4.8.0 -Provides-Extra: all -Requires-Dist: email-validator>=2.0.0; extra == 'all' -Requires-Dist: httpx>=0.23.0; extra == 'all' -Requires-Dist: itsdangerous>=1.1.0; extra == 'all' -Requires-Dist: jinja2>=2.11.2; extra == 'all' -Requires-Dist: orjson>=3.2.1; extra == 'all' -Requires-Dist: pydantic-extra-types>=2.0.0; extra == 'all' -Requires-Dist: pydantic-settings>=2.0.0; extra == 'all' -Requires-Dist: python-multipart>=0.0.7; extra == 'all' -Requires-Dist: pyyaml>=5.3.1; extra == 'all' -Requires-Dist: ujson!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,>=4.0.1; extra == 'all' -Requires-Dist: uvicorn[standard]>=0.12.0; extra == 'all' -Description-Content-Type: text/markdown - -

- FastAPI -

-

- FastAPI framework, high performance, easy to learn, fast to code, ready for production -

-

- - Test - - - Coverage - - - Package version - - - Supported Python versions - -

- ---- - -**Documentation**: https://fastapi.tiangolo.com - -**Source Code**: https://github.com/tiangolo/fastapi - ---- - -FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.8+ based on standard Python type hints. - -The key features are: - -* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance). -* **Fast to code**: Increase the speed to develop features by about 200% to 300%. * -* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. * -* **Intuitive**: Great editor support. Completion everywhere. Less time debugging. -* **Easy**: Designed to be easy to use and learn. Less time reading docs. -* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs. -* **Robust**: Get production-ready code. With automatic interactive documentation. -* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: OpenAPI (previously known as Swagger) and JSON Schema. - -* estimation based on tests on an internal development team, building production applications. - -## Sponsors - - - - - - - - - - - - - - - - - - - - - - -Other sponsors - -## Opinions - -"_[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products._" - -
Kabir Khan - Microsoft (ref)
- ---- - -"_We adopted the **FastAPI** library to spawn a **REST** server that can be queried to obtain **predictions**. [for Ludwig]_" - -
Piero Molino, Yaroslav Dudin, and Sai Sumanth Miryala - Uber (ref)
- ---- - -"_**Netflix** is pleased to announce the open-source release of our **crisis management** orchestration framework: **Dispatch**! [built with **FastAPI**]_" - -
Kevin Glisson, Marc Vilanova, Forest Monsen - Netflix (ref)
- ---- - -"_I’m over the moon excited about **FastAPI**. It’s so fun!_" - -
Brian Okken - Python Bytes podcast host (ref)
- ---- - -"_Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that._" - -
Timothy Crosley - Hug creator (ref)
- ---- - -"_If you're looking to learn one **modern framework** for building REST APIs, check out **FastAPI** [...] It's fast, easy to use and easy to learn [...]_" - -"_We've switched over to **FastAPI** for our **APIs** [...] I think you'll like it [...]_" - -
Ines Montani - Matthew Honnibal - Explosion AI founders - spaCy creators (ref) - (ref)
- ---- - -"_If anyone is looking to build a production Python API, I would highly recommend **FastAPI**. It is **beautifully designed**, **simple to use** and **highly scalable**, it has become a **key component** in our API first development strategy and is driving many automations and services such as our Virtual TAC Engineer._" - -
Deon Pillsbury - Cisco (ref)
- ---- - -## **Typer**, the FastAPI of CLIs - - - -If you are building a CLI app to be used in the terminal instead of a web API, check out **Typer**. - -**Typer** is FastAPI's little sibling. And it's intended to be the **FastAPI of CLIs**. ⌨️ 🚀 - -## Requirements - -Python 3.8+ - -FastAPI stands on the shoulders of giants: - -* Starlette for the web parts. -* Pydantic for the data parts. - -## Installation - -
- -```console -$ pip install fastapi - ----> 100% -``` - -
- -You will also need an ASGI server, for production such as Uvicorn or Hypercorn. - -
- -```console -$ pip install "uvicorn[standard]" - ----> 100% -``` - -
- -## Example - -### Create it - -* Create a file `main.py` with: - -```Python -from typing import Union - -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int, q: Union[str, None] = None): - return {"item_id": item_id, "q": q} -``` - -
-Or use async def... - -If your code uses `async` / `await`, use `async def`: - -```Python hl_lines="9 14" -from typing import Union - -from fastapi import FastAPI - -app = FastAPI() - - -@app.get("/") -async def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -async def read_item(item_id: int, q: Union[str, None] = None): - return {"item_id": item_id, "q": q} -``` - -**Note**: - -If you don't know, check the _"In a hurry?"_ section about `async` and `await` in the docs. - -
- -### Run it - -Run the server with: - -
- -```console -$ uvicorn main:app --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -INFO: Started reloader process [28720] -INFO: Started server process [28722] -INFO: Waiting for application startup. -INFO: Application startup complete. -``` - -
- -
-About the command uvicorn main:app --reload... - -The command `uvicorn main:app` refers to: - -* `main`: the file `main.py` (the Python "module"). -* `app`: the object created inside of `main.py` with the line `app = FastAPI()`. -* `--reload`: make the server restart after code changes. Only do this for development. - -
- -### Check it - -Open your browser at http://127.0.0.1:8000/items/5?q=somequery. - -You will see the JSON response as: - -```JSON -{"item_id": 5, "q": "somequery"} -``` - -You already created an API that: - -* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`. -* Both _paths_ take `GET` operations (also known as HTTP _methods_). -* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`. -* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`. - -### Interactive API docs - -Now go to http://127.0.0.1:8000/docs. - -You will see the automatic interactive API documentation (provided by Swagger UI): - -![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) - -### Alternative API docs - -And now, go to http://127.0.0.1:8000/redoc. - -You will see the alternative automatic documentation (provided by ReDoc): - -![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) - -## Example upgrade - -Now modify the file `main.py` to receive a body from a `PUT` request. - -Declare the body using standard Python types, thanks to Pydantic. - -```Python hl_lines="4 9-12 25-27" -from typing import Union - -from fastapi import FastAPI -from pydantic import BaseModel - -app = FastAPI() - - -class Item(BaseModel): - name: str - price: float - is_offer: Union[bool, None] = None - - -@app.get("/") -def read_root(): - return {"Hello": "World"} - - -@app.get("/items/{item_id}") -def read_item(item_id: int, q: Union[str, None] = None): - return {"item_id": item_id, "q": q} - - -@app.put("/items/{item_id}") -def update_item(item_id: int, item: Item): - return {"item_name": item.name, "item_id": item_id} -``` - -The server should reload automatically (because you added `--reload` to the `uvicorn` command above). - -### Interactive API docs upgrade - -Now go to http://127.0.0.1:8000/docs. - -* The interactive API documentation will be automatically updated, including the new body: - -![Swagger UI](https://fastapi.tiangolo.com/img/index/index-03-swagger-02.png) - -* Click on the button "Try it out", it allows you to fill the parameters and directly interact with the API: - -![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-04-swagger-03.png) - -* Then click on the "Execute" button, the user interface will communicate with your API, send the parameters, get the results and show them on the screen: - -![Swagger UI interaction](https://fastapi.tiangolo.com/img/index/index-05-swagger-04.png) - -### Alternative API docs upgrade - -And now, go to http://127.0.0.1:8000/redoc. - -* The alternative documentation will also reflect the new query parameter and body: - -![ReDoc](https://fastapi.tiangolo.com/img/index/index-06-redoc-02.png) - -### Recap - -In summary, you declare **once** the types of parameters, body, etc. as function parameters. - -You do that with standard modern Python types. - -You don't have to learn a new syntax, the methods or classes of a specific library, etc. - -Just standard **Python 3.8+**. - -For example, for an `int`: - -```Python -item_id: int -``` - -or for a more complex `Item` model: - -```Python -item: Item -``` - -...and with that single declaration you get: - -* Editor support, including: - * Completion. - * Type checks. -* Validation of data: - * Automatic and clear errors when the data is invalid. - * Validation even for deeply nested JSON objects. -* Conversion of input data: coming from the network to Python data and types. Reading from: - * JSON. - * Path parameters. - * Query parameters. - * Cookies. - * Headers. - * Forms. - * Files. -* Conversion of output data: converting from Python data and types to network data (as JSON): - * Convert Python types (`str`, `int`, `float`, `bool`, `list`, etc). - * `datetime` objects. - * `UUID` objects. - * Database models. - * ...and many more. -* Automatic interactive API documentation, including 2 alternative user interfaces: - * Swagger UI. - * ReDoc. - ---- - -Coming back to the previous code example, **FastAPI** will: - -* Validate that there is an `item_id` in the path for `GET` and `PUT` requests. -* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests. - * If it is not, the client will see a useful, clear error. -* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests. - * As the `q` parameter is declared with `= None`, it is optional. - * Without the `None` it would be required (as is the body in the case with `PUT`). -* For `PUT` requests to `/items/{item_id}`, Read the body as JSON: - * Check that it has a required attribute `name` that should be a `str`. - * Check that it has a required attribute `price` that has to be a `float`. - * Check that it has an optional attribute `is_offer`, that should be a `bool`, if present. - * All this would also work for deeply nested JSON objects. -* Convert from and to JSON automatically. -* Document everything with OpenAPI, that can be used by: - * Interactive documentation systems. - * Automatic client code generation systems, for many languages. -* Provide 2 interactive documentation web interfaces directly. - ---- - -We just scratched the surface, but you already get the idea of how it all works. - -Try changing the line with: - -```Python - return {"item_name": item.name, "item_id": item_id} -``` - -...from: - -```Python - ... "item_name": item.name ... -``` - -...to: - -```Python - ... "item_price": item.price ... -``` - -...and see how your editor will auto-complete the attributes and know their types: - -![editor support](https://fastapi.tiangolo.com/img/vscode-completion.png) - -For a more complete example including more features, see the Tutorial - User Guide. - -**Spoiler alert**: the tutorial - user guide includes: - -* Declaration of **parameters** from other different places as: **headers**, **cookies**, **form fields** and **files**. -* How to set **validation constraints** as `maximum_length` or `regex`. -* A very powerful and easy to use **Dependency Injection** system. -* Security and authentication, including support for **OAuth2** with **JWT tokens** and **HTTP Basic** auth. -* More advanced (but equally easy) techniques for declaring **deeply nested JSON models** (thanks to Pydantic). -* **GraphQL** integration with Strawberry and other libraries. -* Many extra features (thanks to Starlette) as: - * **WebSockets** - * extremely easy tests based on HTTPX and `pytest` - * **CORS** - * **Cookie Sessions** - * ...and more. - -## Performance - -Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as one of the fastest Python frameworks available, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*) - -To understand more about it, see the section Benchmarks. - -## Optional Dependencies - -Used by Pydantic: - -* email_validator - for email validation. -* pydantic-settings - for settings management. -* pydantic-extra-types - for extra types to be used with Pydantic. - -Used by Starlette: - -* httpx - Required if you want to use the `TestClient`. -* jinja2 - Required if you want to use the default template configuration. -* python-multipart - Required if you want to support form "parsing", with `request.form()`. -* itsdangerous - Required for `SessionMiddleware` support. -* pyyaml - Required for Starlette's `SchemaGenerator` support (you probably don't need it with FastAPI). -* ujson - Required if you want to use `UJSONResponse`. - -Used by FastAPI / Starlette: - -* uvicorn - for the server that loads and serves your application. -* orjson - Required if you want to use `ORJSONResponse`. - -You can install all of these with `pip install "fastapi[all]"`. - -## License - -This project is licensed under the terms of the MIT license. diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/RECORD b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/RECORD deleted file mode 100644 index d1e8d74..0000000 --- a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/RECORD +++ /dev/null @@ -1,91 +0,0 @@ -fastapi-0.110.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -fastapi-0.110.0.dist-info/METADATA,sha256=Ebx69fGhG8p3peZoASK8c9gbsL6qPaIz6JnxTG4vr8Q,25011 -fastapi-0.110.0.dist-info/RECORD,, -fastapi-0.110.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -fastapi-0.110.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87 -fastapi-0.110.0.dist-info/licenses/LICENSE,sha256=Tsif_IFIW5f-xYSy1KlhAy7v_oNEU4lP2cEnSQbMdE4,1086 -fastapi/__init__.py,sha256=PgUC06wUNNOnq94Ga5VTwCxOGLenuf49PqoERpG_R5k,1081 -fastapi/__pycache__/__init__.cpython-311.pyc,, -fastapi/__pycache__/_compat.cpython-311.pyc,, -fastapi/__pycache__/applications.cpython-311.pyc,, -fastapi/__pycache__/background.cpython-311.pyc,, -fastapi/__pycache__/concurrency.cpython-311.pyc,, -fastapi/__pycache__/datastructures.cpython-311.pyc,, -fastapi/__pycache__/encoders.cpython-311.pyc,, -fastapi/__pycache__/exception_handlers.cpython-311.pyc,, -fastapi/__pycache__/exceptions.cpython-311.pyc,, -fastapi/__pycache__/logger.cpython-311.pyc,, -fastapi/__pycache__/param_functions.cpython-311.pyc,, -fastapi/__pycache__/params.cpython-311.pyc,, -fastapi/__pycache__/requests.cpython-311.pyc,, -fastapi/__pycache__/responses.cpython-311.pyc,, -fastapi/__pycache__/routing.cpython-311.pyc,, -fastapi/__pycache__/staticfiles.cpython-311.pyc,, -fastapi/__pycache__/templating.cpython-311.pyc,, -fastapi/__pycache__/testclient.cpython-311.pyc,, -fastapi/__pycache__/types.cpython-311.pyc,, -fastapi/__pycache__/utils.cpython-311.pyc,, -fastapi/__pycache__/websockets.cpython-311.pyc,, -fastapi/_compat.py,sha256=i_LwkpFVbNCuraFRbc1UmNNocX63BAwjjBXgF59JJVQ,23027 -fastapi/applications.py,sha256=raHUDIrVDjV0Q40S-Zjzjdp_vPGKZmXHfY8TIC8gVSU,176373 -fastapi/background.py,sha256=F1tsrJKfDZaRchNgF9ykB2PcRaPBJTbL4htN45TJAIc,1799 -fastapi/concurrency.py,sha256=AYLnS4judDUmXsNRICtoKSP0prfYDcS8ehBtYW9JhQQ,1403 -fastapi/datastructures.py,sha256=FF1s2g6cAQ5XxlNToB3scgV94Zf3DjdzcaI7ToaTrmg,5797 -fastapi/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -fastapi/dependencies/__pycache__/__init__.cpython-311.pyc,, -fastapi/dependencies/__pycache__/models.cpython-311.pyc,, -fastapi/dependencies/__pycache__/utils.cpython-311.pyc,, -fastapi/dependencies/models.py,sha256=-n-YCxzgVBkurQi49qOTooT71v_oeAhHJ-qQFonxh5o,2494 -fastapi/dependencies/utils.py,sha256=IUY-uZmp56BUZuEiHtTQT0iGNF5ASIst7AzFMzA_As8,30158 -fastapi/encoders.py,sha256=90lbmIW8NZjpPVzbgKhpY49B7TFqa7hrdQDQa70SM9U,11024 -fastapi/exception_handlers.py,sha256=MBrIOA-ugjJDivIi4rSsUJBdTsjuzN76q4yh0q1COKw,1332 -fastapi/exceptions.py,sha256=SQsPxq-QYBZUhq6L4K3B3W7gaSD3Gub2f17erStRagY,5000 -fastapi/logger.py,sha256=I9NNi3ov8AcqbsbC9wl1X-hdItKgYt2XTrx1f99Zpl4,54 -fastapi/middleware/__init__.py,sha256=oQDxiFVcc1fYJUOIFvphnK7pTT5kktmfL32QXpBFvvo,58 -fastapi/middleware/__pycache__/__init__.cpython-311.pyc,, -fastapi/middleware/__pycache__/cors.cpython-311.pyc,, -fastapi/middleware/__pycache__/gzip.cpython-311.pyc,, -fastapi/middleware/__pycache__/httpsredirect.cpython-311.pyc,, -fastapi/middleware/__pycache__/trustedhost.cpython-311.pyc,, -fastapi/middleware/__pycache__/wsgi.cpython-311.pyc,, -fastapi/middleware/cors.py,sha256=ynwjWQZoc_vbhzZ3_ZXceoaSrslHFHPdoM52rXr0WUU,79 -fastapi/middleware/gzip.py,sha256=xM5PcsH8QlAimZw4VDvcmTnqQamslThsfe3CVN2voa0,79 -fastapi/middleware/httpsredirect.py,sha256=rL8eXMnmLijwVkH7_400zHri1AekfeBd6D6qs8ix950,115 -fastapi/middleware/trustedhost.py,sha256=eE5XGRxGa7c5zPnMJDGp3BxaL25k5iVQlhnv-Pk0Pss,109 -fastapi/middleware/wsgi.py,sha256=Z3Ue-7wni4lUZMvH3G9ek__acgYdJstbnpZX_HQAboY,79 -fastapi/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -fastapi/openapi/__pycache__/__init__.cpython-311.pyc,, -fastapi/openapi/__pycache__/constants.cpython-311.pyc,, -fastapi/openapi/__pycache__/docs.cpython-311.pyc,, -fastapi/openapi/__pycache__/models.cpython-311.pyc,, -fastapi/openapi/__pycache__/utils.cpython-311.pyc,, -fastapi/openapi/constants.py,sha256=adGzmis1L1HJRTE3kJ5fmHS_Noq6tIY6pWv_SFzoFDU,153 -fastapi/openapi/docs.py,sha256=Fo_SGB0eEfGvlNLqP-w_jgYifmHTe-3LbO_qC-ncFVY,10387 -fastapi/openapi/models.py,sha256=DEmsWA-9sNqv2H4YneZUW86r1nMwD920EiTvan5kndI,17763 -fastapi/openapi/utils.py,sha256=PUuz_ISarHVPBRyIgfyHz8uwH0eEsDY3rJUfW__I9GI,22303 -fastapi/param_functions.py,sha256=VWEsJbkH8lJZgcJ6fI6uzquui1kgHrDv1i_wXM7cW3M,63896 -fastapi/params.py,sha256=LzjihAvODd3w7-GddraUyVtH1xfwR9smIoQn-Z_g4mg,27807 -fastapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -fastapi/requests.py,sha256=zayepKFcienBllv3snmWI20Gk0oHNVLU4DDhqXBb4LU,142 -fastapi/responses.py,sha256=QNQQlwpKhQoIPZTTWkpc9d_QGeGZ_aVQPaDV3nQ8m7c,1761 -fastapi/routing.py,sha256=KCElIxujAST1KtnzRYtpCdETQTZsws3MhwbI05ZSEfY,174181 -fastapi/security/__init__.py,sha256=bO8pNmxqVRXUjfl2mOKiVZLn0FpBQ61VUYVjmppnbJw,881 -fastapi/security/__pycache__/__init__.cpython-311.pyc,, -fastapi/security/__pycache__/api_key.cpython-311.pyc,, -fastapi/security/__pycache__/base.cpython-311.pyc,, -fastapi/security/__pycache__/http.cpython-311.pyc,, -fastapi/security/__pycache__/oauth2.cpython-311.pyc,, -fastapi/security/__pycache__/open_id_connect_url.cpython-311.pyc,, -fastapi/security/__pycache__/utils.cpython-311.pyc,, -fastapi/security/api_key.py,sha256=bcZbUzTqeR_CI_LXuJdDq1qL322kmhgy5ApOCqgGDi4,9399 -fastapi/security/base.py,sha256=dl4pvbC-RxjfbWgPtCWd8MVU-7CB2SZ22rJDXVCXO6c,141 -fastapi/security/http.py,sha256=_YdhSRRUCGydVDUILygWg0VlkPA28t_gjcy_axD3eOk,13537 -fastapi/security/oauth2.py,sha256=WMkFMfgvOJdu5JT4RrdXS5TWMq6yD-vZXIHST3cHVng,21612 -fastapi/security/open_id_connect_url.py,sha256=Mb8wFxrRh4CrsFW0RcjBEQLASPHGDtZRP6c2dCrspAg,2753 -fastapi/security/utils.py,sha256=bd8T0YM7UQD5ATKucr1bNtAvz_Y3__dVNAv5UebiPvc,293 -fastapi/staticfiles.py,sha256=iirGIt3sdY2QZXd36ijs3Cj-T0FuGFda3cd90kM9Ikw,69 -fastapi/templating.py,sha256=4zsuTWgcjcEainMJFAlW6-gnslm6AgOS1SiiDWfmQxk,76 -fastapi/testclient.py,sha256=nBvaAmX66YldReJNZXPOk1sfuo2Q6hs8bOvIaCep6LQ,66 -fastapi/types.py,sha256=nFb36sK3DSoqoyo7Miwy3meKK5UdFBgkAgLSzQlUVyI,383 -fastapi/utils.py,sha256=LNep9q7ZeTQqK9-KvagV5CWn48KXwExnZGSpDsXT_Tg,8204 -fastapi/websockets.py,sha256=419uncYObEKZG0YcrXscfQQYLSWoE10jqxVMetGdR98,222 diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/REQUESTED b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/WHEEL b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/WHEEL deleted file mode 100644 index 5998f3a..0000000 --- a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.21.1 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/licenses/LICENSE b/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/licenses/LICENSE deleted file mode 100644 index 3e92463..0000000 --- a/.venv/Lib/site-packages/fastapi-0.110.0.dist-info/licenses/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2018 Sebastián Ramírez - -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/site-packages/fastapi/__init__.py b/.venv/Lib/site-packages/fastapi/__init__.py deleted file mode 100644 index 2349692..0000000 --- a/.venv/Lib/site-packages/fastapi/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -"""FastAPI framework, high performance, easy to learn, fast to code, ready for production""" - -__version__ = "0.110.0" - -from starlette import status as status - -from .applications import FastAPI as FastAPI -from .background import BackgroundTasks as BackgroundTasks -from .datastructures import UploadFile as UploadFile -from .exceptions import HTTPException as HTTPException -from .exceptions import WebSocketException as WebSocketException -from .param_functions import Body as Body -from .param_functions import Cookie as Cookie -from .param_functions import Depends as Depends -from .param_functions import File as File -from .param_functions import Form as Form -from .param_functions import Header as Header -from .param_functions import Path as Path -from .param_functions import Query as Query -from .param_functions import Security as Security -from .requests import Request as Request -from .responses import Response as Response -from .routing import APIRouter as APIRouter -from .websockets import WebSocket as WebSocket -from .websockets import WebSocketDisconnect as WebSocketDisconnect diff --git a/.venv/Lib/site-packages/fastapi/_compat.py b/.venv/Lib/site-packages/fastapi/_compat.py deleted file mode 100644 index 35d4a87..0000000 --- a/.venv/Lib/site-packages/fastapi/_compat.py +++ /dev/null @@ -1,634 +0,0 @@ -from collections import deque -from copy import copy -from dataclasses import dataclass, is_dataclass -from enum import Enum -from typing import ( - Any, - Callable, - Deque, - Dict, - FrozenSet, - List, - Mapping, - Sequence, - Set, - Tuple, - Type, - Union, -) - -from fastapi.exceptions import RequestErrorModel -from fastapi.types import IncEx, ModelNameMap, UnionType -from pydantic import BaseModel, create_model -from pydantic.version import VERSION as PYDANTIC_VERSION -from starlette.datastructures import UploadFile -from typing_extensions import Annotated, Literal, get_args, get_origin - -PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.") - - -sequence_annotation_to_type = { - Sequence: list, - List: list, - list: list, - Tuple: tuple, - tuple: tuple, - Set: set, - set: set, - FrozenSet: frozenset, - frozenset: frozenset, - Deque: deque, - deque: deque, -} - -sequence_types = tuple(sequence_annotation_to_type.keys()) - -if PYDANTIC_V2: - from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError - from pydantic import TypeAdapter - from pydantic import ValidationError as ValidationError - from pydantic._internal._schema_generation_shared import ( # type: ignore[attr-defined] - GetJsonSchemaHandler as GetJsonSchemaHandler, - ) - from pydantic._internal._typing_extra import eval_type_lenient - from pydantic._internal._utils import lenient_issubclass as lenient_issubclass - from pydantic.fields import FieldInfo - from pydantic.json_schema import GenerateJsonSchema as GenerateJsonSchema - from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue - from pydantic_core import CoreSchema as CoreSchema - from pydantic_core import PydanticUndefined, PydanticUndefinedType - from pydantic_core import Url as Url - - try: - from pydantic_core.core_schema import ( - with_info_plain_validator_function as with_info_plain_validator_function, - ) - except ImportError: # pragma: no cover - from pydantic_core.core_schema import ( - general_plain_validator_function as with_info_plain_validator_function, # noqa: F401 - ) - - Required = PydanticUndefined - Undefined = PydanticUndefined - UndefinedType = PydanticUndefinedType - evaluate_forwardref = eval_type_lenient - Validator = Any - - class BaseConfig: - pass - - class ErrorWrapper(Exception): - pass - - @dataclass - class ModelField: - field_info: FieldInfo - name: str - mode: Literal["validation", "serialization"] = "validation" - - @property - def alias(self) -> str: - a = self.field_info.alias - return a if a is not None else self.name - - @property - def required(self) -> bool: - return self.field_info.is_required() - - @property - def default(self) -> Any: - return self.get_default() - - @property - def type_(self) -> Any: - return self.field_info.annotation - - def __post_init__(self) -> None: - self._type_adapter: TypeAdapter[Any] = TypeAdapter( - Annotated[self.field_info.annotation, self.field_info] - ) - - def get_default(self) -> Any: - if self.field_info.is_required(): - return Undefined - return self.field_info.get_default(call_default_factory=True) - - def validate( - self, - value: Any, - values: Dict[str, Any] = {}, # noqa: B006 - *, - loc: Tuple[Union[int, str], ...] = (), - ) -> Tuple[Any, Union[List[Dict[str, Any]], None]]: - try: - return ( - self._type_adapter.validate_python(value, from_attributes=True), - None, - ) - except ValidationError as exc: - return None, _regenerate_error_with_loc( - errors=exc.errors(), loc_prefix=loc - ) - - def serialize( - self, - value: Any, - *, - mode: Literal["json", "python"] = "json", - include: Union[IncEx, None] = None, - exclude: Union[IncEx, None] = None, - by_alias: bool = True, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - ) -> Any: - # What calls this code passes a value that already called - # self._type_adapter.validate_python(value) - return self._type_adapter.dump_python( - value, - mode=mode, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - def __hash__(self) -> int: - # Each ModelField is unique for our purposes, to allow making a dict from - # ModelField to its JSON Schema. - return id(self) - - def get_annotation_from_field_info( - annotation: Any, field_info: FieldInfo, field_name: str - ) -> Any: - return annotation - - def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: - return errors # type: ignore[return-value] - - def _model_rebuild(model: Type[BaseModel]) -> None: - model.model_rebuild() - - def _model_dump( - model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any - ) -> Any: - return model.model_dump(mode=mode, **kwargs) - - def _get_model_config(model: BaseModel) -> Any: - return model.model_config - - def get_schema_from_model_field( - *, - field: ModelField, - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - field_mapping: Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - separate_input_output_schemas: bool = True, - ) -> Dict[str, Any]: - override_mode: Union[Literal["validation"], None] = ( - None if separate_input_output_schemas else "validation" - ) - # This expects that GenerateJsonSchema was already used to generate the definitions - json_schema = field_mapping[(field, override_mode or field.mode)] - if "$ref" not in json_schema: - # TODO remove when deprecating Pydantic v1 - # Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207 - json_schema["title"] = ( - field.field_info.title or field.alias.title().replace("_", " ") - ) - return json_schema - - def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap: - return {} - - def get_definitions( - *, - fields: List[ModelField], - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - separate_input_output_schemas: bool = True, - ) -> Tuple[ - Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - Dict[str, Dict[str, Any]], - ]: - override_mode: Union[Literal["validation"], None] = ( - None if separate_input_output_schemas else "validation" - ) - inputs = [ - (field, override_mode or field.mode, field._type_adapter.core_schema) - for field in fields - ] - field_mapping, definitions = schema_generator.generate_definitions( - inputs=inputs - ) - return field_mapping, definitions # type: ignore[return-value] - - def is_scalar_field(field: ModelField) -> bool: - from fastapi import params - - return field_annotation_is_scalar( - field.field_info.annotation - ) and not isinstance(field.field_info, params.Body) - - def is_sequence_field(field: ModelField) -> bool: - return field_annotation_is_sequence(field.field_info.annotation) - - def is_scalar_sequence_field(field: ModelField) -> bool: - return field_annotation_is_scalar_sequence(field.field_info.annotation) - - def is_bytes_field(field: ModelField) -> bool: - return is_bytes_or_nonable_bytes_annotation(field.type_) - - def is_bytes_sequence_field(field: ModelField) -> bool: - return is_bytes_sequence_annotation(field.type_) - - def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo: - cls = type(field_info) - merged_field_info = cls.from_annotation(annotation) - new_field_info = copy(field_info) - new_field_info.metadata = merged_field_info.metadata - new_field_info.annotation = merged_field_info.annotation - return new_field_info - - def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]: - origin_type = ( - get_origin(field.field_info.annotation) or field.field_info.annotation - ) - assert issubclass(origin_type, sequence_types) # type: ignore[arg-type] - return sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return] - - def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]: - error = ValidationError.from_exception_data( - "Field required", [{"type": "missing", "loc": loc, "input": {}}] - ).errors()[0] - error["input"] = None - return error # type: ignore[return-value] - - def create_body_model( - *, fields: Sequence[ModelField], model_name: str - ) -> Type[BaseModel]: - field_params = {f.name: (f.field_info.annotation, f.field_info) for f in fields} - BodyModel: Type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload] - return BodyModel - -else: - from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX - from pydantic import AnyUrl as Url # noqa: F401 - from pydantic import ( # type: ignore[assignment] - BaseConfig as BaseConfig, # noqa: F401 - ) - from pydantic import ValidationError as ValidationError # noqa: F401 - from pydantic.class_validators import ( # type: ignore[no-redef] - Validator as Validator, # noqa: F401 - ) - from pydantic.error_wrappers import ( # type: ignore[no-redef] - ErrorWrapper as ErrorWrapper, # noqa: F401 - ) - from pydantic.errors import MissingError - from pydantic.fields import ( # type: ignore[attr-defined] - SHAPE_FROZENSET, - SHAPE_LIST, - SHAPE_SEQUENCE, - SHAPE_SET, - SHAPE_SINGLETON, - SHAPE_TUPLE, - SHAPE_TUPLE_ELLIPSIS, - ) - from pydantic.fields import FieldInfo as FieldInfo - from pydantic.fields import ( # type: ignore[no-redef,attr-defined] - ModelField as ModelField, # noqa: F401 - ) - from pydantic.fields import ( # type: ignore[no-redef,attr-defined] - Required as Required, # noqa: F401 - ) - from pydantic.fields import ( # type: ignore[no-redef,attr-defined] - Undefined as Undefined, - ) - from pydantic.fields import ( # type: ignore[no-redef, attr-defined] - UndefinedType as UndefinedType, # noqa: F401 - ) - from pydantic.schema import ( - field_schema, - get_flat_models_from_fields, - get_model_name_map, - model_process_schema, - ) - from pydantic.schema import ( # type: ignore[no-redef] # noqa: F401 - get_annotation_from_field_info as get_annotation_from_field_info, - ) - from pydantic.typing import ( # type: ignore[no-redef] - evaluate_forwardref as evaluate_forwardref, # noqa: F401 - ) - from pydantic.utils import ( # type: ignore[no-redef] - lenient_issubclass as lenient_issubclass, # noqa: F401 - ) - - GetJsonSchemaHandler = Any # type: ignore[assignment,misc] - JsonSchemaValue = Dict[str, Any] # type: ignore[misc] - CoreSchema = Any # type: ignore[assignment,misc] - - sequence_shapes = { - SHAPE_LIST, - SHAPE_SET, - SHAPE_FROZENSET, - SHAPE_TUPLE, - SHAPE_SEQUENCE, - SHAPE_TUPLE_ELLIPSIS, - } - sequence_shape_to_type = { - SHAPE_LIST: list, - SHAPE_SET: set, - SHAPE_TUPLE: tuple, - SHAPE_SEQUENCE: list, - SHAPE_TUPLE_ELLIPSIS: list, - } - - @dataclass - class GenerateJsonSchema: # type: ignore[no-redef] - ref_template: str - - class PydanticSchemaGenerationError(Exception): # type: ignore[no-redef] - pass - - def with_info_plain_validator_function( # type: ignore[misc] - function: Callable[..., Any], - *, - ref: Union[str, None] = None, - metadata: Any = None, - serialization: Any = None, - ) -> Any: - return {} - - def get_model_definitions( - *, - flat_models: Set[Union[Type[BaseModel], Type[Enum]]], - model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str], - ) -> Dict[str, Any]: - definitions: Dict[str, Dict[str, Any]] = {} - for model in flat_models: - m_schema, m_definitions, m_nested_models = model_process_schema( - model, model_name_map=model_name_map, ref_prefix=REF_PREFIX - ) - definitions.update(m_definitions) - model_name = model_name_map[model] - if "description" in m_schema: - m_schema["description"] = m_schema["description"].split("\f")[0] - definitions[model_name] = m_schema - return definitions - - def is_pv1_scalar_field(field: ModelField) -> bool: - from fastapi import params - - field_info = field.field_info - if not ( - field.shape == SHAPE_SINGLETON # type: ignore[attr-defined] - and not lenient_issubclass(field.type_, BaseModel) - and not lenient_issubclass(field.type_, dict) - and not field_annotation_is_sequence(field.type_) - and not is_dataclass(field.type_) - and not isinstance(field_info, params.Body) - ): - return False - if field.sub_fields: # type: ignore[attr-defined] - if not all( - is_pv1_scalar_field(f) - for f in field.sub_fields # type: ignore[attr-defined] - ): - return False - return True - - def is_pv1_scalar_sequence_field(field: ModelField) -> bool: - if (field.shape in sequence_shapes) and not lenient_issubclass( # type: ignore[attr-defined] - field.type_, BaseModel - ): - if field.sub_fields is not None: # type: ignore[attr-defined] - for sub_field in field.sub_fields: # type: ignore[attr-defined] - if not is_pv1_scalar_field(sub_field): - return False - return True - if _annotation_is_sequence(field.type_): - return True - return False - - def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]: - use_errors: List[Any] = [] - for error in errors: - if isinstance(error, ErrorWrapper): - new_errors = ValidationError( # type: ignore[call-arg] - errors=[error], model=RequestErrorModel - ).errors() - use_errors.extend(new_errors) - elif isinstance(error, list): - use_errors.extend(_normalize_errors(error)) - else: - use_errors.append(error) - return use_errors - - def _model_rebuild(model: Type[BaseModel]) -> None: - model.update_forward_refs() - - def _model_dump( - model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any - ) -> Any: - return model.dict(**kwargs) - - def _get_model_config(model: BaseModel) -> Any: - return model.__config__ # type: ignore[attr-defined] - - def get_schema_from_model_field( - *, - field: ModelField, - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - field_mapping: Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - separate_input_output_schemas: bool = True, - ) -> Dict[str, Any]: - # This expects that GenerateJsonSchema was already used to generate the definitions - return field_schema( # type: ignore[no-any-return] - field, model_name_map=model_name_map, ref_prefix=REF_PREFIX - )[0] - - def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap: - models = get_flat_models_from_fields(fields, known_models=set()) - return get_model_name_map(models) # type: ignore[no-any-return] - - def get_definitions( - *, - fields: List[ModelField], - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - separate_input_output_schemas: bool = True, - ) -> Tuple[ - Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - Dict[str, Dict[str, Any]], - ]: - models = get_flat_models_from_fields(fields, known_models=set()) - return {}, get_model_definitions( - flat_models=models, model_name_map=model_name_map - ) - - def is_scalar_field(field: ModelField) -> bool: - return is_pv1_scalar_field(field) - - def is_sequence_field(field: ModelField) -> bool: - return field.shape in sequence_shapes or _annotation_is_sequence(field.type_) # type: ignore[attr-defined] - - def is_scalar_sequence_field(field: ModelField) -> bool: - return is_pv1_scalar_sequence_field(field) - - def is_bytes_field(field: ModelField) -> bool: - return lenient_issubclass(field.type_, bytes) - - def is_bytes_sequence_field(field: ModelField) -> bool: - return field.shape in sequence_shapes and lenient_issubclass(field.type_, bytes) # type: ignore[attr-defined] - - def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo: - return copy(field_info) - - def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]: - return sequence_shape_to_type[field.shape](value) # type: ignore[no-any-return,attr-defined] - - def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]: - missing_field_error = ErrorWrapper(MissingError(), loc=loc) # type: ignore[call-arg] - new_error = ValidationError([missing_field_error], RequestErrorModel) - return new_error.errors()[0] # type: ignore[return-value] - - def create_body_model( - *, fields: Sequence[ModelField], model_name: str - ) -> Type[BaseModel]: - BodyModel = create_model(model_name) - for f in fields: - BodyModel.__fields__[f.name] = f # type: ignore[index] - return BodyModel - - -def _regenerate_error_with_loc( - *, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...] -) -> List[Dict[str, Any]]: - updated_loc_errors: List[Any] = [ - {**err, "loc": loc_prefix + err.get("loc", ())} - for err in _normalize_errors(errors) - ] - - return updated_loc_errors - - -def _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool: - if lenient_issubclass(annotation, (str, bytes)): - return False - return lenient_issubclass(annotation, sequence_types) - - -def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool: - return _annotation_is_sequence(annotation) or _annotation_is_sequence( - get_origin(annotation) - ) - - -def value_is_sequence(value: Any) -> bool: - return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type] - - -def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool: - return ( - lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile)) - or _annotation_is_sequence(annotation) - or is_dataclass(annotation) - ) - - -def field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool: - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - return any(field_annotation_is_complex(arg) for arg in get_args(annotation)) - - return ( - _annotation_is_complex(annotation) - or _annotation_is_complex(origin) - or hasattr(origin, "__pydantic_core_schema__") - or hasattr(origin, "__get_pydantic_core_schema__") - ) - - -def field_annotation_is_scalar(annotation: Any) -> bool: - # handle Ellipsis here to make tuple[int, ...] work nicely - return annotation is Ellipsis or not field_annotation_is_complex(annotation) - - -def field_annotation_is_scalar_sequence(annotation: Union[Type[Any], None]) -> bool: - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - at_least_one_scalar_sequence = False - for arg in get_args(annotation): - if field_annotation_is_scalar_sequence(arg): - at_least_one_scalar_sequence = True - continue - elif not field_annotation_is_scalar(arg): - return False - return at_least_one_scalar_sequence - return field_annotation_is_sequence(annotation) and all( - field_annotation_is_scalar(sub_annotation) - for sub_annotation in get_args(annotation) - ) - - -def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool: - if lenient_issubclass(annotation, bytes): - return True - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - for arg in get_args(annotation): - if lenient_issubclass(arg, bytes): - return True - return False - - -def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool: - if lenient_issubclass(annotation, UploadFile): - return True - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - for arg in get_args(annotation): - if lenient_issubclass(arg, UploadFile): - return True - return False - - -def is_bytes_sequence_annotation(annotation: Any) -> bool: - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - at_least_one = False - for arg in get_args(annotation): - if is_bytes_sequence_annotation(arg): - at_least_one = True - continue - return at_least_one - return field_annotation_is_sequence(annotation) and all( - is_bytes_or_nonable_bytes_annotation(sub_annotation) - for sub_annotation in get_args(annotation) - ) - - -def is_uploadfile_sequence_annotation(annotation: Any) -> bool: - origin = get_origin(annotation) - if origin is Union or origin is UnionType: - at_least_one = False - for arg in get_args(annotation): - if is_uploadfile_sequence_annotation(arg): - at_least_one = True - continue - return at_least_one - return field_annotation_is_sequence(annotation) and all( - is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation) - for sub_annotation in get_args(annotation) - ) diff --git a/.venv/Lib/site-packages/fastapi/applications.py b/.venv/Lib/site-packages/fastapi/applications.py deleted file mode 100644 index ffe9da3..0000000 --- a/.venv/Lib/site-packages/fastapi/applications.py +++ /dev/null @@ -1,4585 +0,0 @@ -from enum import Enum -from typing import ( - Any, - Awaitable, - Callable, - Coroutine, - Dict, - List, - Optional, - Sequence, - Type, - TypeVar, - Union, -) - -from fastapi import routing -from fastapi.datastructures import Default, DefaultPlaceholder -from fastapi.exception_handlers import ( - http_exception_handler, - request_validation_exception_handler, - websocket_request_validation_exception_handler, -) -from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError -from fastapi.logger import logger -from fastapi.openapi.docs import ( - get_redoc_html, - get_swagger_ui_html, - get_swagger_ui_oauth2_redirect_html, -) -from fastapi.openapi.utils import get_openapi -from fastapi.params import Depends -from fastapi.types import DecoratedCallable, IncEx -from fastapi.utils import generate_unique_id -from starlette.applications import Starlette -from starlette.datastructures import State -from starlette.exceptions import HTTPException -from starlette.middleware import Middleware -from starlette.middleware.base import BaseHTTPMiddleware -from starlette.requests import Request -from starlette.responses import HTMLResponse, JSONResponse, Response -from starlette.routing import BaseRoute -from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send -from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined] - -AppType = TypeVar("AppType", bound="FastAPI") - - -class FastAPI(Starlette): - """ - `FastAPI` app class, the main entrypoint to use FastAPI. - - Read more in the - [FastAPI docs for First Steps](https://fastapi.tiangolo.com/tutorial/first-steps/). - - ## Example - - ```python - from fastapi import FastAPI - - app = FastAPI() - ``` - """ - - def __init__( - self: AppType, - *, - debug: Annotated[ - bool, - Doc( - """ - Boolean indicating if debug tracebacks should be returned on server - errors. - - Read more in the - [Starlette docs for Applications](https://www.starlette.io/applications/#instantiating-the-application). - """ - ), - ] = False, - routes: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - **Note**: you probably shouldn't use this parameter, it is inherited - from Starlette and supported for compatibility. - - --- - - A list of routes to serve incoming HTTP and WebSocket requests. - """ - ), - deprecated( - """ - You normally wouldn't use this parameter with FastAPI, it is inherited - from Starlette and supported for compatibility. - - In FastAPI, you normally would use the *path operation methods*, - like `app.get()`, `app.post()`, etc. - """ - ), - ] = None, - title: Annotated[ - str, - Doc( - """ - The title of the API. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(title="ChimichangApp") - ``` - """ - ), - ] = "FastAPI", - summary: Annotated[ - Optional[str], - Doc( - """ - A short summary of the API. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(summary="Deadpond's favorite app. Nuff said.") - ``` - """ - ), - ] = None, - description: Annotated[ - str, - Doc( - ''' - A description of the API. Supports Markdown (using - [CommonMark syntax](https://commonmark.org/)). - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI( - description=""" - ChimichangApp API helps you do awesome stuff. 🚀 - - ## Items - - You can **read items**. - - ## Users - - You will be able to: - - * **Create users** (_not implemented_). - * **Read users** (_not implemented_). - - """ - ) - ``` - ''' - ), - ] = "", - version: Annotated[ - str, - Doc( - """ - The version of the API. - - **Note** This is the version of your application, not the version of - the OpenAPI specification nor the version of FastAPI being used. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(version="0.0.1") - ``` - """ - ), - ] = "0.1.0", - openapi_url: Annotated[ - Optional[str], - Doc( - """ - The URL where the OpenAPI schema will be served from. - - If you set it to `None`, no OpenAPI schema will be served publicly, and - the default automatic endpoints `/docs` and `/redoc` will also be - disabled. - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#openapi-url). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(openapi_url="/api/v1/openapi.json") - ``` - """ - ), - ] = "/openapi.json", - openapi_tags: Annotated[ - Optional[List[Dict[str, Any]]], - Doc( - """ - A list of tags used by OpenAPI, these are the same `tags` you can set - in the *path operations*, like: - - * `@app.get("/users/", tags=["users"])` - * `@app.get("/items/", tags=["items"])` - - The order of the tags can be used to specify the order shown in - tools like Swagger UI, used in the automatic path `/docs`. - - It's not required to specify all the tags used. - - The tags that are not declared MAY be organized randomly or based - on the tools' logic. Each tag name in the list MUST be unique. - - The value of each item is a `dict` containing: - - * `name`: The name of the tag. - * `description`: A short description of the tag. - [CommonMark syntax](https://commonmark.org/) MAY be used for rich - text representation. - * `externalDocs`: Additional external documentation for this tag. If - provided, it would contain a `dict` with: - * `description`: A short description of the target documentation. - [CommonMark syntax](https://commonmark.org/) MAY be used for - rich text representation. - * `url`: The URL for the target documentation. Value MUST be in - the form of a URL. - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-tags). - - **Example** - - ```python - from fastapi import FastAPI - - tags_metadata = [ - { - "name": "users", - "description": "Operations with users. The **login** logic is also here.", - }, - { - "name": "items", - "description": "Manage items. So _fancy_ they have their own docs.", - "externalDocs": { - "description": "Items external docs", - "url": "https://fastapi.tiangolo.com/", - }, - }, - ] - - app = FastAPI(openapi_tags=tags_metadata) - ``` - """ - ), - ] = None, - servers: Annotated[ - Optional[List[Dict[str, Union[str, Any]]]], - Doc( - """ - A `list` of `dict`s with connectivity information to a target server. - - You would use it, for example, if your application is served from - different domains and you want to use the same Swagger UI in the - browser to interact with each of them (instead of having multiple - browser tabs open). Or if you want to leave fixed the possible URLs. - - If the servers `list` is not provided, or is an empty `list`, the - default value would be a `dict` with a `url` value of `/`. - - Each item in the `list` is a `dict` containing: - - * `url`: A URL to the target host. This URL supports Server Variables - and MAY be relative, to indicate that the host location is relative - to the location where the OpenAPI document is being served. Variable - substitutions will be made when a variable is named in `{`brackets`}`. - * `description`: An optional string describing the host designated by - the URL. [CommonMark syntax](https://commonmark.org/) MAY be used for - rich text representation. - * `variables`: A `dict` between a variable name and its value. The value - is used for substitution in the server's URL template. - - Read more in the - [FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#additional-servers). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI( - servers=[ - {"url": "https://stag.example.com", "description": "Staging environment"}, - {"url": "https://prod.example.com", "description": "Production environment"}, - ] - ) - ``` - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of global dependencies, they will be applied to each - *path operation*, including in sub-routers. - - Read more about it in the - [FastAPI docs for Global Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/). - - **Example** - - ```python - from fastapi import Depends, FastAPI - - from .dependencies import func_dep_1, func_dep_2 - - app = FastAPI(dependencies=[Depends(func_dep_1), Depends(func_dep_2)]) - ``` - """ - ), - ] = None, - default_response_class: Annotated[ - Type[Response], - Doc( - """ - The default response class to be used. - - Read more in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). - - **Example** - - ```python - from fastapi import FastAPI - from fastapi.responses import ORJSONResponse - - app = FastAPI(default_response_class=ORJSONResponse) - ``` - """ - ), - ] = Default(JSONResponse), - redirect_slashes: Annotated[ - bool, - Doc( - """ - Whether to detect and redirect slashes in URLs when the client doesn't - use the same format. - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(redirect_slashes=True) # the default - - @app.get("/items/") - async def read_items(): - return [{"item_id": "Foo"}] - ``` - - With this app, if a client goes to `/items` (without a trailing slash), - they will be automatically redirected with an HTTP status code of 307 - to `/items/`. - """ - ), - ] = True, - docs_url: Annotated[ - Optional[str], - Doc( - """ - The path to the automatic interactive API documentation. - It is handled in the browser by Swagger UI. - - The default URL is `/docs`. You can disable it by setting it to `None`. - - If `openapi_url` is set to `None`, this will be automatically disabled. - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#docs-urls). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(docs_url="/documentation", redoc_url=None) - ``` - """ - ), - ] = "/docs", - redoc_url: Annotated[ - Optional[str], - Doc( - """ - The path to the alternative automatic interactive API documentation - provided by ReDoc. - - The default URL is `/redoc`. You can disable it by setting it to `None`. - - If `openapi_url` is set to `None`, this will be automatically disabled. - - Read more in the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#docs-urls). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(docs_url="/documentation", redoc_url="redocumentation") - ``` - """ - ), - ] = "/redoc", - swagger_ui_oauth2_redirect_url: Annotated[ - Optional[str], - Doc( - """ - The OAuth2 redirect endpoint for the Swagger UI. - - By default it is `/docs/oauth2-redirect`. - - This is only used if you use OAuth2 (with the "Authorize" button) - with Swagger UI. - """ - ), - ] = "/docs/oauth2-redirect", - swagger_ui_init_oauth: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - OAuth2 configuration for the Swagger UI, by default shown at `/docs`. - - Read more about the available configuration options in the - [Swagger UI docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/). - """ - ), - ] = None, - middleware: Annotated[ - Optional[Sequence[Middleware]], - Doc( - """ - List of middleware to be added when creating the application. - - In FastAPI you would normally do this with `app.add_middleware()` - instead. - - Read more in the - [FastAPI docs for Middleware](https://fastapi.tiangolo.com/tutorial/middleware/). - """ - ), - ] = None, - exception_handlers: Annotated[ - Optional[ - Dict[ - Union[int, Type[Exception]], - Callable[[Request, Any], Coroutine[Any, Any, Response]], - ] - ], - Doc( - """ - A dictionary with handlers for exceptions. - - In FastAPI, you would normally use the decorator - `@app.exception_handler()`. - - Read more in the - [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/). - """ - ), - ] = None, - on_startup: Annotated[ - Optional[Sequence[Callable[[], Any]]], - Doc( - """ - A list of startup event handler functions. - - You should instead use the `lifespan` handlers. - - Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - on_shutdown: Annotated[ - Optional[Sequence[Callable[[], Any]]], - Doc( - """ - A list of shutdown event handler functions. - - You should instead use the `lifespan` handlers. - - Read more in the - [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - lifespan: Annotated[ - Optional[Lifespan[AppType]], - Doc( - """ - A `Lifespan` context manager handler. This replaces `startup` and - `shutdown` functions with a single context manager. - - Read more in the - [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - terms_of_service: Annotated[ - Optional[str], - Doc( - """ - A URL to the Terms of Service for your API. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more at the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - app = FastAPI(terms_of_service="http://example.com/terms/") - ``` - """ - ), - ] = None, - contact: Annotated[ - Optional[Dict[str, Union[str, Any]]], - Doc( - """ - A dictionary with the contact information for the exposed API. - - It can contain several fields. - - * `name`: (`str`) The name of the contact person/organization. - * `url`: (`str`) A URL pointing to the contact information. MUST be in - the format of a URL. - * `email`: (`str`) The email address of the contact person/organization. - MUST be in the format of an email address. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more at the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - app = FastAPI( - contact={ - "name": "Deadpoolio the Amazing", - "url": "http://x-force.example.com/contact/", - "email": "dp@x-force.example.com", - } - ) - ``` - """ - ), - ] = None, - license_info: Annotated[ - Optional[Dict[str, Union[str, Any]]], - Doc( - """ - A dictionary with the license information for the exposed API. - - It can contain several fields. - - * `name`: (`str`) **REQUIRED** (if a `license_info` is set). The - license name used for the API. - * `identifier`: (`str`) An [SPDX](https://spdx.dev/) license expression - for the API. The `identifier` field is mutually exclusive of the `url` - field. Available since OpenAPI 3.1.0, FastAPI 0.99.0. - * `url`: (`str`) A URL to the license used for the API. This MUST be - the format of a URL. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more at the - [FastAPI docs for Metadata and Docs URLs](https://fastapi.tiangolo.com/tutorial/metadata/#metadata-for-api). - - **Example** - - ```python - app = FastAPI( - license_info={ - "name": "Apache 2.0", - "url": "https://www.apache.org/licenses/LICENSE-2.0.html", - } - ) - ``` - """ - ), - ] = None, - openapi_prefix: Annotated[ - str, - Doc( - """ - A URL prefix for the OpenAPI URL. - """ - ), - deprecated( - """ - "openapi_prefix" has been deprecated in favor of "root_path", which - follows more closely the ASGI standard, is simpler, and more - automatic. - """ - ), - ] = "", - root_path: Annotated[ - str, - Doc( - """ - A path prefix handled by a proxy that is not seen by the application - but is seen by external clients, which affects things like Swagger UI. - - Read more about it at the - [FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(root_path="/api/v1") - ``` - """ - ), - ] = "", - root_path_in_servers: Annotated[ - bool, - Doc( - """ - To disable automatically generating the URLs in the `servers` field - in the autogenerated OpenAPI using the `root_path`. - - Read more about it in the - [FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#disable-automatic-server-from-root_path). - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI(root_path_in_servers=False) - ``` - """ - ), - ] = True, - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses to be shown in OpenAPI. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/). - - And in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - OpenAPI callbacks that should apply to all *path operations*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - webhooks: Annotated[ - Optional[routing.APIRouter], - Doc( - """ - Add OpenAPI webhooks. This is similar to `callbacks` but it doesn't - depend on specific *path operations*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - **Note**: This is available since OpenAPI 3.1.0, FastAPI 0.99.0. - - Read more about it in the - [FastAPI docs for OpenAPI Webhooks](https://fastapi.tiangolo.com/advanced/openapi-webhooks/). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark all *path operations* as deprecated. You probably don't need it, - but it's available. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) all the *path operations* in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - swagger_ui_parameters: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Parameters to configure Swagger UI, the autogenerated interactive API - documentation (by default at `/docs`). - - Read more about it in the - [FastAPI docs about how to Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - separate_input_output_schemas: Annotated[ - bool, - Doc( - """ - Whether to generate separate OpenAPI schemas for request body and - response body when the results would be more precise. - - This is particularly useful when automatically generating clients. - - For example, if you have a model like: - - ```python - from pydantic import BaseModel - - class Item(BaseModel): - name: str - tags: list[str] = [] - ``` - - When `Item` is used for input, a request body, `tags` is not required, - the client doesn't have to provide it. - - But when using `Item` for output, for a response body, `tags` is always - available because it has a default value, even if it's just an empty - list. So, the client should be able to always expect it. - - In this case, there would be two different schemas, one for input and - another one for output. - """ - ), - ] = True, - **extra: Annotated[ - Any, - Doc( - """ - Extra keyword arguments to be stored in the app, not used by FastAPI - anywhere. - """ - ), - ], - ) -> None: - self.debug = debug - self.title = title - self.summary = summary - self.description = description - self.version = version - self.terms_of_service = terms_of_service - self.contact = contact - self.license_info = license_info - self.openapi_url = openapi_url - self.openapi_tags = openapi_tags - self.root_path_in_servers = root_path_in_servers - self.docs_url = docs_url - self.redoc_url = redoc_url - self.swagger_ui_oauth2_redirect_url = swagger_ui_oauth2_redirect_url - self.swagger_ui_init_oauth = swagger_ui_init_oauth - self.swagger_ui_parameters = swagger_ui_parameters - self.servers = servers or [] - self.separate_input_output_schemas = separate_input_output_schemas - self.extra = extra - self.openapi_version: Annotated[ - str, - Doc( - """ - The version string of OpenAPI. - - FastAPI will generate OpenAPI version 3.1.0, and will output that as - the OpenAPI version. But some tools, even though they might be - compatible with OpenAPI 3.1.0, might not recognize it as a valid. - - So you could override this value to trick those tools into using - the generated OpenAPI. Have in mind that this is a hack. But if you - avoid using features added in OpenAPI 3.1.0, it might work for your - use case. - - This is not passed as a parameter to the `FastAPI` class to avoid - giving the false idea that FastAPI would generate a different OpenAPI - schema. It is only available as an attribute. - - **Example** - - ```python - from fastapi import FastAPI - - app = FastAPI() - - app.openapi_version = "3.0.2" - ``` - """ - ), - ] = "3.1.0" - self.openapi_schema: Optional[Dict[str, Any]] = None - if self.openapi_url: - assert self.title, "A title must be provided for OpenAPI, e.g.: 'My API'" - assert self.version, "A version must be provided for OpenAPI, e.g.: '2.1.0'" - # TODO: remove when discarding the openapi_prefix parameter - if openapi_prefix: - logger.warning( - '"openapi_prefix" has been deprecated in favor of "root_path", which ' - "follows more closely the ASGI standard, is simpler, and more " - "automatic. Check the docs at " - "https://fastapi.tiangolo.com/advanced/sub-applications/" - ) - self.webhooks: Annotated[ - routing.APIRouter, - Doc( - """ - The `app.webhooks` attribute is an `APIRouter` with the *path - operations* that will be used just for documentation of webhooks. - - Read more about it in the - [FastAPI docs for OpenAPI Webhooks](https://fastapi.tiangolo.com/advanced/openapi-webhooks/). - """ - ), - ] = webhooks or routing.APIRouter() - self.root_path = root_path or openapi_prefix - self.state: Annotated[ - State, - Doc( - """ - A state object for the application. This is the same object for the - entire application, it doesn't change from request to request. - - You normally woudln't use this in FastAPI, for most of the cases you - would instead use FastAPI dependencies. - - This is simply inherited from Starlette. - - Read more about it in the - [Starlette docs for Applications](https://www.starlette.io/applications/#storing-state-on-the-app-instance). - """ - ), - ] = State() - self.dependency_overrides: Annotated[ - Dict[Callable[..., Any], Callable[..., Any]], - Doc( - """ - A dictionary with overrides for the dependencies. - - Each key is the original dependency callable, and the value is the - actual dependency that should be called. - - This is for testing, to replace expensive dependencies with testing - versions. - - Read more about it in the - [FastAPI docs for Testing Dependencies with Overrides](https://fastapi.tiangolo.com/advanced/testing-dependencies/). - """ - ), - ] = {} - self.router: routing.APIRouter = routing.APIRouter( - routes=routes, - redirect_slashes=redirect_slashes, - dependency_overrides_provider=self, - on_startup=on_startup, - on_shutdown=on_shutdown, - lifespan=lifespan, - default_response_class=default_response_class, - dependencies=dependencies, - callbacks=callbacks, - deprecated=deprecated, - include_in_schema=include_in_schema, - responses=responses, - generate_unique_id_function=generate_unique_id_function, - ) - self.exception_handlers: Dict[ - Any, Callable[[Request, Any], Union[Response, Awaitable[Response]]] - ] = {} if exception_handlers is None else dict(exception_handlers) - self.exception_handlers.setdefault(HTTPException, http_exception_handler) - self.exception_handlers.setdefault( - RequestValidationError, request_validation_exception_handler - ) - self.exception_handlers.setdefault( - WebSocketRequestValidationError, - # Starlette still has incorrect type specification for the handlers - websocket_request_validation_exception_handler, # type: ignore - ) - - self.user_middleware: List[Middleware] = ( - [] if middleware is None else list(middleware) - ) - self.middleware_stack: Union[ASGIApp, None] = None - self.setup() - - def openapi(self) -> Dict[str, Any]: - """ - Generate the OpenAPI schema of the application. This is called by FastAPI - internally. - - The first time it is called it stores the result in the attribute - `app.openapi_schema`, and next times it is called, it just returns that same - result. To avoid the cost of generating the schema every time. - - If you need to modify the generated OpenAPI schema, you could modify it. - - Read more in the - [FastAPI docs for OpenAPI](https://fastapi.tiangolo.com/how-to/extending-openapi/). - """ - if not self.openapi_schema: - self.openapi_schema = get_openapi( - title=self.title, - version=self.version, - openapi_version=self.openapi_version, - summary=self.summary, - description=self.description, - terms_of_service=self.terms_of_service, - contact=self.contact, - license_info=self.license_info, - routes=self.routes, - webhooks=self.webhooks.routes, - tags=self.openapi_tags, - servers=self.servers, - separate_input_output_schemas=self.separate_input_output_schemas, - ) - return self.openapi_schema - - def setup(self) -> None: - if self.openapi_url: - urls = (server_data.get("url") for server_data in self.servers) - server_urls = {url for url in urls if url} - - async def openapi(req: Request) -> JSONResponse: - root_path = req.scope.get("root_path", "").rstrip("/") - if root_path not in server_urls: - if root_path and self.root_path_in_servers: - self.servers.insert(0, {"url": root_path}) - server_urls.add(root_path) - return JSONResponse(self.openapi()) - - self.add_route(self.openapi_url, openapi, include_in_schema=False) - if self.openapi_url and self.docs_url: - - async def swagger_ui_html(req: Request) -> HTMLResponse: - root_path = req.scope.get("root_path", "").rstrip("/") - openapi_url = root_path + self.openapi_url - oauth2_redirect_url = self.swagger_ui_oauth2_redirect_url - if oauth2_redirect_url: - oauth2_redirect_url = root_path + oauth2_redirect_url - return get_swagger_ui_html( - openapi_url=openapi_url, - title=self.title + " - Swagger UI", - oauth2_redirect_url=oauth2_redirect_url, - init_oauth=self.swagger_ui_init_oauth, - swagger_ui_parameters=self.swagger_ui_parameters, - ) - - self.add_route(self.docs_url, swagger_ui_html, include_in_schema=False) - - if self.swagger_ui_oauth2_redirect_url: - - async def swagger_ui_redirect(req: Request) -> HTMLResponse: - return get_swagger_ui_oauth2_redirect_html() - - self.add_route( - self.swagger_ui_oauth2_redirect_url, - swagger_ui_redirect, - include_in_schema=False, - ) - if self.openapi_url and self.redoc_url: - - async def redoc_html(req: Request) -> HTMLResponse: - root_path = req.scope.get("root_path", "").rstrip("/") - openapi_url = root_path + self.openapi_url - return get_redoc_html( - openapi_url=openapi_url, title=self.title + " - ReDoc" - ) - - self.add_route(self.redoc_url, redoc_html, include_in_schema=False) - - async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: - if self.root_path: - scope["root_path"] = self.root_path - await super().__call__(scope, receive, send) - - def add_api_route( - self, - path: str, - endpoint: Callable[..., Coroutine[Any, Any, Response]], - *, - response_model: Any = Default(None), - status_code: Optional[int] = None, - tags: Optional[List[Union[str, Enum]]] = None, - dependencies: Optional[Sequence[Depends]] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - response_description: str = "Successful Response", - responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, - deprecated: Optional[bool] = None, - methods: Optional[List[str]] = None, - operation_id: Optional[str] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - include_in_schema: bool = True, - response_class: Union[Type[Response], DefaultPlaceholder] = Default( - JSONResponse - ), - name: Optional[str] = None, - openapi_extra: Optional[Dict[str, Any]] = None, - generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( - generate_unique_id - ), - ) -> None: - self.router.add_api_route( - path, - endpoint=endpoint, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=methods, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def api_route( - self, - path: str, - *, - response_model: Any = Default(None), - status_code: Optional[int] = None, - tags: Optional[List[Union[str, Enum]]] = None, - dependencies: Optional[Sequence[Depends]] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - response_description: str = "Successful Response", - responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, - deprecated: Optional[bool] = None, - methods: Optional[List[str]] = None, - operation_id: Optional[str] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - include_in_schema: bool = True, - response_class: Type[Response] = Default(JSONResponse), - name: Optional[str] = None, - openapi_extra: Optional[Dict[str, Any]] = None, - generate_unique_id_function: Callable[[routing.APIRoute], str] = Default( - generate_unique_id - ), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.router.add_api_route( - path, - func, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=methods, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - return func - - return decorator - - def add_api_websocket_route( - self, - path: str, - endpoint: Callable[..., Any], - name: Optional[str] = None, - *, - dependencies: Optional[Sequence[Depends]] = None, - ) -> None: - self.router.add_api_websocket_route( - path, - endpoint, - name=name, - dependencies=dependencies, - ) - - def websocket( - self, - path: Annotated[ - str, - Doc( - """ - WebSocket path. - """ - ), - ], - name: Annotated[ - Optional[str], - Doc( - """ - A name for the WebSocket. Only used internally. - """ - ), - ] = None, - *, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be used for this - WebSocket. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - """ - ), - ] = None, - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Decorate a WebSocket function. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - - **Example** - - ```python - from fastapi import FastAPI, WebSocket - - app = FastAPI() - - @app.websocket("/ws") - async def websocket_endpoint(websocket: WebSocket): - await websocket.accept() - while True: - data = await websocket.receive_text() - await websocket.send_text(f"Message text was: {data}") - ``` - """ - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_api_websocket_route( - path, - func, - name=name, - dependencies=dependencies, - ) - return func - - return decorator - - def include_router( - self, - router: Annotated[routing.APIRouter, Doc("The `APIRouter` to include.")], - *, - prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to all the *path operations* in this - router. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to all the - *path operations* in this router. - - Read more about it in the - [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - - **Example** - - ```python - from fastapi import Depends, FastAPI - - from .dependencies import get_token_header - from .internal import admin - - app = FastAPI() - - app.include_router( - admin.router, - dependencies=[Depends(get_token_header)], - ) - ``` - """ - ), - ] = None, - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses to be shown in OpenAPI. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/). - - And in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark all the *path operations* in this router as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - **Example** - - ```python - from fastapi import FastAPI - - from .internal import old_api - - app = FastAPI() - - app.include_router( - old_api.router, - deprecated=True, - ) - ``` - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include (or not) all the *path operations* in this router in the - generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - **Example** - - ```python - from fastapi import FastAPI - - from .internal import old_api - - app = FastAPI() - - app.include_router( - old_api.router, - include_in_schema=False, - ) - ``` - """ - ), - ] = True, - default_response_class: Annotated[ - Type[Response], - Doc( - """ - Default response class to be used for the *path operations* in this - router. - - Read more in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). - - **Example** - - ```python - from fastapi import FastAPI - from fastapi.responses import ORJSONResponse - - from .internal import old_api - - app = FastAPI() - - app.include_router( - old_api.router, - default_response_class=ORJSONResponse, - ) - ``` - """ - ), - ] = Default(JSONResponse), - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> None: - """ - Include an `APIRouter` in the same app. - - Read more about it in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/). - - ## Example - - ```python - from fastapi import FastAPI - - from .users import users_router - - app = FastAPI() - - app.include_router(users_router) - ``` - """ - self.router.include_router( - router, - prefix=prefix, - tags=tags, - dependencies=dependencies, - responses=responses, - deprecated=deprecated, - include_in_schema=include_in_schema, - default_response_class=default_response_class, - callbacks=callbacks, - generate_unique_id_function=generate_unique_id_function, - ) - - def get( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP GET operation. - - ## Example - - ```python - from fastapi import FastAPI - - app = FastAPI() - - @app.get("/items/") - def read_items(): - return [{"name": "Empanada"}, {"name": "Arepa"}] - ``` - """ - return self.router.get( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def put( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP PUT operation. - - ## Example - - ```python - from fastapi import FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - - @app.put("/items/{item_id}") - def replace_item(item_id: str, item: Item): - return {"message": "Item replaced", "id": item_id} - ``` - """ - return self.router.put( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def post( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP POST operation. - - ## Example - - ```python - from fastapi import FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - - @app.post("/items/") - def create_item(item: Item): - return {"message": "Item created"} - ``` - """ - return self.router.post( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def delete( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP DELETE operation. - - ## Example - - ```python - from fastapi import FastAPI - - app = FastAPI() - - @app.delete("/items/{item_id}") - def delete_item(item_id: str): - return {"message": "Item deleted"} - ``` - """ - return self.router.delete( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def options( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP OPTIONS operation. - - ## Example - - ```python - from fastapi import FastAPI - - app = FastAPI() - - @app.options("/items/") - def get_item_options(): - return {"additions": ["Aji", "Guacamole"]} - ``` - """ - return self.router.options( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def head( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP HEAD operation. - - ## Example - - ```python - from fastapi import FastAPI, Response - - app = FastAPI() - - @app.head("/items/", status_code=204) - def get_items_headers(response: Response): - response.headers["X-Cat-Dog"] = "Alone in the world" - ``` - """ - return self.router.head( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def patch( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP PATCH operation. - - ## Example - - ```python - from fastapi import FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - - @app.patch("/items/") - def update_item(item: Item): - return {"message": "Item updated in place"} - ``` - """ - return self.router.patch( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def trace( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[routing.APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP TRACE operation. - - ## Example - - ```python - from fastapi import FastAPI - - app = FastAPI() - - @app.put("/items/{item_id}") - def trace_item(item_id: str): - return None - ``` - """ - return self.router.trace( - path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def websocket_route( - self, path: str, name: Union[str, None] = None - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.router.add_websocket_route(path, func, name=name) - return func - - return decorator - - @deprecated( - """ - on_event is deprecated, use lifespan event handlers instead. - - Read more about it in the - [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/). - """ - ) - def on_event( - self, - event_type: Annotated[ - str, - Doc( - """ - The type of event. `startup` or `shutdown`. - """ - ), - ], - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add an event handler for the application. - - `on_event` is deprecated, use `lifespan` event handlers instead. - - Read more about it in the - [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/#alternative-events-deprecated). - """ - return self.router.on_event(event_type) - - def middleware( - self, - middleware_type: Annotated[ - str, - Doc( - """ - The type of middleware. Currently only supports `http`. - """ - ), - ], - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a middleware to the application. - - Read more about it in the - [FastAPI docs for Middleware](https://fastapi.tiangolo.com/tutorial/middleware/). - - ## Example - - ```python - import time - - from fastapi import FastAPI, Request - - app = FastAPI() - - - @app.middleware("http") - async def add_process_time_header(request: Request, call_next): - start_time = time.time() - response = await call_next(request) - process_time = time.time() - start_time - response.headers["X-Process-Time"] = str(process_time) - return response - ``` - """ - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_middleware(BaseHTTPMiddleware, dispatch=func) - return func - - return decorator - - def exception_handler( - self, - exc_class_or_status_code: Annotated[ - Union[int, Type[Exception]], - Doc( - """ - The Exception class this would handle, or a status code. - """ - ), - ], - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add an exception handler to the app. - - Read more about it in the - [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/). - - ## Example - - ```python - from fastapi import FastAPI, Request - from fastapi.responses import JSONResponse - - - class UnicornException(Exception): - def __init__(self, name: str): - self.name = name - - - app = FastAPI() - - - @app.exception_handler(UnicornException) - async def unicorn_exception_handler(request: Request, exc: UnicornException): - return JSONResponse( - status_code=418, - content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."}, - ) - ``` - """ - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_exception_handler(exc_class_or_status_code, func) - return func - - return decorator diff --git a/.venv/Lib/site-packages/fastapi/background.py b/.venv/Lib/site-packages/fastapi/background.py deleted file mode 100644 index 35ab1b2..0000000 --- a/.venv/Lib/site-packages/fastapi/background.py +++ /dev/null @@ -1,59 +0,0 @@ -from typing import Any, Callable - -from starlette.background import BackgroundTasks as StarletteBackgroundTasks -from typing_extensions import Annotated, Doc, ParamSpec # type: ignore [attr-defined] - -P = ParamSpec("P") - - -class BackgroundTasks(StarletteBackgroundTasks): - """ - A collection of background tasks that will be called after a response has been - sent to the client. - - Read more about it in the - [FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/). - - ## Example - - ```python - from fastapi import BackgroundTasks, FastAPI - - app = FastAPI() - - - def write_notification(email: str, message=""): - with open("log.txt", mode="w") as email_file: - content = f"notification for {email}: {message}" - email_file.write(content) - - - @app.post("/send-notification/{email}") - async def send_notification(email: str, background_tasks: BackgroundTasks): - background_tasks.add_task(write_notification, email, message="some notification") - return {"message": "Notification sent in the background"} - ``` - """ - - def add_task( - self, - func: Annotated[ - Callable[P, Any], - Doc( - """ - The function to call after the response is sent. - - It can be a regular `def` function or an `async def` function. - """ - ), - ], - *args: P.args, - **kwargs: P.kwargs, - ) -> None: - """ - Add a function to be called in the background after the response is sent. - - Read more about it in the - [FastAPI docs for Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/). - """ - return super().add_task(func, *args, **kwargs) diff --git a/.venv/Lib/site-packages/fastapi/concurrency.py b/.venv/Lib/site-packages/fastapi/concurrency.py deleted file mode 100644 index 894bd3e..0000000 --- a/.venv/Lib/site-packages/fastapi/concurrency.py +++ /dev/null @@ -1,39 +0,0 @@ -from contextlib import asynccontextmanager as asynccontextmanager -from typing import AsyncGenerator, ContextManager, TypeVar - -import anyio -from anyio import CapacityLimiter -from starlette.concurrency import iterate_in_threadpool as iterate_in_threadpool # noqa -from starlette.concurrency import run_in_threadpool as run_in_threadpool # noqa -from starlette.concurrency import ( # noqa - run_until_first_complete as run_until_first_complete, -) - -_T = TypeVar("_T") - - -@asynccontextmanager -async def contextmanager_in_threadpool( - cm: ContextManager[_T], -) -> AsyncGenerator[_T, None]: - # blocking __exit__ from running waiting on a free thread - # can create race conditions/deadlocks if the context manager itself - # has its own internal pool (e.g. a database connection pool) - # to avoid this we let __exit__ run without a capacity limit - # since we're creating a new limiter for each call, any non-zero limit - # works (1 is arbitrary) - exit_limiter = CapacityLimiter(1) - try: - yield await run_in_threadpool(cm.__enter__) - except Exception as e: - ok = bool( - await anyio.to_thread.run_sync( - cm.__exit__, type(e), e, None, limiter=exit_limiter - ) - ) - if not ok: - raise e - else: - await anyio.to_thread.run_sync( - cm.__exit__, None, None, None, limiter=exit_limiter - ) diff --git a/.venv/Lib/site-packages/fastapi/datastructures.py b/.venv/Lib/site-packages/fastapi/datastructures.py deleted file mode 100644 index ce03e3c..0000000 --- a/.venv/Lib/site-packages/fastapi/datastructures.py +++ /dev/null @@ -1,204 +0,0 @@ -from typing import ( - Any, - BinaryIO, - Callable, - Dict, - Iterable, - Optional, - Type, - TypeVar, - cast, -) - -from fastapi._compat import ( - PYDANTIC_V2, - CoreSchema, - GetJsonSchemaHandler, - JsonSchemaValue, - with_info_plain_validator_function, -) -from starlette.datastructures import URL as URL # noqa: F401 -from starlette.datastructures import Address as Address # noqa: F401 -from starlette.datastructures import FormData as FormData # noqa: F401 -from starlette.datastructures import Headers as Headers # noqa: F401 -from starlette.datastructures import QueryParams as QueryParams # noqa: F401 -from starlette.datastructures import State as State # noqa: F401 -from starlette.datastructures import UploadFile as StarletteUploadFile -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class UploadFile(StarletteUploadFile): - """ - A file uploaded in a request. - - Define it as a *path operation function* (or dependency) parameter. - - If you are using a regular `def` function, you can use the `upload_file.file` - attribute to access the raw standard Python file (blocking, not async), useful and - needed for non-async code. - - Read more about it in the - [FastAPI docs for Request Files](https://fastapi.tiangolo.com/tutorial/request-files/). - - ## Example - - ```python - from typing import Annotated - - from fastapi import FastAPI, File, UploadFile - - app = FastAPI() - - - @app.post("/files/") - async def create_file(file: Annotated[bytes, File()]): - return {"file_size": len(file)} - - - @app.post("/uploadfile/") - async def create_upload_file(file: UploadFile): - return {"filename": file.filename} - ``` - """ - - file: Annotated[ - BinaryIO, - Doc("The standard Python file object (non-async)."), - ] - filename: Annotated[Optional[str], Doc("The original file name.")] - size: Annotated[Optional[int], Doc("The size of the file in bytes.")] - headers: Annotated[Headers, Doc("The headers of the request.")] - content_type: Annotated[ - Optional[str], Doc("The content type of the request, from the headers.") - ] - - async def write( - self, - data: Annotated[ - bytes, - Doc( - """ - The bytes to write to the file. - """ - ), - ], - ) -> None: - """ - Write some bytes to the file. - - You normally wouldn't use this from a file you read in a request. - - To be awaitable, compatible with async, this is run in threadpool. - """ - return await super().write(data) - - async def read( - self, - size: Annotated[ - int, - Doc( - """ - The number of bytes to read from the file. - """ - ), - ] = -1, - ) -> bytes: - """ - Read some bytes from the file. - - To be awaitable, compatible with async, this is run in threadpool. - """ - return await super().read(size) - - async def seek( - self, - offset: Annotated[ - int, - Doc( - """ - The position in bytes to seek to in the file. - """ - ), - ], - ) -> None: - """ - Move to a position in the file. - - Any next read or write will be done from that position. - - To be awaitable, compatible with async, this is run in threadpool. - """ - return await super().seek(offset) - - async def close(self) -> None: - """ - Close the file. - - To be awaitable, compatible with async, this is run in threadpool. - """ - return await super().close() - - @classmethod - def __get_validators__(cls: Type["UploadFile"]) -> Iterable[Callable[..., Any]]: - yield cls.validate - - @classmethod - def validate(cls: Type["UploadFile"], v: Any) -> Any: - if not isinstance(v, StarletteUploadFile): - raise ValueError(f"Expected UploadFile, received: {type(v)}") - return v - - @classmethod - def _validate(cls, __input_value: Any, _: Any) -> "UploadFile": - if not isinstance(__input_value, StarletteUploadFile): - raise ValueError(f"Expected UploadFile, received: {type(__input_value)}") - return cast(UploadFile, __input_value) - - if not PYDANTIC_V2: - - @classmethod - def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: - field_schema.update({"type": "string", "format": "binary"}) - - @classmethod - def __get_pydantic_json_schema__( - cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler - ) -> JsonSchemaValue: - return {"type": "string", "format": "binary"} - - @classmethod - def __get_pydantic_core_schema__( - cls, source: Type[Any], handler: Callable[[Any], CoreSchema] - ) -> CoreSchema: - return with_info_plain_validator_function(cls._validate) - - -class DefaultPlaceholder: - """ - You shouldn't use this class directly. - - It's used internally to recognize when a default value has been overwritten, even - if the overridden default value was truthy. - """ - - def __init__(self, value: Any): - self.value = value - - def __bool__(self) -> bool: - return bool(self.value) - - def __eq__(self, o: object) -> bool: - return isinstance(o, DefaultPlaceholder) and o.value == self.value - - -DefaultType = TypeVar("DefaultType") - - -def Default(value: DefaultType) -> DefaultType: - """ - You shouldn't use this function directly. - - It's used internally to recognize when a default value has been overwritten, even - if the overridden default value was truthy. - """ - return DefaultPlaceholder(value) # type: ignore diff --git a/.venv/Lib/site-packages/fastapi/dependencies/__init__.py b/.venv/Lib/site-packages/fastapi/dependencies/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/fastapi/dependencies/models.py b/.venv/Lib/site-packages/fastapi/dependencies/models.py deleted file mode 100644 index 61ef006..0000000 --- a/.venv/Lib/site-packages/fastapi/dependencies/models.py +++ /dev/null @@ -1,58 +0,0 @@ -from typing import Any, Callable, List, Optional, Sequence - -from fastapi._compat import ModelField -from fastapi.security.base import SecurityBase - - -class SecurityRequirement: - def __init__( - self, security_scheme: SecurityBase, scopes: Optional[Sequence[str]] = None - ): - self.security_scheme = security_scheme - self.scopes = scopes - - -class Dependant: - def __init__( - self, - *, - path_params: Optional[List[ModelField]] = None, - query_params: Optional[List[ModelField]] = None, - header_params: Optional[List[ModelField]] = None, - cookie_params: Optional[List[ModelField]] = None, - body_params: Optional[List[ModelField]] = None, - dependencies: Optional[List["Dependant"]] = None, - security_schemes: Optional[List[SecurityRequirement]] = None, - name: Optional[str] = None, - call: Optional[Callable[..., Any]] = None, - request_param_name: Optional[str] = None, - websocket_param_name: Optional[str] = None, - http_connection_param_name: Optional[str] = None, - response_param_name: Optional[str] = None, - background_tasks_param_name: Optional[str] = None, - security_scopes_param_name: Optional[str] = None, - security_scopes: Optional[List[str]] = None, - use_cache: bool = True, - path: Optional[str] = None, - ) -> None: - self.path_params = path_params or [] - self.query_params = query_params or [] - self.header_params = header_params or [] - self.cookie_params = cookie_params or [] - self.body_params = body_params or [] - self.dependencies = dependencies or [] - self.security_requirements = security_schemes or [] - self.request_param_name = request_param_name - self.websocket_param_name = websocket_param_name - self.http_connection_param_name = http_connection_param_name - self.response_param_name = response_param_name - self.background_tasks_param_name = background_tasks_param_name - self.security_scopes = security_scopes - self.security_scopes_param_name = security_scopes_param_name - self.name = name - self.call = call - self.use_cache = use_cache - # Store the path to be able to re-generate a dependable from it in overrides - self.path = path - # Save the cache key at creation to optimize performance - self.cache_key = (self.call, tuple(sorted(set(self.security_scopes or [])))) diff --git a/.venv/Lib/site-packages/fastapi/dependencies/utils.py b/.venv/Lib/site-packages/fastapi/dependencies/utils.py deleted file mode 100644 index b734734..0000000 --- a/.venv/Lib/site-packages/fastapi/dependencies/utils.py +++ /dev/null @@ -1,816 +0,0 @@ -import inspect -from contextlib import AsyncExitStack, contextmanager -from copy import deepcopy -from typing import ( - Any, - Callable, - Coroutine, - Dict, - ForwardRef, - List, - Mapping, - Optional, - Sequence, - Tuple, - Type, - Union, - cast, -) - -import anyio -from fastapi import params -from fastapi._compat import ( - PYDANTIC_V2, - ErrorWrapper, - ModelField, - Required, - Undefined, - _regenerate_error_with_loc, - copy_field_info, - create_body_model, - evaluate_forwardref, - field_annotation_is_scalar, - get_annotation_from_field_info, - get_missing_field_error, - is_bytes_field, - is_bytes_sequence_field, - is_scalar_field, - is_scalar_sequence_field, - is_sequence_field, - is_uploadfile_or_nonable_uploadfile_annotation, - is_uploadfile_sequence_annotation, - lenient_issubclass, - sequence_types, - serialize_sequence_value, - value_is_sequence, -) -from fastapi.background import BackgroundTasks -from fastapi.concurrency import ( - asynccontextmanager, - contextmanager_in_threadpool, -) -from fastapi.dependencies.models import Dependant, SecurityRequirement -from fastapi.logger import logger -from fastapi.security.base import SecurityBase -from fastapi.security.oauth2 import OAuth2, SecurityScopes -from fastapi.security.open_id_connect_url import OpenIdConnect -from fastapi.utils import create_response_field, get_path_param_names -from pydantic.fields import FieldInfo -from starlette.background import BackgroundTasks as StarletteBackgroundTasks -from starlette.concurrency import run_in_threadpool -from starlette.datastructures import FormData, Headers, QueryParams, UploadFile -from starlette.requests import HTTPConnection, Request -from starlette.responses import Response -from starlette.websockets import WebSocket -from typing_extensions import Annotated, get_args, get_origin - -multipart_not_installed_error = ( - 'Form data requires "python-multipart" to be installed. \n' - 'You can install "python-multipart" with: \n\n' - "pip install python-multipart\n" -) -multipart_incorrect_install_error = ( - 'Form data requires "python-multipart" to be installed. ' - 'It seems you installed "multipart" instead. \n' - 'You can remove "multipart" with: \n\n' - "pip uninstall multipart\n\n" - 'And then install "python-multipart" with: \n\n' - "pip install python-multipart\n" -) - - -def check_file_field(field: ModelField) -> None: - field_info = field.field_info - if isinstance(field_info, params.Form): - try: - # __version__ is available in both multiparts, and can be mocked - from multipart import __version__ # type: ignore - - assert __version__ - try: - # parse_options_header is only available in the right multipart - from multipart.multipart import parse_options_header # type: ignore - - assert parse_options_header - except ImportError: - logger.error(multipart_incorrect_install_error) - raise RuntimeError(multipart_incorrect_install_error) from None - except ImportError: - logger.error(multipart_not_installed_error) - raise RuntimeError(multipart_not_installed_error) from None - - -def get_param_sub_dependant( - *, - param_name: str, - depends: params.Depends, - path: str, - security_scopes: Optional[List[str]] = None, -) -> Dependant: - assert depends.dependency - return get_sub_dependant( - depends=depends, - dependency=depends.dependency, - path=path, - name=param_name, - security_scopes=security_scopes, - ) - - -def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant: - assert callable( - depends.dependency - ), "A parameter-less dependency must have a callable dependency" - return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path) - - -def get_sub_dependant( - *, - depends: params.Depends, - dependency: Callable[..., Any], - path: str, - name: Optional[str] = None, - security_scopes: Optional[List[str]] = None, -) -> Dependant: - security_requirement = None - security_scopes = security_scopes or [] - if isinstance(depends, params.Security): - dependency_scopes = depends.scopes - security_scopes.extend(dependency_scopes) - if isinstance(dependency, SecurityBase): - use_scopes: List[str] = [] - if isinstance(dependency, (OAuth2, OpenIdConnect)): - use_scopes = security_scopes - security_requirement = SecurityRequirement( - security_scheme=dependency, scopes=use_scopes - ) - sub_dependant = get_dependant( - path=path, - call=dependency, - name=name, - security_scopes=security_scopes, - use_cache=depends.use_cache, - ) - if security_requirement: - sub_dependant.security_requirements.append(security_requirement) - return sub_dependant - - -CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]] - - -def get_flat_dependant( - dependant: Dependant, - *, - skip_repeats: bool = False, - visited: Optional[List[CacheKey]] = None, -) -> Dependant: - if visited is None: - visited = [] - visited.append(dependant.cache_key) - - flat_dependant = Dependant( - path_params=dependant.path_params.copy(), - query_params=dependant.query_params.copy(), - header_params=dependant.header_params.copy(), - cookie_params=dependant.cookie_params.copy(), - body_params=dependant.body_params.copy(), - security_schemes=dependant.security_requirements.copy(), - use_cache=dependant.use_cache, - path=dependant.path, - ) - for sub_dependant in dependant.dependencies: - if skip_repeats and sub_dependant.cache_key in visited: - continue - flat_sub = get_flat_dependant( - sub_dependant, skip_repeats=skip_repeats, visited=visited - ) - flat_dependant.path_params.extend(flat_sub.path_params) - flat_dependant.query_params.extend(flat_sub.query_params) - flat_dependant.header_params.extend(flat_sub.header_params) - flat_dependant.cookie_params.extend(flat_sub.cookie_params) - flat_dependant.body_params.extend(flat_sub.body_params) - flat_dependant.security_requirements.extend(flat_sub.security_requirements) - return flat_dependant - - -def get_flat_params(dependant: Dependant) -> List[ModelField]: - flat_dependant = get_flat_dependant(dependant, skip_repeats=True) - return ( - flat_dependant.path_params - + flat_dependant.query_params - + flat_dependant.header_params - + flat_dependant.cookie_params - ) - - -def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature: - signature = inspect.signature(call) - globalns = getattr(call, "__globals__", {}) - typed_params = [ - inspect.Parameter( - name=param.name, - kind=param.kind, - default=param.default, - annotation=get_typed_annotation(param.annotation, globalns), - ) - for param in signature.parameters.values() - ] - typed_signature = inspect.Signature(typed_params) - return typed_signature - - -def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any: - if isinstance(annotation, str): - annotation = ForwardRef(annotation) - annotation = evaluate_forwardref(annotation, globalns, globalns) - return annotation - - -def get_typed_return_annotation(call: Callable[..., Any]) -> Any: - signature = inspect.signature(call) - annotation = signature.return_annotation - - if annotation is inspect.Signature.empty: - return None - - globalns = getattr(call, "__globals__", {}) - return get_typed_annotation(annotation, globalns) - - -def get_dependant( - *, - path: str, - call: Callable[..., Any], - name: Optional[str] = None, - security_scopes: Optional[List[str]] = None, - use_cache: bool = True, -) -> Dependant: - path_param_names = get_path_param_names(path) - endpoint_signature = get_typed_signature(call) - signature_params = endpoint_signature.parameters - dependant = Dependant( - call=call, - name=name, - path=path, - security_scopes=security_scopes, - use_cache=use_cache, - ) - for param_name, param in signature_params.items(): - is_path_param = param_name in path_param_names - type_annotation, depends, param_field = analyze_param( - param_name=param_name, - annotation=param.annotation, - value=param.default, - is_path_param=is_path_param, - ) - if depends is not None: - sub_dependant = get_param_sub_dependant( - param_name=param_name, - depends=depends, - path=path, - security_scopes=security_scopes, - ) - dependant.dependencies.append(sub_dependant) - continue - if add_non_field_param_to_dependency( - param_name=param_name, - type_annotation=type_annotation, - dependant=dependant, - ): - assert ( - param_field is None - ), f"Cannot specify multiple FastAPI annotations for {param_name!r}" - continue - assert param_field is not None - if is_body_param(param_field=param_field, is_path_param=is_path_param): - dependant.body_params.append(param_field) - else: - add_param_to_fields(field=param_field, dependant=dependant) - return dependant - - -def add_non_field_param_to_dependency( - *, param_name: str, type_annotation: Any, dependant: Dependant -) -> Optional[bool]: - if lenient_issubclass(type_annotation, Request): - dependant.request_param_name = param_name - return True - elif lenient_issubclass(type_annotation, WebSocket): - dependant.websocket_param_name = param_name - return True - elif lenient_issubclass(type_annotation, HTTPConnection): - dependant.http_connection_param_name = param_name - return True - elif lenient_issubclass(type_annotation, Response): - dependant.response_param_name = param_name - return True - elif lenient_issubclass(type_annotation, StarletteBackgroundTasks): - dependant.background_tasks_param_name = param_name - return True - elif lenient_issubclass(type_annotation, SecurityScopes): - dependant.security_scopes_param_name = param_name - return True - return None - - -def analyze_param( - *, - param_name: str, - annotation: Any, - value: Any, - is_path_param: bool, -) -> Tuple[Any, Optional[params.Depends], Optional[ModelField]]: - field_info = None - depends = None - type_annotation: Any = Any - use_annotation: Any = Any - if annotation is not inspect.Signature.empty: - use_annotation = annotation - type_annotation = annotation - if get_origin(use_annotation) is Annotated: - annotated_args = get_args(annotation) - type_annotation = annotated_args[0] - fastapi_annotations = [ - arg - for arg in annotated_args[1:] - if isinstance(arg, (FieldInfo, params.Depends)) - ] - fastapi_specific_annotations = [ - arg - for arg in fastapi_annotations - if isinstance(arg, (params.Param, params.Body, params.Depends)) - ] - if fastapi_specific_annotations: - fastapi_annotation: Union[ - FieldInfo, params.Depends, None - ] = fastapi_specific_annotations[-1] - else: - fastapi_annotation = None - if isinstance(fastapi_annotation, FieldInfo): - # Copy `field_info` because we mutate `field_info.default` below. - field_info = copy_field_info( - field_info=fastapi_annotation, annotation=use_annotation - ) - assert field_info.default is Undefined or field_info.default is Required, ( - f"`{field_info.__class__.__name__}` default value cannot be set in" - f" `Annotated` for {param_name!r}. Set the default value with `=` instead." - ) - if value is not inspect.Signature.empty: - assert not is_path_param, "Path parameters cannot have default values" - field_info.default = value - else: - field_info.default = Required - elif isinstance(fastapi_annotation, params.Depends): - depends = fastapi_annotation - - if isinstance(value, params.Depends): - assert depends is None, ( - "Cannot specify `Depends` in `Annotated` and default value" - f" together for {param_name!r}" - ) - assert field_info is None, ( - "Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a" - f" default value together for {param_name!r}" - ) - depends = value - elif isinstance(value, FieldInfo): - assert field_info is None, ( - "Cannot specify FastAPI annotations in `Annotated` and default value" - f" together for {param_name!r}" - ) - field_info = value - if PYDANTIC_V2: - field_info.annotation = type_annotation - - if depends is not None and depends.dependency is None: - depends.dependency = type_annotation - - if lenient_issubclass( - type_annotation, - ( - Request, - WebSocket, - HTTPConnection, - Response, - StarletteBackgroundTasks, - SecurityScopes, - ), - ): - assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}" - assert ( - field_info is None - ), f"Cannot specify FastAPI annotation for type {type_annotation!r}" - elif field_info is None and depends is None: - default_value = value if value is not inspect.Signature.empty else Required - if is_path_param: - # We might check here that `default_value is Required`, but the fact is that the same - # parameter might sometimes be a path parameter and sometimes not. See - # `tests/test_infer_param_optionality.py` for an example. - field_info = params.Path(annotation=use_annotation) - elif is_uploadfile_or_nonable_uploadfile_annotation( - type_annotation - ) or is_uploadfile_sequence_annotation(type_annotation): - field_info = params.File(annotation=use_annotation, default=default_value) - elif not field_annotation_is_scalar(annotation=type_annotation): - field_info = params.Body(annotation=use_annotation, default=default_value) - else: - field_info = params.Query(annotation=use_annotation, default=default_value) - - field = None - if field_info is not None: - if is_path_param: - assert isinstance(field_info, params.Path), ( - f"Cannot use `{field_info.__class__.__name__}` for path param" - f" {param_name!r}" - ) - elif ( - isinstance(field_info, params.Param) - and getattr(field_info, "in_", None) is None - ): - field_info.in_ = params.ParamTypes.query - use_annotation_from_field_info = get_annotation_from_field_info( - use_annotation, - field_info, - param_name, - ) - if not field_info.alias and getattr(field_info, "convert_underscores", None): - alias = param_name.replace("_", "-") - else: - alias = field_info.alias or param_name - field_info.alias = alias - field = create_response_field( - name=param_name, - type_=use_annotation_from_field_info, - default=field_info.default, - alias=alias, - required=field_info.default in (Required, Undefined), - field_info=field_info, - ) - - return type_annotation, depends, field - - -def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool: - if is_path_param: - assert is_scalar_field( - field=param_field - ), "Path params must be of one of the supported types" - return False - elif is_scalar_field(field=param_field): - return False - elif isinstance( - param_field.field_info, (params.Query, params.Header) - ) and is_scalar_sequence_field(param_field): - return False - else: - assert isinstance( - param_field.field_info, params.Body - ), f"Param: {param_field.name} can only be a request body, using Body()" - return True - - -def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None: - field_info = field.field_info - field_info_in = getattr(field_info, "in_", None) - if field_info_in == params.ParamTypes.path: - dependant.path_params.append(field) - elif field_info_in == params.ParamTypes.query: - dependant.query_params.append(field) - elif field_info_in == params.ParamTypes.header: - dependant.header_params.append(field) - else: - assert ( - field_info_in == params.ParamTypes.cookie - ), f"non-body parameters must be in path, query, header or cookie: {field.name}" - dependant.cookie_params.append(field) - - -def is_coroutine_callable(call: Callable[..., Any]) -> bool: - if inspect.isroutine(call): - return inspect.iscoroutinefunction(call) - if inspect.isclass(call): - return False - dunder_call = getattr(call, "__call__", None) # noqa: B004 - return inspect.iscoroutinefunction(dunder_call) - - -def is_async_gen_callable(call: Callable[..., Any]) -> bool: - if inspect.isasyncgenfunction(call): - return True - dunder_call = getattr(call, "__call__", None) # noqa: B004 - return inspect.isasyncgenfunction(dunder_call) - - -def is_gen_callable(call: Callable[..., Any]) -> bool: - if inspect.isgeneratorfunction(call): - return True - dunder_call = getattr(call, "__call__", None) # noqa: B004 - return inspect.isgeneratorfunction(dunder_call) - - -async def solve_generator( - *, call: Callable[..., Any], stack: AsyncExitStack, sub_values: Dict[str, Any] -) -> Any: - if is_gen_callable(call): - cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values)) - elif is_async_gen_callable(call): - cm = asynccontextmanager(call)(**sub_values) - return await stack.enter_async_context(cm) - - -async def solve_dependencies( - *, - request: Union[Request, WebSocket], - dependant: Dependant, - body: Optional[Union[Dict[str, Any], FormData]] = None, - background_tasks: Optional[StarletteBackgroundTasks] = None, - response: Optional[Response] = None, - dependency_overrides_provider: Optional[Any] = None, - dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None, - async_exit_stack: AsyncExitStack, -) -> Tuple[ - Dict[str, Any], - List[Any], - Optional[StarletteBackgroundTasks], - Response, - Dict[Tuple[Callable[..., Any], Tuple[str]], Any], -]: - values: Dict[str, Any] = {} - errors: List[Any] = [] - if response is None: - response = Response() - del response.headers["content-length"] - response.status_code = None # type: ignore - dependency_cache = dependency_cache or {} - sub_dependant: Dependant - for sub_dependant in dependant.dependencies: - sub_dependant.call = cast(Callable[..., Any], sub_dependant.call) - sub_dependant.cache_key = cast( - Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key - ) - call = sub_dependant.call - use_sub_dependant = sub_dependant - if ( - dependency_overrides_provider - and dependency_overrides_provider.dependency_overrides - ): - original_call = sub_dependant.call - call = getattr( - dependency_overrides_provider, "dependency_overrides", {} - ).get(original_call, original_call) - use_path: str = sub_dependant.path # type: ignore - use_sub_dependant = get_dependant( - path=use_path, - call=call, - name=sub_dependant.name, - security_scopes=sub_dependant.security_scopes, - ) - - solved_result = await solve_dependencies( - request=request, - dependant=use_sub_dependant, - body=body, - background_tasks=background_tasks, - response=response, - dependency_overrides_provider=dependency_overrides_provider, - dependency_cache=dependency_cache, - async_exit_stack=async_exit_stack, - ) - ( - sub_values, - sub_errors, - background_tasks, - _, # the subdependency returns the same response we have - sub_dependency_cache, - ) = solved_result - dependency_cache.update(sub_dependency_cache) - if sub_errors: - errors.extend(sub_errors) - continue - if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache: - solved = dependency_cache[sub_dependant.cache_key] - elif is_gen_callable(call) or is_async_gen_callable(call): - solved = await solve_generator( - call=call, stack=async_exit_stack, sub_values=sub_values - ) - elif is_coroutine_callable(call): - solved = await call(**sub_values) - else: - solved = await run_in_threadpool(call, **sub_values) - if sub_dependant.name is not None: - values[sub_dependant.name] = solved - if sub_dependant.cache_key not in dependency_cache: - dependency_cache[sub_dependant.cache_key] = solved - path_values, path_errors = request_params_to_args( - dependant.path_params, request.path_params - ) - query_values, query_errors = request_params_to_args( - dependant.query_params, request.query_params - ) - header_values, header_errors = request_params_to_args( - dependant.header_params, request.headers - ) - cookie_values, cookie_errors = request_params_to_args( - dependant.cookie_params, request.cookies - ) - values.update(path_values) - values.update(query_values) - values.update(header_values) - values.update(cookie_values) - errors += path_errors + query_errors + header_errors + cookie_errors - if dependant.body_params: - ( - body_values, - body_errors, - ) = await request_body_to_args( # body_params checked above - required_params=dependant.body_params, received_body=body - ) - values.update(body_values) - errors.extend(body_errors) - if dependant.http_connection_param_name: - values[dependant.http_connection_param_name] = request - if dependant.request_param_name and isinstance(request, Request): - values[dependant.request_param_name] = request - elif dependant.websocket_param_name and isinstance(request, WebSocket): - values[dependant.websocket_param_name] = request - if dependant.background_tasks_param_name: - if background_tasks is None: - background_tasks = BackgroundTasks() - values[dependant.background_tasks_param_name] = background_tasks - if dependant.response_param_name: - values[dependant.response_param_name] = response - if dependant.security_scopes_param_name: - values[dependant.security_scopes_param_name] = SecurityScopes( - scopes=dependant.security_scopes - ) - return values, errors, background_tasks, response, dependency_cache - - -def request_params_to_args( - required_params: Sequence[ModelField], - received_params: Union[Mapping[str, Any], QueryParams, Headers], -) -> Tuple[Dict[str, Any], List[Any]]: - values = {} - errors = [] - for field in required_params: - if is_scalar_sequence_field(field) and isinstance( - received_params, (QueryParams, Headers) - ): - value = received_params.getlist(field.alias) or field.default - else: - value = received_params.get(field.alias) - field_info = field.field_info - assert isinstance( - field_info, params.Param - ), "Params must be subclasses of Param" - loc = (field_info.in_.value, field.alias) - if value is None: - if field.required: - errors.append(get_missing_field_error(loc=loc)) - else: - values[field.name] = deepcopy(field.default) - continue - v_, errors_ = field.validate(value, values, loc=loc) - if isinstance(errors_, ErrorWrapper): - errors.append(errors_) - elif isinstance(errors_, list): - new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=()) - errors.extend(new_errors) - else: - values[field.name] = v_ - return values, errors - - -async def request_body_to_args( - required_params: List[ModelField], - received_body: Optional[Union[Dict[str, Any], FormData]], -) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]: - values = {} - errors: List[Dict[str, Any]] = [] - if required_params: - field = required_params[0] - field_info = field.field_info - embed = getattr(field_info, "embed", None) - field_alias_omitted = len(required_params) == 1 and not embed - if field_alias_omitted: - received_body = {field.alias: received_body} - - for field in required_params: - loc: Tuple[str, ...] - if field_alias_omitted: - loc = ("body",) - else: - loc = ("body", field.alias) - - value: Optional[Any] = None - if received_body is not None: - if (is_sequence_field(field)) and isinstance(received_body, FormData): - value = received_body.getlist(field.alias) - else: - try: - value = received_body.get(field.alias) - except AttributeError: - errors.append(get_missing_field_error(loc)) - continue - if ( - value is None - or (isinstance(field_info, params.Form) and value == "") - or ( - isinstance(field_info, params.Form) - and is_sequence_field(field) - and len(value) == 0 - ) - ): - if field.required: - errors.append(get_missing_field_error(loc)) - else: - values[field.name] = deepcopy(field.default) - continue - if ( - isinstance(field_info, params.File) - and is_bytes_field(field) - and isinstance(value, UploadFile) - ): - value = await value.read() - elif ( - is_bytes_sequence_field(field) - and isinstance(field_info, params.File) - and value_is_sequence(value) - ): - # For types - assert isinstance(value, sequence_types) # type: ignore[arg-type] - results: List[Union[bytes, str]] = [] - - async def process_fn( - fn: Callable[[], Coroutine[Any, Any, Any]] - ) -> None: - result = await fn() - results.append(result) # noqa: B023 - - async with anyio.create_task_group() as tg: - for sub_value in value: - tg.start_soon(process_fn, sub_value.read) - value = serialize_sequence_value(field=field, value=results) - - v_, errors_ = field.validate(value, values, loc=loc) - - if isinstance(errors_, list): - errors.extend(errors_) - elif errors_: - errors.append(errors_) - else: - values[field.name] = v_ - return values, errors - - -def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]: - flat_dependant = get_flat_dependant(dependant) - if not flat_dependant.body_params: - return None - first_param = flat_dependant.body_params[0] - field_info = first_param.field_info - embed = getattr(field_info, "embed", None) - body_param_names_set = {param.name for param in flat_dependant.body_params} - if len(body_param_names_set) == 1 and not embed: - check_file_field(first_param) - return first_param - # If one field requires to embed, all have to be embedded - # in case a sub-dependency is evaluated with a single unique body field - # That is combined (embedded) with other body fields - for param in flat_dependant.body_params: - setattr(param.field_info, "embed", True) # noqa: B010 - model_name = "Body_" + name - BodyModel = create_body_model( - fields=flat_dependant.body_params, model_name=model_name - ) - required = any(True for f in flat_dependant.body_params if f.required) - BodyFieldInfo_kwargs: Dict[str, Any] = { - "annotation": BodyModel, - "alias": "body", - } - if not required: - BodyFieldInfo_kwargs["default"] = None - if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params): - BodyFieldInfo: Type[params.Body] = params.File - elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params): - BodyFieldInfo = params.Form - else: - BodyFieldInfo = params.Body - - body_param_media_types = [ - f.field_info.media_type - for f in flat_dependant.body_params - if isinstance(f.field_info, params.Body) - ] - if len(set(body_param_media_types)) == 1: - BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0] - final_field = create_response_field( - name="body", - type_=BodyModel, - required=required, - alias="body", - field_info=BodyFieldInfo(**BodyFieldInfo_kwargs), - ) - check_file_field(final_field) - return final_field diff --git a/.venv/Lib/site-packages/fastapi/encoders.py b/.venv/Lib/site-packages/fastapi/encoders.py deleted file mode 100644 index e501713..0000000 --- a/.venv/Lib/site-packages/fastapi/encoders.py +++ /dev/null @@ -1,341 +0,0 @@ -import dataclasses -import datetime -from collections import defaultdict, deque -from decimal import Decimal -from enum import Enum -from ipaddress import ( - IPv4Address, - IPv4Interface, - IPv4Network, - IPv6Address, - IPv6Interface, - IPv6Network, -) -from pathlib import Path, PurePath -from re import Pattern -from types import GeneratorType -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union -from uuid import UUID - -from fastapi.types import IncEx -from pydantic import BaseModel -from pydantic.color import Color -from pydantic.networks import AnyUrl, NameEmail -from pydantic.types import SecretBytes, SecretStr -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - -from ._compat import PYDANTIC_V2, Url, _model_dump - - -# Taken from Pydantic v1 as is -def isoformat(o: Union[datetime.date, datetime.time]) -> str: - return o.isoformat() - - -# Taken from Pydantic v1 as is -# TODO: pv2 should this return strings instead? -def decimal_encoder(dec_value: Decimal) -> Union[int, float]: - """ - Encodes a Decimal as int of there's no exponent, otherwise float - - This is useful when we use ConstrainedDecimal to represent Numeric(x,0) - where a integer (but not int typed) is used. Encoding this as a float - results in failed round-tripping between encode and parse. - Our Id type is a prime example of this. - - >>> decimal_encoder(Decimal("1.0")) - 1.0 - - >>> decimal_encoder(Decimal("1")) - 1 - """ - if dec_value.as_tuple().exponent >= 0: # type: ignore[operator] - return int(dec_value) - else: - return float(dec_value) - - -ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = { - bytes: lambda o: o.decode(), - Color: str, - datetime.date: isoformat, - datetime.datetime: isoformat, - datetime.time: isoformat, - datetime.timedelta: lambda td: td.total_seconds(), - Decimal: decimal_encoder, - Enum: lambda o: o.value, - frozenset: list, - deque: list, - GeneratorType: list, - IPv4Address: str, - IPv4Interface: str, - IPv4Network: str, - IPv6Address: str, - IPv6Interface: str, - IPv6Network: str, - NameEmail: str, - Path: str, - Pattern: lambda o: o.pattern, - SecretBytes: str, - SecretStr: str, - set: list, - UUID: str, - Url: str, - AnyUrl: str, -} - - -def generate_encoders_by_class_tuples( - type_encoder_map: Dict[Any, Callable[[Any], Any]] -) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]: - encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict( - tuple - ) - for type_, encoder in type_encoder_map.items(): - encoders_by_class_tuples[encoder] += (type_,) - return encoders_by_class_tuples - - -encoders_by_class_tuples = generate_encoders_by_class_tuples(ENCODERS_BY_TYPE) - - -def jsonable_encoder( - obj: Annotated[ - Any, - Doc( - """ - The input object to convert to JSON. - """ - ), - ], - include: Annotated[ - Optional[IncEx], - Doc( - """ - Pydantic's `include` parameter, passed to Pydantic models to set the - fields to include. - """ - ), - ] = None, - exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Pydantic's `exclude` parameter, passed to Pydantic models to set the - fields to exclude. - """ - ), - ] = None, - by_alias: Annotated[ - bool, - Doc( - """ - Pydantic's `by_alias` parameter, passed to Pydantic models to define if - the output should use the alias names (when provided) or the Python - attribute names. In an API, if you set an alias, it's probably because you - want to use it in the result, so you probably want to leave this set to - `True`. - """ - ), - ] = True, - exclude_unset: Annotated[ - bool, - Doc( - """ - Pydantic's `exclude_unset` parameter, passed to Pydantic models to define - if it should exclude from the output the fields that were not explicitly - set (and that only had their default values). - """ - ), - ] = False, - exclude_defaults: Annotated[ - bool, - Doc( - """ - Pydantic's `exclude_defaults` parameter, passed to Pydantic models to define - if it should exclude from the output the fields that had the same default - value, even when they were explicitly set. - """ - ), - ] = False, - exclude_none: Annotated[ - bool, - Doc( - """ - Pydantic's `exclude_none` parameter, passed to Pydantic models to define - if it should exclude from the output any fields that have a `None` value. - """ - ), - ] = False, - custom_encoder: Annotated[ - Optional[Dict[Any, Callable[[Any], Any]]], - Doc( - """ - Pydantic's `custom_encoder` parameter, passed to Pydantic models to define - a custom encoder. - """ - ), - ] = None, - sqlalchemy_safe: Annotated[ - bool, - Doc( - """ - Exclude from the output any fields that start with the name `_sa`. - - This is mainly a hack for compatibility with SQLAlchemy objects, they - store internal SQLAlchemy-specific state in attributes named with `_sa`, - and those objects can't (and shouldn't be) serialized to JSON. - """ - ), - ] = True, -) -> Any: - """ - Convert any object to something that can be encoded in JSON. - - This is used internally by FastAPI to make sure anything you return can be - encoded as JSON before it is sent to the client. - - You can also use it yourself, for example to convert objects before saving them - in a database that supports only JSON. - - Read more about it in the - [FastAPI docs for JSON Compatible Encoder](https://fastapi.tiangolo.com/tutorial/encoder/). - """ - custom_encoder = custom_encoder or {} - if custom_encoder: - if type(obj) in custom_encoder: - return custom_encoder[type(obj)](obj) - else: - for encoder_type, encoder_instance in custom_encoder.items(): - if isinstance(obj, encoder_type): - return encoder_instance(obj) - if include is not None and not isinstance(include, (set, dict)): - include = set(include) - if exclude is not None and not isinstance(exclude, (set, dict)): - exclude = set(exclude) - if isinstance(obj, BaseModel): - # TODO: remove when deprecating Pydantic v1 - encoders: Dict[Any, Any] = {} - if not PYDANTIC_V2: - encoders = getattr(obj.__config__, "json_encoders", {}) # type: ignore[attr-defined] - if custom_encoder: - encoders.update(custom_encoder) - obj_dict = _model_dump( - obj, - mode="json", - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_none=exclude_none, - exclude_defaults=exclude_defaults, - ) - if "__root__" in obj_dict: - obj_dict = obj_dict["__root__"] - return jsonable_encoder( - obj_dict, - exclude_none=exclude_none, - exclude_defaults=exclude_defaults, - # TODO: remove when deprecating Pydantic v1 - custom_encoder=encoders, - sqlalchemy_safe=sqlalchemy_safe, - ) - if dataclasses.is_dataclass(obj): - obj_dict = dataclasses.asdict(obj) - return jsonable_encoder( - obj_dict, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - custom_encoder=custom_encoder, - sqlalchemy_safe=sqlalchemy_safe, - ) - if isinstance(obj, Enum): - return obj.value - if isinstance(obj, PurePath): - return str(obj) - if isinstance(obj, (str, int, float, type(None))): - return obj - if isinstance(obj, dict): - encoded_dict = {} - allowed_keys = set(obj.keys()) - if include is not None: - allowed_keys &= set(include) - if exclude is not None: - allowed_keys -= set(exclude) - for key, value in obj.items(): - if ( - ( - not sqlalchemy_safe - or (not isinstance(key, str)) - or (not key.startswith("_sa")) - ) - and (value is not None or not exclude_none) - and key in allowed_keys - ): - encoded_key = jsonable_encoder( - key, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_none=exclude_none, - custom_encoder=custom_encoder, - sqlalchemy_safe=sqlalchemy_safe, - ) - encoded_value = jsonable_encoder( - value, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_none=exclude_none, - custom_encoder=custom_encoder, - sqlalchemy_safe=sqlalchemy_safe, - ) - encoded_dict[encoded_key] = encoded_value - return encoded_dict - if isinstance(obj, (list, set, frozenset, GeneratorType, tuple, deque)): - encoded_list = [] - for item in obj: - encoded_list.append( - jsonable_encoder( - item, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - custom_encoder=custom_encoder, - sqlalchemy_safe=sqlalchemy_safe, - ) - ) - return encoded_list - - if type(obj) in ENCODERS_BY_TYPE: - return ENCODERS_BY_TYPE[type(obj)](obj) - for encoder, classes_tuple in encoders_by_class_tuples.items(): - if isinstance(obj, classes_tuple): - return encoder(obj) - - try: - data = dict(obj) - except Exception as e: - errors: List[Exception] = [] - errors.append(e) - try: - data = vars(obj) - except Exception as e: - errors.append(e) - raise ValueError(errors) from e - return jsonable_encoder( - data, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - custom_encoder=custom_encoder, - sqlalchemy_safe=sqlalchemy_safe, - ) diff --git a/.venv/Lib/site-packages/fastapi/exception_handlers.py b/.venv/Lib/site-packages/fastapi/exception_handlers.py deleted file mode 100644 index 6c2ba7f..0000000 --- a/.venv/Lib/site-packages/fastapi/exception_handlers.py +++ /dev/null @@ -1,34 +0,0 @@ -from fastapi.encoders import jsonable_encoder -from fastapi.exceptions import RequestValidationError, WebSocketRequestValidationError -from fastapi.utils import is_body_allowed_for_status_code -from fastapi.websockets import WebSocket -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.responses import JSONResponse, Response -from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY, WS_1008_POLICY_VIOLATION - - -async def http_exception_handler(request: Request, exc: HTTPException) -> Response: - headers = getattr(exc, "headers", None) - if not is_body_allowed_for_status_code(exc.status_code): - return Response(status_code=exc.status_code, headers=headers) - return JSONResponse( - {"detail": exc.detail}, status_code=exc.status_code, headers=headers - ) - - -async def request_validation_exception_handler( - request: Request, exc: RequestValidationError -) -> JSONResponse: - return JSONResponse( - status_code=HTTP_422_UNPROCESSABLE_ENTITY, - content={"detail": jsonable_encoder(exc.errors())}, - ) - - -async def websocket_request_validation_exception_handler( - websocket: WebSocket, exc: WebSocketRequestValidationError -) -> None: - await websocket.close( - code=WS_1008_POLICY_VIOLATION, reason=jsonable_encoder(exc.errors()) - ) diff --git a/.venv/Lib/site-packages/fastapi/exceptions.py b/.venv/Lib/site-packages/fastapi/exceptions.py deleted file mode 100644 index 680d288..0000000 --- a/.venv/Lib/site-packages/fastapi/exceptions.py +++ /dev/null @@ -1,176 +0,0 @@ -from typing import Any, Dict, Optional, Sequence, Type, Union - -from pydantic import BaseModel, create_model -from starlette.exceptions import HTTPException as StarletteHTTPException -from starlette.exceptions import WebSocketException as StarletteWebSocketException -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class HTTPException(StarletteHTTPException): - """ - An HTTP exception you can raise in your own code to show errors to the client. - - This is for client errors, invalid authentication, invalid data, etc. Not for server - errors in your code. - - Read more about it in the - [FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/). - - ## Example - - ```python - from fastapi import FastAPI, HTTPException - - app = FastAPI() - - items = {"foo": "The Foo Wrestlers"} - - - @app.get("/items/{item_id}") - async def read_item(item_id: str): - if item_id not in items: - raise HTTPException(status_code=404, detail="Item not found") - return {"item": items[item_id]} - ``` - """ - - def __init__( - self, - status_code: Annotated[ - int, - Doc( - """ - HTTP status code to send to the client. - """ - ), - ], - detail: Annotated[ - Any, - Doc( - """ - Any data to be sent to the client in the `detail` key of the JSON - response. - """ - ), - ] = None, - headers: Annotated[ - Optional[Dict[str, str]], - Doc( - """ - Any headers to send to the client in the response. - """ - ), - ] = None, - ) -> None: - super().__init__(status_code=status_code, detail=detail, headers=headers) - - -class WebSocketException(StarletteWebSocketException): - """ - A WebSocket exception you can raise in your own code to show errors to the client. - - This is for client errors, invalid authentication, invalid data, etc. Not for server - errors in your code. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - - ## Example - - ```python - from typing import Annotated - - from fastapi import ( - Cookie, - FastAPI, - WebSocket, - WebSocketException, - status, - ) - - app = FastAPI() - - @app.websocket("/items/{item_id}/ws") - async def websocket_endpoint( - *, - websocket: WebSocket, - session: Annotated[str | None, Cookie()] = None, - item_id: str, - ): - if session is None: - raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION) - await websocket.accept() - while True: - data = await websocket.receive_text() - await websocket.send_text(f"Session cookie is: {session}") - await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}") - ``` - """ - - def __init__( - self, - code: Annotated[ - int, - Doc( - """ - A closing code from the - [valid codes defined in the specification](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1). - """ - ), - ], - reason: Annotated[ - Union[str, None], - Doc( - """ - The reason to close the WebSocket connection. - - It is UTF-8-encoded data. The interpretation of the reason is up to the - application, it is not specified by the WebSocket specification. - - It could contain text that could be human-readable or interpretable - by the client code, etc. - """ - ), - ] = None, - ) -> None: - super().__init__(code=code, reason=reason) - - -RequestErrorModel: Type[BaseModel] = create_model("Request") -WebSocketErrorModel: Type[BaseModel] = create_model("WebSocket") - - -class FastAPIError(RuntimeError): - """ - A generic, FastAPI-specific error. - """ - - -class ValidationException(Exception): - def __init__(self, errors: Sequence[Any]) -> None: - self._errors = errors - - def errors(self) -> Sequence[Any]: - return self._errors - - -class RequestValidationError(ValidationException): - def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None: - super().__init__(errors) - self.body = body - - -class WebSocketRequestValidationError(ValidationException): - pass - - -class ResponseValidationError(ValidationException): - def __init__(self, errors: Sequence[Any], *, body: Any = None) -> None: - super().__init__(errors) - self.body = body - - def __str__(self) -> str: - message = f"{len(self._errors)} validation errors:\n" - for err in self._errors: - message += f" {err}\n" - return message diff --git a/.venv/Lib/site-packages/fastapi/logger.py b/.venv/Lib/site-packages/fastapi/logger.py deleted file mode 100644 index 5b2c4ad..0000000 --- a/.venv/Lib/site-packages/fastapi/logger.py +++ /dev/null @@ -1,3 +0,0 @@ -import logging - -logger = logging.getLogger("fastapi") diff --git a/.venv/Lib/site-packages/fastapi/middleware/__init__.py b/.venv/Lib/site-packages/fastapi/middleware/__init__.py deleted file mode 100644 index 620296d..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.middleware import Middleware as Middleware diff --git a/.venv/Lib/site-packages/fastapi/middleware/cors.py b/.venv/Lib/site-packages/fastapi/middleware/cors.py deleted file mode 100644 index 8dfaad0..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/cors.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.middleware.cors import CORSMiddleware as CORSMiddleware # noqa diff --git a/.venv/Lib/site-packages/fastapi/middleware/gzip.py b/.venv/Lib/site-packages/fastapi/middleware/gzip.py deleted file mode 100644 index bbeb2cc..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/gzip.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.middleware.gzip import GZipMiddleware as GZipMiddleware # noqa diff --git a/.venv/Lib/site-packages/fastapi/middleware/httpsredirect.py b/.venv/Lib/site-packages/fastapi/middleware/httpsredirect.py deleted file mode 100644 index b7a3d8e..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/httpsredirect.py +++ /dev/null @@ -1,3 +0,0 @@ -from starlette.middleware.httpsredirect import ( # noqa - HTTPSRedirectMiddleware as HTTPSRedirectMiddleware, -) diff --git a/.venv/Lib/site-packages/fastapi/middleware/trustedhost.py b/.venv/Lib/site-packages/fastapi/middleware/trustedhost.py deleted file mode 100644 index 08d7e03..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/trustedhost.py +++ /dev/null @@ -1,3 +0,0 @@ -from starlette.middleware.trustedhost import ( # noqa - TrustedHostMiddleware as TrustedHostMiddleware, -) diff --git a/.venv/Lib/site-packages/fastapi/middleware/wsgi.py b/.venv/Lib/site-packages/fastapi/middleware/wsgi.py deleted file mode 100644 index c4c6a79..0000000 --- a/.venv/Lib/site-packages/fastapi/middleware/wsgi.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.middleware.wsgi import WSGIMiddleware as WSGIMiddleware # noqa diff --git a/.venv/Lib/site-packages/fastapi/openapi/__init__.py b/.venv/Lib/site-packages/fastapi/openapi/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/fastapi/openapi/constants.py b/.venv/Lib/site-packages/fastapi/openapi/constants.py deleted file mode 100644 index d724ee3..0000000 --- a/.venv/Lib/site-packages/fastapi/openapi/constants.py +++ /dev/null @@ -1,3 +0,0 @@ -METHODS_WITH_BODY = {"GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"} -REF_PREFIX = "#/components/schemas/" -REF_TEMPLATE = "#/components/schemas/{model}" diff --git a/.venv/Lib/site-packages/fastapi/openapi/docs.py b/.venv/Lib/site-packages/fastapi/openapi/docs.py deleted file mode 100644 index 69473d1..0000000 --- a/.venv/Lib/site-packages/fastapi/openapi/docs.py +++ /dev/null @@ -1,344 +0,0 @@ -import json -from typing import Any, Dict, Optional - -from fastapi.encoders import jsonable_encoder -from starlette.responses import HTMLResponse -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - -swagger_ui_default_parameters: Annotated[ - Dict[str, Any], - Doc( - """ - Default configurations for Swagger UI. - - You can use it as a template to add any other configurations needed. - """ - ), -] = { - "dom_id": "#swagger-ui", - "layout": "BaseLayout", - "deepLinking": True, - "showExtensions": True, - "showCommonExtensions": True, -} - - -def get_swagger_ui_html( - *, - openapi_url: Annotated[ - str, - Doc( - """ - The OpenAPI URL that Swagger UI should load and use. - - This is normally done automatically by FastAPI using the default URL - `/openapi.json`. - """ - ), - ], - title: Annotated[ - str, - Doc( - """ - The HTML `` content, normally shown in the browser tab. - """ - ), - ], - swagger_js_url: Annotated[ - str, - Doc( - """ - The URL to use to load the Swagger UI JavaScript. - - It is normally set to a CDN URL. - """ - ), - ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui-bundle.js", - swagger_css_url: Annotated[ - str, - Doc( - """ - The URL to use to load the Swagger UI CSS. - - It is normally set to a CDN URL. - """ - ), - ] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.9.0/swagger-ui.css", - swagger_favicon_url: Annotated[ - str, - Doc( - """ - The URL of the favicon to use. It is normally shown in the browser tab. - """ - ), - ] = "https://fastapi.tiangolo.com/img/favicon.png", - oauth2_redirect_url: Annotated[ - Optional[str], - Doc( - """ - The OAuth2 redirect URL, it is normally automatically handled by FastAPI. - """ - ), - ] = None, - init_oauth: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - A dictionary with Swagger UI OAuth2 initialization configurations. - """ - ), - ] = None, - swagger_ui_parameters: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Configuration parameters for Swagger UI. - - It defaults to [swagger_ui_default_parameters][fastapi.openapi.docs.swagger_ui_default_parameters]. - """ - ), - ] = None, -) -> HTMLResponse: - """ - Generate and return the HTML that loads Swagger UI for the interactive - API docs (normally served at `/docs`). - - You would only call this function yourself if you needed to override some parts, - for example the URLs to use to load Swagger UI's JavaScript and CSS. - - Read more about it in the - [FastAPI docs for Configure Swagger UI](https://fastapi.tiangolo.com/how-to/configure-swagger-ui/) - and the [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/). - """ - current_swagger_ui_parameters = swagger_ui_default_parameters.copy() - if swagger_ui_parameters: - current_swagger_ui_parameters.update(swagger_ui_parameters) - - html = f""" - <!DOCTYPE html> - <html> - <head> - <link type="text/css" rel="stylesheet" href="{swagger_css_url}"> - <link rel="shortcut icon" href="{swagger_favicon_url}"> - <title>{title} - - -
-
- - - - - - """ - return HTMLResponse(html) - - -def get_redoc_html( - *, - openapi_url: Annotated[ - str, - Doc( - """ - The OpenAPI URL that ReDoc should load and use. - - This is normally done automatically by FastAPI using the default URL - `/openapi.json`. - """ - ), - ], - title: Annotated[ - str, - Doc( - """ - The HTML `` content, normally shown in the browser tab. - """ - ), - ], - redoc_js_url: Annotated[ - str, - Doc( - """ - The URL to use to load the ReDoc JavaScript. - - It is normally set to a CDN URL. - """ - ), - ] = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js", - redoc_favicon_url: Annotated[ - str, - Doc( - """ - The URL of the favicon to use. It is normally shown in the browser tab. - """ - ), - ] = "https://fastapi.tiangolo.com/img/favicon.png", - with_google_fonts: Annotated[ - bool, - Doc( - """ - Load and use Google Fonts. - """ - ), - ] = True, -) -> HTMLResponse: - """ - Generate and return the HTML response that loads ReDoc for the alternative - API docs (normally served at `/redoc`). - - You would only call this function yourself if you needed to override some parts, - for example the URLs to use to load ReDoc's JavaScript and CSS. - - Read more about it in the - [FastAPI docs for Custom Docs UI Static Assets (Self-Hosting)](https://fastapi.tiangolo.com/how-to/custom-docs-ui-assets/). - """ - html = f""" - <!DOCTYPE html> - <html> - <head> - <title>{title} - - - - """ - if with_google_fonts: - html += """ - - """ - html += f""" - - - - - - - - - - - """ - return HTMLResponse(html) - - -def get_swagger_ui_oauth2_redirect_html() -> HTMLResponse: - """ - Generate the HTML response with the OAuth2 redirection for Swagger UI. - - You normally don't need to use or change this. - """ - # copied from https://github.com/swagger-api/swagger-ui/blob/v4.14.0/dist/oauth2-redirect.html - html = """ - - - - Swagger UI: OAuth2 Redirect - - - - - - """ - return HTMLResponse(content=html) diff --git a/.venv/Lib/site-packages/fastapi/openapi/models.py b/.venv/Lib/site-packages/fastapi/openapi/models.py deleted file mode 100644 index 5f3bdbb..0000000 --- a/.venv/Lib/site-packages/fastapi/openapi/models.py +++ /dev/null @@ -1,611 +0,0 @@ -from enum import Enum -from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union - -from fastapi._compat import ( - PYDANTIC_V2, - CoreSchema, - GetJsonSchemaHandler, - JsonSchemaValue, - _model_rebuild, - with_info_plain_validator_function, -) -from fastapi.logger import logger -from pydantic import AnyUrl, BaseModel, Field -from typing_extensions import Annotated, Literal, TypedDict -from typing_extensions import deprecated as typing_deprecated - -try: - import email_validator - - assert email_validator # make autoflake ignore the unused import - from pydantic import EmailStr -except ImportError: # pragma: no cover - - class EmailStr(str): # type: ignore - @classmethod - def __get_validators__(cls) -> Iterable[Callable[..., Any]]: - yield cls.validate - - @classmethod - def validate(cls, v: Any) -> str: - logger.warning( - "email-validator not installed, email fields will be treated as str.\n" - "To install, run: pip install email-validator" - ) - return str(v) - - @classmethod - def _validate(cls, __input_value: Any, _: Any) -> str: - logger.warning( - "email-validator not installed, email fields will be treated as str.\n" - "To install, run: pip install email-validator" - ) - return str(__input_value) - - @classmethod - def __get_pydantic_json_schema__( - cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler - ) -> JsonSchemaValue: - return {"type": "string", "format": "email"} - - @classmethod - def __get_pydantic_core_schema__( - cls, source: Type[Any], handler: Callable[[Any], CoreSchema] - ) -> CoreSchema: - return with_info_plain_validator_function(cls._validate) - - -class Contact(BaseModel): - name: Optional[str] = None - url: Optional[AnyUrl] = None - email: Optional[EmailStr] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class License(BaseModel): - name: str - identifier: Optional[str] = None - url: Optional[AnyUrl] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Info(BaseModel): - title: str - summary: Optional[str] = None - description: Optional[str] = None - termsOfService: Optional[str] = None - contact: Optional[Contact] = None - license: Optional[License] = None - version: str - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class ServerVariable(BaseModel): - enum: Annotated[Optional[List[str]], Field(min_length=1)] = None - default: str - description: Optional[str] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Server(BaseModel): - url: Union[AnyUrl, str] - description: Optional[str] = None - variables: Optional[Dict[str, ServerVariable]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Reference(BaseModel): - ref: str = Field(alias="$ref") - - -class Discriminator(BaseModel): - propertyName: str - mapping: Optional[Dict[str, str]] = None - - -class XML(BaseModel): - name: Optional[str] = None - namespace: Optional[str] = None - prefix: Optional[str] = None - attribute: Optional[bool] = None - wrapped: Optional[bool] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class ExternalDocumentation(BaseModel): - description: Optional[str] = None - url: AnyUrl - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Schema(BaseModel): - # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu - # Core Vocabulary - schema_: Optional[str] = Field(default=None, alias="$schema") - vocabulary: Optional[str] = Field(default=None, alias="$vocabulary") - id: Optional[str] = Field(default=None, alias="$id") - anchor: Optional[str] = Field(default=None, alias="$anchor") - dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor") - ref: Optional[str] = Field(default=None, alias="$ref") - dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef") - defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs") - comment: Optional[str] = Field(default=None, alias="$comment") - # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s - # A Vocabulary for Applying Subschemas - allOf: Optional[List["SchemaOrBool"]] = None - anyOf: Optional[List["SchemaOrBool"]] = None - oneOf: Optional[List["SchemaOrBool"]] = None - not_: Optional["SchemaOrBool"] = Field(default=None, alias="not") - if_: Optional["SchemaOrBool"] = Field(default=None, alias="if") - then: Optional["SchemaOrBool"] = None - else_: Optional["SchemaOrBool"] = Field(default=None, alias="else") - dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None - prefixItems: Optional[List["SchemaOrBool"]] = None - # TODO: uncomment and remove below when deprecating Pydantic v1 - # It generales a list of schemas for tuples, before prefixItems was available - # items: Optional["SchemaOrBool"] = None - items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None - contains: Optional["SchemaOrBool"] = None - properties: Optional[Dict[str, "SchemaOrBool"]] = None - patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None - additionalProperties: Optional["SchemaOrBool"] = None - propertyNames: Optional["SchemaOrBool"] = None - unevaluatedItems: Optional["SchemaOrBool"] = None - unevaluatedProperties: Optional["SchemaOrBool"] = None - # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural - # A Vocabulary for Structural Validation - type: Optional[str] = None - enum: Optional[List[Any]] = None - const: Optional[Any] = None - multipleOf: Optional[float] = Field(default=None, gt=0) - maximum: Optional[float] = None - exclusiveMaximum: Optional[float] = None - minimum: Optional[float] = None - exclusiveMinimum: Optional[float] = None - maxLength: Optional[int] = Field(default=None, ge=0) - minLength: Optional[int] = Field(default=None, ge=0) - pattern: Optional[str] = None - maxItems: Optional[int] = Field(default=None, ge=0) - minItems: Optional[int] = Field(default=None, ge=0) - uniqueItems: Optional[bool] = None - maxContains: Optional[int] = Field(default=None, ge=0) - minContains: Optional[int] = Field(default=None, ge=0) - maxProperties: Optional[int] = Field(default=None, ge=0) - minProperties: Optional[int] = Field(default=None, ge=0) - required: Optional[List[str]] = None - dependentRequired: Optional[Dict[str, Set[str]]] = None - # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c - # Vocabularies for Semantic Content With "format" - format: Optional[str] = None - # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten - # A Vocabulary for the Contents of String-Encoded Data - contentEncoding: Optional[str] = None - contentMediaType: Optional[str] = None - contentSchema: Optional["SchemaOrBool"] = None - # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta - # A Vocabulary for Basic Meta-Data Annotations - title: Optional[str] = None - description: Optional[str] = None - default: Optional[Any] = None - deprecated: Optional[bool] = None - readOnly: Optional[bool] = None - writeOnly: Optional[bool] = None - examples: Optional[List[Any]] = None - # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object - # Schema Object - discriminator: Optional[Discriminator] = None - xml: Optional[XML] = None - externalDocs: Optional[ExternalDocumentation] = None - example: Annotated[ - Optional[Any], - typing_deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents -# A JSON Schema MUST be an object or a boolean. -SchemaOrBool = Union[Schema, bool] - - -class Example(TypedDict, total=False): - summary: Optional[str] - description: Optional[str] - value: Optional[Any] - externalValue: Optional[AnyUrl] - - if PYDANTIC_V2: # type: ignore [misc] - __pydantic_config__ = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class ParameterInType(Enum): - query = "query" - header = "header" - path = "path" - cookie = "cookie" - - -class Encoding(BaseModel): - contentType: Optional[str] = None - headers: Optional[Dict[str, Union["Header", Reference]]] = None - style: Optional[str] = None - explode: Optional[bool] = None - allowReserved: Optional[bool] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class MediaType(BaseModel): - schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") - example: Optional[Any] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - encoding: Optional[Dict[str, Encoding]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class ParameterBase(BaseModel): - description: Optional[str] = None - required: Optional[bool] = None - deprecated: Optional[bool] = None - # Serialization rules for simple scenarios - style: Optional[str] = None - explode: Optional[bool] = None - allowReserved: Optional[bool] = None - schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") - example: Optional[Any] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - # Serialization rules for more complex scenarios - content: Optional[Dict[str, MediaType]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Parameter(ParameterBase): - name: str - in_: ParameterInType = Field(alias="in") - - -class Header(ParameterBase): - pass - - -class RequestBody(BaseModel): - description: Optional[str] = None - content: Dict[str, MediaType] - required: Optional[bool] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Link(BaseModel): - operationRef: Optional[str] = None - operationId: Optional[str] = None - parameters: Optional[Dict[str, Union[Any, str]]] = None - requestBody: Optional[Union[Any, str]] = None - description: Optional[str] = None - server: Optional[Server] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Response(BaseModel): - description: str - headers: Optional[Dict[str, Union[Header, Reference]]] = None - content: Optional[Dict[str, MediaType]] = None - links: Optional[Dict[str, Union[Link, Reference]]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Operation(BaseModel): - tags: Optional[List[str]] = None - summary: Optional[str] = None - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None - operationId: Optional[str] = None - parameters: Optional[List[Union[Parameter, Reference]]] = None - requestBody: Optional[Union[RequestBody, Reference]] = None - # Using Any for Specification Extensions - responses: Optional[Dict[str, Union[Response, Any]]] = None - callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None - deprecated: Optional[bool] = None - security: Optional[List[Dict[str, List[str]]]] = None - servers: Optional[List[Server]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class PathItem(BaseModel): - ref: Optional[str] = Field(default=None, alias="$ref") - summary: Optional[str] = None - description: Optional[str] = None - get: Optional[Operation] = None - put: Optional[Operation] = None - post: Optional[Operation] = None - delete: Optional[Operation] = None - options: Optional[Operation] = None - head: Optional[Operation] = None - patch: Optional[Operation] = None - trace: Optional[Operation] = None - servers: Optional[List[Server]] = None - parameters: Optional[List[Union[Parameter, Reference]]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class SecuritySchemeType(Enum): - apiKey = "apiKey" - http = "http" - oauth2 = "oauth2" - openIdConnect = "openIdConnect" - - -class SecurityBase(BaseModel): - type_: SecuritySchemeType = Field(alias="type") - description: Optional[str] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class APIKeyIn(Enum): - query = "query" - header = "header" - cookie = "cookie" - - -class APIKey(SecurityBase): - type_: SecuritySchemeType = Field(default=SecuritySchemeType.apiKey, alias="type") - in_: APIKeyIn = Field(alias="in") - name: str - - -class HTTPBase(SecurityBase): - type_: SecuritySchemeType = Field(default=SecuritySchemeType.http, alias="type") - scheme: str - - -class HTTPBearer(HTTPBase): - scheme: Literal["bearer"] = "bearer" - bearerFormat: Optional[str] = None - - -class OAuthFlow(BaseModel): - refreshUrl: Optional[str] = None - scopes: Dict[str, str] = {} - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class OAuthFlowImplicit(OAuthFlow): - authorizationUrl: str - - -class OAuthFlowPassword(OAuthFlow): - tokenUrl: str - - -class OAuthFlowClientCredentials(OAuthFlow): - tokenUrl: str - - -class OAuthFlowAuthorizationCode(OAuthFlow): - authorizationUrl: str - tokenUrl: str - - -class OAuthFlows(BaseModel): - implicit: Optional[OAuthFlowImplicit] = None - password: Optional[OAuthFlowPassword] = None - clientCredentials: Optional[OAuthFlowClientCredentials] = None - authorizationCode: Optional[OAuthFlowAuthorizationCode] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class OAuth2(SecurityBase): - type_: SecuritySchemeType = Field(default=SecuritySchemeType.oauth2, alias="type") - flows: OAuthFlows - - -class OpenIdConnect(SecurityBase): - type_: SecuritySchemeType = Field( - default=SecuritySchemeType.openIdConnect, alias="type" - ) - openIdConnectUrl: str - - -SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer] - - -class Components(BaseModel): - schemas: Optional[Dict[str, Union[Schema, Reference]]] = None - responses: Optional[Dict[str, Union[Response, Reference]]] = None - parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None - examples: Optional[Dict[str, Union[Example, Reference]]] = None - requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None - headers: Optional[Dict[str, Union[Header, Reference]]] = None - securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None - links: Optional[Dict[str, Union[Link, Reference]]] = None - # Using Any for Specification Extensions - callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None - pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class Tag(BaseModel): - name: str - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -class OpenAPI(BaseModel): - openapi: str - info: Info - jsonSchemaDialect: Optional[str] = None - servers: Optional[List[Server]] = None - # Using Any for Specification Extensions - paths: Optional[Dict[str, Union[PathItem, Any]]] = None - webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None - components: Optional[Components] = None - security: Optional[List[Dict[str, List[str]]]] = None - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - - if PYDANTIC_V2: - model_config = {"extra": "allow"} - - else: - - class Config: - extra = "allow" - - -_model_rebuild(Schema) -_model_rebuild(Operation) -_model_rebuild(Encoding) diff --git a/.venv/Lib/site-packages/fastapi/openapi/utils.py b/.venv/Lib/site-packages/fastapi/openapi/utils.py deleted file mode 100644 index 5bfb5ac..0000000 --- a/.venv/Lib/site-packages/fastapi/openapi/utils.py +++ /dev/null @@ -1,530 +0,0 @@ -import http.client -import inspect -import warnings -from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast - -from fastapi import routing -from fastapi._compat import ( - GenerateJsonSchema, - JsonSchemaValue, - ModelField, - Undefined, - get_compat_model_name_map, - get_definitions, - get_schema_from_model_field, - lenient_issubclass, -) -from fastapi.datastructures import DefaultPlaceholder -from fastapi.dependencies.models import Dependant -from fastapi.dependencies.utils import get_flat_dependant, get_flat_params -from fastapi.encoders import jsonable_encoder -from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX, REF_TEMPLATE -from fastapi.openapi.models import OpenAPI -from fastapi.params import Body, Param -from fastapi.responses import Response -from fastapi.types import ModelNameMap -from fastapi.utils import ( - deep_dict_update, - generate_operation_id_for_path, - is_body_allowed_for_status_code, -) -from starlette.responses import JSONResponse -from starlette.routing import BaseRoute -from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY -from typing_extensions import Literal - -validation_error_definition = { - "title": "ValidationError", - "type": "object", - "properties": { - "loc": { - "title": "Location", - "type": "array", - "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, - }, - "msg": {"title": "Message", "type": "string"}, - "type": {"title": "Error Type", "type": "string"}, - }, - "required": ["loc", "msg", "type"], -} - -validation_error_response_definition = { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": REF_PREFIX + "ValidationError"}, - } - }, -} - -status_code_ranges: Dict[str, str] = { - "1XX": "Information", - "2XX": "Success", - "3XX": "Redirection", - "4XX": "Client Error", - "5XX": "Server Error", - "DEFAULT": "Default Response", -} - - -def get_openapi_security_definitions( - flat_dependant: Dependant, -) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]: - security_definitions = {} - operation_security = [] - for security_requirement in flat_dependant.security_requirements: - security_definition = jsonable_encoder( - security_requirement.security_scheme.model, - by_alias=True, - exclude_none=True, - ) - security_name = security_requirement.security_scheme.scheme_name - security_definitions[security_name] = security_definition - operation_security.append({security_name: security_requirement.scopes}) - return security_definitions, operation_security - - -def get_openapi_operation_parameters( - *, - all_route_params: Sequence[ModelField], - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - field_mapping: Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - separate_input_output_schemas: bool = True, -) -> List[Dict[str, Any]]: - parameters = [] - for param in all_route_params: - field_info = param.field_info - field_info = cast(Param, field_info) - if not field_info.include_in_schema: - continue - param_schema = get_schema_from_model_field( - field=param, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - parameter = { - "name": param.alias, - "in": field_info.in_.value, - "required": param.required, - "schema": param_schema, - } - if field_info.description: - parameter["description"] = field_info.description - if field_info.openapi_examples: - parameter["examples"] = jsonable_encoder(field_info.openapi_examples) - elif field_info.example != Undefined: - parameter["example"] = jsonable_encoder(field_info.example) - if field_info.deprecated: - parameter["deprecated"] = field_info.deprecated - parameters.append(parameter) - return parameters - - -def get_openapi_operation_request_body( - *, - body_field: Optional[ModelField], - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - field_mapping: Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - separate_input_output_schemas: bool = True, -) -> Optional[Dict[str, Any]]: - if not body_field: - return None - assert isinstance(body_field, ModelField) - body_schema = get_schema_from_model_field( - field=body_field, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - field_info = cast(Body, body_field.field_info) - request_media_type = field_info.media_type - required = body_field.required - request_body_oai: Dict[str, Any] = {} - if required: - request_body_oai["required"] = required - request_media_content: Dict[str, Any] = {"schema": body_schema} - if field_info.openapi_examples: - request_media_content["examples"] = jsonable_encoder( - field_info.openapi_examples - ) - elif field_info.example != Undefined: - request_media_content["example"] = jsonable_encoder(field_info.example) - request_body_oai["content"] = {request_media_type: request_media_content} - return request_body_oai - - -def generate_operation_id( - *, route: routing.APIRoute, method: str -) -> str: # pragma: nocover - warnings.warn( - "fastapi.openapi.utils.generate_operation_id() was deprecated, " - "it is not used internally, and will be removed soon", - DeprecationWarning, - stacklevel=2, - ) - if route.operation_id: - return route.operation_id - path: str = route.path_format - return generate_operation_id_for_path(name=route.name, path=path, method=method) - - -def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str: - if route.summary: - return route.summary - return route.name.replace("_", " ").title() - - -def get_openapi_operation_metadata( - *, route: routing.APIRoute, method: str, operation_ids: Set[str] -) -> Dict[str, Any]: - operation: Dict[str, Any] = {} - if route.tags: - operation["tags"] = route.tags - operation["summary"] = generate_operation_summary(route=route, method=method) - if route.description: - operation["description"] = route.description - operation_id = route.operation_id or route.unique_id - if operation_id in operation_ids: - message = ( - f"Duplicate Operation ID {operation_id} for function " - + f"{route.endpoint.__name__}" - ) - file_name = getattr(route.endpoint, "__globals__", {}).get("__file__") - if file_name: - message += f" at {file_name}" - warnings.warn(message, stacklevel=1) - operation_ids.add(operation_id) - operation["operationId"] = operation_id - if route.deprecated: - operation["deprecated"] = route.deprecated - return operation - - -def get_openapi_path( - *, - route: routing.APIRoute, - operation_ids: Set[str], - schema_generator: GenerateJsonSchema, - model_name_map: ModelNameMap, - field_mapping: Dict[ - Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue - ], - separate_input_output_schemas: bool = True, -) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]: - path = {} - security_schemes: Dict[str, Any] = {} - definitions: Dict[str, Any] = {} - assert route.methods is not None, "Methods must be a list" - if isinstance(route.response_class, DefaultPlaceholder): - current_response_class: Type[Response] = route.response_class.value - else: - current_response_class = route.response_class - assert current_response_class, "A response class is needed to generate OpenAPI" - route_response_media_type: Optional[str] = current_response_class.media_type - if route.include_in_schema: - for method in route.methods: - operation = get_openapi_operation_metadata( - route=route, method=method, operation_ids=operation_ids - ) - parameters: List[Dict[str, Any]] = [] - flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True) - security_definitions, operation_security = get_openapi_security_definitions( - flat_dependant=flat_dependant - ) - if operation_security: - operation.setdefault("security", []).extend(operation_security) - if security_definitions: - security_schemes.update(security_definitions) - all_route_params = get_flat_params(route.dependant) - operation_parameters = get_openapi_operation_parameters( - all_route_params=all_route_params, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - parameters.extend(operation_parameters) - if parameters: - all_parameters = { - (param["in"], param["name"]): param for param in parameters - } - required_parameters = { - (param["in"], param["name"]): param - for param in parameters - if param.get("required") - } - # Make sure required definitions of the same parameter take precedence - # over non-required definitions - all_parameters.update(required_parameters) - operation["parameters"] = list(all_parameters.values()) - if method in METHODS_WITH_BODY: - request_body_oai = get_openapi_operation_request_body( - body_field=route.body_field, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - if request_body_oai: - operation["requestBody"] = request_body_oai - if route.callbacks: - callbacks = {} - for callback in route.callbacks: - if isinstance(callback, routing.APIRoute): - ( - cb_path, - cb_security_schemes, - cb_definitions, - ) = get_openapi_path( - route=callback, - operation_ids=operation_ids, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - callbacks[callback.name] = {callback.path: cb_path} - operation["callbacks"] = callbacks - if route.status_code is not None: - status_code = str(route.status_code) - else: - # It would probably make more sense for all response classes to have an - # explicit default status_code, and to extract it from them, instead of - # doing this inspection tricks, that would probably be in the future - # TODO: probably make status_code a default class attribute for all - # responses in Starlette - response_signature = inspect.signature(current_response_class.__init__) - status_code_param = response_signature.parameters.get("status_code") - if status_code_param is not None: - if isinstance(status_code_param.default, int): - status_code = str(status_code_param.default) - operation.setdefault("responses", {}).setdefault(status_code, {})[ - "description" - ] = route.response_description - if route_response_media_type and is_body_allowed_for_status_code( - route.status_code - ): - response_schema = {"type": "string"} - if lenient_issubclass(current_response_class, JSONResponse): - if route.response_field: - response_schema = get_schema_from_model_field( - field=route.response_field, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - else: - response_schema = {} - operation.setdefault("responses", {}).setdefault( - status_code, {} - ).setdefault("content", {}).setdefault(route_response_media_type, {})[ - "schema" - ] = response_schema - if route.responses: - operation_responses = operation.setdefault("responses", {}) - for ( - additional_status_code, - additional_response, - ) in route.responses.items(): - process_response = additional_response.copy() - process_response.pop("model", None) - status_code_key = str(additional_status_code).upper() - if status_code_key == "DEFAULT": - status_code_key = "default" - openapi_response = operation_responses.setdefault( - status_code_key, {} - ) - assert isinstance( - process_response, dict - ), "An additional response must be a dict" - field = route.response_fields.get(additional_status_code) - additional_field_schema: Optional[Dict[str, Any]] = None - if field: - additional_field_schema = get_schema_from_model_field( - field=field, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - media_type = route_response_media_type or "application/json" - additional_schema = ( - process_response.setdefault("content", {}) - .setdefault(media_type, {}) - .setdefault("schema", {}) - ) - deep_dict_update(additional_schema, additional_field_schema) - status_text: Optional[str] = status_code_ranges.get( - str(additional_status_code).upper() - ) or http.client.responses.get(int(additional_status_code)) - description = ( - process_response.get("description") - or openapi_response.get("description") - or status_text - or "Additional Response" - ) - deep_dict_update(openapi_response, process_response) - openapi_response["description"] = description - http422 = str(HTTP_422_UNPROCESSABLE_ENTITY) - if (all_route_params or route.body_field) and not any( - status in operation["responses"] - for status in [http422, "4XX", "default"] - ): - operation["responses"][http422] = { - "description": "Validation Error", - "content": { - "application/json": { - "schema": {"$ref": REF_PREFIX + "HTTPValidationError"} - } - }, - } - if "ValidationError" not in definitions: - definitions.update( - { - "ValidationError": validation_error_definition, - "HTTPValidationError": validation_error_response_definition, - } - ) - if route.openapi_extra: - deep_dict_update(operation, route.openapi_extra) - path[method.lower()] = operation - return path, security_schemes, definitions - - -def get_fields_from_routes( - routes: Sequence[BaseRoute], -) -> List[ModelField]: - body_fields_from_routes: List[ModelField] = [] - responses_from_routes: List[ModelField] = [] - request_fields_from_routes: List[ModelField] = [] - callback_flat_models: List[ModelField] = [] - for route in routes: - if getattr(route, "include_in_schema", None) and isinstance( - route, routing.APIRoute - ): - if route.body_field: - assert isinstance( - route.body_field, ModelField - ), "A request body must be a Pydantic Field" - body_fields_from_routes.append(route.body_field) - if route.response_field: - responses_from_routes.append(route.response_field) - if route.response_fields: - responses_from_routes.extend(route.response_fields.values()) - if route.callbacks: - callback_flat_models.extend(get_fields_from_routes(route.callbacks)) - params = get_flat_params(route.dependant) - request_fields_from_routes.extend(params) - - flat_models = callback_flat_models + list( - body_fields_from_routes + responses_from_routes + request_fields_from_routes - ) - return flat_models - - -def get_openapi( - *, - title: str, - version: str, - openapi_version: str = "3.1.0", - summary: Optional[str] = None, - description: Optional[str] = None, - routes: Sequence[BaseRoute], - webhooks: Optional[Sequence[BaseRoute]] = None, - tags: Optional[List[Dict[str, Any]]] = None, - servers: Optional[List[Dict[str, Union[str, Any]]]] = None, - terms_of_service: Optional[str] = None, - contact: Optional[Dict[str, Union[str, Any]]] = None, - license_info: Optional[Dict[str, Union[str, Any]]] = None, - separate_input_output_schemas: bool = True, -) -> Dict[str, Any]: - info: Dict[str, Any] = {"title": title, "version": version} - if summary: - info["summary"] = summary - if description: - info["description"] = description - if terms_of_service: - info["termsOfService"] = terms_of_service - if contact: - info["contact"] = contact - if license_info: - info["license"] = license_info - output: Dict[str, Any] = {"openapi": openapi_version, "info": info} - if servers: - output["servers"] = servers - components: Dict[str, Dict[str, Any]] = {} - paths: Dict[str, Dict[str, Any]] = {} - webhook_paths: Dict[str, Dict[str, Any]] = {} - operation_ids: Set[str] = set() - all_fields = get_fields_from_routes(list(routes or []) + list(webhooks or [])) - model_name_map = get_compat_model_name_map(all_fields) - schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE) - field_mapping, definitions = get_definitions( - fields=all_fields, - schema_generator=schema_generator, - model_name_map=model_name_map, - separate_input_output_schemas=separate_input_output_schemas, - ) - for route in routes or []: - if isinstance(route, routing.APIRoute): - result = get_openapi_path( - route=route, - operation_ids=operation_ids, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - if result: - path, security_schemes, path_definitions = result - if path: - paths.setdefault(route.path_format, {}).update(path) - if security_schemes: - components.setdefault("securitySchemes", {}).update( - security_schemes - ) - if path_definitions: - definitions.update(path_definitions) - for webhook in webhooks or []: - if isinstance(webhook, routing.APIRoute): - result = get_openapi_path( - route=webhook, - operation_ids=operation_ids, - schema_generator=schema_generator, - model_name_map=model_name_map, - field_mapping=field_mapping, - separate_input_output_schemas=separate_input_output_schemas, - ) - if result: - path, security_schemes, path_definitions = result - if path: - webhook_paths.setdefault(webhook.path_format, {}).update(path) - if security_schemes: - components.setdefault("securitySchemes", {}).update( - security_schemes - ) - if path_definitions: - definitions.update(path_definitions) - if definitions: - components["schemas"] = {k: definitions[k] for k in sorted(definitions)} - if components: - output["components"] = components - output["paths"] = paths - if webhook_paths: - output["webhooks"] = webhook_paths - if tags: - output["tags"] = tags - return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore diff --git a/.venv/Lib/site-packages/fastapi/param_functions.py b/.venv/Lib/site-packages/fastapi/param_functions.py deleted file mode 100644 index 3f6dbc9..0000000 --- a/.venv/Lib/site-packages/fastapi/param_functions.py +++ /dev/null @@ -1,2360 +0,0 @@ -from typing import Any, Callable, Dict, List, Optional, Sequence, Union - -from fastapi import params -from fastapi._compat import Undefined -from fastapi.openapi.models import Example -from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined] - -_Unset: Any = Undefined - - -def Path( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = ..., - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - """ - Declare a path parameter for a *path operation*. - - Read more about it in the - [FastAPI docs for Path Parameters and Numeric Validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/). - - ```python - from typing import Annotated - - from fastapi import FastAPI, Path - - app = FastAPI() - - - @app.get("/items/{item_id}") - async def read_items( - item_id: Annotated[int, Path(title="The ID of the item to get")], - ): - return {"item_id": item_id} - ``` - """ - return params.Path( - default=default, - default_factory=default_factory, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Query( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.Query( - default=default, - default_factory=default_factory, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Header( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - convert_underscores: Annotated[ - bool, - Doc( - """ - Automatically convert underscores to hyphens in the parameter field name. - - Read more about it in the - [FastAPI docs for Header Parameters](https://fastapi.tiangolo.com/tutorial/header-params/#automatic-conversion) - """ - ), - ] = True, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.Header( - default=default, - default_factory=default_factory, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - convert_underscores=convert_underscores, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Cookie( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.Cookie( - default=default, - default_factory=default_factory, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Body( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - embed: Annotated[ - bool, - Doc( - """ - When `embed` is `True`, the parameter will be expected in a JSON body as a - key instead of being the JSON body itself. - - This happens automatically when more than one `Body` parameter is declared. - - Read more about it in the - [FastAPI docs for Body - Multiple Parameters](https://fastapi.tiangolo.com/tutorial/body-multiple-params/#embed-a-single-body-parameter). - """ - ), - ] = False, - media_type: Annotated[ - str, - Doc( - """ - The media type of this parameter field. Changing it would affect the - generated OpenAPI, but currently it doesn't affect the parsing of the data. - """ - ), - ] = "application/json", - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.Body( - default=default, - default_factory=default_factory, - embed=embed, - media_type=media_type, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Form( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - media_type: Annotated[ - str, - Doc( - """ - The media type of this parameter field. Changing it would affect the - generated OpenAPI, but currently it doesn't affect the parsing of the data. - """ - ), - ] = "application/x-www-form-urlencoded", - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.Form( - default=default, - default_factory=default_factory, - media_type=media_type, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def File( # noqa: N802 - default: Annotated[ - Any, - Doc( - """ - Default value if the parameter field is not set. - """ - ), - ] = Undefined, - *, - default_factory: Annotated[ - Union[Callable[[], Any], None], - Doc( - """ - A callable to generate the default value. - - This doesn't affect `Path` parameters as the value is always required. - The parameter is available only for compatibility. - """ - ), - ] = _Unset, - media_type: Annotated[ - str, - Doc( - """ - The media type of this parameter field. Changing it would affect the - generated OpenAPI, but currently it doesn't affect the parsing of the data. - """ - ), - ] = "multipart/form-data", - alias: Annotated[ - Optional[str], - Doc( - """ - An alternative name for the parameter field. - - This will be used to extract the data and for the generated OpenAPI. - It is particularly useful when you can't use the name you want because it - is a Python reserved keyword or similar. - """ - ), - ] = None, - alias_priority: Annotated[ - Union[int, None], - Doc( - """ - Priority of the alias. This affects whether an alias generator is used. - """ - ), - ] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Whitelist' validation step. The parameter field will be the single one - allowed by the alias or set of aliases defined. - """ - ), - ] = None, - serialization_alias: Annotated[ - Union[str, None], - Doc( - """ - 'Blacklist' validation step. The vanilla parameter field will be the - single one of the alias' or set of aliases' fields and all the other - fields will be ignored at serialization time. - """ - ), - ] = None, - title: Annotated[ - Optional[str], - Doc( - """ - Human-readable title. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Human-readable description. - """ - ), - ] = None, - gt: Annotated[ - Optional[float], - Doc( - """ - Greater than. If set, value must be greater than this. Only applicable to - numbers. - """ - ), - ] = None, - ge: Annotated[ - Optional[float], - Doc( - """ - Greater than or equal. If set, value must be greater than or equal to - this. Only applicable to numbers. - """ - ), - ] = None, - lt: Annotated[ - Optional[float], - Doc( - """ - Less than. If set, value must be less than this. Only applicable to numbers. - """ - ), - ] = None, - le: Annotated[ - Optional[float], - Doc( - """ - Less than or equal. If set, value must be less than or equal to this. - Only applicable to numbers. - """ - ), - ] = None, - min_length: Annotated[ - Optional[int], - Doc( - """ - Minimum length for strings. - """ - ), - ] = None, - max_length: Annotated[ - Optional[int], - Doc( - """ - Maximum length for strings. - """ - ), - ] = None, - pattern: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - ] = None, - regex: Annotated[ - Optional[str], - Doc( - """ - RegEx pattern for strings. - """ - ), - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Annotated[ - Union[str, None], - Doc( - """ - Parameter field name for discriminating the type in a tagged union. - """ - ), - ] = None, - strict: Annotated[ - Union[bool, None], - Doc( - """ - If `True`, strict validation is applied to the field. - """ - ), - ] = _Unset, - multiple_of: Annotated[ - Union[float, None], - Doc( - """ - Value must be a multiple of this. Only applicable to numbers. - """ - ), - ] = _Unset, - allow_inf_nan: Annotated[ - Union[bool, None], - Doc( - """ - Allow `inf`, `-inf`, `nan`. Only applicable to numbers. - """ - ), - ] = _Unset, - max_digits: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of allow digits for strings. - """ - ), - ] = _Unset, - decimal_places: Annotated[ - Union[int, None], - Doc( - """ - Maximum number of decimal places allowed for numbers. - """ - ), - ] = _Unset, - examples: Annotated[ - Optional[List[Any]], - Doc( - """ - Example values for this field. - """ - ), - ] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Annotated[ - Optional[Dict[str, Example]], - Doc( - """ - OpenAPI-specific examples. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Swagger UI (that provides the `/docs` interface) has better support for the - OpenAPI-specific examples than the JSON Schema `examples`, that's the main - use case for this. - - Read more about it in the - [FastAPI docs for Declare Request Example Data](https://fastapi.tiangolo.com/tutorial/schema-extra-example/#using-the-openapi_examples-parameter). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this parameter field as deprecated. - - It will affect the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) this parameter field in the generated OpenAPI. - You probably don't need it, but it's available. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - json_schema_extra: Annotated[ - Union[Dict[str, Any], None], - Doc( - """ - Any additional JSON schema data. - """ - ), - ] = None, - **extra: Annotated[ - Any, - Doc( - """ - Include extra fields used by the JSON Schema. - """ - ), - deprecated( - """ - The `extra` kwargs is deprecated. Use `json_schema_extra` instead. - """ - ), - ], -) -> Any: - return params.File( - default=default, - default_factory=default_factory, - media_type=media_type, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - example=example, - examples=examples, - openapi_examples=openapi_examples, - deprecated=deprecated, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -def Depends( # noqa: N802 - dependency: Annotated[ - Optional[Callable[..., Any]], - Doc( - """ - A "dependable" callable (like a function). - - Don't call it directly, FastAPI will call it for you, just pass the object - directly. - """ - ), - ] = None, - *, - use_cache: Annotated[ - bool, - Doc( - """ - By default, after a dependency is called the first time in a request, if - the dependency is declared again for the rest of the request (for example - if the dependency is needed by several dependencies), the value will be - re-used for the rest of the request. - - Set `use_cache` to `False` to disable this behavior and ensure the - dependency is called again (if declared more than once) in the same request. - """ - ), - ] = True, -) -> Any: - """ - Declare a FastAPI dependency. - - It takes a single "dependable" callable (like a function). - - Don't call it directly, FastAPI will call it for you. - - Read more about it in the - [FastAPI docs for Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/). - - **Example** - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - - app = FastAPI() - - - async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100): - return {"q": q, "skip": skip, "limit": limit} - - - @app.get("/items/") - async def read_items(commons: Annotated[dict, Depends(common_parameters)]): - return commons - ``` - """ - return params.Depends(dependency=dependency, use_cache=use_cache) - - -def Security( # noqa: N802 - dependency: Annotated[ - Optional[Callable[..., Any]], - Doc( - """ - A "dependable" callable (like a function). - - Don't call it directly, FastAPI will call it for you, just pass the object - directly. - """ - ), - ] = None, - *, - scopes: Annotated[ - Optional[Sequence[str]], - Doc( - """ - OAuth2 scopes required for the *path operation* that uses this Security - dependency. - - The term "scope" comes from the OAuth2 specification, it seems to be - intentionaly vague and interpretable. It normally refers to permissions, - in cases to roles. - - These scopes are integrated with OpenAPI (and the API docs at `/docs`). - So they are visible in the OpenAPI specification. - ) - """ - ), - ] = None, - use_cache: Annotated[ - bool, - Doc( - """ - By default, after a dependency is called the first time in a request, if - the dependency is declared again for the rest of the request (for example - if the dependency is needed by several dependencies), the value will be - re-used for the rest of the request. - - Set `use_cache` to `False` to disable this behavior and ensure the - dependency is called again (if declared more than once) in the same request. - """ - ), - ] = True, -) -> Any: - """ - Declare a FastAPI Security dependency. - - The only difference with a regular dependency is that it can declare OAuth2 - scopes that will be integrated with OpenAPI and the automatic UI docs (by default - at `/docs`). - - It takes a single "dependable" callable (like a function). - - Don't call it directly, FastAPI will call it for you. - - Read more about it in the - [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/) and - in the - [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/). - - **Example** - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - - from .db import User - from .security import get_current_active_user - - app = FastAPI() - - @app.get("/users/me/items/") - async def read_own_items( - current_user: Annotated[User, Security(get_current_active_user, scopes=["items"])] - ): - return [{"item_id": "Foo", "owner": current_user.username}] - ``` - """ - return params.Security(dependency=dependency, scopes=scopes, use_cache=use_cache) diff --git a/.venv/Lib/site-packages/fastapi/params.py b/.venv/Lib/site-packages/fastapi/params.py deleted file mode 100644 index b40944d..0000000 --- a/.venv/Lib/site-packages/fastapi/params.py +++ /dev/null @@ -1,777 +0,0 @@ -import warnings -from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Sequence, Union - -from fastapi.openapi.models import Example -from pydantic.fields import FieldInfo -from typing_extensions import Annotated, deprecated - -from ._compat import PYDANTIC_V2, Undefined - -_Unset: Any = Undefined - - -class ParamTypes(Enum): - query = "query" - header = "header" - path = "path" - cookie = "cookie" - - -class Param(FieldInfo): - in_: ParamTypes - - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - self.deprecated = deprecated - if example is not _Unset: - warnings.warn( - "`example` has been deprecated, please use `examples` instead", - category=DeprecationWarning, - stacklevel=4, - ) - self.example = example - self.include_in_schema = include_in_schema - self.openapi_examples = openapi_examples - kwargs = dict( - default=default, - default_factory=default_factory, - alias=alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - discriminator=discriminator, - multiple_of=multiple_of, - allow_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - **extra, - ) - if examples is not None: - kwargs["examples"] = examples - if regex is not None: - warnings.warn( - "`regex` has been deprecated, please use `pattern` instead", - category=DeprecationWarning, - stacklevel=4, - ) - current_json_schema_extra = json_schema_extra or extra - if PYDANTIC_V2: - kwargs.update( - { - "annotation": annotation, - "alias_priority": alias_priority, - "validation_alias": validation_alias, - "serialization_alias": serialization_alias, - "strict": strict, - "json_schema_extra": current_json_schema_extra, - } - ) - kwargs["pattern"] = pattern or regex - else: - kwargs["regex"] = pattern or regex - kwargs.update(**current_json_schema_extra) - use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset} - - super().__init__(**use_kwargs) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.default})" - - -class Path(Param): - in_ = ParamTypes.path - - def __init__( - self, - default: Any = ..., - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - assert default is ..., "Path parameters cannot have a default value" - self.in_ = self.in_ - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class Query(Param): - in_ = ParamTypes.query - - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class Header(Param): - in_ = ParamTypes.header - - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - convert_underscores: bool = True, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - self.convert_underscores = convert_underscores - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class Cookie(Param): - in_ = ParamTypes.cookie - - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class Body(FieldInfo): - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - embed: bool = False, - media_type: str = "application/json", - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - self.embed = embed - self.media_type = media_type - self.deprecated = deprecated - if example is not _Unset: - warnings.warn( - "`example` has been deprecated, please use `examples` instead", - category=DeprecationWarning, - stacklevel=4, - ) - self.example = example - self.include_in_schema = include_in_schema - self.openapi_examples = openapi_examples - kwargs = dict( - default=default, - default_factory=default_factory, - alias=alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - discriminator=discriminator, - multiple_of=multiple_of, - allow_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - **extra, - ) - if examples is not None: - kwargs["examples"] = examples - if regex is not None: - warnings.warn( - "`regex` has been depreacated, please use `pattern` instead", - category=DeprecationWarning, - stacklevel=4, - ) - current_json_schema_extra = json_schema_extra or extra - if PYDANTIC_V2: - kwargs.update( - { - "annotation": annotation, - "alias_priority": alias_priority, - "validation_alias": validation_alias, - "serialization_alias": serialization_alias, - "strict": strict, - "json_schema_extra": current_json_schema_extra, - } - ) - kwargs["pattern"] = pattern or regex - else: - kwargs["regex"] = pattern or regex - kwargs.update(**current_json_schema_extra) - - use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset} - - super().__init__(**use_kwargs) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self.default})" - - -class Form(Body): - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - media_type: str = "application/x-www-form-urlencoded", - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - embed=True, - media_type=media_type, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class File(Form): - def __init__( - self, - default: Any = Undefined, - *, - default_factory: Union[Callable[[], Any], None] = _Unset, - annotation: Optional[Any] = None, - media_type: str = "multipart/form-data", - alias: Optional[str] = None, - alias_priority: Union[int, None] = _Unset, - # TODO: update when deprecating Pydantic v1, import these types - # validation_alias: str | AliasPath | AliasChoices | None - validation_alias: Union[str, None] = None, - serialization_alias: Union[str, None] = None, - title: Optional[str] = None, - description: Optional[str] = None, - gt: Optional[float] = None, - ge: Optional[float] = None, - lt: Optional[float] = None, - le: Optional[float] = None, - min_length: Optional[int] = None, - max_length: Optional[int] = None, - pattern: Optional[str] = None, - regex: Annotated[ - Optional[str], - deprecated( - "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." - ), - ] = None, - discriminator: Union[str, None] = None, - strict: Union[bool, None] = _Unset, - multiple_of: Union[float, None] = _Unset, - allow_inf_nan: Union[bool, None] = _Unset, - max_digits: Union[int, None] = _Unset, - decimal_places: Union[int, None] = _Unset, - examples: Optional[List[Any]] = None, - example: Annotated[ - Optional[Any], - deprecated( - "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " - "although still supported. Use examples instead." - ), - ] = _Unset, - openapi_examples: Optional[Dict[str, Example]] = None, - deprecated: Optional[bool] = None, - include_in_schema: bool = True, - json_schema_extra: Union[Dict[str, Any], None] = None, - **extra: Any, - ): - super().__init__( - default=default, - default_factory=default_factory, - annotation=annotation, - media_type=media_type, - alias=alias, - alias_priority=alias_priority, - validation_alias=validation_alias, - serialization_alias=serialization_alias, - title=title, - description=description, - gt=gt, - ge=ge, - lt=lt, - le=le, - min_length=min_length, - max_length=max_length, - pattern=pattern, - regex=regex, - discriminator=discriminator, - strict=strict, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - deprecated=deprecated, - example=example, - examples=examples, - openapi_examples=openapi_examples, - include_in_schema=include_in_schema, - json_schema_extra=json_schema_extra, - **extra, - ) - - -class Depends: - def __init__( - self, dependency: Optional[Callable[..., Any]] = None, *, use_cache: bool = True - ): - self.dependency = dependency - self.use_cache = use_cache - - def __repr__(self) -> str: - attr = getattr(self.dependency, "__name__", type(self.dependency).__name__) - cache = "" if self.use_cache else ", use_cache=False" - return f"{self.__class__.__name__}({attr}{cache})" - - -class Security(Depends): - def __init__( - self, - dependency: Optional[Callable[..., Any]] = None, - *, - scopes: Optional[Sequence[str]] = None, - use_cache: bool = True, - ): - super().__init__(dependency=dependency, use_cache=use_cache) - self.scopes = scopes or [] diff --git a/.venv/Lib/site-packages/fastapi/py.typed b/.venv/Lib/site-packages/fastapi/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/fastapi/requests.py b/.venv/Lib/site-packages/fastapi/requests.py deleted file mode 100644 index d16552c..0000000 --- a/.venv/Lib/site-packages/fastapi/requests.py +++ /dev/null @@ -1,2 +0,0 @@ -from starlette.requests import HTTPConnection as HTTPConnection # noqa: F401 -from starlette.requests import Request as Request # noqa: F401 diff --git a/.venv/Lib/site-packages/fastapi/responses.py b/.venv/Lib/site-packages/fastapi/responses.py deleted file mode 100644 index 6c8db6f..0000000 --- a/.venv/Lib/site-packages/fastapi/responses.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import Any - -from starlette.responses import FileResponse as FileResponse # noqa -from starlette.responses import HTMLResponse as HTMLResponse # noqa -from starlette.responses import JSONResponse as JSONResponse # noqa -from starlette.responses import PlainTextResponse as PlainTextResponse # noqa -from starlette.responses import RedirectResponse as RedirectResponse # noqa -from starlette.responses import Response as Response # noqa -from starlette.responses import StreamingResponse as StreamingResponse # noqa - -try: - import ujson -except ImportError: # pragma: nocover - ujson = None # type: ignore - - -try: - import orjson -except ImportError: # pragma: nocover - orjson = None # type: ignore - - -class UJSONResponse(JSONResponse): - """ - JSON response using the high-performance ujson library to serialize data to JSON. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/). - """ - - def render(self, content: Any) -> bytes: - assert ujson is not None, "ujson must be installed to use UJSONResponse" - return ujson.dumps(content, ensure_ascii=False).encode("utf-8") - - -class ORJSONResponse(JSONResponse): - """ - JSON response using the high-performance orjson library to serialize data to JSON. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/). - """ - - def render(self, content: Any) -> bytes: - assert orjson is not None, "orjson must be installed to use ORJSONResponse" - return orjson.dumps( - content, option=orjson.OPT_NON_STR_KEYS | orjson.OPT_SERIALIZE_NUMPY - ) diff --git a/.venv/Lib/site-packages/fastapi/routing.py b/.venv/Lib/site-packages/fastapi/routing.py deleted file mode 100644 index 23a32d1..0000000 --- a/.venv/Lib/site-packages/fastapi/routing.py +++ /dev/null @@ -1,4385 +0,0 @@ -import asyncio -import dataclasses -import email.message -import inspect -import json -from contextlib import AsyncExitStack -from enum import Enum, IntEnum -from typing import ( - Any, - Callable, - Coroutine, - Dict, - List, - Optional, - Sequence, - Set, - Tuple, - Type, - Union, -) - -from fastapi import params -from fastapi._compat import ( - ModelField, - Undefined, - _get_model_config, - _model_dump, - _normalize_errors, - lenient_issubclass, -) -from fastapi.datastructures import Default, DefaultPlaceholder -from fastapi.dependencies.models import Dependant -from fastapi.dependencies.utils import ( - get_body_field, - get_dependant, - get_parameterless_sub_dependant, - get_typed_return_annotation, - solve_dependencies, -) -from fastapi.encoders import jsonable_encoder -from fastapi.exceptions import ( - FastAPIError, - RequestValidationError, - ResponseValidationError, - WebSocketRequestValidationError, -) -from fastapi.types import DecoratedCallable, IncEx -from fastapi.utils import ( - create_cloned_field, - create_response_field, - generate_unique_id, - get_value_or_default, - is_body_allowed_for_status_code, -) -from pydantic import BaseModel -from starlette import routing -from starlette.concurrency import run_in_threadpool -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.responses import JSONResponse, Response -from starlette.routing import ( - BaseRoute, - Match, - compile_path, - get_name, - request_response, - websocket_session, -) -from starlette.routing import Mount as Mount # noqa -from starlette.types import ASGIApp, Lifespan, Scope -from starlette.websockets import WebSocket -from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined] - - -def _prepare_response_content( - res: Any, - *, - exclude_unset: bool, - exclude_defaults: bool = False, - exclude_none: bool = False, -) -> Any: - if isinstance(res, BaseModel): - read_with_orm_mode = getattr(_get_model_config(res), "read_with_orm_mode", None) - if read_with_orm_mode: - # Let from_orm extract the data from this model instead of converting - # it now to a dict. - # Otherwise, there's no way to extract lazy data that requires attribute - # access instead of dict iteration, e.g. lazy relationships. - return res - return _model_dump( - res, - by_alias=True, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - elif isinstance(res, list): - return [ - _prepare_response_content( - item, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - for item in res - ] - elif isinstance(res, dict): - return { - k: _prepare_response_content( - v, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - for k, v in res.items() - } - elif dataclasses.is_dataclass(res): - return dataclasses.asdict(res) - return res - - -async def serialize_response( - *, - field: Optional[ModelField] = None, - response_content: Any, - include: Optional[IncEx] = None, - exclude: Optional[IncEx] = None, - by_alias: bool = True, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - is_coroutine: bool = True, -) -> Any: - if field: - errors = [] - if not hasattr(field, "serialize"): - # pydantic v1 - response_content = _prepare_response_content( - response_content, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - if is_coroutine: - value, errors_ = field.validate(response_content, {}, loc=("response",)) - else: - value, errors_ = await run_in_threadpool( - field.validate, response_content, {}, loc=("response",) - ) - if isinstance(errors_, list): - errors.extend(errors_) - elif errors_: - errors.append(errors_) - if errors: - raise ResponseValidationError( - errors=_normalize_errors(errors), body=response_content - ) - - if hasattr(field, "serialize"): - return field.serialize( - value, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - - return jsonable_encoder( - value, - include=include, - exclude=exclude, - by_alias=by_alias, - exclude_unset=exclude_unset, - exclude_defaults=exclude_defaults, - exclude_none=exclude_none, - ) - else: - return jsonable_encoder(response_content) - - -async def run_endpoint_function( - *, dependant: Dependant, values: Dict[str, Any], is_coroutine: bool -) -> Any: - # Only called by get_request_handler. Has been split into its own function to - # facilitate profiling endpoints, since inner functions are harder to profile. - assert dependant.call is not None, "dependant.call must be a function" - - if is_coroutine: - return await dependant.call(**values) - else: - return await run_in_threadpool(dependant.call, **values) - - -def get_request_handler( - dependant: Dependant, - body_field: Optional[ModelField] = None, - status_code: Optional[int] = None, - response_class: Union[Type[Response], DefaultPlaceholder] = Default(JSONResponse), - response_field: Optional[ModelField] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - dependency_overrides_provider: Optional[Any] = None, -) -> Callable[[Request], Coroutine[Any, Any, Response]]: - assert dependant.call is not None, "dependant.call must be a function" - is_coroutine = asyncio.iscoroutinefunction(dependant.call) - is_body_form = body_field and isinstance(body_field.field_info, params.Form) - if isinstance(response_class, DefaultPlaceholder): - actual_response_class: Type[Response] = response_class.value - else: - actual_response_class = response_class - - async def app(request: Request) -> Response: - response: Union[Response, None] = None - async with AsyncExitStack() as file_stack: - try: - body: Any = None - if body_field: - if is_body_form: - body = await request.form() - file_stack.push_async_callback(body.close) - else: - body_bytes = await request.body() - if body_bytes: - json_body: Any = Undefined - content_type_value = request.headers.get("content-type") - if not content_type_value: - json_body = await request.json() - else: - message = email.message.Message() - message["content-type"] = content_type_value - if message.get_content_maintype() == "application": - subtype = message.get_content_subtype() - if subtype == "json" or subtype.endswith("+json"): - json_body = await request.json() - if json_body != Undefined: - body = json_body - else: - body = body_bytes - except json.JSONDecodeError as e: - validation_error = RequestValidationError( - [ - { - "type": "json_invalid", - "loc": ("body", e.pos), - "msg": "JSON decode error", - "input": {}, - "ctx": {"error": e.msg}, - } - ], - body=e.doc, - ) - raise validation_error from e - except HTTPException: - # If a middleware raises an HTTPException, it should be raised again - raise - except Exception as e: - http_error = HTTPException( - status_code=400, detail="There was an error parsing the body" - ) - raise http_error from e - errors: List[Any] = [] - async with AsyncExitStack() as async_exit_stack: - solved_result = await solve_dependencies( - request=request, - dependant=dependant, - body=body, - dependency_overrides_provider=dependency_overrides_provider, - async_exit_stack=async_exit_stack, - ) - values, errors, background_tasks, sub_response, _ = solved_result - if not errors: - raw_response = await run_endpoint_function( - dependant=dependant, values=values, is_coroutine=is_coroutine - ) - if isinstance(raw_response, Response): - if raw_response.background is None: - raw_response.background = background_tasks - response = raw_response - else: - response_args: Dict[str, Any] = {"background": background_tasks} - # If status_code was set, use it, otherwise use the default from the - # response class, in the case of redirect it's 307 - current_status_code = ( - status_code if status_code else sub_response.status_code - ) - if current_status_code is not None: - response_args["status_code"] = current_status_code - if sub_response.status_code: - response_args["status_code"] = sub_response.status_code - content = await serialize_response( - field=response_field, - response_content=raw_response, - include=response_model_include, - exclude=response_model_exclude, - by_alias=response_model_by_alias, - exclude_unset=response_model_exclude_unset, - exclude_defaults=response_model_exclude_defaults, - exclude_none=response_model_exclude_none, - is_coroutine=is_coroutine, - ) - response = actual_response_class(content, **response_args) - if not is_body_allowed_for_status_code(response.status_code): - response.body = b"" - response.headers.raw.extend(sub_response.headers.raw) - if errors: - validation_error = RequestValidationError( - _normalize_errors(errors), body=body - ) - raise validation_error - if response is None: - raise FastAPIError( - "No response object was returned. There's a high chance that the " - "application code is raising an exception and a dependency with yield " - "has a block with a bare except, or a block with except Exception, " - "and is not raising the exception again. Read more about it in the " - "docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except" - ) - return response - - return app - - -def get_websocket_app( - dependant: Dependant, dependency_overrides_provider: Optional[Any] = None -) -> Callable[[WebSocket], Coroutine[Any, Any, Any]]: - async def app(websocket: WebSocket) -> None: - async with AsyncExitStack() as async_exit_stack: - # TODO: remove this scope later, after a few releases - # This scope fastapi_astack is no longer used by FastAPI, kept for - # compatibility, just in case - websocket.scope["fastapi_astack"] = async_exit_stack - solved_result = await solve_dependencies( - request=websocket, - dependant=dependant, - dependency_overrides_provider=dependency_overrides_provider, - async_exit_stack=async_exit_stack, - ) - values, errors, _, _2, _3 = solved_result - if errors: - raise WebSocketRequestValidationError(_normalize_errors(errors)) - assert dependant.call is not None, "dependant.call must be a function" - await dependant.call(**values) - - return app - - -class APIWebSocketRoute(routing.WebSocketRoute): - def __init__( - self, - path: str, - endpoint: Callable[..., Any], - *, - name: Optional[str] = None, - dependencies: Optional[Sequence[params.Depends]] = None, - dependency_overrides_provider: Optional[Any] = None, - ) -> None: - self.path = path - self.endpoint = endpoint - self.name = get_name(endpoint) if name is None else name - self.dependencies = list(dependencies or []) - self.path_regex, self.path_format, self.param_convertors = compile_path(path) - self.dependant = get_dependant(path=self.path_format, call=self.endpoint) - for depends in self.dependencies[::-1]: - self.dependant.dependencies.insert( - 0, - get_parameterless_sub_dependant(depends=depends, path=self.path_format), - ) - - self.app = websocket_session( - get_websocket_app( - dependant=self.dependant, - dependency_overrides_provider=dependency_overrides_provider, - ) - ) - - def matches(self, scope: Scope) -> Tuple[Match, Scope]: - match, child_scope = super().matches(scope) - if match != Match.NONE: - child_scope["route"] = self - return match, child_scope - - -class APIRoute(routing.Route): - def __init__( - self, - path: str, - endpoint: Callable[..., Any], - *, - response_model: Any = Default(None), - status_code: Optional[int] = None, - tags: Optional[List[Union[str, Enum]]] = None, - dependencies: Optional[Sequence[params.Depends]] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - response_description: str = "Successful Response", - responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, - deprecated: Optional[bool] = None, - name: Optional[str] = None, - methods: Optional[Union[Set[str], List[str]]] = None, - operation_id: Optional[str] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - include_in_schema: bool = True, - response_class: Union[Type[Response], DefaultPlaceholder] = Default( - JSONResponse - ), - dependency_overrides_provider: Optional[Any] = None, - callbacks: Optional[List[BaseRoute]] = None, - openapi_extra: Optional[Dict[str, Any]] = None, - generate_unique_id_function: Union[ - Callable[["APIRoute"], str], DefaultPlaceholder - ] = Default(generate_unique_id), - ) -> None: - self.path = path - self.endpoint = endpoint - if isinstance(response_model, DefaultPlaceholder): - return_annotation = get_typed_return_annotation(endpoint) - if lenient_issubclass(return_annotation, Response): - response_model = None - else: - response_model = return_annotation - self.response_model = response_model - self.summary = summary - self.response_description = response_description - self.deprecated = deprecated - self.operation_id = operation_id - self.response_model_include = response_model_include - self.response_model_exclude = response_model_exclude - self.response_model_by_alias = response_model_by_alias - self.response_model_exclude_unset = response_model_exclude_unset - self.response_model_exclude_defaults = response_model_exclude_defaults - self.response_model_exclude_none = response_model_exclude_none - self.include_in_schema = include_in_schema - self.response_class = response_class - self.dependency_overrides_provider = dependency_overrides_provider - self.callbacks = callbacks - self.openapi_extra = openapi_extra - self.generate_unique_id_function = generate_unique_id_function - self.tags = tags or [] - self.responses = responses or {} - self.name = get_name(endpoint) if name is None else name - self.path_regex, self.path_format, self.param_convertors = compile_path(path) - if methods is None: - methods = ["GET"] - self.methods: Set[str] = {method.upper() for method in methods} - if isinstance(generate_unique_id_function, DefaultPlaceholder): - current_generate_unique_id: Callable[ - ["APIRoute"], str - ] = generate_unique_id_function.value - else: - current_generate_unique_id = generate_unique_id_function - self.unique_id = self.operation_id or current_generate_unique_id(self) - # normalize enums e.g. http.HTTPStatus - if isinstance(status_code, IntEnum): - status_code = int(status_code) - self.status_code = status_code - if self.response_model: - assert is_body_allowed_for_status_code( - status_code - ), f"Status code {status_code} must not have a response body" - response_name = "Response_" + self.unique_id - self.response_field = create_response_field( - name=response_name, - type_=self.response_model, - mode="serialization", - ) - # Create a clone of the field, so that a Pydantic submodel is not returned - # as is just because it's an instance of a subclass of a more limited class - # e.g. UserInDB (containing hashed_password) could be a subclass of User - # that doesn't have the hashed_password. But because it's a subclass, it - # would pass the validation and be returned as is. - # By being a new field, no inheritance will be passed as is. A new model - # will always be created. - # TODO: remove when deprecating Pydantic v1 - self.secure_cloned_response_field: Optional[ - ModelField - ] = create_cloned_field(self.response_field) - else: - self.response_field = None # type: ignore - self.secure_cloned_response_field = None - self.dependencies = list(dependencies or []) - self.description = description or inspect.cleandoc(self.endpoint.__doc__ or "") - # if a "form feed" character (page break) is found in the description text, - # truncate description text to the content preceding the first "form feed" - self.description = self.description.split("\f")[0].strip() - response_fields = {} - for additional_status_code, response in self.responses.items(): - assert isinstance(response, dict), "An additional response must be a dict" - model = response.get("model") - if model: - assert is_body_allowed_for_status_code( - additional_status_code - ), f"Status code {additional_status_code} must not have a response body" - response_name = f"Response_{additional_status_code}_{self.unique_id}" - response_field = create_response_field(name=response_name, type_=model) - response_fields[additional_status_code] = response_field - if response_fields: - self.response_fields: Dict[Union[int, str], ModelField] = response_fields - else: - self.response_fields = {} - - assert callable(endpoint), "An endpoint must be a callable" - self.dependant = get_dependant(path=self.path_format, call=self.endpoint) - for depends in self.dependencies[::-1]: - self.dependant.dependencies.insert( - 0, - get_parameterless_sub_dependant(depends=depends, path=self.path_format), - ) - self.body_field = get_body_field(dependant=self.dependant, name=self.unique_id) - self.app = request_response(self.get_route_handler()) - - def get_route_handler(self) -> Callable[[Request], Coroutine[Any, Any, Response]]: - return get_request_handler( - dependant=self.dependant, - body_field=self.body_field, - status_code=self.status_code, - response_class=self.response_class, - response_field=self.secure_cloned_response_field, - response_model_include=self.response_model_include, - response_model_exclude=self.response_model_exclude, - response_model_by_alias=self.response_model_by_alias, - response_model_exclude_unset=self.response_model_exclude_unset, - response_model_exclude_defaults=self.response_model_exclude_defaults, - response_model_exclude_none=self.response_model_exclude_none, - dependency_overrides_provider=self.dependency_overrides_provider, - ) - - def matches(self, scope: Scope) -> Tuple[Match, Scope]: - match, child_scope = super().matches(scope) - if match != Match.NONE: - child_scope["route"] = self - return match, child_scope - - -class APIRouter(routing.Router): - """ - `APIRouter` class, used to group *path operations*, for example to structure - an app in multiple files. It would then be included in the `FastAPI` app, or - in another `APIRouter` (ultimately included in the app). - - Read more about it in the - [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/). - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - - app = FastAPI() - router = APIRouter() - - - @router.get("/users/", tags=["users"]) - async def read_users(): - return [{"username": "Rick"}, {"username": "Morty"}] - - - app.include_router(router) - ``` - """ - - def __init__( - self, - *, - prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to all the *path operations* in this - router. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to all the - *path operations* in this router. - - Read more about it in the - [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - default_response_class: Annotated[ - Type[Response], - Doc( - """ - The default response class to be used. - - Read more in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). - """ - ), - ] = Default(JSONResponse), - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses to be shown in OpenAPI. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/). - - And in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - OpenAPI callbacks that should apply to all *path operations* in this - router. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - routes: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - **Note**: you probably shouldn't use this parameter, it is inherited - from Starlette and supported for compatibility. - - --- - - A list of routes to serve incoming HTTP and WebSocket requests. - """ - ), - deprecated( - """ - You normally wouldn't use this parameter with FastAPI, it is inherited - from Starlette and supported for compatibility. - - In FastAPI, you normally would use the *path operation methods*, - like `router.get()`, `router.post()`, etc. - """ - ), - ] = None, - redirect_slashes: Annotated[ - bool, - Doc( - """ - Whether to detect and redirect slashes in URLs when the client doesn't - use the same format. - """ - ), - ] = True, - default: Annotated[ - Optional[ASGIApp], - Doc( - """ - Default function handler for this router. Used to handle - 404 Not Found errors. - """ - ), - ] = None, - dependency_overrides_provider: Annotated[ - Optional[Any], - Doc( - """ - Only used internally by FastAPI to handle dependency overrides. - - You shouldn't need to use it. It normally points to the `FastAPI` app - object. - """ - ), - ] = None, - route_class: Annotated[ - Type[APIRoute], - Doc( - """ - Custom route (*path operation*) class to be used by this router. - - Read more about it in the - [FastAPI docs for Custom Request and APIRoute class](https://fastapi.tiangolo.com/how-to/custom-request-and-route/#custom-apiroute-class-in-a-router). - """ - ), - ] = APIRoute, - on_startup: Annotated[ - Optional[Sequence[Callable[[], Any]]], - Doc( - """ - A list of startup event handler functions. - - You should instead use the `lifespan` handlers. - - Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - on_shutdown: Annotated[ - Optional[Sequence[Callable[[], Any]]], - Doc( - """ - A list of shutdown event handler functions. - - You should instead use the `lifespan` handlers. - - Read more in the - [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - # the generic to Lifespan[AppType] is the type of the top level application - # which the router cannot know statically, so we use typing.Any - lifespan: Annotated[ - Optional[Lifespan[Any]], - Doc( - """ - A `Lifespan` context manager handler. This replaces `startup` and - `shutdown` functions with a single context manager. - - Read more in the - [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark all *path operations* in this router as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - To include (or not) all the *path operations* in this router in the - generated OpenAPI. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> None: - super().__init__( - routes=routes, - redirect_slashes=redirect_slashes, - default=default, - on_startup=on_startup, - on_shutdown=on_shutdown, - lifespan=lifespan, - ) - if prefix: - assert prefix.startswith("/"), "A path prefix must start with '/'" - assert not prefix.endswith( - "/" - ), "A path prefix must not end with '/', as the routes will start with '/'" - self.prefix = prefix - self.tags: List[Union[str, Enum]] = tags or [] - self.dependencies = list(dependencies or []) - self.deprecated = deprecated - self.include_in_schema = include_in_schema - self.responses = responses or {} - self.callbacks = callbacks or [] - self.dependency_overrides_provider = dependency_overrides_provider - self.route_class = route_class - self.default_response_class = default_response_class - self.generate_unique_id_function = generate_unique_id_function - - def route( - self, - path: str, - methods: Optional[List[str]] = None, - name: Optional[str] = None, - include_in_schema: bool = True, - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_route( - path, - func, - methods=methods, - name=name, - include_in_schema=include_in_schema, - ) - return func - - return decorator - - def add_api_route( - self, - path: str, - endpoint: Callable[..., Any], - *, - response_model: Any = Default(None), - status_code: Optional[int] = None, - tags: Optional[List[Union[str, Enum]]] = None, - dependencies: Optional[Sequence[params.Depends]] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - response_description: str = "Successful Response", - responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, - deprecated: Optional[bool] = None, - methods: Optional[Union[Set[str], List[str]]] = None, - operation_id: Optional[str] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - include_in_schema: bool = True, - response_class: Union[Type[Response], DefaultPlaceholder] = Default( - JSONResponse - ), - name: Optional[str] = None, - route_class_override: Optional[Type[APIRoute]] = None, - callbacks: Optional[List[BaseRoute]] = None, - openapi_extra: Optional[Dict[str, Any]] = None, - generate_unique_id_function: Union[ - Callable[[APIRoute], str], DefaultPlaceholder - ] = Default(generate_unique_id), - ) -> None: - route_class = route_class_override or self.route_class - responses = responses or {} - combined_responses = {**self.responses, **responses} - current_response_class = get_value_or_default( - response_class, self.default_response_class - ) - current_tags = self.tags.copy() - if tags: - current_tags.extend(tags) - current_dependencies = self.dependencies.copy() - if dependencies: - current_dependencies.extend(dependencies) - current_callbacks = self.callbacks.copy() - if callbacks: - current_callbacks.extend(callbacks) - current_generate_unique_id = get_value_or_default( - generate_unique_id_function, self.generate_unique_id_function - ) - route = route_class( - self.prefix + path, - endpoint=endpoint, - response_model=response_model, - status_code=status_code, - tags=current_tags, - dependencies=current_dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=combined_responses, - deprecated=deprecated or self.deprecated, - methods=methods, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema and self.include_in_schema, - response_class=current_response_class, - name=name, - dependency_overrides_provider=self.dependency_overrides_provider, - callbacks=current_callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=current_generate_unique_id, - ) - self.routes.append(route) - - def api_route( - self, - path: str, - *, - response_model: Any = Default(None), - status_code: Optional[int] = None, - tags: Optional[List[Union[str, Enum]]] = None, - dependencies: Optional[Sequence[params.Depends]] = None, - summary: Optional[str] = None, - description: Optional[str] = None, - response_description: str = "Successful Response", - responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None, - deprecated: Optional[bool] = None, - methods: Optional[List[str]] = None, - operation_id: Optional[str] = None, - response_model_include: Optional[IncEx] = None, - response_model_exclude: Optional[IncEx] = None, - response_model_by_alias: bool = True, - response_model_exclude_unset: bool = False, - response_model_exclude_defaults: bool = False, - response_model_exclude_none: bool = False, - include_in_schema: bool = True, - response_class: Type[Response] = Default(JSONResponse), - name: Optional[str] = None, - callbacks: Optional[List[BaseRoute]] = None, - openapi_extra: Optional[Dict[str, Any]] = None, - generate_unique_id_function: Callable[[APIRoute], str] = Default( - generate_unique_id - ), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_api_route( - path, - func, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=methods, - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - return func - - return decorator - - def add_api_websocket_route( - self, - path: str, - endpoint: Callable[..., Any], - name: Optional[str] = None, - *, - dependencies: Optional[Sequence[params.Depends]] = None, - ) -> None: - current_dependencies = self.dependencies.copy() - if dependencies: - current_dependencies.extend(dependencies) - - route = APIWebSocketRoute( - self.prefix + path, - endpoint=endpoint, - name=name, - dependencies=current_dependencies, - dependency_overrides_provider=self.dependency_overrides_provider, - ) - self.routes.append(route) - - def websocket( - self, - path: Annotated[ - str, - Doc( - """ - WebSocket path. - """ - ), - ], - name: Annotated[ - Optional[str], - Doc( - """ - A name for the WebSocket. Only used internally. - """ - ), - ] = None, - *, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be used for this - WebSocket. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - """ - ), - ] = None, - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Decorate a WebSocket function. - - Read more about it in the - [FastAPI docs for WebSockets](https://fastapi.tiangolo.com/advanced/websockets/). - - **Example** - - ## Example - - ```python - from fastapi import APIRouter, FastAPI, WebSocket - - app = FastAPI() - router = APIRouter() - - @router.websocket("/ws") - async def websocket_endpoint(websocket: WebSocket): - await websocket.accept() - while True: - data = await websocket.receive_text() - await websocket.send_text(f"Message text was: {data}") - - app.include_router(router) - ``` - """ - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_api_websocket_route( - path, func, name=name, dependencies=dependencies - ) - return func - - return decorator - - def websocket_route( - self, path: str, name: Union[str, None] = None - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_websocket_route(path, func, name=name) - return func - - return decorator - - def include_router( - self, - router: Annotated["APIRouter", Doc("The `APIRouter` to include.")], - *, - prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to all the *path operations* in this - router. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to all the - *path operations* in this router. - - Read more about it in the - [FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - default_response_class: Annotated[ - Type[Response], - Doc( - """ - The default response class to be used. - - Read more in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class). - """ - ), - ] = Default(JSONResponse), - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses to be shown in OpenAPI. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/). - - And in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies). - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - OpenAPI callbacks that should apply to all *path operations* in this - router. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark all *path operations* in this router as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include (or not) all the *path operations* in this router in the - generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = True, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> None: - """ - Include another `APIRouter` in the same current `APIRouter`. - - Read more about it in the - [FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/). - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - - app = FastAPI() - internal_router = APIRouter() - users_router = APIRouter() - - @users_router.get("/users/") - def read_users(): - return [{"name": "Rick"}, {"name": "Morty"}] - - internal_router.include_router(users_router) - app.include_router(internal_router) - ``` - """ - if prefix: - assert prefix.startswith("/"), "A path prefix must start with '/'" - assert not prefix.endswith( - "/" - ), "A path prefix must not end with '/', as the routes will start with '/'" - else: - for r in router.routes: - path = getattr(r, "path") # noqa: B009 - name = getattr(r, "name", "unknown") - if path is not None and not path: - raise FastAPIError( - f"Prefix and path cannot be both empty (path operation: {name})" - ) - if responses is None: - responses = {} - for route in router.routes: - if isinstance(route, APIRoute): - combined_responses = {**responses, **route.responses} - use_response_class = get_value_or_default( - route.response_class, - router.default_response_class, - default_response_class, - self.default_response_class, - ) - current_tags = [] - if tags: - current_tags.extend(tags) - if route.tags: - current_tags.extend(route.tags) - current_dependencies: List[params.Depends] = [] - if dependencies: - current_dependencies.extend(dependencies) - if route.dependencies: - current_dependencies.extend(route.dependencies) - current_callbacks = [] - if callbacks: - current_callbacks.extend(callbacks) - if route.callbacks: - current_callbacks.extend(route.callbacks) - current_generate_unique_id = get_value_or_default( - route.generate_unique_id_function, - router.generate_unique_id_function, - generate_unique_id_function, - self.generate_unique_id_function, - ) - self.add_api_route( - prefix + route.path, - route.endpoint, - response_model=route.response_model, - status_code=route.status_code, - tags=current_tags, - dependencies=current_dependencies, - summary=route.summary, - description=route.description, - response_description=route.response_description, - responses=combined_responses, - deprecated=route.deprecated or deprecated or self.deprecated, - methods=route.methods, - operation_id=route.operation_id, - response_model_include=route.response_model_include, - response_model_exclude=route.response_model_exclude, - response_model_by_alias=route.response_model_by_alias, - response_model_exclude_unset=route.response_model_exclude_unset, - response_model_exclude_defaults=route.response_model_exclude_defaults, - response_model_exclude_none=route.response_model_exclude_none, - include_in_schema=route.include_in_schema - and self.include_in_schema - and include_in_schema, - response_class=use_response_class, - name=route.name, - route_class_override=type(route), - callbacks=current_callbacks, - openapi_extra=route.openapi_extra, - generate_unique_id_function=current_generate_unique_id, - ) - elif isinstance(route, routing.Route): - methods = list(route.methods or []) - self.add_route( - prefix + route.path, - route.endpoint, - methods=methods, - include_in_schema=route.include_in_schema, - name=route.name, - ) - elif isinstance(route, APIWebSocketRoute): - current_dependencies = [] - if dependencies: - current_dependencies.extend(dependencies) - if route.dependencies: - current_dependencies.extend(route.dependencies) - self.add_api_websocket_route( - prefix + route.path, - route.endpoint, - dependencies=current_dependencies, - name=route.name, - ) - elif isinstance(route, routing.WebSocketRoute): - self.add_websocket_route( - prefix + route.path, route.endpoint, name=route.name - ) - for handler in router.on_startup: - self.add_event_handler("startup", handler) - for handler in router.on_shutdown: - self.add_event_handler("shutdown", handler) - - def get( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP GET operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - - app = FastAPI() - router = APIRouter() - - @router.get("/items/") - def read_items(): - return [{"name": "Empanada"}, {"name": "Arepa"}] - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["GET"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def put( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP PUT operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - router = APIRouter() - - @router.put("/items/{item_id}") - def replace_item(item_id: str, item: Item): - return {"message": "Item replaced", "id": item_id} - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["PUT"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def post( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP POST operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - router = APIRouter() - - @router.post("/items/") - def create_item(item: Item): - return {"message": "Item created"} - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["POST"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def delete( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP DELETE operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - - app = FastAPI() - router = APIRouter() - - @router.delete("/items/{item_id}") - def delete_item(item_id: str): - return {"message": "Item deleted"} - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["DELETE"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def options( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP OPTIONS operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - - app = FastAPI() - router = APIRouter() - - @router.options("/items/") - def get_item_options(): - return {"additions": ["Aji", "Guacamole"]} - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["OPTIONS"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def head( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP HEAD operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - router = APIRouter() - - @router.head("/items/", status_code=204) - def get_items_headers(response: Response): - response.headers["X-Cat-Dog"] = "Alone in the world" - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["HEAD"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def patch( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP PATCH operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - router = APIRouter() - - @router.patch("/items/") - def update_item(item: Item): - return {"message": "Item updated in place"} - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["PATCH"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - def trace( - self, - path: Annotated[ - str, - Doc( - """ - The URL path to be used for this *path operation*. - - For example, in `http://example.com/items`, the path is `/items`. - """ - ), - ], - *, - response_model: Annotated[ - Any, - Doc( - """ - The type to use for the response. - - It could be any valid Pydantic *field* type. So, it doesn't have to - be a Pydantic model, it could be other things, like a `list`, `dict`, - etc. - - It will be used for: - - * Documentation: the generated OpenAPI (and the UI at `/docs`) will - show it as the response (JSON Schema). - * Serialization: you could return an arbitrary object and the - `response_model` would be used to serialize that object into the - corresponding JSON. - * Filtering: the JSON sent to the client will only contain the data - (fields) defined in the `response_model`. If you returned an object - that contains an attribute `password` but the `response_model` does - not include that field, the JSON sent to the client would not have - that `password`. - * Validation: whatever you return will be serialized with the - `response_model`, converting any data as necessary to generate the - corresponding JSON. But if the data in the object returned is not - valid, that would mean a violation of the contract with the client, - so it's an error from the API developer. So, FastAPI will raise an - error and return a 500 error code (Internal Server Error). - - Read more about it in the - [FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). - """ - ), - ] = Default(None), - status_code: Annotated[ - Optional[int], - Doc( - """ - The default status code to be used for the response. - - You could override the status code by returning a response directly. - - Read more about it in the - [FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/). - """ - ), - ] = None, - tags: Annotated[ - Optional[List[Union[str, Enum]]], - Doc( - """ - A list of tags to be applied to the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags). - """ - ), - ] = None, - dependencies: Annotated[ - Optional[Sequence[params.Depends]], - Doc( - """ - A list of dependencies (using `Depends()`) to be applied to the - *path operation*. - - Read more about it in the - [FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/). - """ - ), - ] = None, - summary: Annotated[ - Optional[str], - Doc( - """ - A summary for the *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - A description for the *path operation*. - - If not provided, it will be extracted automatically from the docstring - of the *path operation function*. - - It can contain Markdown. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/). - """ - ), - ] = None, - response_description: Annotated[ - str, - Doc( - """ - The description for the default response. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = "Successful Response", - responses: Annotated[ - Optional[Dict[Union[int, str], Dict[str, Any]]], - Doc( - """ - Additional responses that could be returned by this *path operation*. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - deprecated: Annotated[ - Optional[bool], - Doc( - """ - Mark this *path operation* as deprecated. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - operation_id: Annotated[ - Optional[str], - Doc( - """ - Custom operation ID to be used by this *path operation*. - - By default, it is generated automatically. - - If you provide a custom operation ID, you need to make sure it is - unique for the whole API. - - You can customize the - operation ID generation with the parameter - `generate_unique_id_function` in the `FastAPI` class. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = None, - response_model_include: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to include only certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_exclude: Annotated[ - Optional[IncEx], - Doc( - """ - Configuration passed to Pydantic to exclude certain fields in the - response data. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = None, - response_model_by_alias: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response model - should be serialized by alias when an alias is used. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude). - """ - ), - ] = True, - response_model_exclude_unset: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that were not set and - have their default values. This is different from - `response_model_exclude_defaults` in that if the fields are set, - they will be included in the response, even if the value is the same - as the default. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_defaults: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data - should have all the fields, including the ones that have the same value - as the default. This is different from `response_model_exclude_unset` - in that if the fields are set but contain the same default values, - they will be excluded from the response. - - When `True`, default values are omitted from the response. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter). - """ - ), - ] = False, - response_model_exclude_none: Annotated[ - bool, - Doc( - """ - Configuration passed to Pydantic to define if the response data should - exclude fields set to `None`. - - This is much simpler (less smart) than `response_model_exclude_unset` - and `response_model_exclude_defaults`. You probably want to use one of - those two instead of this one, as those allow returning `None` values - when it makes sense. - - Read more about it in the - [FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none). - """ - ), - ] = False, - include_in_schema: Annotated[ - bool, - Doc( - """ - Include this *path operation* in the generated OpenAPI schema. - - This affects the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi). - """ - ), - ] = True, - response_class: Annotated[ - Type[Response], - Doc( - """ - Response class to be used for this *path operation*. - - This will not be used if you return a response directly. - - Read more about it in the - [FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse). - """ - ), - ] = Default(JSONResponse), - name: Annotated[ - Optional[str], - Doc( - """ - Name for this *path operation*. Only used internally. - """ - ), - ] = None, - callbacks: Annotated[ - Optional[List[BaseRoute]], - Doc( - """ - List of *path operations* that will be used as OpenAPI callbacks. - - This is only for OpenAPI documentation, the callbacks won't be used - directly. - - It will be added to the generated OpenAPI (e.g. visible at `/docs`). - - Read more about it in the - [FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/). - """ - ), - ] = None, - openapi_extra: Annotated[ - Optional[Dict[str, Any]], - Doc( - """ - Extra metadata to be included in the OpenAPI schema for this *path - operation*. - - Read more about it in the - [FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema). - """ - ), - ] = None, - generate_unique_id_function: Annotated[ - Callable[[APIRoute], str], - Doc( - """ - Customize the function used to generate unique IDs for the *path - operations* shown in the generated OpenAPI. - - This is particularly useful when automatically generating clients or - SDKs for your API. - - Read more about it in the - [FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function). - """ - ), - ] = Default(generate_unique_id), - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add a *path operation* using an HTTP TRACE operation. - - ## Example - - ```python - from fastapi import APIRouter, FastAPI - from pydantic import BaseModel - - class Item(BaseModel): - name: str - description: str | None = None - - app = FastAPI() - router = APIRouter() - - @router.trace("/items/{item_id}") - def trace_item(item_id: str): - return None - - app.include_router(router) - ``` - """ - return self.api_route( - path=path, - response_model=response_model, - status_code=status_code, - tags=tags, - dependencies=dependencies, - summary=summary, - description=description, - response_description=response_description, - responses=responses, - deprecated=deprecated, - methods=["TRACE"], - operation_id=operation_id, - response_model_include=response_model_include, - response_model_exclude=response_model_exclude, - response_model_by_alias=response_model_by_alias, - response_model_exclude_unset=response_model_exclude_unset, - response_model_exclude_defaults=response_model_exclude_defaults, - response_model_exclude_none=response_model_exclude_none, - include_in_schema=include_in_schema, - response_class=response_class, - name=name, - callbacks=callbacks, - openapi_extra=openapi_extra, - generate_unique_id_function=generate_unique_id_function, - ) - - @deprecated( - """ - on_event is deprecated, use lifespan event handlers instead. - - Read more about it in the - [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/). - """ - ) - def on_event( - self, - event_type: Annotated[ - str, - Doc( - """ - The type of event. `startup` or `shutdown`. - """ - ), - ], - ) -> Callable[[DecoratedCallable], DecoratedCallable]: - """ - Add an event handler for the router. - - `on_event` is deprecated, use `lifespan` event handlers instead. - - Read more about it in the - [FastAPI docs for Lifespan Events](https://fastapi.tiangolo.com/advanced/events/#alternative-events-deprecated). - """ - - def decorator(func: DecoratedCallable) -> DecoratedCallable: - self.add_event_handler(event_type, func) - return func - - return decorator diff --git a/.venv/Lib/site-packages/fastapi/security/__init__.py b/.venv/Lib/site-packages/fastapi/security/__init__.py deleted file mode 100644 index 3aa6bf2..0000000 --- a/.venv/Lib/site-packages/fastapi/security/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from .api_key import APIKeyCookie as APIKeyCookie -from .api_key import APIKeyHeader as APIKeyHeader -from .api_key import APIKeyQuery as APIKeyQuery -from .http import HTTPAuthorizationCredentials as HTTPAuthorizationCredentials -from .http import HTTPBasic as HTTPBasic -from .http import HTTPBasicCredentials as HTTPBasicCredentials -from .http import HTTPBearer as HTTPBearer -from .http import HTTPDigest as HTTPDigest -from .oauth2 import OAuth2 as OAuth2 -from .oauth2 import OAuth2AuthorizationCodeBearer as OAuth2AuthorizationCodeBearer -from .oauth2 import OAuth2PasswordBearer as OAuth2PasswordBearer -from .oauth2 import OAuth2PasswordRequestForm as OAuth2PasswordRequestForm -from .oauth2 import OAuth2PasswordRequestFormStrict as OAuth2PasswordRequestFormStrict -from .oauth2 import SecurityScopes as SecurityScopes -from .open_id_connect_url import OpenIdConnect as OpenIdConnect diff --git a/.venv/Lib/site-packages/fastapi/security/api_key.py b/.venv/Lib/site-packages/fastapi/security/api_key.py deleted file mode 100644 index b1a6b4f..0000000 --- a/.venv/Lib/site-packages/fastapi/security/api_key.py +++ /dev/null @@ -1,301 +0,0 @@ -from typing import Optional - -from fastapi.openapi.models import APIKey, APIKeyIn -from fastapi.security.base import SecurityBase -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.status import HTTP_403_FORBIDDEN -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class APIKeyBase(SecurityBase): - pass - - -class APIKeyQuery(APIKeyBase): - """ - API key authentication using a query parameter. - - This defines the name of the query parameter that should be provided in the request - with the API key and integrates that into the OpenAPI documentation. It extracts - the key value sent in the query parameter automatically and provides it as the - dependency result. But it doesn't define how to send that API key to the client. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be a string containing the key value. - - ## Example - - ```python - from fastapi import Depends, FastAPI - from fastapi.security import APIKeyQuery - - app = FastAPI() - - query_scheme = APIKeyQuery(name="api_key") - - - @app.get("/items/") - async def read_items(api_key: str = Depends(query_scheme)): - return {"api_key": api_key} - ``` - """ - - def __init__( - self, - *, - name: Annotated[ - str, - Doc("Query parameter name."), - ], - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the query parameter is not provided, `APIKeyQuery` will - automatically cancel the request and sebd the client an error. - - If `auto_error` is set to `False`, when the query parameter is not - available, instead of erroring out, the dependency result will be - `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in a query - parameter or in an HTTP Bearer token). - """ - ), - ] = True, - ): - self.model: APIKey = APIKey( - **{"in": APIKeyIn.query}, # type: ignore[arg-type] - name=name, - description=description, - ) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request) -> Optional[str]: - api_key = request.query_params.get(self.model.name) - if not api_key: - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return api_key - - -class APIKeyHeader(APIKeyBase): - """ - API key authentication using a header. - - This defines the name of the header that should be provided in the request with - the API key and integrates that into the OpenAPI documentation. It extracts - the key value sent in the header automatically and provides it as the dependency - result. But it doesn't define how to send that key to the client. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be a string containing the key value. - - ## Example - - ```python - from fastapi import Depends, FastAPI - from fastapi.security import APIKeyHeader - - app = FastAPI() - - header_scheme = APIKeyHeader(name="x-key") - - - @app.get("/items/") - async def read_items(key: str = Depends(header_scheme)): - return {"key": key} - ``` - """ - - def __init__( - self, - *, - name: Annotated[str, Doc("Header name.")], - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the header is not provided, `APIKeyHeader` will - automatically cancel the request and send the client an error. - - If `auto_error` is set to `False`, when the header is not available, - instead of erroring out, the dependency result will be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in a header or - in an HTTP Bearer token). - """ - ), - ] = True, - ): - self.model: APIKey = APIKey( - **{"in": APIKeyIn.header}, # type: ignore[arg-type] - name=name, - description=description, - ) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request) -> Optional[str]: - api_key = request.headers.get(self.model.name) - if not api_key: - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return api_key - - -class APIKeyCookie(APIKeyBase): - """ - API key authentication using a cookie. - - This defines the name of the cookie that should be provided in the request with - the API key and integrates that into the OpenAPI documentation. It extracts - the key value sent in the cookie automatically and provides it as the dependency - result. But it doesn't define how to set that cookie. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be a string containing the key value. - - ## Example - - ```python - from fastapi import Depends, FastAPI - from fastapi.security import APIKeyCookie - - app = FastAPI() - - cookie_scheme = APIKeyCookie(name="session") - - - @app.get("/items/") - async def read_items(session: str = Depends(cookie_scheme)): - return {"session": session} - ``` - """ - - def __init__( - self, - *, - name: Annotated[str, Doc("Cookie name.")], - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the cookie is not provided, `APIKeyCookie` will - automatically cancel the request and send the client an error. - - If `auto_error` is set to `False`, when the cookie is not available, - instead of erroring out, the dependency result will be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in a cookie or - in an HTTP Bearer token). - """ - ), - ] = True, - ): - self.model: APIKey = APIKey( - **{"in": APIKeyIn.cookie}, # type: ignore[arg-type] - name=name, - description=description, - ) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request) -> Optional[str]: - api_key = request.cookies.get(self.model.name) - if not api_key: - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return api_key diff --git a/.venv/Lib/site-packages/fastapi/security/base.py b/.venv/Lib/site-packages/fastapi/security/base.py deleted file mode 100644 index c43555d..0000000 --- a/.venv/Lib/site-packages/fastapi/security/base.py +++ /dev/null @@ -1,6 +0,0 @@ -from fastapi.openapi.models import SecurityBase as SecurityBaseModel - - -class SecurityBase: - model: SecurityBaseModel - scheme_name: str diff --git a/.venv/Lib/site-packages/fastapi/security/http.py b/.venv/Lib/site-packages/fastapi/security/http.py deleted file mode 100644 index 738455d..0000000 --- a/.venv/Lib/site-packages/fastapi/security/http.py +++ /dev/null @@ -1,420 +0,0 @@ -import binascii -from base64 import b64decode -from typing import Optional - -from fastapi.exceptions import HTTPException -from fastapi.openapi.models import HTTPBase as HTTPBaseModel -from fastapi.openapi.models import HTTPBearer as HTTPBearerModel -from fastapi.security.base import SecurityBase -from fastapi.security.utils import get_authorization_scheme_param -from pydantic import BaseModel -from starlette.requests import Request -from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class HTTPBasicCredentials(BaseModel): - """ - The HTTP Basic credendials given as the result of using `HTTPBasic` in a - dependency. - - Read more about it in the - [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/). - """ - - username: Annotated[str, Doc("The HTTP Basic username.")] - password: Annotated[str, Doc("The HTTP Basic password.")] - - -class HTTPAuthorizationCredentials(BaseModel): - """ - The HTTP authorization credentials in the result of using `HTTPBearer` or - `HTTPDigest` in a dependency. - - The HTTP authorization header value is split by the first space. - - The first part is the `scheme`, the second part is the `credentials`. - - For example, in an HTTP Bearer token scheme, the client will send a header - like: - - ``` - Authorization: Bearer deadbeef12346 - ``` - - In this case: - - * `scheme` will have the value `"Bearer"` - * `credentials` will have the value `"deadbeef12346"` - """ - - scheme: Annotated[ - str, - Doc( - """ - The HTTP authorization scheme extracted from the header value. - """ - ), - ] - credentials: Annotated[ - str, - Doc( - """ - The HTTP authorization credentials extracted from the header value. - """ - ), - ] - - -class HTTPBase(SecurityBase): - def __init__( - self, - *, - scheme: str, - scheme_name: Optional[str] = None, - description: Optional[str] = None, - auto_error: bool = True, - ): - self.model = HTTPBaseModel(scheme=scheme, description=description) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__( - self, request: Request - ) -> Optional[HTTPAuthorizationCredentials]: - authorization = request.headers.get("Authorization") - scheme, credentials = get_authorization_scheme_param(authorization) - if not (authorization and scheme and credentials): - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) - - -class HTTPBasic(HTTPBase): - """ - HTTP Basic authentication. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be an `HTTPBasicCredentials` object containing the - `username` and the `password`. - - Read more about it in the - [FastAPI docs for HTTP Basic Auth](https://fastapi.tiangolo.com/advanced/security/http-basic-auth/). - - ## Example - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - from fastapi.security import HTTPBasic, HTTPBasicCredentials - - app = FastAPI() - - security = HTTPBasic() - - - @app.get("/users/me") - def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]): - return {"username": credentials.username, "password": credentials.password} - ``` - """ - - def __init__( - self, - *, - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - realm: Annotated[ - Optional[str], - Doc( - """ - HTTP Basic authentication realm. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the HTTP Basic authentication is not provided (a - header), `HTTPBasic` will automatically cancel the request and send the - client an error. - - If `auto_error` is set to `False`, when the HTTP Basic authentication - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in HTTP Basic - authentication or in an HTTP Bearer token). - """ - ), - ] = True, - ): - self.model = HTTPBaseModel(scheme="basic", description=description) - self.scheme_name = scheme_name or self.__class__.__name__ - self.realm = realm - self.auto_error = auto_error - - async def __call__( # type: ignore - self, request: Request - ) -> Optional[HTTPBasicCredentials]: - authorization = request.headers.get("Authorization") - scheme, param = get_authorization_scheme_param(authorization) - if self.realm: - unauthorized_headers = {"WWW-Authenticate": f'Basic realm="{self.realm}"'} - else: - unauthorized_headers = {"WWW-Authenticate": "Basic"} - if not authorization or scheme.lower() != "basic": - if self.auto_error: - raise HTTPException( - status_code=HTTP_401_UNAUTHORIZED, - detail="Not authenticated", - headers=unauthorized_headers, - ) - else: - return None - invalid_user_credentials_exc = HTTPException( - status_code=HTTP_401_UNAUTHORIZED, - detail="Invalid authentication credentials", - headers=unauthorized_headers, - ) - try: - data = b64decode(param).decode("ascii") - except (ValueError, UnicodeDecodeError, binascii.Error): - raise invalid_user_credentials_exc # noqa: B904 - username, separator, password = data.partition(":") - if not separator: - raise invalid_user_credentials_exc - return HTTPBasicCredentials(username=username, password=password) - - -class HTTPBearer(HTTPBase): - """ - HTTP Bearer token authentication. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be an `HTTPAuthorizationCredentials` object containing - the `scheme` and the `credentials`. - - ## Example - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer - - app = FastAPI() - - security = HTTPBearer() - - - @app.get("/users/me") - def read_current_user( - credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)] - ): - return {"scheme": credentials.scheme, "credentials": credentials.credentials} - ``` - """ - - def __init__( - self, - *, - bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None, - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the HTTP Bearer token not provided (in an - `Authorization` header), `HTTPBearer` will automatically cancel the - request and send the client an error. - - If `auto_error` is set to `False`, when the HTTP Bearer token - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in an HTTP - Bearer token or in a cookie). - """ - ), - ] = True, - ): - self.model = HTTPBearerModel(bearerFormat=bearerFormat, description=description) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__( - self, request: Request - ) -> Optional[HTTPAuthorizationCredentials]: - authorization = request.headers.get("Authorization") - scheme, credentials = get_authorization_scheme_param(authorization) - if not (authorization and scheme and credentials): - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - if scheme.lower() != "bearer": - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, - detail="Invalid authentication credentials", - ) - else: - return None - return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) - - -class HTTPDigest(HTTPBase): - """ - HTTP Digest authentication. - - ## Usage - - Create an instance object and use that object as the dependency in `Depends()`. - - The dependency result will be an `HTTPAuthorizationCredentials` object containing - the `scheme` and the `credentials`. - - ## Example - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest - - app = FastAPI() - - security = HTTPDigest() - - - @app.get("/users/me") - def read_current_user( - credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)] - ): - return {"scheme": credentials.scheme, "credentials": credentials.credentials} - ``` - """ - - def __init__( - self, - *, - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if the HTTP Digest not provided, `HTTPDigest` will - automatically cancel the request and send the client an error. - - If `auto_error` is set to `False`, when the HTTP Digest is not - available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, in HTTP - Digest or in a cookie). - """ - ), - ] = True, - ): - self.model = HTTPBaseModel(scheme="digest", description=description) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__( - self, request: Request - ) -> Optional[HTTPAuthorizationCredentials]: - authorization = request.headers.get("Authorization") - scheme, credentials = get_authorization_scheme_param(authorization) - if not (authorization and scheme and credentials): - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - if scheme.lower() != "digest": - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, - detail="Invalid authentication credentials", - ) - return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials) diff --git a/.venv/Lib/site-packages/fastapi/security/oauth2.py b/.venv/Lib/site-packages/fastapi/security/oauth2.py deleted file mode 100644 index be3e18c..0000000 --- a/.venv/Lib/site-packages/fastapi/security/oauth2.py +++ /dev/null @@ -1,638 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast - -from fastapi.exceptions import HTTPException -from fastapi.openapi.models import OAuth2 as OAuth2Model -from fastapi.openapi.models import OAuthFlows as OAuthFlowsModel -from fastapi.param_functions import Form -from fastapi.security.base import SecurityBase -from fastapi.security.utils import get_authorization_scheme_param -from starlette.requests import Request -from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN - -# TODO: import from typing when deprecating Python 3.9 -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class OAuth2PasswordRequestForm: - """ - This is a dependency class to collect the `username` and `password` as form data - for an OAuth2 password flow. - - The OAuth2 specification dictates that for a password flow the data should be - collected using form data (instead of JSON) and that it should have the specific - fields `username` and `password`. - - All the initialization parameters are extracted from the request. - - Read more about it in the - [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). - - ## Example - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - from fastapi.security import OAuth2PasswordRequestForm - - app = FastAPI() - - - @app.post("/login") - def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): - data = {} - data["scopes"] = [] - for scope in form_data.scopes: - data["scopes"].append(scope) - if form_data.client_id: - data["client_id"] = form_data.client_id - if form_data.client_secret: - data["client_secret"] = form_data.client_secret - return data - ``` - - Note that for OAuth2 the scope `items:read` is a single scope in an opaque string. - You could have custom internal logic to separate it by colon caracters (`:`) or - similar, and get the two parts `items` and `read`. Many applications do that to - group and organize permisions, you could do it as well in your application, just - know that that it is application specific, it's not part of the specification. - """ - - def __init__( - self, - *, - grant_type: Annotated[ - Union[str, None], - Form(pattern="password"), - Doc( - """ - The OAuth2 spec says it is required and MUST be the fixed string - "password". Nevertheless, this dependency class is permissive and - allows not passing it. If you want to enforce it, use instead the - `OAuth2PasswordRequestFormStrict` dependency. - """ - ), - ] = None, - username: Annotated[ - str, - Form(), - Doc( - """ - `username` string. The OAuth2 spec requires the exact field name - `username`. - """ - ), - ], - password: Annotated[ - str, - Form(), - Doc( - """ - `password` string. The OAuth2 spec requires the exact field name - `password". - """ - ), - ], - scope: Annotated[ - str, - Form(), - Doc( - """ - A single string with actually several scopes separated by spaces. Each - scope is also a string. - - For example, a single string with: - - ```python - "items:read items:write users:read profile openid" - ```` - - would represent the scopes: - - * `items:read` - * `items:write` - * `users:read` - * `profile` - * `openid` - """ - ), - ] = "", - client_id: Annotated[ - Union[str, None], - Form(), - Doc( - """ - If there's a `client_id`, it can be sent as part of the form fields. - But the OAuth2 specification recommends sending the `client_id` and - `client_secret` (if any) using HTTP Basic auth. - """ - ), - ] = None, - client_secret: Annotated[ - Union[str, None], - Form(), - Doc( - """ - If there's a `client_password` (and a `client_id`), they can be sent - as part of the form fields. But the OAuth2 specification recommends - sending the `client_id` and `client_secret` (if any) using HTTP Basic - auth. - """ - ), - ] = None, - ): - self.grant_type = grant_type - self.username = username - self.password = password - self.scopes = scope.split() - self.client_id = client_id - self.client_secret = client_secret - - -class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm): - """ - This is a dependency class to collect the `username` and `password` as form data - for an OAuth2 password flow. - - The OAuth2 specification dictates that for a password flow the data should be - collected using form data (instead of JSON) and that it should have the specific - fields `username` and `password`. - - All the initialization parameters are extracted from the request. - - The only difference between `OAuth2PasswordRequestFormStrict` and - `OAuth2PasswordRequestForm` is that `OAuth2PasswordRequestFormStrict` requires the - client to send the form field `grant_type` with the value `"password"`, which - is required in the OAuth2 specification (it seems that for no particular reason), - while for `OAuth2PasswordRequestForm` `grant_type` is optional. - - Read more about it in the - [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). - - ## Example - - ```python - from typing import Annotated - - from fastapi import Depends, FastAPI - from fastapi.security import OAuth2PasswordRequestForm - - app = FastAPI() - - - @app.post("/login") - def login(form_data: Annotated[OAuth2PasswordRequestFormStrict, Depends()]): - data = {} - data["scopes"] = [] - for scope in form_data.scopes: - data["scopes"].append(scope) - if form_data.client_id: - data["client_id"] = form_data.client_id - if form_data.client_secret: - data["client_secret"] = form_data.client_secret - return data - ``` - - Note that for OAuth2 the scope `items:read` is a single scope in an opaque string. - You could have custom internal logic to separate it by colon caracters (`:`) or - similar, and get the two parts `items` and `read`. Many applications do that to - group and organize permisions, you could do it as well in your application, just - know that that it is application specific, it's not part of the specification. - - - grant_type: the OAuth2 spec says it is required and MUST be the fixed string "password". - This dependency is strict about it. If you want to be permissive, use instead the - OAuth2PasswordRequestForm dependency class. - username: username string. The OAuth2 spec requires the exact field name "username". - password: password string. The OAuth2 spec requires the exact field name "password". - scope: Optional string. Several scopes (each one a string) separated by spaces. E.g. - "items:read items:write users:read profile openid" - client_id: optional string. OAuth2 recommends sending the client_id and client_secret (if any) - using HTTP Basic auth, as: client_id:client_secret - client_secret: optional string. OAuth2 recommends sending the client_id and client_secret (if any) - using HTTP Basic auth, as: client_id:client_secret - """ - - def __init__( - self, - grant_type: Annotated[ - str, - Form(pattern="password"), - Doc( - """ - The OAuth2 spec says it is required and MUST be the fixed string - "password". This dependency is strict about it. If you want to be - permissive, use instead the `OAuth2PasswordRequestForm` dependency - class. - """ - ), - ], - username: Annotated[ - str, - Form(), - Doc( - """ - `username` string. The OAuth2 spec requires the exact field name - `username`. - """ - ), - ], - password: Annotated[ - str, - Form(), - Doc( - """ - `password` string. The OAuth2 spec requires the exact field name - `password". - """ - ), - ], - scope: Annotated[ - str, - Form(), - Doc( - """ - A single string with actually several scopes separated by spaces. Each - scope is also a string. - - For example, a single string with: - - ```python - "items:read items:write users:read profile openid" - ```` - - would represent the scopes: - - * `items:read` - * `items:write` - * `users:read` - * `profile` - * `openid` - """ - ), - ] = "", - client_id: Annotated[ - Union[str, None], - Form(), - Doc( - """ - If there's a `client_id`, it can be sent as part of the form fields. - But the OAuth2 specification recommends sending the `client_id` and - `client_secret` (if any) using HTTP Basic auth. - """ - ), - ] = None, - client_secret: Annotated[ - Union[str, None], - Form(), - Doc( - """ - If there's a `client_password` (and a `client_id`), they can be sent - as part of the form fields. But the OAuth2 specification recommends - sending the `client_id` and `client_secret` (if any) using HTTP Basic - auth. - """ - ), - ] = None, - ): - super().__init__( - grant_type=grant_type, - username=username, - password=password, - scope=scope, - client_id=client_id, - client_secret=client_secret, - ) - - -class OAuth2(SecurityBase): - """ - This is the base class for OAuth2 authentication, an instance of it would be used - as a dependency. All other OAuth2 classes inherit from it and customize it for - each OAuth2 flow. - - You normally would not create a new class inheriting from it but use one of the - existing subclasses, and maybe compose them if you want to support multiple flows. - - Read more about it in the - [FastAPI docs for Security](https://fastapi.tiangolo.com/tutorial/security/). - """ - - def __init__( - self, - *, - flows: Annotated[ - Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]], - Doc( - """ - The dictionary of OAuth2 flows. - """ - ), - ] = OAuthFlowsModel(), - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if no HTTP Authorization header is provided, required for - OAuth2 authentication, it will automatically cancel the request and - send the client an error. - - If `auto_error` is set to `False`, when the HTTP Authorization header - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, with OAuth2 - or in a cookie). - """ - ), - ] = True, - ): - self.model = OAuth2Model( - flows=cast(OAuthFlowsModel, flows), description=description - ) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request) -> Optional[str]: - authorization = request.headers.get("Authorization") - if not authorization: - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return authorization - - -class OAuth2PasswordBearer(OAuth2): - """ - OAuth2 flow for authentication using a bearer token obtained with a password. - An instance of it would be used as a dependency. - - Read more about it in the - [FastAPI docs for Simple OAuth2 with Password and Bearer](https://fastapi.tiangolo.com/tutorial/security/simple-oauth2/). - """ - - def __init__( - self, - tokenUrl: Annotated[ - str, - Doc( - """ - The URL to obtain the OAuth2 token. This would be the *path operation* - that has `OAuth2PasswordRequestForm` as a dependency. - """ - ), - ], - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - scopes: Annotated[ - Optional[Dict[str, str]], - Doc( - """ - The OAuth2 scopes that would be required by the *path operations* that - use this dependency. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if no HTTP Auhtorization header is provided, required for - OAuth2 authentication, it will automatically cancel the request and - send the client an error. - - If `auto_error` is set to `False`, when the HTTP Authorization header - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, with OAuth2 - or in a cookie). - """ - ), - ] = True, - ): - if not scopes: - scopes = {} - flows = OAuthFlowsModel( - password=cast(Any, {"tokenUrl": tokenUrl, "scopes": scopes}) - ) - super().__init__( - flows=flows, - scheme_name=scheme_name, - description=description, - auto_error=auto_error, - ) - - async def __call__(self, request: Request) -> Optional[str]: - authorization = request.headers.get("Authorization") - scheme, param = get_authorization_scheme_param(authorization) - if not authorization or scheme.lower() != "bearer": - if self.auto_error: - raise HTTPException( - status_code=HTTP_401_UNAUTHORIZED, - detail="Not authenticated", - headers={"WWW-Authenticate": "Bearer"}, - ) - else: - return None - return param - - -class OAuth2AuthorizationCodeBearer(OAuth2): - """ - OAuth2 flow for authentication using a bearer token obtained with an OAuth2 code - flow. An instance of it would be used as a dependency. - """ - - def __init__( - self, - authorizationUrl: str, - tokenUrl: Annotated[ - str, - Doc( - """ - The URL to obtain the OAuth2 token. - """ - ), - ], - refreshUrl: Annotated[ - Optional[str], - Doc( - """ - The URL to refresh the token and obtain a new one. - """ - ), - ] = None, - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - scopes: Annotated[ - Optional[Dict[str, str]], - Doc( - """ - The OAuth2 scopes that would be required by the *path operations* that - use this dependency. - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if no HTTP Auhtorization header is provided, required for - OAuth2 authentication, it will automatically cancel the request and - send the client an error. - - If `auto_error` is set to `False`, when the HTTP Authorization header - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, with OAuth2 - or in a cookie). - """ - ), - ] = True, - ): - if not scopes: - scopes = {} - flows = OAuthFlowsModel( - authorizationCode=cast( - Any, - { - "authorizationUrl": authorizationUrl, - "tokenUrl": tokenUrl, - "refreshUrl": refreshUrl, - "scopes": scopes, - }, - ) - ) - super().__init__( - flows=flows, - scheme_name=scheme_name, - description=description, - auto_error=auto_error, - ) - - async def __call__(self, request: Request) -> Optional[str]: - authorization = request.headers.get("Authorization") - scheme, param = get_authorization_scheme_param(authorization) - if not authorization or scheme.lower() != "bearer": - if self.auto_error: - raise HTTPException( - status_code=HTTP_401_UNAUTHORIZED, - detail="Not authenticated", - headers={"WWW-Authenticate": "Bearer"}, - ) - else: - return None # pragma: nocover - return param - - -class SecurityScopes: - """ - This is a special class that you can define in a parameter in a dependency to - obtain the OAuth2 scopes required by all the dependencies in the same chain. - - This way, multiple dependencies can have different scopes, even when used in the - same *path operation*. And with this, you can access all the scopes required in - all those dependencies in a single place. - - Read more about it in the - [FastAPI docs for OAuth2 scopes](https://fastapi.tiangolo.com/advanced/security/oauth2-scopes/). - """ - - def __init__( - self, - scopes: Annotated[ - Optional[List[str]], - Doc( - """ - This will be filled by FastAPI. - """ - ), - ] = None, - ): - self.scopes: Annotated[ - List[str], - Doc( - """ - The list of all the scopes required by dependencies. - """ - ), - ] = scopes or [] - self.scope_str: Annotated[ - str, - Doc( - """ - All the scopes required by all the dependencies in a single string - separated by spaces, as defined in the OAuth2 specification. - """ - ), - ] = " ".join(self.scopes) diff --git a/.venv/Lib/site-packages/fastapi/security/open_id_connect_url.py b/.venv/Lib/site-packages/fastapi/security/open_id_connect_url.py deleted file mode 100644 index c612b47..0000000 --- a/.venv/Lib/site-packages/fastapi/security/open_id_connect_url.py +++ /dev/null @@ -1,84 +0,0 @@ -from typing import Optional - -from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel -from fastapi.security.base import SecurityBase -from starlette.exceptions import HTTPException -from starlette.requests import Request -from starlette.status import HTTP_403_FORBIDDEN -from typing_extensions import Annotated, Doc # type: ignore [attr-defined] - - -class OpenIdConnect(SecurityBase): - """ - OpenID Connect authentication class. An instance of it would be used as a - dependency. - """ - - def __init__( - self, - *, - openIdConnectUrl: Annotated[ - str, - Doc( - """ - The OpenID Connect URL. - """ - ), - ], - scheme_name: Annotated[ - Optional[str], - Doc( - """ - Security scheme name. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - description: Annotated[ - Optional[str], - Doc( - """ - Security scheme description. - - It will be included in the generated OpenAPI (e.g. visible at `/docs`). - """ - ), - ] = None, - auto_error: Annotated[ - bool, - Doc( - """ - By default, if no HTTP Auhtorization header is provided, required for - OpenID Connect authentication, it will automatically cancel the request - and send the client an error. - - If `auto_error` is set to `False`, when the HTTP Authorization header - is not available, instead of erroring out, the dependency result will - be `None`. - - This is useful when you want to have optional authentication. - - It is also useful when you want to have authentication that can be - provided in one of multiple optional ways (for example, with OpenID - Connect or in a cookie). - """ - ), - ] = True, - ): - self.model = OpenIdConnectModel( - openIdConnectUrl=openIdConnectUrl, description=description - ) - self.scheme_name = scheme_name or self.__class__.__name__ - self.auto_error = auto_error - - async def __call__(self, request: Request) -> Optional[str]: - authorization = request.headers.get("Authorization") - if not authorization: - if self.auto_error: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Not authenticated" - ) - else: - return None - return authorization diff --git a/.venv/Lib/site-packages/fastapi/security/utils.py b/.venv/Lib/site-packages/fastapi/security/utils.py deleted file mode 100644 index fa7a450..0000000 --- a/.venv/Lib/site-packages/fastapi/security/utils.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Optional, Tuple - - -def get_authorization_scheme_param( - authorization_header_value: Optional[str], -) -> Tuple[str, str]: - if not authorization_header_value: - return "", "" - scheme, _, param = authorization_header_value.partition(" ") - return scheme, param diff --git a/.venv/Lib/site-packages/fastapi/staticfiles.py b/.venv/Lib/site-packages/fastapi/staticfiles.py deleted file mode 100644 index 299015d..0000000 --- a/.venv/Lib/site-packages/fastapi/staticfiles.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.staticfiles import StaticFiles as StaticFiles # noqa diff --git a/.venv/Lib/site-packages/fastapi/templating.py b/.venv/Lib/site-packages/fastapi/templating.py deleted file mode 100644 index 0cb8684..0000000 --- a/.venv/Lib/site-packages/fastapi/templating.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.templating import Jinja2Templates as Jinja2Templates # noqa diff --git a/.venv/Lib/site-packages/fastapi/testclient.py b/.venv/Lib/site-packages/fastapi/testclient.py deleted file mode 100644 index 4012406..0000000 --- a/.venv/Lib/site-packages/fastapi/testclient.py +++ /dev/null @@ -1 +0,0 @@ -from starlette.testclient import TestClient as TestClient # noqa diff --git a/.venv/Lib/site-packages/fastapi/types.py b/.venv/Lib/site-packages/fastapi/types.py deleted file mode 100644 index 3205654..0000000 --- a/.venv/Lib/site-packages/fastapi/types.py +++ /dev/null @@ -1,10 +0,0 @@ -import types -from enum import Enum -from typing import Any, Callable, Dict, Set, Type, TypeVar, Union - -from pydantic import BaseModel - -DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any]) -UnionType = getattr(types, "UnionType", Union) -ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str] -IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]] diff --git a/.venv/Lib/site-packages/fastapi/utils.py b/.venv/Lib/site-packages/fastapi/utils.py deleted file mode 100644 index 53b2fa0..0000000 --- a/.venv/Lib/site-packages/fastapi/utils.py +++ /dev/null @@ -1,229 +0,0 @@ -import re -import warnings -from dataclasses import is_dataclass -from typing import ( - TYPE_CHECKING, - Any, - Dict, - MutableMapping, - Optional, - Set, - Type, - Union, - cast, -) -from weakref import WeakKeyDictionary - -import fastapi -from fastapi._compat import ( - PYDANTIC_V2, - BaseConfig, - ModelField, - PydanticSchemaGenerationError, - Undefined, - UndefinedType, - Validator, - lenient_issubclass, -) -from fastapi.datastructures import DefaultPlaceholder, DefaultType -from pydantic import BaseModel, create_model -from pydantic.fields import FieldInfo -from typing_extensions import Literal - -if TYPE_CHECKING: # pragma: nocover - from .routing import APIRoute - -# Cache for `create_cloned_field` -_CLONED_TYPES_CACHE: MutableMapping[ - Type[BaseModel], Type[BaseModel] -] = WeakKeyDictionary() - - -def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool: - if status_code is None: - return True - # Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1 - if status_code in { - "default", - "1XX", - "2XX", - "3XX", - "4XX", - "5XX", - }: - return True - current_status_code = int(status_code) - return not (current_status_code < 200 or current_status_code in {204, 205, 304}) - - -def get_path_param_names(path: str) -> Set[str]: - return set(re.findall("{(.*?)}", path)) - - -def create_response_field( - name: str, - type_: Type[Any], - class_validators: Optional[Dict[str, Validator]] = None, - default: Optional[Any] = Undefined, - required: Union[bool, UndefinedType] = Undefined, - model_config: Type[BaseConfig] = BaseConfig, - field_info: Optional[FieldInfo] = None, - alias: Optional[str] = None, - mode: Literal["validation", "serialization"] = "validation", -) -> ModelField: - """ - Create a new response field. Raises if type_ is invalid. - """ - class_validators = class_validators or {} - if PYDANTIC_V2: - field_info = field_info or FieldInfo( - annotation=type_, default=default, alias=alias - ) - else: - field_info = field_info or FieldInfo() - kwargs = {"name": name, "field_info": field_info} - if PYDANTIC_V2: - kwargs.update({"mode": mode}) - else: - kwargs.update( - { - "type_": type_, - "class_validators": class_validators, - "default": default, - "required": required, - "model_config": model_config, - "alias": alias, - } - ) - try: - return ModelField(**kwargs) # type: ignore[arg-type] - except (RuntimeError, PydanticSchemaGenerationError): - raise fastapi.exceptions.FastAPIError( - "Invalid args for response field! Hint: " - f"check that {type_} is a valid Pydantic field type. " - "If you are using a return type annotation that is not a valid Pydantic " - "field (e.g. Union[Response, dict, None]) you can disable generating the " - "response model from the type annotation with the path operation decorator " - "parameter response_model=None. Read more: " - "https://fastapi.tiangolo.com/tutorial/response-model/" - ) from None - - -def create_cloned_field( - field: ModelField, - *, - cloned_types: Optional[MutableMapping[Type[BaseModel], Type[BaseModel]]] = None, -) -> ModelField: - if PYDANTIC_V2: - return field - # cloned_types caches already cloned types to support recursive models and improve - # performance by avoiding unnecessary cloning - if cloned_types is None: - cloned_types = _CLONED_TYPES_CACHE - - original_type = field.type_ - if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"): - original_type = original_type.__pydantic_model__ - use_type = original_type - if lenient_issubclass(original_type, BaseModel): - original_type = cast(Type[BaseModel], original_type) - use_type = cloned_types.get(original_type) - if use_type is None: - use_type = create_model(original_type.__name__, __base__=original_type) - cloned_types[original_type] = use_type - for f in original_type.__fields__.values(): - use_type.__fields__[f.name] = create_cloned_field( - f, cloned_types=cloned_types - ) - new_field = create_response_field(name=field.name, type_=use_type) - new_field.has_alias = field.has_alias # type: ignore[attr-defined] - new_field.alias = field.alias # type: ignore[misc] - new_field.class_validators = field.class_validators # type: ignore[attr-defined] - new_field.default = field.default # type: ignore[misc] - new_field.required = field.required # type: ignore[misc] - new_field.model_config = field.model_config # type: ignore[attr-defined] - new_field.field_info = field.field_info - new_field.allow_none = field.allow_none # type: ignore[attr-defined] - new_field.validate_always = field.validate_always # type: ignore[attr-defined] - if field.sub_fields: # type: ignore[attr-defined] - new_field.sub_fields = [ # type: ignore[attr-defined] - create_cloned_field(sub_field, cloned_types=cloned_types) - for sub_field in field.sub_fields # type: ignore[attr-defined] - ] - if field.key_field: # type: ignore[attr-defined] - new_field.key_field = create_cloned_field( # type: ignore[attr-defined] - field.key_field, # type: ignore[attr-defined] - cloned_types=cloned_types, - ) - new_field.validators = field.validators # type: ignore[attr-defined] - new_field.pre_validators = field.pre_validators # type: ignore[attr-defined] - new_field.post_validators = field.post_validators # type: ignore[attr-defined] - new_field.parse_json = field.parse_json # type: ignore[attr-defined] - new_field.shape = field.shape # type: ignore[attr-defined] - new_field.populate_validators() # type: ignore[attr-defined] - return new_field - - -def generate_operation_id_for_path( - *, name: str, path: str, method: str -) -> str: # pragma: nocover - warnings.warn( - "fastapi.utils.generate_operation_id_for_path() was deprecated, " - "it is not used internally, and will be removed soon", - DeprecationWarning, - stacklevel=2, - ) - operation_id = f"{name}{path}" - operation_id = re.sub(r"\W", "_", operation_id) - operation_id = f"{operation_id}_{method.lower()}" - return operation_id - - -def generate_unique_id(route: "APIRoute") -> str: - operation_id = f"{route.name}{route.path_format}" - operation_id = re.sub(r"\W", "_", operation_id) - assert route.methods - operation_id = f"{operation_id}_{list(route.methods)[0].lower()}" - return operation_id - - -def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) -> None: - for key, value in update_dict.items(): - if ( - key in main_dict - and isinstance(main_dict[key], dict) - and isinstance(value, dict) - ): - deep_dict_update(main_dict[key], value) - elif ( - key in main_dict - and isinstance(main_dict[key], list) - and isinstance(update_dict[key], list) - ): - main_dict[key] = main_dict[key] + update_dict[key] - else: - main_dict[key] = value - - -def get_value_or_default( - first_item: Union[DefaultPlaceholder, DefaultType], - *extra_items: Union[DefaultPlaceholder, DefaultType], -) -> Union[DefaultPlaceholder, DefaultType]: - """ - Pass items or `DefaultPlaceholder`s by descending priority. - - The first one to _not_ be a `DefaultPlaceholder` will be returned. - - Otherwise, the first item (a `DefaultPlaceholder`) will be returned. - """ - items = (first_item,) + extra_items - for item in items: - if not isinstance(item, DefaultPlaceholder): - return item - return first_item - - -def match_pydantic_error_url(error_type: str) -> Any: - from dirty_equals import IsStr - - return IsStr(regex=rf"^https://errors\.pydantic\.dev/.*/v/{error_type}") diff --git a/.venv/Lib/site-packages/fastapi/websockets.py b/.venv/Lib/site-packages/fastapi/websockets.py deleted file mode 100644 index 55a4ac4..0000000 --- a/.venv/Lib/site-packages/fastapi/websockets.py +++ /dev/null @@ -1,3 +0,0 @@ -from starlette.websockets import WebSocket as WebSocket # noqa -from starlette.websockets import WebSocketDisconnect as WebSocketDisconnect # noqa -from starlette.websockets import WebSocketState as WebSocketState # noqa diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/AUTHORS b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/AUTHORS deleted file mode 100644 index 42a5c22..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/AUTHORS +++ /dev/null @@ -1,51 +0,0 @@ -Original Authors ----------------- -* Armin Rigo -* Christian Tismer - -Contributors ------------- -* Al Stone -* Alexander Schmidt -* Alexey Borzenkov -* Andreas Schwab -* Armin Ronacher -* Bin Wang -* Bob Ippolito -* ChangBo Guo -* Christoph Gohlke -* Denis Bilenko -* Dirk Mueller -* Donovan Preston -* Fantix King -* Floris Bruynooghe -* Fredrik Fornwall -* Gerd Woetzel -* Giel van Schijndel -* Gökhan Karabulut -* Gustavo Niemeyer -* Guy Rozendorn -* Hye-Shik Chang -* Jared Kuolt -* Jason Madden -* Josh Snyder -* Kyle Ambroff -* Laszlo Boszormenyi -* Mao Han -* Marc Abramowitz -* Marc Schlaich -* Marcin Bachry -* Matt Madison -* Matt Turner -* Michael Ellerman -* Michael Matz -* Ralf Schmitt -* Robie Basak -* Ronny Pfannschmidt -* Samual M. Rushing -* Tony Bowles -* Tony Breeds -* Trevor Bowen -* Tulio Magno Quites Machado Filho -* Ulrich Weigand -* Victor Stinner diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/INSTALLER b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE deleted file mode 100644 index b73a4a1..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -The following files are derived from Stackless Python and are subject to the -same license as Stackless Python: - - src/greenlet/slp_platformselect.h - files in src/greenlet/platform/ directory - -See LICENSE.PSF and http://www.stackless.com/ for details. - -Unless otherwise noted, the files in greenlet have been released under the -following MIT license: - -Copyright (c) Armin Rigo, Christian Tismer and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE.PSF b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE.PSF deleted file mode 100644 index d3b509a..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/LICENSE.PSF +++ /dev/null @@ -1,47 +0,0 @@ -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011 Python Software Foundation; All Rights Reserved" are retained in Python -alone or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/METADATA b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/METADATA deleted file mode 100644 index 1529410..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/METADATA +++ /dev/null @@ -1,103 +0,0 @@ -Metadata-Version: 2.1 -Name: greenlet -Version: 3.1.1 -Summary: Lightweight in-process concurrent programming -Home-page: https://greenlet.readthedocs.io/ -Author: Alexey Borzenkov -Author-email: snaury@gmail.com -Maintainer: Jason Madden -Maintainer-email: jason@seecoresoftware.com -License: MIT License -Project-URL: Bug Tracker, https://github.com/python-greenlet/greenlet/issues -Project-URL: Source Code, https://github.com/python-greenlet/greenlet/ -Project-URL: Documentation, https://greenlet.readthedocs.io/ -Keywords: greenlet coroutine concurrency threads cooperative -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Natural Language :: English -Classifier: Programming Language :: C -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 :: 3.13 -Classifier: Operating System :: OS Independent -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE -License-File: LICENSE.PSF -License-File: AUTHORS -Provides-Extra: docs -Requires-Dist: Sphinx ; extra == 'docs' -Requires-Dist: furo ; extra == 'docs' -Provides-Extra: test -Requires-Dist: objgraph ; extra == 'test' -Requires-Dist: psutil ; extra == 'test' - -.. This file is included into docs/history.rst - - -Greenlets are lightweight coroutines for in-process concurrent -programming. - -The "greenlet" package is a spin-off of `Stackless`_, a version of -CPython that supports micro-threads called "tasklets". Tasklets run -pseudo-concurrently (typically in a single or a few OS-level threads) -and are synchronized with data exchanges on "channels". - -A "greenlet", on the other hand, is a still more primitive notion of -micro-thread with no implicit scheduling; coroutines, in other words. -This is useful when you want to control exactly when your code runs. -You can build custom scheduled micro-threads on top of greenlet; -however, it seems that greenlets are useful on their own as a way to -make advanced control flow structures. For example, we can recreate -generators; the difference with Python's own generators is that our -generators can call nested functions and the nested functions can -yield values too. (Additionally, you don't need a "yield" keyword. See -the example in `test_generator.py -`_). - -Greenlets are provided as a C extension module for the regular unmodified -interpreter. - -.. _`Stackless`: http://www.stackless.com - - -Who is using Greenlet? -====================== - -There are several libraries that use Greenlet as a more flexible -alternative to Python's built in coroutine support: - - - `Concurrence`_ - - `Eventlet`_ - - `Gevent`_ - -.. _Concurrence: http://opensource.hyves.org/concurrence/ -.. _Eventlet: http://eventlet.net/ -.. _Gevent: http://www.gevent.org/ - -Getting Greenlet -================ - -The easiest way to get Greenlet is to install it with pip:: - - pip install greenlet - - -Source code archives and binary distributions are available on the -python package index at https://pypi.org/project/greenlet - -The source code repository is hosted on github: -https://github.com/python-greenlet/greenlet - -Documentation is available on readthedocs.org: -https://greenlet.readthedocs.io diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/RECORD b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/RECORD deleted file mode 100644 index 3111f25..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/RECORD +++ /dev/null @@ -1,122 +0,0 @@ -../../include/site/python3.11/greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755 -greenlet-3.1.1.dist-info/AUTHORS,sha256=swW28t2knVRxRkaEQNZtO7MP9Sgnompb7B6cNgJM8Gk,849 -greenlet-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -greenlet-3.1.1.dist-info/LICENSE,sha256=dpgx1uXfrywggC-sz_H6-0wgJd2PYlPfpH_K1Z1NCXk,1434 -greenlet-3.1.1.dist-info/LICENSE.PSF,sha256=5f88I8EQ5JTNfXNsEP2W1GJFe6_soxCEDbZScpjH1Gs,2424 -greenlet-3.1.1.dist-info/METADATA,sha256=98pubBCLPmBVc3KVRmetjFyd8jA9e9BXhD_07cAcccc,3933 -greenlet-3.1.1.dist-info/RECORD,, -greenlet-3.1.1.dist-info/WHEEL,sha256=qW4RD1rfHm8ZRUjJbXUnZHDNPCXHt6Rq0mgR8lv_JEg,101 -greenlet-3.1.1.dist-info/top_level.txt,sha256=YSnRsCRoO61JGlP57o8iKL6rdLWDWuiyKD8ekpWUsDc,9 -greenlet/CObjects.cpp,sha256=OPej1bWBgc4sRrTRQ2aFFML9pzDYKlKhlJSjsI0X_eU,3508 -greenlet/PyGreenlet.cpp,sha256=ogWsQ5VhSdItWRLLpWOgSuqYuM3QwQ4cVCxOQIgHx6E,23441 -greenlet/PyGreenlet.hpp,sha256=2ZQlOxYNoy7QwD7mppFoOXe_At56NIsJ0eNsE_hoSsw,1463 -greenlet/PyGreenletUnswitchable.cpp,sha256=PQE0fSZa_IOyUM44IESHkJoD2KtGW3dkhkmZSYY3WHs,4375 -greenlet/PyModule.cpp,sha256=J2TH06dGcNEarioS6NbWXkdME8hJY05XVbdqLrfO5w4,8587 -greenlet/TBrokenGreenlet.cpp,sha256=smN26uC7ahAbNYiS10rtWPjCeTG4jevM8siA2sjJiXg,1021 -greenlet/TExceptionState.cpp,sha256=U7Ctw9fBdNraS0d174MoQW7bN-ae209Ta0JuiKpcpVI,1359 -greenlet/TGreenlet.cpp,sha256=HGYGKpmKYqQ842tASW-QaaV8wua4a5XV_quYKPDsV_Y,25731 -greenlet/TGreenlet.hpp,sha256=mMHcb_rSuozdDiGJjX3GgyYkWgVM4kuO1UgbUP84BlU,27869 -greenlet/TGreenletGlobals.cpp,sha256=YyEmDjKf1g32bsL-unIUScFLnnA1fzLWf2gOMd-D0Zw,3264 -greenlet/TMainGreenlet.cpp,sha256=fvgb8HHB-FVTPEKjR1s_ifCZSpp5D5YQByik0CnIABg,3276 -greenlet/TPythonState.cpp,sha256=FxRdi76lTGXaQKWwkq82VaCfIRdF2Z-fh-TlRTMjYqg,15359 -greenlet/TStackState.cpp,sha256=V444I8Jj9DhQz-9leVW_9dtiSRjaE1NMlgDG02Xxq-Y,7381 -greenlet/TThreadState.hpp,sha256=2Jgg7DtGggMYR_x3CLAvAFf1mIdIDtQvSSItcdmX4ZQ,19131 -greenlet/TThreadStateCreator.hpp,sha256=uYTexDWooXSSgUc5uh-Mhm5BQi3-kR6CqpizvNynBFQ,2610 -greenlet/TThreadStateDestroy.cpp,sha256=wt7lQwLI0mi_JtnZB_jB4bUmfCa5b6nQhA7XOmnI1yk,9568 -greenlet/TUserGreenlet.cpp,sha256=uemg0lwKXtYB0yzmvyYdIIAsKnNkifXM1OJ2OlrFP1A,23553 -greenlet/__init__.py,sha256=OOmvT6_vn_SekdPzkj4qm6hjfikXMmdNZYDmGTOaRNo,1723 -greenlet/__pycache__/__init__.cpython-311.pyc,, -greenlet/_greenlet.cp311-win_amd64.pyd,sha256=HjcMMexT7ZNYnwqs3WhXxe9hJhH0Hkli-elGK8jT9YA,218112 -greenlet/greenlet.cpp,sha256=WdItb1yWL9WNsTqJNf0Iw8ZwDHD49pkDP0rIRGBg2pw,10996 -greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755 -greenlet/greenlet_allocator.hpp,sha256=kxyWW4Qdwlrc7ufgdb5vd6Y7jhauQ699Kod0mqiO1iM,1582 -greenlet/greenlet_compiler_compat.hpp,sha256=nRxpLN9iNbnLVyFDeVmOwyeeNm6scQrOed1l7JQYMCM,4346 -greenlet/greenlet_cpython_add_pending.hpp,sha256=apAwIhGlgYrnYn03zWL6Sxy68kltDeb1e0QupZfb3DQ,6043 -greenlet/greenlet_cpython_compat.hpp,sha256=L_jig3dm2bsJWRazrhlokma2NfnwixoQ0cydshh6ce4,3964 -greenlet/greenlet_exceptions.hpp,sha256=06Bx81DtVaJTa6RtiMcV141b-XHv4ppEgVItkblcLWY,4503 -greenlet/greenlet_internal.hpp,sha256=Ajc-_09W4xWzm9XfyXHAeQAFUgKGKsnJwYsTCoNy3ns,2709 -greenlet/greenlet_refs.hpp,sha256=OnbA91yZf3QHH6-eJccvoNDAaN-pQBMMrclFU1Ot3J4,34436 -greenlet/greenlet_slp_switch.hpp,sha256=kM1QHA2iV-gH4cFyN6lfIagHQxvJZjWOVJdIxRE3TlQ,3198 -greenlet/greenlet_thread_support.hpp,sha256=XUJ6ljWjf9OYyuOILiz8e_yHvT3fbaUiHdhiPNQUV4s,867 -greenlet/platform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -greenlet/platform/__pycache__/__init__.cpython-311.pyc,, -greenlet/platform/setup_switch_x64_masm.cmd,sha256=ZpClUJeU0ujEPSTWNSepP0W2f9XiYQKA8QKSoVou8EU,143 -greenlet/platform/switch_aarch64_gcc.h,sha256=GKC0yWNXnbK2X--X6aguRCMj2Tg7hDU1Zkl3RljDvC8,4307 -greenlet/platform/switch_alpha_unix.h,sha256=Z-SvF8JQV3oxWT8JRbL9RFu4gRFxPdJ7cviM8YayMmw,671 -greenlet/platform/switch_amd64_unix.h,sha256=EcSFCBlodEBhqhKjcJqY_5Dn_jn7pKpkJlOvp7gFXLI,2748 -greenlet/platform/switch_arm32_gcc.h,sha256=Z3KkHszdgq6uU4YN3BxvKMG2AdDnovwCCNrqGWZ1Lyo,2479 -greenlet/platform/switch_arm32_ios.h,sha256=mm5_R9aXB92hyxzFRwB71M60H6AlvHjrpTrc72Pz3l8,1892 -greenlet/platform/switch_arm64_masm.asm,sha256=4kpTtfy7rfcr8j1CpJLAK21EtZpGDAJXWRU68HEy5A8,1245 -greenlet/platform/switch_arm64_masm.obj,sha256=DmLnIB_icoEHAz1naue_pJPTZgR9ElM7-Nmztr-o9_U,746 -greenlet/platform/switch_arm64_msvc.h,sha256=RqK5MHLmXI3Q-FQ7tm32KWnbDNZKnkJdq8CR89cz640,398 -greenlet/platform/switch_csky_gcc.h,sha256=kDikyiPpewP71KoBZQO_MukDTXTXBiC7x-hF0_2DL0w,1331 -greenlet/platform/switch_loongarch64_linux.h,sha256=7M-Dhc4Q8tRbJCJhalDLwU6S9Mx8MjmN1RbTDgIvQTM,779 -greenlet/platform/switch_m68k_gcc.h,sha256=VSa6NpZhvyyvF-Q58CTIWSpEDo4FKygOyTz00whctlw,928 -greenlet/platform/switch_mips_unix.h,sha256=E0tYsqc5anDY1BhenU1l8DW-nVHC_BElzLgJw3TGtPk,1426 -greenlet/platform/switch_ppc64_aix.h,sha256=_BL0iyRr3ZA5iPlr3uk9SJ5sNRWGYLrXcZ5z-CE9anE,3860 -greenlet/platform/switch_ppc64_linux.h,sha256=0rriT5XyxPb0GqsSSn_bP9iQsnjsPbBmu0yqo5goSyQ,3815 -greenlet/platform/switch_ppc_aix.h,sha256=pHA4slEjUFP3J3SYm1TAlNPhgb2G_PAtax5cO8BEe1A,2941 -greenlet/platform/switch_ppc_linux.h,sha256=YwrlKUzxlXuiKMQqr6MFAV1bPzWnmvk6X1AqJZEpOWU,2759 -greenlet/platform/switch_ppc_macosx.h,sha256=Z6KN_ud0n6nC3ltJrNz2qtvER6vnRAVRNH9mdIDpMxY,2624 -greenlet/platform/switch_ppc_unix.h,sha256=-ZG7MSSPEA5N4qO9PQChtyEJ-Fm6qInhyZm_ZBHTtMg,2652 -greenlet/platform/switch_riscv_unix.h,sha256=Xg0wBen8Je21LWzFtLNLvUUYq6p9n_WY7AUQbiBVyyk,865 -greenlet/platform/switch_s390_unix.h,sha256=RRlGu957ybmq95qNNY4Qw1mcaoT3eBnW5KbVwu48KX8,2763 -greenlet/platform/switch_sh_gcc.h,sha256=mcRJBTu-2UBf4kZtX601qofwuDuy-Y-hnxJtrcaB7do,901 -greenlet/platform/switch_sparc_sun_gcc.h,sha256=xZish9GsMHBienUbUMsX1-ZZ-as7hs36sVhYIE3ew8Y,2797 -greenlet/platform/switch_x32_unix.h,sha256=nM98PKtzTWc1lcM7TRMUZJzskVdR1C69U1UqZRWX0GE,1509 -greenlet/platform/switch_x64_masm.asm,sha256=nu6n2sWyXuXfpPx40d9YmLfHXUc1sHgeTvX1kUzuvEM,1841 -greenlet/platform/switch_x64_masm.obj,sha256=GNtTNxYdo7idFUYsQv-mrXWgyT5EJ93-9q90lN6svtQ,1078 -greenlet/platform/switch_x64_msvc.h,sha256=LIeasyKo_vHzspdMzMHbosRhrBfKI4BkQOh4qcTHyJw,1805 -greenlet/platform/switch_x86_msvc.h,sha256=TtGOwinbFfnn6clxMNkCz8i6OmgB6kVRrShoF5iT9to,12838 -greenlet/platform/switch_x86_unix.h,sha256=VplW9H0FF0cZHw1DhJdIUs5q6YLS4cwb2nYwjF83R1s,3059 -greenlet/slp_platformselect.h,sha256=s-U-BrZ3qwwfI-6W9zWw2rb404OksZYbxYC2w5kSMXM,3841 -greenlet/tests/__init__.py,sha256=cj2-qpMXnlVRLbMLX-rPNNMVJ42ZssdxHd84NSQ3YXw,9246 -greenlet/tests/__pycache__/__init__.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pyc,, -greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pyc,, -greenlet/tests/__pycache__/leakcheck.cpython-311.pyc,, -greenlet/tests/__pycache__/test_contextvars.cpython-311.pyc,, -greenlet/tests/__pycache__/test_cpp.cpython-311.pyc,, -greenlet/tests/__pycache__/test_extension_interface.cpython-311.pyc,, -greenlet/tests/__pycache__/test_gc.cpython-311.pyc,, -greenlet/tests/__pycache__/test_generator.cpython-311.pyc,, -greenlet/tests/__pycache__/test_generator_nested.cpython-311.pyc,, -greenlet/tests/__pycache__/test_greenlet.cpython-311.pyc,, -greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pyc,, -greenlet/tests/__pycache__/test_leaks.cpython-311.pyc,, -greenlet/tests/__pycache__/test_stack_saved.cpython-311.pyc,, -greenlet/tests/__pycache__/test_throw.cpython-311.pyc,, -greenlet/tests/__pycache__/test_tracing.cpython-311.pyc,, -greenlet/tests/__pycache__/test_version.cpython-311.pyc,, -greenlet/tests/__pycache__/test_weakref.cpython-311.pyc,, -greenlet/tests/_test_extension.c,sha256=vkeGA-6oeJcGILsD7oIrT1qZop2GaTOHXiNT7mcSl-0,5773 -greenlet/tests/_test_extension.cp311-win_amd64.pyd,sha256=RqCio8fNDiXhn2a8WMjMHDQSrtiSGUOciWK96iJSgx8,14336 -greenlet/tests/_test_extension_cpp.cp311-win_amd64.pyd,sha256=IDJJJncwhpCsNl73G1elMI_LJ69sZTNPbzea-XeA_N4,15872 -greenlet/tests/_test_extension_cpp.cpp,sha256=e0kVnaB8CCaEhE9yHtNyfqTjevsPDKKx-zgxk7PPK48,6565 -greenlet/tests/fail_clearing_run_switches.py,sha256=o433oA_nUCtOPaMEGc8VEhZIKa71imVHXFw7TsXaP8M,1263 -greenlet/tests/fail_cpp_exception.py,sha256=o_ZbipWikok8Bjc-vjiQvcb5FHh2nVW-McGKMLcMzh0,985 -greenlet/tests/fail_initialstub_already_started.py,sha256=txENn5IyzGx2p-XR1XB7qXmC8JX_4mKDEA8kYBXUQKc,1961 -greenlet/tests/fail_slp_switch.py,sha256=rJBZcZfTWR3e2ERQtPAud6YKShiDsP84PmwOJbp4ey0,524 -greenlet/tests/fail_switch_three_greenlets.py,sha256=zSitV7rkNnaoHYVzAGGLnxz-yPtohXJJzaE8ehFDQ0M,956 -greenlet/tests/fail_switch_three_greenlets2.py,sha256=FPJensn2EJxoropl03JSTVP3kgP33k04h6aDWWozrOk,1285 -greenlet/tests/fail_switch_two_greenlets.py,sha256=1CaI8s3504VbbF1vj1uBYuy-zxBHVzHPIAd1LIc8ONg,817 -greenlet/tests/leakcheck.py,sha256=inbfM7_oVzd8jIKGxCgo4JqpFZaDAnWPkSULJ8vIE1s,11964 -greenlet/tests/test_contextvars.py,sha256=0n5pR_lbpAppc5wFfK0e1SwYLM-fsSFp72B5_ArLPGE,10348 -greenlet/tests/test_cpp.py,sha256=hpxhFAdKJTpAVZP8CBGs1ZcrKdscI9BaDZk4btkI5d4,2736 -greenlet/tests/test_extension_interface.py,sha256=eJ3cwLacdK2WbsrC-4DgeyHdwLRcG4zx7rrkRtqSzC4,3829 -greenlet/tests/test_gc.py,sha256=PCOaRpIyjNnNlDogGL3FZU_lrdXuM-pv1rxeE5TP5mc,2923 -greenlet/tests/test_generator.py,sha256=tONXiTf98VGm347o1b-810daPiwdla5cbpFg6QI1R1g,1240 -greenlet/tests/test_generator_nested.py,sha256=7v4HOYrf1XZP39dk5IUMubdZ8yc3ynwZcqj9GUJyMSA,3718 -greenlet/tests/test_greenlet.py,sha256=zoAy56MtEyz5P93Iknpt2pPjNO3ePYrgM7SDE8Cw_uI,45990 -greenlet/tests/test_greenlet_trash.py,sha256=n2dBlQfOoEO1ODatFi8QdhboH3fB86YtqzcYMYOXxbw,7947 -greenlet/tests/test_leaks.py,sha256=wskLqCAvqZ3qTZkam_wXzd-E5zelUjlXS5Ss8KshtZY,17465 -greenlet/tests/test_stack_saved.py,sha256=eyzqNY2VCGuGlxhT_In6TvZ6Okb0AXFZVyBEnK1jDwA,446 -greenlet/tests/test_throw.py,sha256=u2TQ_WvvCd6N6JdXWIxVEcXkKu5fepDlz9dktYdmtng,3712 -greenlet/tests/test_tracing.py,sha256=VlwzMU0C1noospZhuUMyB7MHw200emIvGCN_6G2p2ZU,8250 -greenlet/tests/test_version.py,sha256=O9DpAITsOFgiRcjd4odQ7ejmwx_N9Q1zQENVcbtFHIc,1339 -greenlet/tests/test_weakref.py,sha256=F8M23btEF87bIbpptLNBORosbQqNZGiYeKMqYjWrsak,883 diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/WHEEL b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/WHEEL deleted file mode 100644 index b454aa7..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (75.1.0) -Root-Is-Purelib: false -Tag: cp311-cp311-win_amd64 - diff --git a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/top_level.txt b/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/top_level.txt deleted file mode 100644 index 46725be..0000000 --- a/.venv/Lib/site-packages/greenlet-3.1.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -greenlet diff --git a/.venv/Lib/site-packages/greenlet/CObjects.cpp b/.venv/Lib/site-packages/greenlet/CObjects.cpp deleted file mode 100644 index c135995..0000000 --- a/.venv/Lib/site-packages/greenlet/CObjects.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef COBJECTS_CPP -#define COBJECTS_CPP -/***************************************************************************** - * C interface - * - * These are exported using the CObject API - */ -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-function" -#endif - -#include "greenlet_exceptions.hpp" - -#include "greenlet_internal.hpp" -#include "greenlet_refs.hpp" - - -#include "TThreadStateDestroy.cpp" - -#include "PyGreenlet.hpp" - -using greenlet::PyErrOccurred; -using greenlet::Require; - - - -extern "C" { -static PyGreenlet* -PyGreenlet_GetCurrent(void) -{ - return GET_THREAD_STATE().state().get_current().relinquish_ownership(); -} - -static int -PyGreenlet_SetParent(PyGreenlet* g, PyGreenlet* nparent) -{ - return green_setparent((PyGreenlet*)g, (PyObject*)nparent, NULL); -} - -static PyGreenlet* -PyGreenlet_New(PyObject* run, PyGreenlet* parent) -{ - using greenlet::refs::NewDictReference; - // In the past, we didn't use green_new and green_init, but that - // was a maintenance issue because we duplicated code. This way is - // much safer, but slightly slower. If that's a problem, we could - // refactor green_init to separate argument parsing from initialization. - OwnedGreenlet g = OwnedGreenlet::consuming(green_new(&PyGreenlet_Type, nullptr, nullptr)); - if (!g) { - return NULL; - } - - try { - NewDictReference kwargs; - if (run) { - kwargs.SetItem(mod_globs->str_run, run); - } - if (parent) { - kwargs.SetItem("parent", (PyObject*)parent); - } - - Require(green_init(g.borrow(), mod_globs->empty_tuple, kwargs.borrow())); - } - catch (const PyErrOccurred&) { - return nullptr; - } - - return g.relinquish_ownership(); -} - -static PyObject* -PyGreenlet_Switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return NULL; - } - - if (args == NULL) { - args = mod_globs->empty_tuple; - } - - if (kwargs == NULL || !PyDict_Check(kwargs)) { - kwargs = NULL; - } - - return green_switch(self, args, kwargs); -} - -static PyObject* -PyGreenlet_Throw(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return nullptr; - } - try { - PyErrPieces err_pieces(typ, val, tb); - return internal_green_throw(self, err_pieces).relinquish_ownership(); - } - catch (const PyErrOccurred&) { - return nullptr; - } -} - - - -static int -Extern_PyGreenlet_MAIN(PyGreenlet* self) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return -1; - } - return self->pimpl->main(); -} - -static int -Extern_PyGreenlet_ACTIVE(PyGreenlet* self) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return -1; - } - return self->pimpl->active(); -} - -static int -Extern_PyGreenlet_STARTED(PyGreenlet* self) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return -1; - } - return self->pimpl->started(); -} - -static PyGreenlet* -Extern_PyGreenlet_GET_PARENT(PyGreenlet* self) -{ - if (!PyGreenlet_Check(self)) { - PyErr_BadArgument(); - return NULL; - } - // This can return NULL even if there is no exception - return self->pimpl->parent().acquire(); -} -} // extern C. - -/** End C API ****************************************************************/ -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - - -#endif diff --git a/.venv/Lib/site-packages/greenlet/PyGreenlet.cpp b/.venv/Lib/site-packages/greenlet/PyGreenlet.cpp deleted file mode 100644 index 29c0bba..0000000 --- a/.venv/Lib/site-packages/greenlet/PyGreenlet.cpp +++ /dev/null @@ -1,738 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -#ifndef PYGREENLET_CPP -#define PYGREENLET_CPP -/***************** -The Python slot functions for TGreenlet. - */ - - -#define PY_SSIZE_T_CLEAN -#include -#include "structmember.h" // PyMemberDef - -#include "greenlet_internal.hpp" -#include "TThreadStateDestroy.cpp" -#include "TGreenlet.hpp" -// #include "TUserGreenlet.cpp" -// #include "TMainGreenlet.cpp" -// #include "TBrokenGreenlet.cpp" - - -#include "greenlet_refs.hpp" -#include "greenlet_slp_switch.hpp" - -#include "greenlet_thread_support.hpp" -#include "TGreenlet.hpp" - -#include "TGreenletGlobals.cpp" -#include "TThreadStateDestroy.cpp" -#include "PyGreenlet.hpp" -// #include "TGreenlet.cpp" - -// #include "TExceptionState.cpp" -// #include "TPythonState.cpp" -// #include "TStackState.cpp" - -using greenlet::LockGuard; -using greenlet::LockInitError; -using greenlet::PyErrOccurred; -using greenlet::Require; - -using greenlet::g_handle_exit; -using greenlet::single_result; - -using greenlet::Greenlet; -using greenlet::UserGreenlet; -using greenlet::MainGreenlet; -using greenlet::BrokenGreenlet; -using greenlet::ThreadState; -using greenlet::PythonState; - - - -static PyGreenlet* -green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)) -{ - PyGreenlet* o = - (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); - if (o) { - new UserGreenlet(o, GET_THREAD_STATE().state().borrow_current()); - assert(Py_REFCNT(o) == 1); - } - return o; -} - - -// green_init is used in the tp_init slot. So it's important that -// it can be called directly from CPython. Thus, we don't use -// BorrowedGreenlet and BorrowedObject --- although in theory -// these should be binary layout compatible, that may not be -// guaranteed to be the case (32-bit linux ppc possibly). -static int -green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs) -{ - PyArgParseParam run; - PyArgParseParam nparent; - static const char* kwlist[] = { - "run", - "parent", - NULL - }; - - // recall: The O specifier does NOT increase the reference count. - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "|OO:green", (char**)kwlist, &run, &nparent)) { - return -1; - } - - if (run) { - if (green_setrun(self, run, NULL)) { - return -1; - } - } - if (nparent && !nparent.is_None()) { - return green_setparent(self, nparent, NULL); - } - return 0; -} - - - -static int -green_traverse(PyGreenlet* self, visitproc visit, void* arg) -{ - // We must only visit referenced objects, i.e. only objects - // Py_INCREF'ed by this greenlet (directly or indirectly): - // - // - stack_prev is not visited: holds previous stack pointer, but it's not - // referenced - // - frames are not visited as we don't strongly reference them; - // alive greenlets are not garbage collected - // anyway. This can be a problem, however, if this greenlet is - // never allowed to finish, and is referenced from the frame: we - // have an uncollectible cycle in that case. Note that the - // frame object itself is also frequently not even tracked by the GC - // starting with Python 3.7 (frames are allocated by the - // interpreter untracked, and only become tracked when their - // evaluation is finished if they have a refcount > 1). All of - // this is to say that we should probably strongly reference - // the frame object. Doing so, while always allowing GC on a - // greenlet, solves several leaks for us. - - Py_VISIT(self->dict); - if (!self->pimpl) { - // Hmm. I have seen this at interpreter shutdown time, - // I think. That's very odd because this doesn't go away until - // we're ``green_dealloc()``, at which point we shouldn't be - // traversed anymore. - return 0; - } - - return self->pimpl->tp_traverse(visit, arg); -} - -static int -green_is_gc(PyObject* _self) -{ - BorrowedGreenlet self(_self); - int result = 0; - /* Main greenlet can be garbage collected since it can only - become unreachable if the underlying thread exited. - Active greenlets --- including those that are suspended --- - cannot be garbage collected, however. - */ - if (self->main() || !self->active()) { - result = 1; - } - // The main greenlet pointer will eventually go away after the thread dies. - if (self->was_running_in_dead_thread()) { - // Our thread is dead! We can never run again. Might as well - // GC us. Note that if a tuple containing only us and other - // immutable objects had been scanned before this, when we - // would have returned 0, the tuple will take itself out of GC - // tracking and never be investigated again. So that could - // result in both us and the tuple leaking due to an - // unreachable/uncollectible reference. The same goes for - // dictionaries. - // - // It's not a great idea to be changing our GC state on the - // fly. - result = 1; - } - return result; -} - - -static int -green_clear(PyGreenlet* self) -{ - /* Greenlet is only cleared if it is about to be collected. - Since active greenlets are not garbage collectable, we can - be sure that, even if they are deallocated during clear, - nothing they reference is in unreachable or finalizers, - so even if it switches we are relatively safe. */ - // XXX: Are we responsible for clearing weakrefs here? - Py_CLEAR(self->dict); - return self->pimpl->tp_clear(); -} - -/** - * Returns 0 on failure (the object was resurrected) or 1 on success. - **/ -static int -_green_dealloc_kill_started_non_main_greenlet(BorrowedGreenlet self) -{ - /* Hacks hacks hacks copied from instance_dealloc() */ - /* Temporarily resurrect the greenlet. */ - assert(self.REFCNT() == 0); - Py_SET_REFCNT(self.borrow(), 1); - /* Save the current exception, if any. */ - PyErrPieces saved_err; - try { - // BY THE TIME WE GET HERE, the state may actually be going - // away - // if we're shutting down the interpreter and freeing thread - // entries, - // this could result in freeing greenlets that were leaked. So - // we can't try to read the state. - self->deallocing_greenlet_in_thread( - self->thread_state() - ? static_cast(GET_THREAD_STATE()) - : nullptr); - } - catch (const PyErrOccurred&) { - PyErr_WriteUnraisable(self.borrow_o()); - /* XXX what else should we do? */ - } - /* Check for no resurrection must be done while we keep - * our internal reference, otherwise PyFile_WriteObject - * causes recursion if using Py_INCREF/Py_DECREF - */ - if (self.REFCNT() == 1 && self->active()) { - /* Not resurrected, but still not dead! - XXX what else should we do? we complain. */ - PyObject* f = PySys_GetObject("stderr"); - Py_INCREF(self.borrow_o()); /* leak! */ - if (f != NULL) { - PyFile_WriteString("GreenletExit did not kill ", f); - PyFile_WriteObject(self.borrow_o(), f, 0); - PyFile_WriteString("\n", f); - } - } - /* Restore the saved exception. */ - saved_err.PyErrRestore(); - /* Undo the temporary resurrection; can't use DECREF here, - * it would cause a recursive call. - */ - assert(self.REFCNT() > 0); - - Py_ssize_t refcnt = self.REFCNT() - 1; - Py_SET_REFCNT(self.borrow_o(), refcnt); - if (refcnt != 0) { - /* Resurrected! */ - _Py_NewReference(self.borrow_o()); - Py_SET_REFCNT(self.borrow_o(), refcnt); - /* Better to use tp_finalizer slot (PEP 442) - * and call ``PyObject_CallFinalizerFromDealloc``, - * but that's only supported in Python 3.4+; see - * Modules/_io/iobase.c for an example. - * - * The following approach is copied from iobase.c in CPython 2.7. - * (along with much of this function in general). Here's their - * comment: - * - * When called from a heap type's dealloc, the type will be - * decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ - if (PyType_HasFeature(self.TYPE(), Py_TPFLAGS_HEAPTYPE)) { - Py_INCREF(self.TYPE()); - } - - PyObject_GC_Track((PyObject*)self); - - _Py_DEC_REFTOTAL; -#ifdef COUNT_ALLOCS - --Py_TYPE(self)->tp_frees; - --Py_TYPE(self)->tp_allocs; -#endif /* COUNT_ALLOCS */ - return 0; - } - return 1; -} - - -static void -green_dealloc(PyGreenlet* self) -{ - PyObject_GC_UnTrack(self); - BorrowedGreenlet me(self); - if (me->active() - && me->started() - && !me->main()) { - if (!_green_dealloc_kill_started_non_main_greenlet(me)) { - return; - } - } - - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*)self); - } - Py_CLEAR(self->dict); - - if (self->pimpl) { - // In case deleting this, which frees some memory, - // somehow winds up calling back into us. That's usually a - //bug in our code. - Greenlet* p = self->pimpl; - self->pimpl = nullptr; - delete p; - } - // and finally we're done. self is now invalid. - Py_TYPE(self)->tp_free((PyObject*)self); -} - - - -static OwnedObject -internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces) -{ - PyObject* result = nullptr; - err_pieces.PyErrRestore(); - assert(PyErr_Occurred()); - if (self->started() && !self->active()) { - /* dead greenlet: turn GreenletExit into a regular return */ - result = g_handle_exit(OwnedObject()).relinquish_ownership(); - } - self->args() <<= result; - - return single_result(self->g_switch()); -} - - - -PyDoc_STRVAR( - green_switch_doc, - "switch(*args, **kwargs)\n" - "\n" - "Switch execution to this greenlet.\n" - "\n" - "If this greenlet has never been run, then this greenlet\n" - "will be switched to using the body of ``self.run(*args, **kwargs)``.\n" - "\n" - "If the greenlet is active (has been run, but was switch()'ed\n" - "out before leaving its run function), then this greenlet will\n" - "be resumed and the return value to its switch call will be\n" - "None if no arguments are given, the given argument if one\n" - "argument is given, or the args tuple and keyword args dict if\n" - "multiple arguments are given.\n" - "\n" - "If the greenlet is dead, or is the current greenlet then this\n" - "function will simply return the arguments using the same rules as\n" - "above.\n"); - -static PyObject* -green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs) -{ - using greenlet::SwitchingArgs; - SwitchingArgs switch_args(OwnedObject::owning(args), OwnedObject::owning(kwargs)); - self->pimpl->may_switch_away(); - self->pimpl->args() <<= switch_args; - - // If we're switching out of a greenlet, and that switch is the - // last thing the greenlet does, the greenlet ought to be able to - // go ahead and die at that point. Currently, someone else must - // manually switch back to the greenlet so that we "fall off the - // end" and can perform cleanup. You'd think we'd be able to - // figure out that this is happening using the frame's ``f_lasti`` - // member, which is supposed to be an index into - // ``frame->f_code->co_code``, the bytecode string. However, in - // recent interpreters, ``f_lasti`` tends not to be updated thanks - // to things like the PREDICT() macros in ceval.c. So it doesn't - // really work to do that in many cases. For example, the Python - // code: - // def run(): - // greenlet.getcurrent().parent.switch() - // produces bytecode of len 16, with the actual call to switch() - // being at index 10 (in Python 3.10). However, the reported - // ``f_lasti`` we actually see is...5! (Which happens to be the - // second byte of the CALL_METHOD op for ``getcurrent()``). - - try { - //OwnedObject result = single_result(self->pimpl->g_switch()); - OwnedObject result(single_result(self->pimpl->g_switch())); -#ifndef NDEBUG - // Note that the current greenlet isn't necessarily self. If self - // finished, we went to one of its parents. - assert(!self->pimpl->args()); - - const BorrowedGreenlet& current = GET_THREAD_STATE().state().borrow_current(); - // It's possible it's never been switched to. - assert(!current->args()); -#endif - PyObject* p = result.relinquish_ownership(); - - if (!p && !PyErr_Occurred()) { - // This shouldn't be happening anymore, so the asserts - // are there for debug builds. Non-debug builds - // crash "gracefully" in this case, although there is an - // argument to be made for killing the process in all - // cases --- for this to be the case, our switches - // probably nested in an incorrect way, so the state is - // suspicious. Nothing should be corrupt though, just - // confused at the Python level. Letting this propagate is - // probably good enough. - assert(p || PyErr_Occurred()); - throw PyErrOccurred( - mod_globs->PyExc_GreenletError, - "Greenlet.switch() returned NULL without an exception set." - ); - } - return p; - } - catch(const PyErrOccurred&) { - return nullptr; - } -} - -PyDoc_STRVAR( - green_throw_doc, - "Switches execution to this greenlet, but immediately raises the\n" - "given exception in this greenlet. If no argument is provided, the " - "exception\n" - "defaults to `greenlet.GreenletExit`. The normal exception\n" - "propagation rules apply, as described for `switch`. Note that calling " - "this\n" - "method is almost equivalent to the following::\n" - "\n" - " def raiser():\n" - " raise typ, val, tb\n" - " g_raiser = greenlet(raiser, parent=g)\n" - " g_raiser.switch()\n" - "\n" - "except that this trick does not work for the\n" - "`greenlet.GreenletExit` exception, which would not propagate\n" - "from ``g_raiser`` to ``g``.\n"); - -static PyObject* -green_throw(PyGreenlet* self, PyObject* args) -{ - PyArgParseParam typ(mod_globs->PyExc_GreenletExit); - PyArgParseParam val; - PyArgParseParam tb; - - if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) { - return nullptr; - } - - assert(typ.borrow() || val.borrow()); - - self->pimpl->may_switch_away(); - try { - // Both normalizing the error and the actual throw_greenlet - // could throw PyErrOccurred. - PyErrPieces err_pieces(typ.borrow(), val.borrow(), tb.borrow()); - - return internal_green_throw(self, err_pieces).relinquish_ownership(); - } - catch (const PyErrOccurred&) { - return nullptr; - } -} - -static int -green_bool(PyGreenlet* self) -{ - return self->pimpl->active(); -} - -/** - * CAUTION: Allocates memory, may run GC and arbitrary Python code. - */ -static PyObject* -green_getdict(PyGreenlet* self, void* UNUSED(context)) -{ - if (self->dict == NULL) { - self->dict = PyDict_New(); - if (self->dict == NULL) { - return NULL; - } - } - Py_INCREF(self->dict); - return self->dict; -} - -static int -green_setdict(PyGreenlet* self, PyObject* val, void* UNUSED(context)) -{ - PyObject* tmp; - - if (val == NULL) { - PyErr_SetString(PyExc_TypeError, "__dict__ may not be deleted"); - return -1; - } - if (!PyDict_Check(val)) { - PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary"); - return -1; - } - tmp = self->dict; - Py_INCREF(val); - self->dict = val; - Py_XDECREF(tmp); - return 0; -} - -static bool -_green_not_dead(BorrowedGreenlet self) -{ - // XXX: Where else should we do this? - // Probably on entry to most Python-facing functions? - if (self->was_running_in_dead_thread()) { - self->deactivate_and_free(); - return false; - } - return self->active() || !self->started(); -} - - -static PyObject* -green_getdead(PyGreenlet* self, void* UNUSED(context)) -{ - if (_green_not_dead(self)) { - Py_RETURN_FALSE; - } - else { - Py_RETURN_TRUE; - } -} - -static PyObject* -green_get_stack_saved(PyGreenlet* self, void* UNUSED(context)) -{ - return PyLong_FromSsize_t(self->pimpl->stack_saved()); -} - - -static PyObject* -green_getrun(PyGreenlet* self, void* UNUSED(context)) -{ - try { - OwnedObject result(BorrowedGreenlet(self)->run()); - return result.relinquish_ownership(); - } - catch(const PyErrOccurred&) { - return nullptr; - } -} - - -static int -green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context)) -{ - try { - BorrowedGreenlet(self)->run(nrun); - return 0; - } - catch(const PyErrOccurred&) { - return -1; - } -} - -static PyObject* -green_getparent(PyGreenlet* self, void* UNUSED(context)) -{ - return BorrowedGreenlet(self)->parent().acquire_or_None(); -} - - -static int -green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context)) -{ - try { - BorrowedGreenlet(self)->parent(nparent); - } - catch(const PyErrOccurred&) { - return -1; - } - return 0; -} - - -static PyObject* -green_getcontext(const PyGreenlet* self, void* UNUSED(context)) -{ - const Greenlet *const g = self->pimpl; - try { - OwnedObject result(g->context()); - return result.relinquish_ownership(); - } - catch(const PyErrOccurred&) { - return nullptr; - } -} - -static int -green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context)) -{ - try { - BorrowedGreenlet(self)->context(nctx); - return 0; - } - catch(const PyErrOccurred&) { - return -1; - } -} - - -static PyObject* -green_getframe(PyGreenlet* self, void* UNUSED(context)) -{ - const PythonState::OwnedFrame& top_frame = BorrowedGreenlet(self)->top_frame(); - return top_frame.acquire_or_None(); -} - - -static PyObject* -green_getstate(PyGreenlet* self) -{ - PyErr_Format(PyExc_TypeError, - "cannot serialize '%s' object", - Py_TYPE(self)->tp_name); - return nullptr; -} - -static PyObject* -green_repr(PyGreenlet* _self) -{ - BorrowedGreenlet self(_self); - /* - Return a string like - - - The handling of greenlets across threads is not super good. - We mostly use the internal definitions of these terms, but they - generally should make sense to users as well. - */ - PyObject* result; - int never_started = !self->started() && !self->active(); - - const char* const tp_name = Py_TYPE(self)->tp_name; - - if (_green_not_dead(self)) { - /* XXX: The otid= is almost useless because you can't correlate it to - any thread identifier exposed to Python. We could use - PyThreadState_GET()->thread_id, but we'd need to save that in the - greenlet, or save the whole PyThreadState object itself. - - As it stands, its only useful for identifying greenlets from the same thread. - */ - const char* state_in_thread; - if (self->was_running_in_dead_thread()) { - // The thread it was running in is dead! - // This can happen, especially at interpreter shut down. - // It complicates debugging output because it may be - // impossible to access the current thread state at that - // time. Thus, don't access the current thread state. - state_in_thread = " (thread exited)"; - } - else { - state_in_thread = GET_THREAD_STATE().state().is_current(self) - ? " current" - : (self->started() ? " suspended" : ""); - } - result = PyUnicode_FromFormat( - "<%s object at %p (otid=%p)%s%s%s%s>", - tp_name, - self.borrow_o(), - self->thread_state(), - state_in_thread, - self->active() ? " active" : "", - never_started ? " pending" : " started", - self->main() ? " main" : "" - ); - } - else { - result = PyUnicode_FromFormat( - "<%s object at %p (otid=%p) %sdead>", - tp_name, - self.borrow_o(), - self->thread_state(), - self->was_running_in_dead_thread() - ? "(thread exited) " - : "" - ); - } - - return result; -} - - -static PyMethodDef green_methods[] = { - { - .ml_name="switch", - .ml_meth=reinterpret_cast(green_switch), - .ml_flags=METH_VARARGS | METH_KEYWORDS, - .ml_doc=green_switch_doc - }, - {.ml_name="throw", .ml_meth=(PyCFunction)green_throw, .ml_flags=METH_VARARGS, .ml_doc=green_throw_doc}, - {.ml_name="__getstate__", .ml_meth=(PyCFunction)green_getstate, .ml_flags=METH_NOARGS, .ml_doc=NULL}, - {.ml_name=NULL, .ml_meth=NULL} /* sentinel */ -}; - -static PyGetSetDef green_getsets[] = { - /* name, getter, setter, doc, context pointer */ - {.name="__dict__", .get=(getter)green_getdict, .set=(setter)green_setdict}, - {.name="run", .get=(getter)green_getrun, .set=(setter)green_setrun}, - {.name="parent", .get=(getter)green_getparent, .set=(setter)green_setparent}, - {.name="gr_frame", .get=(getter)green_getframe }, - { - .name="gr_context", - .get=(getter)green_getcontext, - .set=(setter)green_setcontext - }, - {.name="dead", .get=(getter)green_getdead}, - {.name="_stack_saved", .get=(getter)green_get_stack_saved}, - {.name=NULL} -}; - -static PyMemberDef green_members[] = { - {.name=NULL} -}; - -static PyNumberMethods green_as_number = { - .nb_bool=(inquiry)green_bool, -}; - - -PyTypeObject PyGreenlet_Type = { - .ob_base=PyVarObject_HEAD_INIT(NULL, 0) - .tp_name="greenlet.greenlet", /* tp_name */ - .tp_basicsize=sizeof(PyGreenlet), /* tp_basicsize */ - /* methods */ - .tp_dealloc=(destructor)green_dealloc, /* tp_dealloc */ - .tp_repr=(reprfunc)green_repr, /* tp_repr */ - .tp_as_number=&green_as_number, /* tp_as _number*/ - .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - .tp_doc="greenlet(run=None, parent=None) -> greenlet\n\n" - "Creates a new greenlet object (without running it).\n\n" - " - *run* -- The callable to invoke.\n" - " - *parent* -- The parent greenlet. The default is the current " - "greenlet.", /* tp_doc */ - .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */ - .tp_clear=(inquiry)green_clear, /* tp_clear */ - .tp_weaklistoffset=offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */ - - .tp_methods=green_methods, /* tp_methods */ - .tp_members=green_members, /* tp_members */ - .tp_getset=green_getsets, /* tp_getset */ - .tp_dictoffset=offsetof(PyGreenlet, dict), /* tp_dictoffset */ - .tp_init=(initproc)green_init, /* tp_init */ - .tp_alloc=PyType_GenericAlloc, /* tp_alloc */ - .tp_new=(newfunc)green_new, /* tp_new */ - .tp_free=PyObject_GC_Del, /* tp_free */ - .tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */ -}; - -#endif - -// Local Variables: -// flycheck-clang-include-path: ("/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8") -// End: diff --git a/.venv/Lib/site-packages/greenlet/PyGreenlet.hpp b/.venv/Lib/site-packages/greenlet/PyGreenlet.hpp deleted file mode 100644 index df6cd80..0000000 --- a/.venv/Lib/site-packages/greenlet/PyGreenlet.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef PYGREENLET_HPP -#define PYGREENLET_HPP - - -#include "greenlet.h" -#include "greenlet_compiler_compat.hpp" -#include "greenlet_refs.hpp" - - -using greenlet::refs::OwnedGreenlet; -using greenlet::refs::BorrowedGreenlet; -using greenlet::refs::BorrowedObject;; -using greenlet::refs::OwnedObject; -using greenlet::refs::PyErrPieces; - - -// XXX: These doesn't really belong here, it's not a Python slot. -static OwnedObject internal_green_throw(BorrowedGreenlet self, PyErrPieces& err_pieces); - -static PyGreenlet* green_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)); -static int green_clear(PyGreenlet* self); -static int green_init(PyGreenlet* self, PyObject* args, PyObject* kwargs); -static int green_setparent(PyGreenlet* self, PyObject* nparent, void* UNUSED(context)); -static int green_setrun(PyGreenlet* self, PyObject* nrun, void* UNUSED(context)); -static int green_traverse(PyGreenlet* self, visitproc visit, void* arg); -static void green_dealloc(PyGreenlet* self); -static PyObject* green_getparent(PyGreenlet* self, void* UNUSED(context)); - -static int green_is_gc(PyObject* self); -static PyObject* green_getdead(PyGreenlet* self, void* UNUSED(context)); -static PyObject* green_getrun(PyGreenlet* self, void* UNUSED(context)); -static int green_setcontext(PyGreenlet* self, PyObject* nctx, void* UNUSED(context)); -static PyObject* green_getframe(PyGreenlet* self, void* UNUSED(context)); -static PyObject* green_repr(PyGreenlet* self); -#endif diff --git a/.venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp b/.venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp deleted file mode 100644 index 1b768ee..0000000 --- a/.venv/Lib/site-packages/greenlet/PyGreenletUnswitchable.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - Implementation of the Python slots for PyGreenletUnswitchable_Type -*/ -#ifndef PY_GREENLET_UNSWITCHABLE_CPP -#define PY_GREENLET_UNSWITCHABLE_CPP - - - -#define PY_SSIZE_T_CLEAN -#include -#include "structmember.h" // PyMemberDef - -#include "greenlet_internal.hpp" -// Code after this point can assume access to things declared in stdint.h, -// including the fixed-width types. This goes for the platform-specific switch functions -// as well. -#include "greenlet_refs.hpp" -#include "greenlet_slp_switch.hpp" - -#include "greenlet_thread_support.hpp" -#include "TGreenlet.hpp" - -#include "TGreenlet.cpp" -#include "TGreenletGlobals.cpp" -#include "TThreadStateDestroy.cpp" - - -using greenlet::LockGuard; -using greenlet::LockInitError; -using greenlet::PyErrOccurred; -using greenlet::Require; - -using greenlet::g_handle_exit; -using greenlet::single_result; - -using greenlet::Greenlet; -using greenlet::UserGreenlet; -using greenlet::MainGreenlet; -using greenlet::BrokenGreenlet; -using greenlet::ThreadState; -using greenlet::PythonState; - - -#include "PyGreenlet.hpp" - -static PyGreenlet* -green_unswitchable_new(PyTypeObject* type, PyObject* UNUSED(args), PyObject* UNUSED(kwds)) -{ - PyGreenlet* o = - (PyGreenlet*)PyBaseObject_Type.tp_new(type, mod_globs->empty_tuple, mod_globs->empty_dict); - if (o) { - new BrokenGreenlet(o, GET_THREAD_STATE().state().borrow_current()); - assert(Py_REFCNT(o) == 1); - } - return o; -} - -static PyObject* -green_unswitchable_getforce(PyGreenlet* self, void* UNUSED(context)) -{ - BrokenGreenlet* broken = dynamic_cast(self->pimpl); - return PyBool_FromLong(broken->_force_switch_error); -} - -static int -green_unswitchable_setforce(PyGreenlet* self, PyObject* nforce, void* UNUSED(context)) -{ - if (!nforce) { - PyErr_SetString( - PyExc_AttributeError, - "Cannot delete force_switch_error" - ); - return -1; - } - BrokenGreenlet* broken = dynamic_cast(self->pimpl); - int is_true = PyObject_IsTrue(nforce); - if (is_true == -1) { - return -1; - } - broken->_force_switch_error = is_true; - return 0; -} - -static PyObject* -green_unswitchable_getforceslp(PyGreenlet* self, void* UNUSED(context)) -{ - BrokenGreenlet* broken = dynamic_cast(self->pimpl); - return PyBool_FromLong(broken->_force_slp_switch_error); -} - -static int -green_unswitchable_setforceslp(PyGreenlet* self, PyObject* nforce, void* UNUSED(context)) -{ - if (!nforce) { - PyErr_SetString( - PyExc_AttributeError, - "Cannot delete force_slp_switch_error" - ); - return -1; - } - BrokenGreenlet* broken = dynamic_cast(self->pimpl); - int is_true = PyObject_IsTrue(nforce); - if (is_true == -1) { - return -1; - } - broken->_force_slp_switch_error = is_true; - return 0; -} - -static PyGetSetDef green_unswitchable_getsets[] = { - /* name, getter, setter, doc, closure (context pointer) */ - { - .name="force_switch_error", - .get=(getter)green_unswitchable_getforce, - .set=(setter)green_unswitchable_setforce, - .doc=NULL - }, - { - .name="force_slp_switch_error", - .get=(getter)green_unswitchable_getforceslp, - .set=(setter)green_unswitchable_setforceslp, - .doc=nullptr - }, - {.name=nullptr} -}; - -PyTypeObject PyGreenletUnswitchable_Type = { - .ob_base=PyVarObject_HEAD_INIT(NULL, 0) - .tp_name="greenlet._greenlet.UnswitchableGreenlet", - .tp_dealloc= (destructor)green_dealloc, /* tp_dealloc */ - .tp_flags=G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - .tp_doc="Undocumented internal class", /* tp_doc */ - .tp_traverse=(traverseproc)green_traverse, /* tp_traverse */ - .tp_clear=(inquiry)green_clear, /* tp_clear */ - - .tp_getset=green_unswitchable_getsets, /* tp_getset */ - .tp_base=&PyGreenlet_Type, /* tp_base */ - .tp_init=(initproc)green_init, /* tp_init */ - .tp_alloc=PyType_GenericAlloc, /* tp_alloc */ - .tp_new=(newfunc)green_unswitchable_new, /* tp_new */ - .tp_free=PyObject_GC_Del, /* tp_free */ - .tp_is_gc=(inquiry)green_is_gc, /* tp_is_gc */ -}; - - -#endif diff --git a/.venv/Lib/site-packages/greenlet/PyModule.cpp b/.venv/Lib/site-packages/greenlet/PyModule.cpp deleted file mode 100644 index 6adcb5c..0000000 --- a/.venv/Lib/site-packages/greenlet/PyModule.cpp +++ /dev/null @@ -1,292 +0,0 @@ -#ifndef PY_MODULE_CPP -#define PY_MODULE_CPP - -#include "greenlet_internal.hpp" - - -#include "TGreenletGlobals.cpp" -#include "TMainGreenlet.cpp" -#include "TThreadStateDestroy.cpp" - -using greenlet::LockGuard; -using greenlet::ThreadState; - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-function" -# pragma clang diagnostic ignored "-Wunused-variable" -#endif - -PyDoc_STRVAR(mod_getcurrent_doc, - "getcurrent() -> greenlet\n" - "\n" - "Returns the current greenlet (i.e. the one which called this " - "function).\n"); - -static PyObject* -mod_getcurrent(PyObject* UNUSED(module)) -{ - return GET_THREAD_STATE().state().get_current().relinquish_ownership_o(); -} - -PyDoc_STRVAR(mod_settrace_doc, - "settrace(callback) -> object\n" - "\n" - "Sets a new tracing function and returns the previous one.\n"); -static PyObject* -mod_settrace(PyObject* UNUSED(module), PyObject* args) -{ - PyArgParseParam tracefunc; - if (!PyArg_ParseTuple(args, "O", &tracefunc)) { - return NULL; - } - ThreadState& state = GET_THREAD_STATE(); - OwnedObject previous = state.get_tracefunc(); - if (!previous) { - previous = Py_None; - } - - state.set_tracefunc(tracefunc); - - return previous.relinquish_ownership(); -} - -PyDoc_STRVAR(mod_gettrace_doc, - "gettrace() -> object\n" - "\n" - "Returns the currently set tracing function, or None.\n"); - -static PyObject* -mod_gettrace(PyObject* UNUSED(module)) -{ - OwnedObject tracefunc = GET_THREAD_STATE().state().get_tracefunc(); - if (!tracefunc) { - tracefunc = Py_None; - } - return tracefunc.relinquish_ownership(); -} - - - -PyDoc_STRVAR(mod_set_thread_local_doc, - "set_thread_local(key, value) -> None\n" - "\n" - "Set a value in the current thread-local dictionary. Debugging only.\n"); - -static PyObject* -mod_set_thread_local(PyObject* UNUSED(module), PyObject* args) -{ - PyArgParseParam key; - PyArgParseParam value; - PyObject* result = NULL; - - if (PyArg_UnpackTuple(args, "set_thread_local", 2, 2, &key, &value)) { - if(PyDict_SetItem( - PyThreadState_GetDict(), // borrow - key, - value) == 0 ) { - // success - Py_INCREF(Py_None); - result = Py_None; - } - } - return result; -} - -PyDoc_STRVAR(mod_get_pending_cleanup_count_doc, - "get_pending_cleanup_count() -> Integer\n" - "\n" - "Get the number of greenlet cleanup operations pending. Testing only.\n"); - - -static PyObject* -mod_get_pending_cleanup_count(PyObject* UNUSED(module)) -{ - LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); - return PyLong_FromSize_t(mod_globs->thread_states_to_destroy.size()); -} - -PyDoc_STRVAR(mod_get_total_main_greenlets_doc, - "get_total_main_greenlets() -> Integer\n" - "\n" - "Quickly return the number of main greenlets that exist. Testing only.\n"); - -static PyObject* -mod_get_total_main_greenlets(PyObject* UNUSED(module)) -{ - return PyLong_FromSize_t(G_TOTAL_MAIN_GREENLETS); -} - - - -PyDoc_STRVAR(mod_get_clocks_used_doing_optional_cleanup_doc, - "get_clocks_used_doing_optional_cleanup() -> Integer\n" - "\n" - "Get the number of clock ticks the program has used doing optional " - "greenlet cleanup.\n" - "Beginning in greenlet 2.0, greenlet tries to find and dispose of greenlets\n" - "that leaked after a thread exited. This requires invoking Python's garbage collector,\n" - "which may have a performance cost proportional to the number of live objects.\n" - "This function returns the amount of processor time\n" - "greenlet has used to do this. In programs that run with very large amounts of live\n" - "objects, this metric can be used to decide whether the cost of doing this cleanup\n" - "is worth the memory leak being corrected. If not, you can disable the cleanup\n" - "using ``enable_optional_cleanup(False)``.\n" - "The units are arbitrary and can only be compared to themselves (similarly to ``time.clock()``);\n" - "for example, to see how it scales with your heap. You can attempt to convert them into seconds\n" - "by dividing by the value of CLOCKS_PER_SEC." - "If cleanup has been disabled, returns None." - "\n" - "This is an implementation specific, provisional API. It may be changed or removed\n" - "in the future.\n" - ".. versionadded:: 2.0" - ); -static PyObject* -mod_get_clocks_used_doing_optional_cleanup(PyObject* UNUSED(module)) -{ - std::clock_t& clocks = ThreadState::clocks_used_doing_gc(); - - if (clocks == std::clock_t(-1)) { - Py_RETURN_NONE; - } - // This might not actually work on some implementations; clock_t - // is an opaque type. - return PyLong_FromSsize_t(clocks); -} - -PyDoc_STRVAR(mod_enable_optional_cleanup_doc, - "mod_enable_optional_cleanup(bool) -> None\n" - "\n" - "Enable or disable optional cleanup operations.\n" - "See ``get_clocks_used_doing_optional_cleanup()`` for details.\n" - ); -static PyObject* -mod_enable_optional_cleanup(PyObject* UNUSED(module), PyObject* flag) -{ - int is_true = PyObject_IsTrue(flag); - if (is_true == -1) { - return nullptr; - } - - std::clock_t& clocks = ThreadState::clocks_used_doing_gc(); - if (is_true) { - // If we already have a value, we don't want to lose it. - if (clocks == std::clock_t(-1)) { - clocks = 0; - } - } - else { - clocks = std::clock_t(-1); - } - Py_RETURN_NONE; -} - - - - -#if !GREENLET_PY313 -PyDoc_STRVAR(mod_get_tstate_trash_delete_nesting_doc, - "get_tstate_trash_delete_nesting() -> Integer\n" - "\n" - "Return the 'trash can' nesting level. Testing only.\n"); -static PyObject* -mod_get_tstate_trash_delete_nesting(PyObject* UNUSED(module)) -{ - PyThreadState* tstate = PyThreadState_GET(); - -#if GREENLET_PY312 - return PyLong_FromLong(tstate->trash.delete_nesting); -#else - return PyLong_FromLong(tstate->trash_delete_nesting); -#endif -} -#endif - - - - -static PyMethodDef GreenMethods[] = { - { - .ml_name="getcurrent", - .ml_meth=(PyCFunction)mod_getcurrent, - .ml_flags=METH_NOARGS, - .ml_doc=mod_getcurrent_doc - }, - { - .ml_name="settrace", - .ml_meth=(PyCFunction)mod_settrace, - .ml_flags=METH_VARARGS, - .ml_doc=mod_settrace_doc - }, - { - .ml_name="gettrace", - .ml_meth=(PyCFunction)mod_gettrace, - .ml_flags=METH_NOARGS, - .ml_doc=mod_gettrace_doc - }, - { - .ml_name="set_thread_local", - .ml_meth=(PyCFunction)mod_set_thread_local, - .ml_flags=METH_VARARGS, - .ml_doc=mod_set_thread_local_doc - }, - { - .ml_name="get_pending_cleanup_count", - .ml_meth=(PyCFunction)mod_get_pending_cleanup_count, - .ml_flags=METH_NOARGS, - .ml_doc=mod_get_pending_cleanup_count_doc - }, - { - .ml_name="get_total_main_greenlets", - .ml_meth=(PyCFunction)mod_get_total_main_greenlets, - .ml_flags=METH_NOARGS, - .ml_doc=mod_get_total_main_greenlets_doc - }, - { - .ml_name="get_clocks_used_doing_optional_cleanup", - .ml_meth=(PyCFunction)mod_get_clocks_used_doing_optional_cleanup, - .ml_flags=METH_NOARGS, - .ml_doc=mod_get_clocks_used_doing_optional_cleanup_doc - }, - { - .ml_name="enable_optional_cleanup", - .ml_meth=(PyCFunction)mod_enable_optional_cleanup, - .ml_flags=METH_O, - .ml_doc=mod_enable_optional_cleanup_doc - }, -#if !GREENLET_PY313 - { - .ml_name="get_tstate_trash_delete_nesting", - .ml_meth=(PyCFunction)mod_get_tstate_trash_delete_nesting, - .ml_flags=METH_NOARGS, - .ml_doc=mod_get_tstate_trash_delete_nesting_doc - }, -#endif - {.ml_name=NULL, .ml_meth=NULL} /* Sentinel */ -}; - -static const char* const copy_on_greentype[] = { - "getcurrent", - "error", - "GreenletExit", - "settrace", - "gettrace", - NULL -}; - -static struct PyModuleDef greenlet_module_def = { - .m_base=PyModuleDef_HEAD_INIT, - .m_name="greenlet._greenlet", - .m_doc=NULL, - .m_size=-1, - .m_methods=GreenMethods, -}; - - -#endif - -#ifdef __clang__ -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#endif diff --git a/.venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp b/.venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp deleted file mode 100644 index 7e9ab5b..0000000 --- a/.venv/Lib/site-packages/greenlet/TBrokenGreenlet.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::UserGreenlet. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ - -#include "TGreenlet.hpp" - -namespace greenlet { - -void* BrokenGreenlet::operator new(size_t UNUSED(count)) -{ - return allocator.allocate(1); -} - - -void BrokenGreenlet::operator delete(void* ptr) -{ - return allocator.deallocate(static_cast(ptr), - 1); -} - -greenlet::PythonAllocator greenlet::BrokenGreenlet::allocator; - -bool -BrokenGreenlet::force_slp_switch_error() const noexcept -{ - return this->_force_slp_switch_error; -} - -UserGreenlet::switchstack_result_t BrokenGreenlet::g_switchstack(void) -{ - if (this->_force_switch_error) { - return switchstack_result_t(-1); - } - return UserGreenlet::g_switchstack(); -} - -}; //namespace greenlet diff --git a/.venv/Lib/site-packages/greenlet/TExceptionState.cpp b/.venv/Lib/site-packages/greenlet/TExceptionState.cpp deleted file mode 100644 index 08a94ae..0000000 --- a/.venv/Lib/site-packages/greenlet/TExceptionState.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef GREENLET_EXCEPTION_STATE_CPP -#define GREENLET_EXCEPTION_STATE_CPP - -#include -#include "TGreenlet.hpp" - -namespace greenlet { - - -ExceptionState::ExceptionState() -{ - this->clear(); -} - -void ExceptionState::operator<<(const PyThreadState *const tstate) noexcept -{ - this->exc_info = tstate->exc_info; - this->exc_state = tstate->exc_state; -} - -void ExceptionState::operator>>(PyThreadState *const tstate) noexcept -{ - tstate->exc_state = this->exc_state; - tstate->exc_info = - this->exc_info ? this->exc_info : &tstate->exc_state; - this->clear(); -} - -void ExceptionState::clear() noexcept -{ - this->exc_info = nullptr; - this->exc_state.exc_value = nullptr; -#if !GREENLET_PY311 - this->exc_state.exc_type = nullptr; - this->exc_state.exc_traceback = nullptr; -#endif - this->exc_state.previous_item = nullptr; -} - -int ExceptionState::tp_traverse(visitproc visit, void* arg) noexcept -{ - Py_VISIT(this->exc_state.exc_value); -#if !GREENLET_PY311 - Py_VISIT(this->exc_state.exc_type); - Py_VISIT(this->exc_state.exc_traceback); -#endif - return 0; -} - -void ExceptionState::tp_clear() noexcept -{ - Py_CLEAR(this->exc_state.exc_value); -#if !GREENLET_PY311 - Py_CLEAR(this->exc_state.exc_type); - Py_CLEAR(this->exc_state.exc_traceback); -#endif -} - - -}; // namespace greenlet - -#endif // GREENLET_EXCEPTION_STATE_CPP diff --git a/.venv/Lib/site-packages/greenlet/TGreenlet.cpp b/.venv/Lib/site-packages/greenlet/TGreenlet.cpp deleted file mode 100644 index 4698a17..0000000 --- a/.venv/Lib/site-packages/greenlet/TGreenlet.cpp +++ /dev/null @@ -1,718 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::Greenlet. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#ifndef TGREENLET_CPP -#define TGREENLET_CPP -#include "greenlet_internal.hpp" -#include "TGreenlet.hpp" - - -#include "TGreenletGlobals.cpp" -#include "TThreadStateDestroy.cpp" - -namespace greenlet { - -Greenlet::Greenlet(PyGreenlet* p) - : Greenlet(p, StackState()) -{ -} - -Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack) - : _self(p), stack_state(initial_stack) -{ - assert(p->pimpl == nullptr); - p->pimpl = this; -} - -Greenlet::~Greenlet() -{ - // XXX: Can't do this. tp_clear is a virtual function, and by the - // time we're here, we've sliced off our child classes. - //this->tp_clear(); - this->_self->pimpl = nullptr; -} - -bool -Greenlet::force_slp_switch_error() const noexcept -{ - return false; -} - -void -Greenlet::release_args() -{ - this->switch_args.CLEAR(); -} - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state)) -{ - // If we're killed because we lost all references in the - // middle of a switch, that's ok. Don't reset the args/kwargs, - // we still want to pass them to the parent. - PyErr_SetString(mod_globs->PyExc_GreenletExit, - "Killing the greenlet because all references have vanished."); - // To get here it had to have run before - return this->g_switch(); -} - -inline void -Greenlet::slp_restore_state() noexcept -{ -#ifdef SLP_BEFORE_RESTORE_STATE - SLP_BEFORE_RESTORE_STATE(); -#endif - this->stack_state.copy_heap_to_stack( - this->thread_state()->borrow_current()->stack_state); -} - - -inline int -Greenlet::slp_save_state(char *const stackref) noexcept -{ - // XXX: This used to happen in the middle, before saving, but - // after finding the next owner. Does that matter? This is - // only defined for Sparc/GCC where it flushes register - // windows to the stack (I think) -#ifdef SLP_BEFORE_SAVE_STATE - SLP_BEFORE_SAVE_STATE(); -#endif - return this->stack_state.copy_stack_to_heap(stackref, - this->thread_state()->borrow_current()->stack_state); -} - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -Greenlet::on_switchstack_or_initialstub_failure( - Greenlet* target, - const Greenlet::switchstack_result_t& err, - const bool target_was_me, - const bool was_initial_stub) -{ - // If we get here, either g_initialstub() - // failed, or g_switchstack() failed. Either one of those - // cases SHOULD leave us in the original greenlet with a valid stack. - if (!PyErr_Occurred()) { - PyErr_SetString( - PyExc_SystemError, - was_initial_stub - ? "Failed to switch stacks into a greenlet for the first time." - : "Failed to switch stacks into a running greenlet."); - } - this->release_args(); - - if (target && !target_was_me) { - target->murder_in_place(); - } - - assert(!err.the_new_current_greenlet); - assert(!err.origin_greenlet); - return OwnedObject(); - -} - -OwnedGreenlet -Greenlet::g_switchstack_success() noexcept -{ - PyThreadState* tstate = PyThreadState_GET(); - // restore the saved state - this->python_state >> tstate; - this->exception_state >> tstate; - - // The thread state hasn't been changed yet. - ThreadState* thread_state = this->thread_state(); - OwnedGreenlet result(thread_state->get_current()); - thread_state->set_current(this->self()); - //assert(thread_state->borrow_current().borrow() == this->_self); - return result; -} - -Greenlet::switchstack_result_t -Greenlet::g_switchstack(void) -{ - // if any of these assertions fail, it's likely because we - // switched away and tried to switch back to us. Early stages of - // switching are not reentrant because we re-use ``this->args()``. - // Switching away would happen if we trigger a garbage collection - // (by just using some Python APIs that happen to allocate Python - // objects) and some garbage had weakref callbacks or __del__ that - // switches (people don't write code like that by hand, but with - // gevent it's possible without realizing it) - assert(this->args() || PyErr_Occurred()); - { /* save state */ - if (this->thread_state()->is_current(this->self())) { - // Hmm, nothing to do. - // TODO: Does this bypass trace events that are - // important? - return switchstack_result_t(0, - this, this->thread_state()->borrow_current()); - } - BorrowedGreenlet current = this->thread_state()->borrow_current(); - PyThreadState* tstate = PyThreadState_GET(); - - current->python_state << tstate; - current->exception_state << tstate; - this->python_state.will_switch_from(tstate); - switching_thread_state = this; - current->expose_frames(); - } - assert(this->args() || PyErr_Occurred()); - // If this is the first switch into a greenlet, this will - // return twice, once with 1 in the new greenlet, once with 0 - // in the origin. - int err; - if (this->force_slp_switch_error()) { - err = -1; - } - else { - err = slp_switch(); - } - - if (err < 0) { /* error */ - // Tested by - // test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running - // - // It's not clear if it's worth trying to clean up and - // continue here. Failing to switch stacks is a big deal which - // may not be recoverable (who knows what state the stack is in). - // Also, we've stolen references in preparation for calling - // ``g_switchstack_success()`` and we don't have a clean - // mechanism for backing that all out. - Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt."); - } - - // No stack-based variables are valid anymore. - - // But the global is volatile so we can reload it without the - // compiler caching it from earlier. - Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this - switching_thread_state = nullptr; - // except that no stack variables are valid, we would: - // assert(this == greenlet_that_switched_in); - - // switchstack success is where we restore the exception state, - // etc. It returns the origin greenlet because its convenient. - - OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success(); - assert(greenlet_that_switched_in->args() || PyErr_Occurred()); - return switchstack_result_t(err, greenlet_that_switched_in, origin); -} - - -inline void -Greenlet::check_switch_allowed() const -{ - // TODO: Make this take a parameter of the current greenlet, - // or current main greenlet, to make the check for - // cross-thread switching cheaper. Surely somewhere up the - // call stack we've already accessed the thread local variable. - - // We expect to always have a main greenlet now; accessing the thread state - // created it. However, if we get here and cleanup has already - // begun because we're a greenlet that was running in a - // (now dead) thread, these invariants will not hold true. In - // fact, accessing `this->thread_state` may not even be possible. - - // If the thread this greenlet was running in is dead, - // we'll still have a reference to a main greenlet, but the - // thread state pointer we have is bogus. - // TODO: Give the objects an API to determine if they belong - // to a dead thread. - - const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage(); - - if (!main_greenlet) { - throw PyErrOccurred(mod_globs->PyExc_GreenletError, - "cannot switch to a garbage collected greenlet"); - } - - if (!main_greenlet->thread_state()) { - throw PyErrOccurred(mod_globs->PyExc_GreenletError, - "cannot switch to a different thread (which happens to have exited)"); - } - - // The main greenlet we found was from the .parent lineage. - // That may or may not have any relationship to the main - // greenlet of the running thread. We can't actually access - // our this->thread_state members to try to check that, - // because it could be in the process of getting destroyed, - // but setting the main_greenlet->thread_state member to NULL - // may not be visible yet. So we need to check against the - // current thread state (once the cheaper checks are out of - // the way) - const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet(); - if ( - // lineage main greenlet is not this thread's greenlet - current_main_greenlet != main_greenlet - || ( - // atteched to some thread - this->main_greenlet() - // XXX: Same condition as above. Was this supposed to be - // this->main_greenlet()? - && current_main_greenlet != main_greenlet) - // switching into a known dead thread (XXX: which, if we get here, - // is bad, because we just accessed the thread state, which is - // gone!) - || (!current_main_greenlet->thread_state())) { - // CAUTION: This may trigger memory allocations, gc, and - // arbitrary Python code. - throw PyErrOccurred( - mod_globs->PyExc_GreenletError, - "Cannot switch to a different thread\n\tCurrent: %R\n\tExpected: %R", - current_main_greenlet, main_greenlet); - } -} - -const OwnedObject -Greenlet::context() const -{ - using greenlet::PythonStateContext; - OwnedObject result; - - if (this->is_currently_running_in_some_thread()) { - /* Currently running greenlet: context is stored in the thread state, - not the greenlet object. */ - if (GET_THREAD_STATE().state().is_current(this->self())) { - result = PythonStateContext::context(PyThreadState_GET()); - } - else { - throw ValueError( - "cannot get context of a " - "greenlet that is running in a different thread"); - } - } - else { - /* Greenlet is not running: just return context. */ - result = this->python_state.context(); - } - if (!result) { - result = OwnedObject::None(); - } - return result; -} - - -void -Greenlet::context(BorrowedObject given) -{ - using greenlet::PythonStateContext; - if (!given) { - throw AttributeError("can't delete context attribute"); - } - if (given.is_None()) { - /* "Empty context" is stored as NULL, not None. */ - given = nullptr; - } - - //checks type, incrs refcnt - greenlet::refs::OwnedContext context(given); - PyThreadState* tstate = PyThreadState_GET(); - - if (this->is_currently_running_in_some_thread()) { - if (!GET_THREAD_STATE().state().is_current(this->self())) { - throw ValueError("cannot set context of a greenlet" - " that is running in a different thread"); - } - - /* Currently running greenlet: context is stored in the thread state, - not the greenlet object. */ - OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate)); - PythonStateContext::context(tstate, context.relinquish_ownership()); - } - else { - /* Greenlet is not running: just set context. Note that the - greenlet may be dead.*/ - this->python_state.context() = context; - } -} - -/** - * CAUTION: May invoke arbitrary Python code. - * - * Figure out what the result of ``greenlet.switch(arg, kwargs)`` - * should be and transfers ownership of it to the left-hand-side. - * - * If switch() was just passed an arg tuple, then we'll just return that. - * If only keyword arguments were passed, then we'll pass the keyword - * argument dict. Otherwise, we'll create a tuple of (args, kwargs) and - * return both. - * - * CAUTION: This may allocate a new tuple object, which may - * cause the Python garbage collector to run, which in turn may - * run arbitrary Python code that switches. - */ -OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept -{ - // Because this may invoke arbitrary Python code, which could - // result in switching back to us, we need to get the - // arguments locally on the stack. - assert(rhs); - OwnedObject args = rhs.args(); - OwnedObject kwargs = rhs.kwargs(); - rhs.CLEAR(); - // We shouldn't be called twice for the same switch. - assert(args || kwargs); - assert(!rhs); - - if (!kwargs) { - lhs = args; - } - else if (!PyDict_Size(kwargs.borrow())) { - lhs = args; - } - else if (!PySequence_Length(args.borrow())) { - lhs = kwargs; - } - else { - // PyTuple_Pack allocates memory, may GC, may run arbitrary - // Python code. - lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow())); - } - return lhs; -} - -static OwnedObject -g_handle_exit(const OwnedObject& greenlet_result) -{ - if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) { - /* catch and ignore GreenletExit */ - PyErrFetchParam val; - PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam()); - if (!val) { - return OwnedObject::None(); - } - return OwnedObject(val); - } - - if (greenlet_result) { - // package the result into a 1-tuple - // PyTuple_Pack increments the reference of its arguments, - // so we always need to decref the greenlet result; - // the owner will do that. - return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow())); - } - - return OwnedObject(); -} - - - -/** - * May run arbitrary Python code. - */ -OwnedObject -Greenlet::g_switch_finish(const switchstack_result_t& err) -{ - assert(err.the_new_current_greenlet == this); - - ThreadState& state = *this->thread_state(); - // Because calling the trace function could do arbitrary things, - // including switching away from this greenlet and then maybe - // switching back, we need to capture the arguments now so that - // they don't change. - OwnedObject result; - if (this->args()) { - result <<= this->args(); - } - else { - assert(PyErr_Occurred()); - } - assert(!this->args()); - try { - // Our only caller handles the bad error case - assert(err.status >= 0); - assert(state.borrow_current() == this->self()); - if (OwnedObject tracefunc = state.get_tracefunc()) { - assert(result || PyErr_Occurred()); - g_calltrace(tracefunc, - result ? mod_globs->event_switch : mod_globs->event_throw, - err.origin_greenlet, - this->self()); - } - // The above could have invoked arbitrary Python code, but - // it couldn't switch back to this object and *also* - // throw an exception, so the args won't have changed. - - if (PyErr_Occurred()) { - // We get here if we fell of the end of the run() function - // raising an exception. The switch itself was - // successful, but the function raised. - // valgrind reports that memory allocated here can still - // be reached after a test run. - throw PyErrOccurred::from_current(); - } - return result; - } - catch (const PyErrOccurred&) { - /* Turn switch errors into switch throws */ - /* Turn trace errors into switch throws */ - this->release_args(); - throw; - } -} - -void -Greenlet::g_calltrace(const OwnedObject& tracefunc, - const greenlet::refs::ImmortalEventName& event, - const BorrowedGreenlet& origin, - const BorrowedGreenlet& target) -{ - PyErrPieces saved_exc; - try { - TracingGuard tracing_guard; - // TODO: We have saved the active exception (if any) that's - // about to be raised. In the 'throw' case, we could provide - // the exception to the tracefunction, which seems very helpful. - tracing_guard.CallTraceFunction(tracefunc, event, origin, target); - } - catch (const PyErrOccurred&) { - // In case of exceptions trace function is removed, - // and any existing exception is replaced with the tracing - // exception. - GET_THREAD_STATE().state().set_tracefunc(Py_None); - throw; - } - - saved_exc.PyErrRestore(); - assert( - (event == mod_globs->event_throw && PyErr_Occurred()) - || (event == mod_globs->event_switch && !PyErr_Occurred()) - ); -} - -void -Greenlet::murder_in_place() -{ - if (this->active()) { - assert(!this->is_currently_running_in_some_thread()); - this->deactivate_and_free(); - } -} - -inline void -Greenlet::deactivate_and_free() -{ - if (!this->active()) { - return; - } - // Throw away any saved stack. - this->stack_state = StackState(); - assert(!this->stack_state.active()); - // Throw away any Python references. - // We're holding a borrowed reference to the last - // frame we executed. Since we borrowed it, the - // normal traversal, clear, and dealloc functions - // ignore it, meaning it leaks. (The thread state - // object can't find it to clear it when that's - // deallocated either, because by definition if we - // got an object on this list, it wasn't - // running and the thread state doesn't have - // this frame.) - // So here, we *do* clear it. - this->python_state.tp_clear(true); -} - -bool -Greenlet::belongs_to_thread(const ThreadState* thread_state) const -{ - if (!this->thread_state() // not running anywhere, or thread - // exited - || !thread_state) { // same, or there is no thread state. - return false; - } - return true; -} - - -void -Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state) -{ - /* Cannot raise an exception to kill the greenlet if - it is not running in the same thread! */ - if (this->belongs_to_thread(current_thread_state)) { - assert(current_thread_state); - // To get here it had to have run before - /* Send the greenlet a GreenletExit exception. */ - - // We don't care about the return value, only whether an - // exception happened. - this->throw_GreenletExit_during_dealloc(*current_thread_state); - return; - } - - // Not the same thread! Temporarily save the greenlet - // into its thread's deleteme list, *if* it exists. - // If that thread has already exited, and processed its pending - // cleanup, we'll never be able to clean everything up: we won't - // be able to raise an exception. - // That's mostly OK! Since we can't add it to a list, our refcount - // won't increase, and we'll go ahead with the DECREFs later. - ThreadState *const thread_state = this->thread_state(); - if (thread_state) { - thread_state->delete_when_thread_running(this->self()); - } - else { - // The thread is dead, we can't raise an exception. - // We need to make it look non-active, though, so that dealloc - // finishes killing it. - this->deactivate_and_free(); - } - return; -} - - -int -Greenlet::tp_traverse(visitproc visit, void* arg) -{ - - int result; - if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) { - return result; - } - //XXX: This is ugly. But so is handling everything having to do - //with the top frame. - bool visit_top_frame = this->was_running_in_dead_thread(); - // When true, the thread is dead. Our implicit weak reference to the - // frame is now all that's left; we consider ourselves to - // strongly own it now. - if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) { - return result; - } - return 0; -} - -int -Greenlet::tp_clear() -{ - bool own_top_frame = this->was_running_in_dead_thread(); - this->exception_state.tp_clear(); - this->python_state.tp_clear(own_top_frame); - return 0; -} - -bool Greenlet::is_currently_running_in_some_thread() const -{ - return this->stack_state.active() && !this->python_state.top_frame(); -} - -#if GREENLET_PY312 -void GREENLET_NOINLINE(Greenlet::expose_frames)() -{ - if (!this->python_state.top_frame()) { - return; - } - - _PyInterpreterFrame* last_complete_iframe = nullptr; - _PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame; - while (iframe) { - // We must make a copy before looking at the iframe contents, - // since iframe might point to a portion of the greenlet's C stack - // that was spilled when switching greenlets. - _PyInterpreterFrame iframe_copy; - this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe)); - if (!_PyFrame_IsIncomplete(&iframe_copy)) { - // If the iframe were OWNED_BY_CSTACK then it would always be - // incomplete. Since it's not incomplete, it's not on the C stack - // and we can access it through the original `iframe` pointer - // directly. This is important since GetFrameObject might - // lazily _create_ the frame object and we don't want the - // interpreter to lose track of it. - assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK); - - // We really want to just write: - // PyFrameObject* frame = _PyFrame_GetFrameObject(iframe); - // but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject - // which is not a visible symbol in libpython. The easiest - // way to get a public function to call it is using - // PyFrame_GetBack, which is defined as follows: - // assert(frame != NULL); - // assert(!_PyFrame_IsIncomplete(frame->f_frame)); - // PyFrameObject *back = frame->f_back; - // if (back == NULL) { - // _PyInterpreterFrame *prev = frame->f_frame->previous; - // prev = _PyFrame_GetFirstComplete(prev); - // if (prev) { - // back = _PyFrame_GetFrameObject(prev); - // } - // } - // return (PyFrameObject*)Py_XNewRef(back); - if (!iframe->frame_obj) { - PyFrameObject dummy_frame; - _PyInterpreterFrame dummy_iframe; - dummy_frame.f_back = nullptr; - dummy_frame.f_frame = &dummy_iframe; - // force the iframe to be considered complete without - // needing to check its code object: - dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR; - dummy_iframe.previous = iframe; - assert(!_PyFrame_IsIncomplete(&dummy_iframe)); - // Drop the returned reference immediately; the iframe - // continues to hold a strong reference - Py_XDECREF(PyFrame_GetBack(&dummy_frame)); - assert(iframe->frame_obj); - } - - // This is a complete frame, so make the last one of those we saw - // point at it, bypassing any incomplete frames (which may have - // been on the C stack) in between the two. We're overwriting - // last_complete_iframe->previous and need that to be reversible, - // so we store the original previous ptr in the frame object - // (which we must have created on a previous iteration through - // this loop). The frame object has a bunch of storage that is - // only used when its iframe is OWNED_BY_FRAME_OBJECT, which only - // occurs when the frame object outlives the frame's execution, - // which can't have happened yet because the frame is currently - // executing as far as the interpreter is concerned. So, we can - // reuse it for our own purposes. - assert(iframe->owner == FRAME_OWNED_BY_THREAD - || iframe->owner == FRAME_OWNED_BY_GENERATOR); - if (last_complete_iframe) { - assert(last_complete_iframe->frame_obj); - memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], - &last_complete_iframe->previous, sizeof(void *)); - last_complete_iframe->previous = iframe; - } - last_complete_iframe = iframe; - } - // Frames that are OWNED_BY_FRAME_OBJECT are linked via the - // frame's f_back while all others are linked via the iframe's - // previous ptr. Since all the frames we traverse are running - // as far as the interpreter is concerned, we don't have to - // worry about the OWNED_BY_FRAME_OBJECT case. - iframe = iframe_copy.previous; - } - - // Give the outermost complete iframe a null previous pointer to - // account for any potential incomplete/C-stack iframes between it - // and the actual top-of-stack - if (last_complete_iframe) { - assert(last_complete_iframe->frame_obj); - memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0], - &last_complete_iframe->previous, sizeof(void *)); - last_complete_iframe->previous = nullptr; - } -} -#else -void Greenlet::expose_frames() -{ - -} -#endif - -}; // namespace greenlet -#endif diff --git a/.venv/Lib/site-packages/greenlet/TGreenlet.hpp b/.venv/Lib/site-packages/greenlet/TGreenlet.hpp deleted file mode 100644 index 512f7fb..0000000 --- a/.venv/Lib/site-packages/greenlet/TGreenlet.hpp +++ /dev/null @@ -1,813 +0,0 @@ -#ifndef GREENLET_GREENLET_HPP -#define GREENLET_GREENLET_HPP -/* - * Declarations of the core data structures. -*/ - -#define PY_SSIZE_T_CLEAN -#include - -#include "greenlet_compiler_compat.hpp" -#include "greenlet_refs.hpp" -#include "greenlet_cpython_compat.hpp" -#include "greenlet_allocator.hpp" - -using greenlet::refs::OwnedObject; -using greenlet::refs::OwnedGreenlet; -using greenlet::refs::OwnedMainGreenlet; -using greenlet::refs::BorrowedGreenlet; - -#if PY_VERSION_HEX < 0x30B00A6 -# define _PyCFrame CFrame -# define _PyInterpreterFrame _interpreter_frame -#endif - -#if GREENLET_PY312 -# define Py_BUILD_CORE -# include "internal/pycore_frame.h" -#endif - -// XXX: TODO: Work to remove all virtual functions -// for speed of calling and size of objects (no vtable). -// One pattern is the Curiously Recurring Template -namespace greenlet -{ - class ExceptionState - { - private: - G_NO_COPIES_OF_CLS(ExceptionState); - - // Even though these are borrowed objects, we actually own - // them, when they're not null. - // XXX: Express that in the API. - private: - _PyErr_StackItem* exc_info; - _PyErr_StackItem exc_state; - public: - ExceptionState(); - void operator<<(const PyThreadState *const tstate) noexcept; - void operator>>(PyThreadState* tstate) noexcept; - void clear() noexcept; - - int tp_traverse(visitproc visit, void* arg) noexcept; - void tp_clear() noexcept; - }; - - template - void operator<<(const PyThreadState *const tstate, T& exc); - - class PythonStateContext - { - protected: - greenlet::refs::OwnedContext _context; - public: - inline const greenlet::refs::OwnedContext& context() const - { - return this->_context; - } - inline greenlet::refs::OwnedContext& context() - { - return this->_context; - } - - inline void tp_clear() - { - this->_context.CLEAR(); - } - - template - inline static PyObject* context(T* tstate) - { - return tstate->context; - } - - template - inline static void context(T* tstate, PyObject* new_context) - { - tstate->context = new_context; - tstate->context_ver++; - } - }; - class SwitchingArgs; - class PythonState : public PythonStateContext - { - public: - typedef greenlet::refs::OwnedReference OwnedFrame; - private: - G_NO_COPIES_OF_CLS(PythonState); - // We own this if we're suspended (although currently we don't - // tp_traverse into it; that's a TODO). If we're running, it's - // empty. If we get deallocated and *still* have a frame, it - // won't be reachable from the place that normally decref's - // it, so we need to do it (hence owning it). - OwnedFrame _top_frame; -#if GREENLET_USE_CFRAME - _PyCFrame* cframe; - int use_tracing; -#endif -#if GREENLET_PY312 - int py_recursion_depth; - int c_recursion_depth; -#else - int recursion_depth; -#endif -#if GREENLET_PY313 - PyObject *delete_later; -#else - int trash_delete_nesting; -#endif -#if GREENLET_PY311 - _PyInterpreterFrame* current_frame; - _PyStackChunk* datastack_chunk; - PyObject** datastack_top; - PyObject** datastack_limit; -#endif - // The PyInterpreterFrame list on 3.12+ contains some entries that are - // on the C stack, which can't be directly accessed while a greenlet is - // suspended. In order to keep greenlet gr_frame introspection working, - // we adjust stack switching to rewrite the interpreter frame list - // to skip these C-stack frames; we call this "exposing" the greenlet's - // frames because it makes them valid to work with in Python. Then when - // the greenlet is resumed we need to remember to reverse the operation - // we did. The C-stack frames are "entry frames" which are a low-level - // interpreter detail; they're not needed for introspection, but do - // need to be present for the eval loop to work. - void unexpose_frames(); - - public: - - PythonState(); - // You can use this for testing whether we have a frame - // or not. It returns const so they can't modify it. - const OwnedFrame& top_frame() const noexcept; - - inline void operator<<(const PyThreadState *const tstate) noexcept; - inline void operator>>(PyThreadState* tstate) noexcept; - void clear() noexcept; - - int tp_traverse(visitproc visit, void* arg, bool visit_top_frame) noexcept; - void tp_clear(bool own_top_frame) noexcept; - void set_initial_state(const PyThreadState* const tstate) noexcept; -#if GREENLET_USE_CFRAME - void set_new_cframe(_PyCFrame& frame) noexcept; -#endif - - void may_switch_away() noexcept; - inline void will_switch_from(PyThreadState *const origin_tstate) noexcept; - void did_finish(PyThreadState* tstate) noexcept; - }; - - class StackState - { - // By having only plain C (POD) members, no virtual functions - // or bases, we get a trivial assignment operator generated - // for us. However, that's not safe since we do manage memory. - // So we declare an assignment operator that only works if we - // don't have any memory allocated. (We don't use - // std::shared_ptr for reference counting just to keep this - // object small) - private: - char* _stack_start; - char* stack_stop; - char* stack_copy; - intptr_t _stack_saved; - StackState* stack_prev; - inline int copy_stack_to_heap_up_to(const char* const stop) noexcept; - inline void free_stack_copy() noexcept; - - public: - /** - * Creates a started, but inactive, state, using *current* - * as the previous. - */ - StackState(void* mark, StackState& current); - /** - * Creates an inactive, unstarted, state. - */ - StackState(); - ~StackState(); - StackState(const StackState& other); - StackState& operator=(const StackState& other); - inline void copy_heap_to_stack(const StackState& current) noexcept; - inline int copy_stack_to_heap(char* const stackref, const StackState& current) noexcept; - inline bool started() const noexcept; - inline bool main() const noexcept; - inline bool active() const noexcept; - inline void set_active() noexcept; - inline void set_inactive() noexcept; - inline intptr_t stack_saved() const noexcept; - inline char* stack_start() const noexcept; - static inline StackState make_main() noexcept; -#ifdef GREENLET_USE_STDIO - friend std::ostream& operator<<(std::ostream& os, const StackState& s); -#endif - - // Fill in [dest, dest + n) with the values that would be at - // [src, src + n) while this greenlet is running. This is like memcpy - // except that if the greenlet is suspended it accounts for the portion - // of the greenlet's stack that was spilled to the heap. `src` may - // be on this greenlet's stack, or on the heap, but not on a different - // greenlet's stack. - void copy_from_stack(void* dest, const void* src, size_t n) const; - }; -#ifdef GREENLET_USE_STDIO - std::ostream& operator<<(std::ostream& os, const StackState& s); -#endif - - class SwitchingArgs - { - private: - G_NO_ASSIGNMENT_OF_CLS(SwitchingArgs); - // If args and kwargs are both false (NULL), this is a *throw*, not a - // switch. PyErr_... must have been called already. - OwnedObject _args; - OwnedObject _kwargs; - public: - - SwitchingArgs() - {} - - SwitchingArgs(const OwnedObject& args, const OwnedObject& kwargs) - : _args(args), - _kwargs(kwargs) - {} - - SwitchingArgs(const SwitchingArgs& other) - : _args(other._args), - _kwargs(other._kwargs) - {} - - const OwnedObject& args() - { - return this->_args; - } - - const OwnedObject& kwargs() - { - return this->_kwargs; - } - - /** - * Moves ownership from the argument to this object. - */ - SwitchingArgs& operator<<=(SwitchingArgs& other) - { - if (this != &other) { - this->_args = other._args; - this->_kwargs = other._kwargs; - other.CLEAR(); - } - return *this; - } - - /** - * Acquires ownership of the argument (consumes the reference). - */ - SwitchingArgs& operator<<=(PyObject* args) - { - this->_args = OwnedObject::consuming(args); - this->_kwargs.CLEAR(); - return *this; - } - - /** - * Acquires ownership of the argument. - * - * Sets the args to be the given value; clears the kwargs. - */ - SwitchingArgs& operator<<=(OwnedObject& args) - { - assert(&args != &this->_args); - this->_args = args; - this->_kwargs.CLEAR(); - args.CLEAR(); - - return *this; - } - - explicit operator bool() const noexcept - { - return this->_args || this->_kwargs; - } - - inline void CLEAR() - { - this->_args.CLEAR(); - this->_kwargs.CLEAR(); - } - - const std::string as_str() const noexcept - { - return PyUnicode_AsUTF8( - OwnedObject::consuming( - PyUnicode_FromFormat( - "SwitchingArgs(args=%R, kwargs=%R)", - this->_args.borrow(), - this->_kwargs.borrow() - ) - ).borrow() - ); - } - }; - - class ThreadState; - - class UserGreenlet; - class MainGreenlet; - - class Greenlet - { - private: - G_NO_COPIES_OF_CLS(Greenlet); - PyGreenlet* const _self; - private: - // XXX: Work to remove these. - friend class ThreadState; - friend class UserGreenlet; - friend class MainGreenlet; - protected: - ExceptionState exception_state; - SwitchingArgs switch_args; - StackState stack_state; - PythonState python_state; - Greenlet(PyGreenlet* p, const StackState& initial_state); - public: - // This constructor takes ownership of the PyGreenlet, by - // setting ``p->pimpl = this;``. - Greenlet(PyGreenlet* p); - virtual ~Greenlet(); - - const OwnedObject context() const; - - // You MUST call this _very_ early in the switching process to - // prepare anything that may need prepared. This might perform - // garbage collections or otherwise run arbitrary Python code. - // - // One specific use of it is for Python 3.11+, preventing - // running arbitrary code at unsafe times. See - // PythonState::may_switch_away(). - inline void may_switch_away() - { - this->python_state.may_switch_away(); - } - - inline void context(refs::BorrowedObject new_context); - - inline SwitchingArgs& args() - { - return this->switch_args; - } - - virtual const refs::BorrowedMainGreenlet main_greenlet() const = 0; - - inline intptr_t stack_saved() const noexcept - { - return this->stack_state.stack_saved(); - } - - // This is used by the macro SLP_SAVE_STATE to compute the - // difference in stack sizes. It might be nice to handle the - // computation ourself, but the type of the result - // varies by platform, so doing it in the macro is the - // simplest way. - inline const char* stack_start() const noexcept - { - return this->stack_state.stack_start(); - } - - virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state); - virtual OwnedObject g_switch() = 0; - /** - * Force the greenlet to appear dead. Used when it's not - * possible to throw an exception into a greenlet anymore. - * - * This losses access to the thread state and the main greenlet. - */ - virtual void murder_in_place(); - - /** - * Called when somebody notices we were running in a dead - * thread to allow cleaning up resources (because we can't - * raise GreenletExit into it anymore). - * This is very similar to ``murder_in_place()``, except that - * it DOES NOT lose the main greenlet or thread state. - */ - inline void deactivate_and_free(); - - - // Called when some thread wants to deallocate a greenlet - // object. - // The thread may or may not be the same thread the greenlet - // was running in. - // The thread state will be null if the thread the greenlet - // was running in was known to have exited. - void deallocing_greenlet_in_thread(const ThreadState* current_state); - - // Must be called on 3.12+ before exposing a suspended greenlet's - // frames to user code. This rewrites the linked list of interpreter - // frames to skip the ones that are being stored on the C stack (which - // can't be safely accessed while the greenlet is suspended because - // that stack space might be hosting a different greenlet), and - // sets PythonState::frames_were_exposed so we remember to restore - // the original list before resuming the greenlet. The C-stack frames - // are a low-level interpreter implementation detail; while they're - // important to the bytecode eval loop, they're superfluous for - // introspection purposes. - void expose_frames(); - - - // TODO: Figure out how to make these non-public. - inline void slp_restore_state() noexcept; - inline int slp_save_state(char *const stackref) noexcept; - - inline bool is_currently_running_in_some_thread() const; - virtual bool belongs_to_thread(const ThreadState* state) const; - - inline bool started() const - { - return this->stack_state.started(); - } - inline bool active() const - { - return this->stack_state.active(); - } - inline bool main() const - { - return this->stack_state.main(); - } - virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const = 0; - - virtual const OwnedGreenlet parent() const = 0; - virtual void parent(const refs::BorrowedObject new_parent) = 0; - - inline const PythonState::OwnedFrame& top_frame() - { - return this->python_state.top_frame(); - } - - virtual const OwnedObject& run() const = 0; - virtual void run(const refs::BorrowedObject nrun) = 0; - - - virtual int tp_traverse(visitproc visit, void* arg); - virtual int tp_clear(); - - - // Return the thread state that the greenlet is running in, or - // null if the greenlet is not running or the thread is known - // to have exited. - virtual ThreadState* thread_state() const noexcept = 0; - - // Return true if the greenlet is known to have been running - // (active) in a thread that has now exited. - virtual bool was_running_in_dead_thread() const noexcept = 0; - - // Return a borrowed greenlet that is the Python object - // this object represents. - inline BorrowedGreenlet self() const noexcept - { - return BorrowedGreenlet(this->_self); - } - - // For testing. If this returns true, we should pretend that - // slp_switch() failed. - virtual bool force_slp_switch_error() const noexcept; - - protected: - inline void release_args(); - - // The functions that must not be inlined are declared virtual. - // We also mark them as protected, not private, so that the - // compiler is forced to call them through a function pointer. - // (A sufficiently smart compiler could directly call a private - // virtual function since it can never be overridden in a - // subclass). - - // Also TODO: Switch away from integer error codes and to enums, - // or throw exceptions when possible. - struct switchstack_result_t - { - int status; - Greenlet* the_new_current_greenlet; - OwnedGreenlet origin_greenlet; - - switchstack_result_t() - : status(0), - the_new_current_greenlet(nullptr) - {} - - switchstack_result_t(int err) - : status(err), - the_new_current_greenlet(nullptr) - {} - - switchstack_result_t(int err, Greenlet* state, OwnedGreenlet& origin) - : status(err), - the_new_current_greenlet(state), - origin_greenlet(origin) - { - } - - switchstack_result_t(int err, Greenlet* state, const BorrowedGreenlet& origin) - : status(err), - the_new_current_greenlet(state), - origin_greenlet(origin) - { - } - - switchstack_result_t(const switchstack_result_t& other) - : status(other.status), - the_new_current_greenlet(other.the_new_current_greenlet), - origin_greenlet(other.origin_greenlet) - {} - - switchstack_result_t& operator=(const switchstack_result_t& other) - { - this->status = other.status; - this->the_new_current_greenlet = other.the_new_current_greenlet; - this->origin_greenlet = other.origin_greenlet; - return *this; - } - }; - - OwnedObject on_switchstack_or_initialstub_failure( - Greenlet* target, - const switchstack_result_t& err, - const bool target_was_me=false, - const bool was_initial_stub=false); - - // Returns the previous greenlet we just switched away from. - virtual OwnedGreenlet g_switchstack_success() noexcept; - - - // Check the preconditions for switching to this greenlet; if they - // aren't met, throws PyErrOccurred. Most callers will want to - // catch this and clear the arguments - inline void check_switch_allowed() const; - class GreenletStartedWhileInPython : public std::runtime_error - { - public: - GreenletStartedWhileInPython() : std::runtime_error("") - {} - }; - - protected: - - - /** - Perform a stack switch into this greenlet. - - This temporarily sets the global variable - ``switching_thread_state`` to this greenlet; as soon as the - call to ``slp_switch`` completes, this is reset to NULL. - Consequently, this depends on the GIL. - - TODO: Adopt the stackman model and pass ``slp_switch`` a - callback function and context pointer; this eliminates the - need for global variables altogether. - - Because the stack switch happens in this function, this - function can't use its own stack (local) variables, set - before the switch, and then accessed after the switch. - - Further, you con't even access ``g_thread_state_global`` - before and after the switch from the global variable. - Because it is thread local some compilers cache it in a - register/on the stack, notably new versions of MSVC; this - breaks with strange crashes sometime later, because writing - to anything in ``g_thread_state_global`` after the switch - is actually writing to random memory. For this reason, we - call a non-inlined function to finish the operation. (XXX: - The ``/GT`` MSVC compiler argument probably fixes that.) - - It is very important that stack switch is 'atomic', i.e. no - calls into other Python code allowed (except very few that - are safe), because global variables are very fragile. (This - should no longer be the case with thread-local variables.) - - */ - // Made virtual to facilitate subclassing UserGreenlet for testing. - virtual switchstack_result_t g_switchstack(void); - -class TracingGuard -{ -private: - PyThreadState* tstate; -public: - TracingGuard() - : tstate(PyThreadState_GET()) - { - PyThreadState_EnterTracing(this->tstate); - } - - ~TracingGuard() - { - PyThreadState_LeaveTracing(this->tstate); - this->tstate = nullptr; - } - - inline void CallTraceFunction(const OwnedObject& tracefunc, - const greenlet::refs::ImmortalEventName& event, - const BorrowedGreenlet& origin, - const BorrowedGreenlet& target) - { - // TODO: This calls tracefunc(event, (origin, target)). Add a shortcut - // function for that that's specialized to avoid the Py_BuildValue - // string parsing, or start with just using "ON" format with PyTuple_Pack(2, - // origin, target). That seems like what the N format is meant - // for. - // XXX: Why does event not automatically cast back to a PyObject? - // It tries to call the "deleted constructor ImmortalEventName - // const" instead. - assert(tracefunc); - assert(event); - assert(origin); - assert(target); - greenlet::refs::NewReference retval( - PyObject_CallFunction( - tracefunc.borrow(), - "O(OO)", - event.borrow(), - origin.borrow(), - target.borrow() - )); - if (!retval) { - throw PyErrOccurred::from_current(); - } - } -}; - - static void - g_calltrace(const OwnedObject& tracefunc, - const greenlet::refs::ImmortalEventName& event, - const greenlet::refs::BorrowedGreenlet& origin, - const BorrowedGreenlet& target); - private: - OwnedObject g_switch_finish(const switchstack_result_t& err); - - }; - - class UserGreenlet : public Greenlet - { - private: - static greenlet::PythonAllocator allocator; - OwnedMainGreenlet _main_greenlet; - OwnedObject _run_callable; - OwnedGreenlet _parent; - public: - static void* operator new(size_t UNUSED(count)); - static void operator delete(void* ptr); - - UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent); - virtual ~UserGreenlet(); - - virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const; - virtual bool was_running_in_dead_thread() const noexcept; - virtual ThreadState* thread_state() const noexcept; - virtual OwnedObject g_switch(); - virtual const OwnedObject& run() const - { - if (this->started() || !this->_run_callable) { - throw AttributeError("run"); - } - return this->_run_callable; - } - virtual void run(const refs::BorrowedObject nrun); - - virtual const OwnedGreenlet parent() const; - virtual void parent(const refs::BorrowedObject new_parent); - - virtual const refs::BorrowedMainGreenlet main_greenlet() const; - - virtual void murder_in_place(); - virtual bool belongs_to_thread(const ThreadState* state) const; - virtual int tp_traverse(visitproc visit, void* arg); - virtual int tp_clear(); - class ParentIsCurrentGuard - { - private: - OwnedGreenlet oldparent; - UserGreenlet* greenlet; - G_NO_COPIES_OF_CLS(ParentIsCurrentGuard); - public: - ParentIsCurrentGuard(UserGreenlet* p, const ThreadState& thread_state); - ~ParentIsCurrentGuard(); - }; - virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state); - protected: - virtual switchstack_result_t g_initialstub(void* mark); - private: - // This function isn't meant to return. - // This accepts raw pointers and the ownership of them at the - // same time. The caller should use ``inner_bootstrap(origin.relinquish_ownership())``. - void inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run); - }; - - class BrokenGreenlet : public UserGreenlet - { - private: - static greenlet::PythonAllocator allocator; - public: - bool _force_switch_error = false; - bool _force_slp_switch_error = false; - - static void* operator new(size_t UNUSED(count)); - static void operator delete(void* ptr); - BrokenGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent) - : UserGreenlet(p, the_parent) - {} - virtual ~BrokenGreenlet() - {} - - virtual switchstack_result_t g_switchstack(void); - virtual bool force_slp_switch_error() const noexcept; - - }; - - class MainGreenlet : public Greenlet - { - private: - static greenlet::PythonAllocator allocator; - refs::BorrowedMainGreenlet _self; - ThreadState* _thread_state; - G_NO_COPIES_OF_CLS(MainGreenlet); - public: - static void* operator new(size_t UNUSED(count)); - static void operator delete(void* ptr); - - MainGreenlet(refs::BorrowedMainGreenlet::PyType*, ThreadState*); - virtual ~MainGreenlet(); - - - virtual const OwnedObject& run() const; - virtual void run(const refs::BorrowedObject nrun); - - virtual const OwnedGreenlet parent() const; - virtual void parent(const refs::BorrowedObject new_parent); - - virtual const refs::BorrowedMainGreenlet main_greenlet() const; - - virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const; - virtual bool was_running_in_dead_thread() const noexcept; - virtual ThreadState* thread_state() const noexcept; - void thread_state(ThreadState*) noexcept; - virtual OwnedObject g_switch(); - virtual int tp_traverse(visitproc visit, void* arg); - }; - - // Instantiate one on the stack to save the GC state, - // and then disable GC. When it goes out of scope, GC will be - // restored to its original state. Sadly, these APIs are only - // available on 3.10+; luckily, we only need them on 3.11+. -#if GREENLET_PY310 - class GCDisabledGuard - { - private: - int was_enabled = 0; - public: - GCDisabledGuard() - : was_enabled(PyGC_IsEnabled()) - { - PyGC_Disable(); - } - - ~GCDisabledGuard() - { - if (this->was_enabled) { - PyGC_Enable(); - } - } - }; -#endif - - OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept; - - //TODO: Greenlet::g_switch() should call this automatically on its - //return value. As it is, the module code is calling it. - static inline OwnedObject - single_result(const OwnedObject& results) - { - if (results - && PyTuple_Check(results.borrow()) - && PyTuple_GET_SIZE(results.borrow()) == 1) { - PyObject* result = PyTuple_GET_ITEM(results.borrow(), 0); - assert(result); - return OwnedObject::owning(result); - } - return results; - } - - - static OwnedObject - g_handle_exit(const OwnedObject& greenlet_result); - - - template - void operator<<(const PyThreadState *const lhs, T& rhs) - { - rhs.operator<<(lhs); - } - -} // namespace greenlet ; - -#endif diff --git a/.venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp b/.venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp deleted file mode 100644 index 0087d2f..0000000 --- a/.venv/Lib/site-packages/greenlet/TGreenletGlobals.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of GreenletGlobals. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#ifndef T_GREENLET_GLOBALS -#define T_GREENLET_GLOBALS - -#include "greenlet_refs.hpp" -#include "greenlet_exceptions.hpp" -#include "greenlet_thread_support.hpp" -#include "greenlet_internal.hpp" - -namespace greenlet { - -// This encapsulates what were previously module global "constants" -// established at init time. -// This is a step towards Python3 style module state that allows -// reloading. -// -// In an earlier iteration of this code, we used placement new to be -// able to allocate this object statically still, so that references -// to its members don't incur an extra pointer indirection. -// But under some scenarios, that could result in crashes at -// shutdown because apparently the destructor was getting run twice? -class GreenletGlobals -{ - -public: - const greenlet::refs::ImmortalEventName event_switch; - const greenlet::refs::ImmortalEventName event_throw; - const greenlet::refs::ImmortalException PyExc_GreenletError; - const greenlet::refs::ImmortalException PyExc_GreenletExit; - const greenlet::refs::ImmortalObject empty_tuple; - const greenlet::refs::ImmortalObject empty_dict; - const greenlet::refs::ImmortalString str_run; - Mutex* const thread_states_to_destroy_lock; - greenlet::cleanup_queue_t thread_states_to_destroy; - - GreenletGlobals() : - event_switch("switch"), - event_throw("throw"), - PyExc_GreenletError("greenlet.error"), - PyExc_GreenletExit("greenlet.GreenletExit", PyExc_BaseException), - empty_tuple(Require(PyTuple_New(0))), - empty_dict(Require(PyDict_New())), - str_run("run"), - thread_states_to_destroy_lock(new Mutex()) - {} - - ~GreenletGlobals() - { - // This object is (currently) effectively immortal, and not - // just because of those placement new tricks; if we try to - // deallocate the static object we allocated, and overwrote, - // we would be doing so at C++ teardown time, which is after - // the final Python GIL is released, and we can't use the API - // then. - // (The members will still be destructed, but they also don't - // do any deallocation.) - } - - void queue_to_destroy(ThreadState* ts) const - { - // we're currently accessed through a static const object, - // implicitly marking our members as const, so code can't just - // call push_back (or pop_back) without casting away the - // const. - // - // Do that for callers. - greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy); - q.push_back(ts); - } - - ThreadState* take_next_to_destroy() const - { - greenlet::cleanup_queue_t& q = const_cast(this->thread_states_to_destroy); - ThreadState* result = q.back(); - q.pop_back(); - return result; - } -}; - -}; // namespace greenlet - -static const greenlet::GreenletGlobals* mod_globs; - -#endif // T_GREENLET_GLOBALS diff --git a/.venv/Lib/site-packages/greenlet/TMainGreenlet.cpp b/.venv/Lib/site-packages/greenlet/TMainGreenlet.cpp deleted file mode 100644 index a2a9cfe..0000000 --- a/.venv/Lib/site-packages/greenlet/TMainGreenlet.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::MainGreenlet. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#ifndef T_MAIN_GREENLET_CPP -#define T_MAIN_GREENLET_CPP - -#include "TGreenlet.hpp" - - - -// Protected by the GIL. Incremented when we create a main greenlet, -// in a new thread, decremented when it is destroyed. -static Py_ssize_t G_TOTAL_MAIN_GREENLETS; - -namespace greenlet { -greenlet::PythonAllocator MainGreenlet::allocator; - -void* MainGreenlet::operator new(size_t UNUSED(count)) -{ - return allocator.allocate(1); -} - - -void MainGreenlet::operator delete(void* ptr) -{ - return allocator.deallocate(static_cast(ptr), - 1); -} - - -MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state) - : Greenlet(p, StackState::make_main()), - _self(p), - _thread_state(state) -{ - G_TOTAL_MAIN_GREENLETS++; -} - -MainGreenlet::~MainGreenlet() -{ - G_TOTAL_MAIN_GREENLETS--; - this->tp_clear(); -} - -ThreadState* -MainGreenlet::thread_state() const noexcept -{ - return this->_thread_state; -} - -void -MainGreenlet::thread_state(ThreadState* t) noexcept -{ - assert(!t); - this->_thread_state = t; -} - - -const BorrowedMainGreenlet -MainGreenlet::main_greenlet() const -{ - return this->_self; -} - -BorrowedMainGreenlet -MainGreenlet::find_main_greenlet_in_lineage() const -{ - return BorrowedMainGreenlet(this->_self); -} - -bool -MainGreenlet::was_running_in_dead_thread() const noexcept -{ - return !this->_thread_state; -} - -OwnedObject -MainGreenlet::g_switch() -{ - try { - this->check_switch_allowed(); - } - catch (const PyErrOccurred&) { - this->release_args(); - throw; - } - - switchstack_result_t err = this->g_switchstack(); - if (err.status < 0) { - // XXX: This code path is untested, but it is shared - // with the UserGreenlet path that is tested. - return this->on_switchstack_or_initialstub_failure( - this, - err, - true, // target was me - false // was initial stub - ); - } - - return err.the_new_current_greenlet->g_switch_finish(err); -} - -int -MainGreenlet::tp_traverse(visitproc visit, void* arg) -{ - if (this->_thread_state) { - // we've already traversed main, (self), don't do it again. - int result = this->_thread_state->tp_traverse(visit, arg, false); - if (result) { - return result; - } - } - return Greenlet::tp_traverse(visit, arg); -} - -const OwnedObject& -MainGreenlet::run() const -{ - throw AttributeError("Main greenlets do not have a run attribute."); -} - -void -MainGreenlet::run(const BorrowedObject UNUSED(nrun)) -{ - throw AttributeError("Main greenlets do not have a run attribute."); -} - -void -MainGreenlet::parent(const BorrowedObject raw_new_parent) -{ - if (!raw_new_parent) { - throw AttributeError("can't delete attribute"); - } - throw AttributeError("cannot set the parent of a main greenlet"); -} - -const OwnedGreenlet -MainGreenlet::parent() const -{ - return OwnedGreenlet(); // null becomes None -} - -}; // namespace greenlet - -#endif diff --git a/.venv/Lib/site-packages/greenlet/TPythonState.cpp b/.venv/Lib/site-packages/greenlet/TPythonState.cpp deleted file mode 100644 index cc5dff5..0000000 --- a/.venv/Lib/site-packages/greenlet/TPythonState.cpp +++ /dev/null @@ -1,393 +0,0 @@ -#ifndef GREENLET_PYTHON_STATE_CPP -#define GREENLET_PYTHON_STATE_CPP - -#include -#include "TGreenlet.hpp" - -namespace greenlet { - -PythonState::PythonState() - : _top_frame() -#if GREENLET_USE_CFRAME - ,cframe(nullptr) - ,use_tracing(0) -#endif -#if GREENLET_PY312 - ,py_recursion_depth(0) - ,c_recursion_depth(0) -#else - ,recursion_depth(0) -#endif -#if GREENLET_PY313 - ,delete_later(nullptr) -#else - ,trash_delete_nesting(0) -#endif -#if GREENLET_PY311 - ,current_frame(nullptr) - ,datastack_chunk(nullptr) - ,datastack_top(nullptr) - ,datastack_limit(nullptr) -#endif -{ -#if GREENLET_USE_CFRAME - /* - The PyThreadState->cframe pointer usually points to memory on - the stack, alloceted in a call into PyEval_EvalFrameDefault. - - Initially, before any evaluation begins, it points to the - initial PyThreadState object's ``root_cframe`` object, which is - statically allocated for the lifetime of the thread. - - A greenlet can last for longer than a call to - PyEval_EvalFrameDefault, so we can't set its ``cframe`` pointer - to be the current ``PyThreadState->cframe``; nor could we use - one from the greenlet parent for the same reason. Yet a further - no: we can't allocate one scoped to the greenlet and then - destroy it when the greenlet is deallocated, because inside the - interpreter the _PyCFrame objects form a linked list, and that too - can result in accessing memory beyond its dynamic lifetime (if - the greenlet doesn't actually finish before it dies, its entry - could still be in the list). - - Using the ``root_cframe`` is problematic, though, because its - members are never modified by the interpreter and are set to 0, - meaning that its ``use_tracing`` flag is never updated. We don't - want to modify that value in the ``root_cframe`` ourself: it - *shouldn't* matter much because we should probably never get - back to the point where that's the only cframe on the stack; - even if it did matter, the major consequence of an incorrect - value for ``use_tracing`` is that if its true the interpreter - does some extra work --- however, it's just good code hygiene. - - Our solution: before a greenlet runs, after its initial - creation, it uses the ``root_cframe`` just to have something to - put there. However, once the greenlet is actually switched to - for the first time, ``g_initialstub`` (which doesn't actually - "return" while the greenlet is running) stores a new _PyCFrame on - its local stack, and copies the appropriate values from the - currently running _PyCFrame; this is then made the _PyCFrame for the - newly-minted greenlet. ``g_initialstub`` then proceeds to call - ``glet.run()``, which results in ``PyEval_...`` adding the - _PyCFrame to the list. Switches continue as normal. Finally, when - the greenlet finishes, the call to ``glet.run()`` returns and - the _PyCFrame is taken out of the linked list and the stack value - is now unused and free to expire. - - XXX: I think we can do better. If we're deallocing in the same - thread, can't we traverse the list and unlink our frame? - Can we just keep a reference to the thread state in case we - dealloc in another thread? (Is that even possible if we're still - running and haven't returned from g_initialstub?) - */ - this->cframe = &PyThreadState_GET()->root_cframe; -#endif -} - - -inline void PythonState::may_switch_away() noexcept -{ -#if GREENLET_PY311 - // PyThreadState_GetFrame is probably going to have to allocate a - // new frame object. That may trigger garbage collection. Because - // we call this during the early phases of a switch (it doesn't - // matter to which greenlet, as this has a global effect), if a GC - // triggers a switch away, two things can happen, both bad: - // - We might not get switched back to, halting forward progress. - // this is pathological, but possible. - // - We might get switched back to with a different set of - // arguments or a throw instead of a switch. That would corrupt - // our state (specifically, PyErr_Occurred() and this->args() - // would no longer agree). - // - // Thus, when we call this API, we need to have GC disabled. - // This method serves as a bottleneck we call when maybe beginning - // a switch. In this way, it is always safe -- no risk of GC -- to - // use ``_GetFrame()`` whenever we need to, just as it was in - // <=3.10 (because subsequent calls will be cached and not - // allocate memory). - - GCDisabledGuard no_gc; - Py_XDECREF(PyThreadState_GetFrame(PyThreadState_GET())); -#endif -} - -void PythonState::operator<<(const PyThreadState *const tstate) noexcept -{ - this->_context.steal(tstate->context); -#if GREENLET_USE_CFRAME - /* - IMPORTANT: ``cframe`` is a pointer into the STACK. Thus, because - the call to ``slp_switch()`` changes the contents of the stack, - you cannot read from ``ts_current->cframe`` after that call and - necessarily get the same values you get from reading it here. - Anything you need to restore from now to then must be saved in a - global/threadlocal variable (because we can't use stack - variables here either). For things that need to persist across - the switch, use `will_switch_from`. - */ - this->cframe = tstate->cframe; - #if !GREENLET_PY312 - this->use_tracing = tstate->cframe->use_tracing; - #endif -#endif // GREENLET_USE_CFRAME -#if GREENLET_PY311 - #if GREENLET_PY312 - this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; - this->c_recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; - #else // not 312 - this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; - #endif // GREENLET_PY312 - #if GREENLET_PY313 - this->current_frame = tstate->current_frame; - #elif GREENLET_USE_CFRAME - this->current_frame = tstate->cframe->current_frame; - #endif - this->datastack_chunk = tstate->datastack_chunk; - this->datastack_top = tstate->datastack_top; - this->datastack_limit = tstate->datastack_limit; - - PyFrameObject *frame = PyThreadState_GetFrame((PyThreadState *)tstate); - Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new - // reference. - this->_top_frame.steal(frame); - #if GREENLET_PY313 - this->delete_later = Py_XNewRef(tstate->delete_later); - #elif GREENLET_PY312 - this->trash_delete_nesting = tstate->trash.delete_nesting; - #else // not 312 - this->trash_delete_nesting = tstate->trash_delete_nesting; - #endif // GREENLET_PY312 -#else // Not 311 - this->recursion_depth = tstate->recursion_depth; - this->_top_frame.steal(tstate->frame); - this->trash_delete_nesting = tstate->trash_delete_nesting; -#endif // GREENLET_PY311 -} - -#if GREENLET_PY312 -void GREENLET_NOINLINE(PythonState::unexpose_frames)() -{ - if (!this->top_frame()) { - return; - } - - // See GreenletState::expose_frames() and the comment on frames_were_exposed - // for more information about this logic. - _PyInterpreterFrame *iframe = this->_top_frame->f_frame; - while (iframe != nullptr) { - _PyInterpreterFrame *prev_exposed = iframe->previous; - assert(iframe->frame_obj); - memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0], - sizeof(void *)); - iframe = prev_exposed; - } -} -#else -void PythonState::unexpose_frames() -{} -#endif - -void PythonState::operator>>(PyThreadState *const tstate) noexcept -{ - tstate->context = this->_context.relinquish_ownership(); - /* Incrementing this value invalidates the contextvars cache, - which would otherwise remain valid across switches */ - tstate->context_ver++; -#if GREENLET_USE_CFRAME - tstate->cframe = this->cframe; - /* - If we were tracing, we need to keep tracing. - There should never be the possibility of hitting the - root_cframe here. See note above about why we can't - just copy this from ``origin->cframe->use_tracing``. - */ - #if !GREENLET_PY312 - tstate->cframe->use_tracing = this->use_tracing; - #endif -#endif // GREENLET_USE_CFRAME -#if GREENLET_PY311 - #if GREENLET_PY312 - tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth; - tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT - this->c_recursion_depth; - this->unexpose_frames(); - #else // \/ 3.11 - tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth; - #endif // GREENLET_PY312 - #if GREENLET_PY313 - tstate->current_frame = this->current_frame; - #elif GREENLET_USE_CFRAME - tstate->cframe->current_frame = this->current_frame; - #endif - tstate->datastack_chunk = this->datastack_chunk; - tstate->datastack_top = this->datastack_top; - tstate->datastack_limit = this->datastack_limit; - this->_top_frame.relinquish_ownership(); - #if GREENLET_PY313 - Py_XDECREF(tstate->delete_later); - tstate->delete_later = this->delete_later; - Py_CLEAR(this->delete_later); - #elif GREENLET_PY312 - tstate->trash.delete_nesting = this->trash_delete_nesting; - #else // not 3.12 - tstate->trash_delete_nesting = this->trash_delete_nesting; - #endif // GREENLET_PY312 -#else // not 3.11 - tstate->frame = this->_top_frame.relinquish_ownership(); - tstate->recursion_depth = this->recursion_depth; - tstate->trash_delete_nesting = this->trash_delete_nesting; -#endif // GREENLET_PY311 -} - -inline void PythonState::will_switch_from(PyThreadState *const origin_tstate) noexcept -{ -#if GREENLET_USE_CFRAME && !GREENLET_PY312 - // The weird thing is, we don't actually save this for an - // effect on the current greenlet, it's saved for an - // effect on the target greenlet. That is, we want - // continuity of this setting across the greenlet switch. - this->use_tracing = origin_tstate->cframe->use_tracing; -#endif -} - -void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept -{ - this->_top_frame = nullptr; -#if GREENLET_PY312 - this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; - // XXX: TODO: Comment from a reviewer: - // Should this be ``Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining``? - // But to me it looks more like that might not be the right - // initialization either? - this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining; -#elif GREENLET_PY311 - this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; -#else - this->recursion_depth = tstate->recursion_depth; -#endif -} -// TODO: Better state management about when we own the top frame. -int PythonState::tp_traverse(visitproc visit, void* arg, bool own_top_frame) noexcept -{ - Py_VISIT(this->_context.borrow()); - if (own_top_frame) { - Py_VISIT(this->_top_frame.borrow()); - } - return 0; -} - -void PythonState::tp_clear(bool own_top_frame) noexcept -{ - PythonStateContext::tp_clear(); - // If we get here owning a frame, - // we got dealloc'd without being finished. We may or may not be - // in the same thread. - if (own_top_frame) { - this->_top_frame.CLEAR(); - } -} - -#if GREENLET_USE_CFRAME -void PythonState::set_new_cframe(_PyCFrame& frame) noexcept -{ - frame = *PyThreadState_GET()->cframe; - /* Make the target greenlet refer to the stack value. */ - this->cframe = &frame; - /* - And restore the link to the previous frame so this one gets - unliked appropriately. - */ - this->cframe->previous = &PyThreadState_GET()->root_cframe; -} -#endif - -const PythonState::OwnedFrame& PythonState::top_frame() const noexcept -{ - return this->_top_frame; -} - -void PythonState::did_finish(PyThreadState* tstate) noexcept -{ -#if GREENLET_PY311 - // See https://github.com/gevent/gevent/issues/1924 and - // https://github.com/python-greenlet/greenlet/issues/328. In - // short, Python 3.11 allocates memory for frames as a sort of - // linked list that's kept as part of PyThreadState in the - // ``datastack_chunk`` member and friends. These are saved and - // restored as part of switching greenlets. - // - // When we initially switch to a greenlet, we set those to NULL. - // That causes the frame management code to treat this like a - // brand new thread and start a fresh list of chunks, beginning - // with a new "root" chunk. As we make calls in this greenlet, - // those chunks get added, and as calls return, they get popped. - // But the frame code (pystate.c) is careful to make sure that the - // root chunk never gets popped. - // - // Thus, when a greenlet exits for the last time, there will be at - // least a single root chunk that we must be responsible for - // deallocating. - // - // The complex part is that these chunks are allocated and freed - // using ``_PyObject_VirtualAlloc``/``Free``. Those aren't public - // functions, and they aren't exported for linking. It so happens - // that we know they are just thin wrappers around the Arena - // allocator, so we can use that directly to deallocate in a - // compatible way. - // - // CAUTION: Check this implementation detail on every major version. - // - // It might be nice to be able to do this in our destructor, but - // can we be sure that no one else is using that memory? Plus, as - // described below, our pointers may not even be valid anymore. As - // a special case, there is one time that we know we can do this, - // and that's from the destructor of the associated UserGreenlet - // (NOT main greenlet) - PyObjectArenaAllocator alloc; - _PyStackChunk* chunk = nullptr; - if (tstate) { - // We really did finish, we can never be switched to again. - chunk = tstate->datastack_chunk; - // Unfortunately, we can't do much sanity checking. Our - // this->datastack_chunk pointer is out of date (evaluation may - // have popped down through it already) so we can't verify that - // we deallocate it. I don't think we can even check datastack_top - // for the same reason. - - PyObject_GetArenaAllocator(&alloc); - tstate->datastack_chunk = nullptr; - tstate->datastack_limit = nullptr; - tstate->datastack_top = nullptr; - - } - else if (this->datastack_chunk) { - // The UserGreenlet (NOT the main greenlet!) is being deallocated. If we're - // still holding a stack chunk, it's garbage because we know - // we can never switch back to let cPython clean it up. - // Because the last time we got switched away from, and we - // haven't run since then, we know our chain is valid and can - // be dealloced. - chunk = this->datastack_chunk; - PyObject_GetArenaAllocator(&alloc); - } - - if (alloc.free && chunk) { - // In case the arena mechanism has been torn down already. - while (chunk) { - _PyStackChunk *prev = chunk->previous; - chunk->previous = nullptr; - alloc.free(alloc.ctx, chunk, chunk->size); - chunk = prev; - } - } - - this->datastack_chunk = nullptr; - this->datastack_limit = nullptr; - this->datastack_top = nullptr; -#endif -} - - -}; // namespace greenlet - -#endif // GREENLET_PYTHON_STATE_CPP diff --git a/.venv/Lib/site-packages/greenlet/TStackState.cpp b/.venv/Lib/site-packages/greenlet/TStackState.cpp deleted file mode 100644 index 9743ab5..0000000 --- a/.venv/Lib/site-packages/greenlet/TStackState.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef GREENLET_STACK_STATE_CPP -#define GREENLET_STACK_STATE_CPP - -#include "TGreenlet.hpp" - -namespace greenlet { - -#ifdef GREENLET_USE_STDIO -#include -using std::cerr; -using std::endl; - -std::ostream& operator<<(std::ostream& os, const StackState& s) -{ - os << "StackState(stack_start=" << (void*)s._stack_start - << ", stack_stop=" << (void*)s.stack_stop - << ", stack_copy=" << (void*)s.stack_copy - << ", stack_saved=" << s._stack_saved - << ", stack_prev=" << s.stack_prev - << ", addr=" << &s - << ")"; - return os; -} -#endif - -StackState::StackState(void* mark, StackState& current) - : _stack_start(nullptr), - stack_stop((char*)mark), - stack_copy(nullptr), - _stack_saved(0), - /* Skip a dying greenlet */ - stack_prev(current._stack_start - ? ¤t - : current.stack_prev) -{ -} - -StackState::StackState() - : _stack_start(nullptr), - stack_stop(nullptr), - stack_copy(nullptr), - _stack_saved(0), - stack_prev(nullptr) -{ -} - -StackState::StackState(const StackState& other) -// can't use a delegating constructor because of -// MSVC for Python 2.7 - : _stack_start(nullptr), - stack_stop(nullptr), - stack_copy(nullptr), - _stack_saved(0), - stack_prev(nullptr) -{ - this->operator=(other); -} - -StackState& StackState::operator=(const StackState& other) -{ - if (&other == this) { - return *this; - } - if (other._stack_saved) { - throw std::runtime_error("Refusing to steal memory."); - } - - //If we have memory allocated, dispose of it - this->free_stack_copy(); - - this->_stack_start = other._stack_start; - this->stack_stop = other.stack_stop; - this->stack_copy = other.stack_copy; - this->_stack_saved = other._stack_saved; - this->stack_prev = other.stack_prev; - return *this; -} - -inline void StackState::free_stack_copy() noexcept -{ - PyMem_Free(this->stack_copy); - this->stack_copy = nullptr; - this->_stack_saved = 0; -} - -inline void StackState::copy_heap_to_stack(const StackState& current) noexcept -{ - - /* Restore the heap copy back into the C stack */ - if (this->_stack_saved != 0) { - memcpy(this->_stack_start, this->stack_copy, this->_stack_saved); - this->free_stack_copy(); - } - StackState* owner = const_cast(¤t); - if (!owner->_stack_start) { - owner = owner->stack_prev; /* greenlet is dying, skip it */ - } - while (owner && owner->stack_stop <= this->stack_stop) { - // cerr << "\tOwner: " << owner << endl; - owner = owner->stack_prev; /* find greenlet with more stack */ - } - this->stack_prev = owner; - // cerr << "\tFinished with: " << *this << endl; -} - -inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept -{ - /* Save more of g's stack into the heap -- at least up to 'stop' - g->stack_stop |________| - | | - | __ stop . . . . . - | | ==> . . - |________| _______ - | | | | - | | | | - g->stack_start | | |_______| g->stack_copy - */ - intptr_t sz1 = this->_stack_saved; - intptr_t sz2 = stop - this->_stack_start; - assert(this->_stack_start); - if (sz2 > sz1) { - char* c = (char*)PyMem_Realloc(this->stack_copy, sz2); - if (!c) { - PyErr_NoMemory(); - return -1; - } - memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1); - this->stack_copy = c; - this->_stack_saved = sz2; - } - return 0; -} - -inline int StackState::copy_stack_to_heap(char* const stackref, - const StackState& current) noexcept -{ - /* must free all the C stack up to target_stop */ - const char* const target_stop = this->stack_stop; - - StackState* owner = const_cast(¤t); - assert(owner->_stack_saved == 0); // everything is present on the stack - if (!owner->_stack_start) { - owner = owner->stack_prev; /* not saved if dying */ - } - else { - owner->_stack_start = stackref; - } - - while (owner->stack_stop < target_stop) { - /* ts_current is entierely within the area to free */ - if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) { - return -1; /* XXX */ - } - owner = owner->stack_prev; - } - if (owner != this) { - if (owner->copy_stack_to_heap_up_to(target_stop)) { - return -1; /* XXX */ - } - } - return 0; -} - -inline bool StackState::started() const noexcept -{ - return this->stack_stop != nullptr; -} - -inline bool StackState::main() const noexcept -{ - return this->stack_stop == (char*)-1; -} - -inline bool StackState::active() const noexcept -{ - return this->_stack_start != nullptr; -} - -inline void StackState::set_active() noexcept -{ - assert(this->_stack_start == nullptr); - this->_stack_start = (char*)1; -} - -inline void StackState::set_inactive() noexcept -{ - this->_stack_start = nullptr; - // XXX: What if we still have memory out there? - // That case is actually triggered by - // test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks) - // and - // test_issue251_issue252_need_to_collect_in_background - // (greenlet.tests.test_leaks.TestLeaks) - // - // Those objects never get deallocated, so the destructor never - // runs. - // It *seems* safe to clean up the memory here? - if (this->_stack_saved) { - this->free_stack_copy(); - } -} - -inline intptr_t StackState::stack_saved() const noexcept -{ - return this->_stack_saved; -} - -inline char* StackState::stack_start() const noexcept -{ - return this->_stack_start; -} - - -inline StackState StackState::make_main() noexcept -{ - StackState s; - s._stack_start = (char*)1; - s.stack_stop = (char*)-1; - return s; -} - -StackState::~StackState() -{ - if (this->_stack_saved != 0) { - this->free_stack_copy(); - } -} - -void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const -{ - char* dest = static_cast(vdest); - const char* src = static_cast(vsrc); - if (src + n <= this->_stack_start - || src >= this->_stack_start + this->_stack_saved - || this->_stack_saved == 0) { - // Nothing we're copying was spilled from the stack - memcpy(dest, src, n); - return; - } - - if (src < this->_stack_start) { - // Copy the part before the saved stack. - // We know src + n > _stack_start due to the test above. - const size_t nbefore = this->_stack_start - src; - memcpy(dest, src, nbefore); - dest += nbefore; - src += nbefore; - n -= nbefore; - } - // We know src >= _stack_start after the before-copy, and - // src < _stack_start + _stack_saved due to the first if condition - size_t nspilled = std::min(n, this->_stack_start + this->_stack_saved - src); - memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled); - dest += nspilled; - src += nspilled; - n -= nspilled; - if (n > 0) { - // Copy the part after the saved stack - memcpy(dest, src, n); - } -} - -}; // namespace greenlet - -#endif // GREENLET_STACK_STATE_CPP diff --git a/.venv/Lib/site-packages/greenlet/TThreadState.hpp b/.venv/Lib/site-packages/greenlet/TThreadState.hpp deleted file mode 100644 index e4e6f6c..0000000 --- a/.venv/Lib/site-packages/greenlet/TThreadState.hpp +++ /dev/null @@ -1,497 +0,0 @@ -#ifndef GREENLET_THREAD_STATE_HPP -#define GREENLET_THREAD_STATE_HPP - -#include -#include - -#include "greenlet_internal.hpp" -#include "greenlet_refs.hpp" -#include "greenlet_thread_support.hpp" - -using greenlet::refs::BorrowedObject; -using greenlet::refs::BorrowedGreenlet; -using greenlet::refs::BorrowedMainGreenlet; -using greenlet::refs::OwnedMainGreenlet; -using greenlet::refs::OwnedObject; -using greenlet::refs::OwnedGreenlet; -using greenlet::refs::OwnedList; -using greenlet::refs::PyErrFetchParam; -using greenlet::refs::PyArgParseParam; -using greenlet::refs::ImmortalString; -using greenlet::refs::CreatedModule; -using greenlet::refs::PyErrPieces; -using greenlet::refs::NewReference; - -namespace greenlet { -/** - * Thread-local state of greenlets. - * - * Each native thread will get exactly one of these objects, - * automatically accessed through the best available thread-local - * mechanism the compiler supports (``thread_local`` for C++11 - * compilers or ``__thread``/``declspec(thread)`` for older GCC/clang - * or MSVC, respectively.) - * - * Previously, we kept thread-local state mostly in a bunch of - * ``static volatile`` variables in the main greenlet file.. This had - * the problem of requiring extra checks, loops, and great care - * accessing these variables if we potentially invoked any Python code - * that could release the GIL, because the state could change out from - * under us. Making the variables thread-local solves this problem. - * - * When we detected that a greenlet API accessing the current greenlet - * was invoked from a different thread than the greenlet belonged to, - * we stored a reference to the greenlet in the Python thread - * dictionary for the thread the greenlet belonged to. This could lead - * to memory leaks if the thread then exited (because of a reference - * cycle, as greenlets referred to the thread dictionary, and deleting - * non-current greenlets leaked their frame plus perhaps arguments on - * the C stack). If a thread exited while still having running - * greenlet objects (perhaps that had just switched back to the main - * greenlet), and did not invoke one of the greenlet APIs *in that - * thread, immediately before it exited, without some other thread - * then being invoked*, such a leak was guaranteed. - * - * This can be partly solved by using compiler thread-local variables - * instead of the Python thread dictionary, thus avoiding a cycle. - * - * To fully solve this problem, we need a reliable way to know that a - * thread is done and we should clean up the main greenlet. On POSIX, - * we can use the destructor function of ``pthread_key_create``, but - * there's nothing similar on Windows; a C++11 thread local object - * reliably invokes its destructor when the thread it belongs to exits - * (non-C++11 compilers offer ``__thread`` or ``declspec(thread)`` to - * create thread-local variables, but they can't hold C++ objects that - * invoke destructors; the C++11 version is the most portable solution - * I found). When the thread exits, we can drop references and - * otherwise manipulate greenlets and frames that we know can no - * longer be switched to. For compilers that don't support C++11 - * thread locals, we have a solution that uses the python thread - * dictionary, though it may not collect everything as promptly as - * other compilers do, if some other library is using the thread - * dictionary and has a cycle or extra reference. - * - * There are two small wrinkles. The first is that when the thread - * exits, it is too late to actually invoke Python APIs: the Python - * thread state is gone, and the GIL is released. To solve *this* - * problem, our destructor uses ``Py_AddPendingCall`` to transfer the - * destruction work to the main thread. (This is not an issue for the - * dictionary solution.) - * - * The second is that once the thread exits, the thread local object - * is invalid and we can't even access a pointer to it, so we can't - * pass it to ``Py_AddPendingCall``. This is handled by actually using - * a second object that's thread local (ThreadStateCreator) and having - * it dynamically allocate this object so it can live until the - * pending call runs. - */ - - - -class ThreadState { -private: - // As of commit 08ad1dd7012b101db953f492e0021fb08634afad - // this class needed 56 bytes in o Py_DEBUG build - // on 64-bit macOS 11. - // Adding the vector takes us up to 80 bytes () - - /* Strong reference to the main greenlet */ - OwnedMainGreenlet main_greenlet; - - /* Strong reference to the current greenlet. */ - OwnedGreenlet current_greenlet; - - /* Strong reference to the trace function, if any. */ - OwnedObject tracefunc; - - typedef std::vector > deleteme_t; - /* A vector of raw PyGreenlet pointers representing things that need - deleted when this thread is running. The vector owns the - references, but you need to manually INCREF/DECREF as you use - them. We don't use a vector because we - make copy of this vector, and that would become O(n) as all the - refcounts are incremented in the copy. - */ - deleteme_t deleteme; - -#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED - void* exception_state; -#endif - - static std::clock_t _clocks_used_doing_gc; - static ImmortalString get_referrers_name; - static PythonAllocator allocator; - - G_NO_COPIES_OF_CLS(ThreadState); - - - // Allocates a main greenlet for the thread state. If this fails, - // exits the process. Called only during constructing a ThreadState. - MainGreenlet* alloc_main() - { - PyGreenlet* gmain; - - /* create the main greenlet for this thread */ - gmain = reinterpret_cast(PyType_GenericAlloc(&PyGreenlet_Type, 0)); - if (gmain == NULL) { - throw PyFatalError("alloc_main failed to alloc"); //exits the process - } - - MainGreenlet* const main = new MainGreenlet(gmain, this); - - assert(Py_REFCNT(gmain) == 1); - assert(gmain->pimpl == main); - return main; - } - - -public: - static void* operator new(size_t UNUSED(count)) - { - return ThreadState::allocator.allocate(1); - } - - static void operator delete(void* ptr) - { - return ThreadState::allocator.deallocate(static_cast(ptr), - 1); - } - - static void init() - { - ThreadState::get_referrers_name = "get_referrers"; - ThreadState::_clocks_used_doing_gc = 0; - } - - ThreadState() - { - -#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED - this->exception_state = slp_get_exception_state(); -#endif - - // XXX: Potentially dangerous, exposing a not fully - // constructed object. - MainGreenlet* const main = this->alloc_main(); - this->main_greenlet = OwnedMainGreenlet::consuming( - main->self() - ); - assert(this->main_greenlet); - this->current_greenlet = main->self(); - // The main greenlet starts with 1 refs: The returned one. We - // then copied it to the current greenlet. - assert(this->main_greenlet.REFCNT() == 2); - } - - inline void restore_exception_state() - { -#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED - // It's probably important this be inlined and only call C - // functions to avoid adding an SEH frame. - slp_set_exception_state(this->exception_state); -#endif - } - - inline bool has_main_greenlet() const noexcept - { - return bool(this->main_greenlet); - } - - // Called from the ThreadStateCreator when we're in non-standard - // threading mode. In that case, there is an object in the Python - // thread state dictionary that points to us. The main greenlet - // also traverses into us, in which case it's crucial not to - // traverse back into the main greenlet. - int tp_traverse(visitproc visit, void* arg, bool traverse_main=true) - { - if (traverse_main) { - Py_VISIT(main_greenlet.borrow_o()); - } - if (traverse_main || current_greenlet != main_greenlet) { - Py_VISIT(current_greenlet.borrow_o()); - } - Py_VISIT(tracefunc.borrow()); - return 0; - } - - inline BorrowedMainGreenlet borrow_main_greenlet() const noexcept - { - assert(this->main_greenlet); - assert(this->main_greenlet.REFCNT() >= 2); - return this->main_greenlet; - }; - - inline OwnedMainGreenlet get_main_greenlet() const noexcept - { - return this->main_greenlet; - } - - /** - * In addition to returning a new reference to the currunt - * greenlet, this performs any maintenance needed. - */ - inline OwnedGreenlet get_current() - { - /* green_dealloc() cannot delete greenlets from other threads, so - it stores them in the thread dict; delete them now. */ - this->clear_deleteme_list(); - //assert(this->current_greenlet->main_greenlet == this->main_greenlet); - //assert(this->main_greenlet->main_greenlet == this->main_greenlet); - return this->current_greenlet; - } - - /** - * As for non-const get_current(); - */ - inline BorrowedGreenlet borrow_current() - { - this->clear_deleteme_list(); - return this->current_greenlet; - } - - /** - * Does no maintenance. - */ - inline OwnedGreenlet get_current() const - { - return this->current_greenlet; - } - - template - inline bool is_current(const refs::PyObjectPointer& obj) const - { - return this->current_greenlet.borrow_o() == obj.borrow_o(); - } - - inline void set_current(const OwnedGreenlet& target) - { - this->current_greenlet = target; - } - -private: - /** - * Deref and remove the greenlets from the deleteme list. Must be - * holding the GIL. - * - * If *murder* is true, then we must be called from a different - * thread than the one that these greenlets were running in. - * In that case, if the greenlet was actually running, we destroy - * the frame reference and otherwise make it appear dead before - * proceeding; otherwise, we would try (and fail) to raise an - * exception in it and wind up right back in this list. - */ - inline void clear_deleteme_list(const bool murder=false) - { - if (!this->deleteme.empty()) { - // It's possible we could add items to this list while - // running Python code if there's a thread switch, so we - // need to defensively copy it before that can happen. - deleteme_t copy = this->deleteme; - this->deleteme.clear(); // in case things come back on the list - for(deleteme_t::iterator it = copy.begin(), end = copy.end(); - it != end; - ++it ) { - PyGreenlet* to_del = *it; - if (murder) { - // Force each greenlet to appear dead; we can't raise an - // exception into it anymore anyway. - to_del->pimpl->murder_in_place(); - } - - // The only reference to these greenlets should be in - // this list, decreffing them should let them be - // deleted again, triggering calls to green_dealloc() - // in the correct thread (if we're not murdering). - // This may run arbitrary Python code and switch - // threads or greenlets! - Py_DECREF(to_del); - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(nullptr); - PyErr_Clear(); - } - } - } - } - -public: - - /** - * Returns a new reference, or a false object. - */ - inline OwnedObject get_tracefunc() const - { - return tracefunc; - }; - - - inline void set_tracefunc(BorrowedObject tracefunc) - { - assert(tracefunc); - if (tracefunc == BorrowedObject(Py_None)) { - this->tracefunc.CLEAR(); - } - else { - this->tracefunc = tracefunc; - } - } - - /** - * Given a reference to a greenlet that some other thread - * attempted to delete (has a refcount of 0) store it for later - * deletion when the thread this state belongs to is current. - */ - inline void delete_when_thread_running(PyGreenlet* to_del) - { - Py_INCREF(to_del); - this->deleteme.push_back(to_del); - } - - /** - * Set to std::clock_t(-1) to disable. - */ - inline static std::clock_t& clocks_used_doing_gc() - { - return ThreadState::_clocks_used_doing_gc; - } - - ~ThreadState() - { - if (!PyInterpreterState_Head()) { - // We shouldn't get here (our callers protect us) - // but if we do, all we can do is bail early. - return; - } - - // We should not have an "origin" greenlet; that only exists - // for the temporary time during a switch, which should not - // be in progress as the thread dies. - //assert(!this->switching_state.origin); - - this->tracefunc.CLEAR(); - - // Forcibly GC as much as we can. - this->clear_deleteme_list(true); - - // The pending call did this. - assert(this->main_greenlet->thread_state() == nullptr); - - // If the main greenlet is the current greenlet, - // then we "fell off the end" and the thread died. - // It's possible that there is some other greenlet that - // switched to us, leaving a reference to the main greenlet - // on the stack, somewhere uncollectible. Try to detect that. - if (this->current_greenlet == this->main_greenlet && this->current_greenlet) { - assert(this->current_greenlet->is_currently_running_in_some_thread()); - // Drop one reference we hold. - this->current_greenlet.CLEAR(); - assert(!this->current_greenlet); - // Only our reference to the main greenlet should be left, - // But hold onto the pointer in case we need to do extra cleanup. - PyGreenlet* old_main_greenlet = this->main_greenlet.borrow(); - Py_ssize_t cnt = this->main_greenlet.REFCNT(); - this->main_greenlet.CLEAR(); - if (ThreadState::_clocks_used_doing_gc != std::clock_t(-1) - && cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) { - // Highly likely that the reference is somewhere on - // the stack, not reachable by GC. Verify. - // XXX: This is O(n) in the total number of objects. - // TODO: Add a way to disable this at runtime, and - // another way to report on it. - std::clock_t begin = std::clock(); - NewReference gc(PyImport_ImportModule("gc")); - if (gc) { - OwnedObject get_referrers = gc.PyRequireAttr(ThreadState::get_referrers_name); - OwnedList refs(get_referrers.PyCall(old_main_greenlet)); - if (refs && refs.empty()) { - assert(refs.REFCNT() == 1); - // We found nothing! So we left a dangling - // reference: Probably the last thing some - // other greenlet did was call - // 'getcurrent().parent.switch()' to switch - // back to us. Clean it up. This will be the - // case on CPython 3.7 and newer, as they use - // an internal calling conversion that avoids - // creating method objects and storing them on - // the stack. - Py_DECREF(old_main_greenlet); - } - else if (refs - && refs.size() == 1 - && PyCFunction_Check(refs.at(0)) - && Py_REFCNT(refs.at(0)) == 2) { - assert(refs.REFCNT() == 1); - // Ok, we found a C method that refers to the - // main greenlet, and its only referenced - // twice, once in the list we just created, - // once from...somewhere else. If we can't - // find where else, then this is a leak. - // This happens in older versions of CPython - // that create a bound method object somewhere - // on the stack that we'll never get back to. - if (PyCFunction_GetFunction(refs.at(0).borrow()) == (PyCFunction)green_switch) { - BorrowedObject function_w = refs.at(0); - refs.clear(); // destroy the reference - // from the list. - // back to one reference. Can *it* be - // found? - assert(function_w.REFCNT() == 1); - refs = get_referrers.PyCall(function_w); - if (refs && refs.empty()) { - // Nope, it can't be found so it won't - // ever be GC'd. Drop it. - Py_CLEAR(function_w); - } - } - } - std::clock_t end = std::clock(); - ThreadState::_clocks_used_doing_gc += (end - begin); - } - } - } - - // We need to make sure this greenlet appears to be dead, - // because otherwise deallocing it would fail to raise an - // exception in it (the thread is dead) and put it back in our - // deleteme list. - if (this->current_greenlet) { - this->current_greenlet->murder_in_place(); - this->current_greenlet.CLEAR(); - } - - if (this->main_greenlet) { - // Couldn't have been the main greenlet that was running - // when the thread exited (because we already cleared this - // pointer if it was). This shouldn't be possible? - - // If the main greenlet was current when the thread died (it - // should be, right?) then we cleared its self pointer above - // when we cleared the current greenlet's main greenlet pointer. - // assert(this->main_greenlet->main_greenlet == this->main_greenlet - // || !this->main_greenlet->main_greenlet); - // // self reference, probably gone - // this->main_greenlet->main_greenlet.CLEAR(); - - // This will actually go away when the ivar is destructed. - this->main_greenlet.CLEAR(); - } - - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(NULL); - PyErr_Clear(); - } - - } - -}; - -ImmortalString ThreadState::get_referrers_name(nullptr); -PythonAllocator ThreadState::allocator; -std::clock_t ThreadState::_clocks_used_doing_gc(0); - - - - - -}; // namespace greenlet - -#endif diff --git a/.venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp b/.venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp deleted file mode 100644 index 2ec7ab5..0000000 --- a/.venv/Lib/site-packages/greenlet/TThreadStateCreator.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef GREENLET_THREAD_STATE_CREATOR_HPP -#define GREENLET_THREAD_STATE_CREATOR_HPP - -#include -#include - -#include "greenlet_internal.hpp" -#include "greenlet_refs.hpp" -#include "greenlet_thread_support.hpp" - -#include "TThreadState.hpp" - -namespace greenlet { - - -typedef void (*ThreadStateDestructor)(ThreadState* const); - -template -class ThreadStateCreator -{ -private: - // Initialized to 1, and, if still 1, created on access. - // Set to 0 on destruction. - ThreadState* _state; - G_NO_COPIES_OF_CLS(ThreadStateCreator); - - inline bool has_initialized_state() const noexcept - { - return this->_state != (ThreadState*)1; - } - - inline bool has_state() const noexcept - { - return this->has_initialized_state() && this->_state != nullptr; - } - -public: - - // Only one of these, auto created per thread. - // Constructing the state constructs the MainGreenlet. - ThreadStateCreator() : - _state((ThreadState*)1) - { - } - - ~ThreadStateCreator() - { - if (this->has_state()) { - Destructor(this->_state); - } - - this->_state = nullptr; - } - - inline ThreadState& state() - { - // The main greenlet will own this pointer when it is created, - // which will be right after this. The plan is to give every - // greenlet a pointer to the main greenlet for the thread it - // runs in; if we are doing something cross-thread, we need to - // access the pointer from the main greenlet. Deleting the - // thread, and hence the thread-local storage, will delete the - // state pointer in the main greenlet. - if (!this->has_initialized_state()) { - // XXX: Assuming allocation never fails - this->_state = new ThreadState; - // For non-standard threading, we need to store an object - // in the Python thread state dictionary so that it can be - // DECREF'd when the thread ends (ideally; the dict could - // last longer) and clean this object up. - } - if (!this->_state) { - throw std::runtime_error("Accessing state after destruction."); - } - return *this->_state; - } - - operator ThreadState&() - { - return this->state(); - } - - operator ThreadState*() - { - return &this->state(); - } - - inline int tp_traverse(visitproc visit, void* arg) - { - if (this->has_state()) { - return this->_state->tp_traverse(visit, arg); - } - return 0; - } - -}; - - - -}; // namespace greenlet - -#endif diff --git a/.venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp b/.venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp deleted file mode 100644 index 37fcc8c..0000000 --- a/.venv/Lib/site-packages/greenlet/TThreadStateDestroy.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of the ThreadState destructors. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#ifndef T_THREADSTATE_DESTROY -#define T_THREADSTATE_DESTROY - -#include "TGreenlet.hpp" - -#include "greenlet_thread_support.hpp" -#include "greenlet_cpython_add_pending.hpp" -#include "greenlet_compiler_compat.hpp" -#include "TGreenletGlobals.cpp" -#include "TThreadState.hpp" -#include "TThreadStateCreator.hpp" - -namespace greenlet { - -extern "C" { - -struct ThreadState_DestroyNoGIL -{ - /** - This function uses the same lock that the PendingCallback does - */ - static void - MarkGreenletDeadAndQueueCleanup(ThreadState* const state) - { -#if GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK - return; -#endif - // We are *NOT* holding the GIL. Our thread is in the middle - // of its death throes and the Python thread state is already - // gone so we can't use most Python APIs. One that is safe is - // ``Py_AddPendingCall``, unless the interpreter itself has - // been torn down. There is a limited number of calls that can - // be queued: 32 (NPENDINGCALLS) in CPython 3.10, so we - // coalesce these calls using our own queue. - - if (!MarkGreenletDeadIfNeeded(state)) { - // No state, or no greenlet - return; - } - - // XXX: Because we don't have the GIL, this is a race condition. - if (!PyInterpreterState_Head()) { - // We have to leak the thread state, if the - // interpreter has shut down when we're getting - // deallocated, we can't run the cleanup code that - // deleting it would imply. - return; - } - - AddToCleanupQueue(state); - - } - -private: - - // If the state has an allocated main greenlet: - // - mark the greenlet as dead by disassociating it from the state; - // - return 1 - // Otherwise, return 0. - static bool - MarkGreenletDeadIfNeeded(ThreadState* const state) - { - if (state && state->has_main_greenlet()) { - // mark the thread as dead ASAP. - // this is racy! If we try to throw or switch to a - // greenlet from this thread from some other thread before - // we clear the state pointer, it won't realize the state - // is dead which can crash the process. - PyGreenlet* p(state->borrow_main_greenlet().borrow()); - assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr); - dynamic_cast(p->pimpl)->thread_state(nullptr); - return true; - } - return false; - } - - static void - AddToCleanupQueue(ThreadState* const state) - { - assert(state && state->has_main_greenlet()); - - // NOTE: Because we're not holding the GIL here, some other - // Python thread could run and call ``os.fork()``, which would - // be bad if that happened while we are holding the cleanup - // lock (it wouldn't function in the child process). - // Make a best effort to try to keep the duration we hold the - // lock short. - // TODO: On platforms that support it, use ``pthread_atfork`` to - // drop this lock. - LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); - - mod_globs->queue_to_destroy(state); - if (mod_globs->thread_states_to_destroy.size() == 1) { - // We added the first item to the queue. We need to schedule - // the cleanup. - - // A size greater than 1 means that we have already added the pending call, - // and in fact, it may be executing now. - // If it is executing, our lock makes sure that it will see the item we just added - // to the queue on its next iteration (after we release the lock) - // - // A size of 1 means there is no pending call, OR the pending call is - // currently executing, has dropped the lock, and is deleting the last item - // from the queue; its next iteration will go ahead and delete the item we just added. - // And the pending call we schedule here will have no work to do. - int result = AddPendingCall( - PendingCallback_DestroyQueueWithGIL, - nullptr); - if (result < 0) { - // Hmm, what can we do here? - fprintf(stderr, - "greenlet: WARNING: failed in call to Py_AddPendingCall; " - "expect a memory leak.\n"); - } - } - } - - static int - PendingCallback_DestroyQueueWithGIL(void* UNUSED(arg)) - { - // We're holding the GIL here, so no Python code should be able to - // run to call ``os.fork()``. - while (1) { - ThreadState* to_destroy; - { - LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock); - if (mod_globs->thread_states_to_destroy.empty()) { - break; - } - to_destroy = mod_globs->take_next_to_destroy(); - } - assert(to_destroy); - assert(to_destroy->has_main_greenlet()); - // Drop the lock while we do the actual deletion. - // This allows other calls to MarkGreenletDeadAndQueueCleanup - // to enter and add to our queue. - DestroyOneWithGIL(to_destroy); - } - return 0; - } - - static void - DestroyOneWithGIL(const ThreadState* const state) - { - // Holding the GIL. - // Passed a non-shared pointer to the actual thread state. - // state -> main greenlet - assert(state->has_main_greenlet()); - PyGreenlet* main(state->borrow_main_greenlet()); - // When we need to do cross-thread operations, we check this. - // A NULL value means the thread died some time ago. - // We do this here, rather than in a Python dealloc function - // for the greenlet, in case there's still a reference out - // there. - dynamic_cast(main->pimpl)->thread_state(nullptr); - - delete state; // Deleting this runs the destructor, DECREFs the main greenlet. - } - - // ensure this is actually defined. - static_assert(GREENLET_BROKEN_PY_ADD_PENDING == 1 || GREENLET_BROKEN_PY_ADD_PENDING == 0, - "GREENLET_BROKEN_PY_ADD_PENDING not defined correctly."); - -#if GREENLET_BROKEN_PY_ADD_PENDING - static int _push_pending_call(struct _pending_calls *pending, - int (*func)(void *), void *arg) - { - int i = pending->last; - int j = (i + 1) % NPENDINGCALLS; - if (j == pending->first) { - return -1; /* Queue full */ - } - pending->calls[i].func = func; - pending->calls[i].arg = arg; - pending->last = j; - return 0; - } - - static int AddPendingCall(int (*func)(void *), void *arg) - { - _PyRuntimeState *runtime = &_PyRuntime; - if (!runtime) { - // obviously impossible - return 0; - } - struct _pending_calls *pending = &runtime->ceval.pending; - if (!pending->lock) { - return 0; - } - int result = 0; - PyThread_acquire_lock(pending->lock, WAIT_LOCK); - if (!pending->finishing) { - result = _push_pending_call(pending, func, arg); - } - PyThread_release_lock(pending->lock); - SIGNAL_PENDING_CALLS(&runtime->ceval); - return result; - } -#else - // Python < 3.8 or >= 3.9 - static int AddPendingCall(int (*func)(void*), void* arg) - { - // If the interpreter is in the middle of finalizing, we can't add a - // pending call. Trying to do so will end up in a SIGSEGV, as - // Py_AddPendingCall will not be able to get the interpreter and will - // try to dereference a NULL pointer. It's possible this can still - // segfault if we happen to get context switched, and maybe we should - // just always implement our own AddPendingCall, but I'd like to see if - // this works first -#if GREENLET_PY313 - if (Py_IsFinalizing()) { -#else - if (_Py_IsFinalizing()) { -#endif -#ifdef GREENLET_DEBUG - // No need to log in the general case. Yes, we'll leak, - // but we're shutting down so it should be ok. - fprintf(stderr, - "greenlet: WARNING: Interpreter is finalizing. Ignoring " - "call to Py_AddPendingCall; \n"); -#endif - return 0; - } - return Py_AddPendingCall(func, arg); - } -#endif - - - - -}; -}; - -}; // namespace greenlet - -// The intent when GET_THREAD_STATE() is needed multiple times in a -// function is to take a reference to its return value in a local -// variable, to avoid the thread-local indirection. On some platforms -// (macOS), accessing a thread-local involves a function call (plus an -// initial function call in each function that uses a thread local); -// in contrast, static volatile variables are at some pre-computed -// offset. -typedef greenlet::ThreadStateCreator ThreadStateCreator; -static thread_local ThreadStateCreator g_thread_state_global; -#define GET_THREAD_STATE() g_thread_state_global - -#endif //T_THREADSTATE_DESTROY diff --git a/.venv/Lib/site-packages/greenlet/TUserGreenlet.cpp b/.venv/Lib/site-packages/greenlet/TUserGreenlet.cpp deleted file mode 100644 index 73a8133..0000000 --- a/.venv/Lib/site-packages/greenlet/TUserGreenlet.cpp +++ /dev/null @@ -1,662 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/** - * Implementation of greenlet::UserGreenlet. - * - * Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#ifndef T_USER_GREENLET_CPP -#define T_USER_GREENLET_CPP - -#include "greenlet_internal.hpp" -#include "TGreenlet.hpp" - -#include "TThreadStateDestroy.cpp" - - -namespace greenlet { -using greenlet::refs::BorrowedMainGreenlet; -greenlet::PythonAllocator UserGreenlet::allocator; - -void* UserGreenlet::operator new(size_t UNUSED(count)) -{ - return allocator.allocate(1); -} - - -void UserGreenlet::operator delete(void* ptr) -{ - return allocator.deallocate(static_cast(ptr), - 1); -} - - -UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent) - : Greenlet(p), _parent(the_parent) -{ -} - -UserGreenlet::~UserGreenlet() -{ - // Python 3.11: If we don't clear out the raw frame datastack - // when deleting an unfinished greenlet, - // TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails. - this->python_state.did_finish(nullptr); - this->tp_clear(); -} - - -const BorrowedMainGreenlet -UserGreenlet::main_greenlet() const -{ - return this->_main_greenlet; -} - - -BorrowedMainGreenlet -UserGreenlet::find_main_greenlet_in_lineage() const -{ - if (this->started()) { - assert(this->_main_greenlet); - return BorrowedMainGreenlet(this->_main_greenlet); - } - - if (!this->_parent) { - /* garbage collected greenlet in chain */ - // XXX: WHAT? - return BorrowedMainGreenlet(nullptr); - } - - return this->_parent->find_main_greenlet_in_lineage(); -} - - -/** - * CAUTION: This will allocate memory and may trigger garbage - * collection and arbitrary Python code. - */ -OwnedObject -UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state) -{ - /* The dying greenlet cannot be a parent of ts_current - because the 'parent' field chain would hold a - reference */ - UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state); - - // We don't care about the return value, only whether an - // exception happened. Whether or not an exception happens, - // we need to restore the parent in case the greenlet gets - // resurrected. - return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state); -} - -ThreadState* -UserGreenlet::thread_state() const noexcept -{ - // TODO: maybe make this throw, if the thread state isn't there? - // if (!this->main_greenlet) { - // throw std::runtime_error("No thread state"); // TODO: Better exception - // } - if (!this->_main_greenlet) { - return nullptr; - } - return this->_main_greenlet->thread_state(); -} - - -bool -UserGreenlet::was_running_in_dead_thread() const noexcept -{ - return this->_main_greenlet && !this->thread_state(); -} - -OwnedObject -UserGreenlet::g_switch() -{ - assert(this->args() || PyErr_Occurred()); - - try { - this->check_switch_allowed(); - } - catch (const PyErrOccurred&) { - this->release_args(); - throw; - } - - // Switching greenlets used to attempt to clean out ones that need - // deleted *if* we detected a thread switch. Should it still do - // that? - // An issue is that if we delete a greenlet from another thread, - // it gets queued to this thread, and ``kill_greenlet()`` switches - // back into the greenlet - - /* find the real target by ignoring dead greenlets, - and if necessary starting a greenlet. */ - switchstack_result_t err; - Greenlet* target = this; - // TODO: probably cleaner to handle the case where we do - // switch to ourself separately from the other cases. - // This can probably even further be simplified if we keep - // track of the switching_state we're going for and just call - // into g_switch() if it's not ourself. The main problem with that - // is that we would be using more stack space. - bool target_was_me = true; - bool was_initial_stub = false; - while (target) { - if (target->active()) { - if (!target_was_me) { - target->args() <<= this->args(); - assert(!this->args()); - } - err = target->g_switchstack(); - break; - } - if (!target->started()) { - // We never encounter a main greenlet that's not started. - assert(!target->main()); - UserGreenlet* real_target = static_cast(target); - assert(real_target); - void* dummymarker; - was_initial_stub = true; - if (!target_was_me) { - target->args() <<= this->args(); - assert(!this->args()); - } - try { - // This can only throw back to us while we're - // still in this greenlet. Once the new greenlet - // is bootstrapped, it has its own exception state. - err = real_target->g_initialstub(&dummymarker); - } - catch (const PyErrOccurred&) { - this->release_args(); - throw; - } - catch (const GreenletStartedWhileInPython&) { - // The greenlet was started sometime before this - // greenlet actually switched to it, i.e., - // "concurrent" calls to switch() or throw(). - // We need to retry the switch. - // Note that the current greenlet has been reset - // to this one (or we wouldn't be running!) - continue; - } - break; - } - - target = target->parent(); - target_was_me = false; - } - // The ``this`` pointer and all other stack or register based - // variables are invalid now, at least where things succeed - // above. - // But this one, probably not so much? It's not clear if it's - // safe to throw an exception at this point. - - if (err.status < 0) { - // If we get here, either g_initialstub() - // failed, or g_switchstack() failed. Either one of those - // cases SHOULD leave us in the original greenlet with a valid - // stack. - return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub); - } - - // err.the_new_current_greenlet would be the same as ``target``, - // if target wasn't probably corrupt. - return err.the_new_current_greenlet->g_switch_finish(err); -} - - - -Greenlet::switchstack_result_t -UserGreenlet::g_initialstub(void* mark) -{ - OwnedObject run; - - // We need to grab a reference to the current switch arguments - // in case we're entered concurrently during the call to - // GetAttr() and have to try again. - // We'll restore them when we return in that case. - // Scope them tightly to avoid ref leaks. - { - SwitchingArgs args(this->args()); - - /* save exception in case getattr clears it */ - PyErrPieces saved; - - /* - self.run is the object to call in the new greenlet. - This could run arbitrary python code and switch greenlets! - */ - run = this->self().PyRequireAttr(mod_globs->str_run); - /* restore saved exception */ - saved.PyErrRestore(); - - - /* recheck that it's safe to switch in case greenlet reparented anywhere above */ - this->check_switch_allowed(); - - /* by the time we got here another start could happen elsewhere, - * that means it should now be a regular switch. - * This can happen if the Python code is a subclass that implements - * __getattribute__ or __getattr__, or makes ``run`` a descriptor; - * all of those can run arbitrary code that switches back into - * this greenlet. - */ - if (this->stack_state.started()) { - // the successful switch cleared these out, we need to - // restore our version. They will be copied on up to the - // next target. - assert(!this->args()); - this->args() <<= args; - throw GreenletStartedWhileInPython(); - } - } - - // Sweet, if we got here, we have the go-ahead and will switch - // greenlets. - // Nothing we do from here on out should allow for a thread or - // greenlet switch: No arbitrary calls to Python, including - // decref'ing - -#if GREENLET_USE_CFRAME - /* OK, we need it, we're about to switch greenlets, save the state. */ - /* - See green_new(). This is a stack-allocated variable used - while *self* is in PyObject_Call(). - We want to defer copying the state info until we're sure - we need it and are in a stable place to do so. - */ - _PyCFrame trace_info; - - this->python_state.set_new_cframe(trace_info); -#endif - /* start the greenlet */ - ThreadState& thread_state = GET_THREAD_STATE().state(); - this->stack_state = StackState(mark, - thread_state.borrow_current()->stack_state); - this->python_state.set_initial_state(PyThreadState_GET()); - this->exception_state.clear(); - this->_main_greenlet = thread_state.get_main_greenlet(); - - /* perform the initial switch */ - switchstack_result_t err = this->g_switchstack(); - /* returns twice! - The 1st time with ``err == 1``: we are in the new greenlet. - This one owns a greenlet that used to be current. - The 2nd time with ``err <= 0``: back in the caller's - greenlet; this happens if the child finishes or switches - explicitly to us. Either way, the ``err`` variable is - created twice at the same memory location, but possibly - having different ``origin`` values. Note that it's not - constructed for the second time until the switch actually happens. - */ - if (err.status == 1) { - // In the new greenlet. - - // This never returns! Calling inner_bootstrap steals - // the contents of our run object within this stack frame, so - // it is not valid to do anything with it. - try { - this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(), - run.relinquish_ownership()); - } - // Getting a C++ exception here isn't good. It's probably a - // bug in the underlying greenlet, meaning it's probably a - // C++ extension. We're going to abort anyway, but try to - // display some nice information *if* possible. Some obscure - // platforms don't properly support this (old 32-bit Arm, see see - // https://github.com/python-greenlet/greenlet/issues/385); that's not - // great, but should usually be OK because, as mentioned above, we're - // terminating anyway. - // - // The catching is tested by - // ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``. - // - // PyErrOccurred can theoretically be thrown by - // inner_bootstrap() -> g_switch_finish(), but that should - // never make it back to here. It is a std::exception and - // would be caught if it is. - catch (const std::exception& e) { - std::string base = "greenlet: Unhandled C++ exception: "; - base += e.what(); - Py_FatalError(base.c_str()); - } - catch (...) { - // Some compilers/runtimes use exceptions internally. - // It appears that GCC on Linux with libstdc++ throws an - // exception internally at process shutdown time to unwind - // stacks and clean up resources. Depending on exactly - // where we are when the process exits, that could result - // in an unknown exception getting here. If we - // Py_FatalError() or abort() here, we interfere with - // orderly process shutdown. Throwing the exception on up - // is the right thing to do. - // - // gevent's ``examples/dns_mass_resolve.py`` demonstrates this. -#ifndef NDEBUG - fprintf(stderr, - "greenlet: inner_bootstrap threw unknown exception; " - "is the process terminating?\n"); -#endif - throw; - } - Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n"); - } - - - // In contrast, notice that we're keeping the origin greenlet - // around as an owned reference; we need it to call the trace - // function for the switch back into the parent. It was only - // captured at the time the switch actually happened, though, - // so we haven't been keeping an extra reference around this - // whole time. - - /* back in the parent */ - if (err.status < 0) { - /* start failed badly, restore greenlet state */ - this->stack_state = StackState(); - this->_main_greenlet.CLEAR(); - // CAUTION: This may run arbitrary Python code. - run.CLEAR(); // inner_bootstrap didn't run, we own the reference. - } - - // In the success case, the spawned code (inner_bootstrap) will - // take care of decrefing this, so we relinquish ownership so as - // to not double-decref. - - run.relinquish_ownership(); - - return err; -} - - -void -UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run) -{ - // The arguments here would be another great place for move. - // As it is, we take them as a reference so that when we clear - // them we clear what's on the stack above us. Do that NOW, and - // without using a C++ RAII object, - // so there's no way that exiting the parent frame can clear it, - // or we clear it unexpectedly. This arises in the context of the - // interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325 - //PyObject* run = _run.relinquish_ownership(); - - /* in the new greenlet */ - assert(this->thread_state()->borrow_current() == BorrowedGreenlet(this->_self)); - // C++ exceptions cannot propagate to the parent greenlet from - // here. (TODO: Do we need a catch(...) clause, perhaps on the - // function itself? ALl we could do is terminate the program.) - // NOTE: On 32-bit Windows, the call chain is extremely - // important here in ways that are subtle, having to do with - // the depth of the SEH list. The call to restore it MUST NOT - // add a new SEH handler to the list, or we'll restore it to - // the wrong thing. - this->thread_state()->restore_exception_state(); - /* stack variables from above are no good and also will not unwind! */ - // EXCEPT: That can't be true, we access run, among others, here. - - this->stack_state.set_active(); /* running */ - - // We're about to possibly run Python code again, which - // could switch back/away to/from us, so we need to grab the - // arguments locally. - SwitchingArgs args; - args <<= this->args(); - assert(!this->args()); - - // XXX: We could clear this much earlier, right? - // Or would that introduce the possibility of running Python - // code when we don't want to? - // CAUTION: This may run arbitrary Python code. - this->_run_callable.CLEAR(); - - - // The first switch we need to manually call the trace - // function here instead of in g_switch_finish, because we - // never return there. - if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) { - OwnedGreenlet trace_origin; - trace_origin = origin_greenlet; - try { - g_calltrace(tracefunc, - args ? mod_globs->event_switch : mod_globs->event_throw, - trace_origin, - this->_self); - } - catch (const PyErrOccurred&) { - /* Turn trace errors into switch throws */ - args.CLEAR(); - } - } - - // We no longer need the origin, it was only here for - // tracing. - // We may never actually exit this stack frame so we need - // to explicitly clear it. - // This could run Python code and switch. - Py_CLEAR(origin_greenlet); - - OwnedObject result; - if (!args) { - /* pending exception */ - result = NULL; - } - else { - /* call g.run(*args, **kwargs) */ - // This could result in further switches - try { - //result = run.PyCall(args.args(), args.kwargs()); - // CAUTION: Just invoking this, before the function even - // runs, may cause memory allocations, which may trigger - // GC, which may run arbitrary Python code. - result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow())); - } - catch (...) { - // Unhandled C++ exception! - - // If we declare ourselves as noexcept, if we don't catch - // this here, most platforms will just abort() the - // process. But on 64-bit Windows with older versions of - // the C runtime, this can actually corrupt memory and - // just return. We see this when compiling with the - // Windows 7.0 SDK targeting Windows Server 2008, but not - // when using the Appveyor Visual Studio 2019 image. So - // this currently only affects Python 2.7 on Windows 64. - // That is, the tests pass and the runtime aborts - // everywhere else. - // - // However, if we catch it and try to continue with a - // Python error, then all Windows 64 bit platforms corrupt - // memory. So all we can do is manually abort, hopefully - // with a good error message. (Note that the above was - // tested WITHOUT the `/EHr` switch being used at compile - // time, so MSVC may have "optimized" out important - // checking. Using that switch, we may be in a better - // place in terms of memory corruption.) But sometimes it - // can't be caught here at all, which is confusing but not - // terribly surprising; so again, the G_NOEXCEPT_WIN32 - // plus "/EHr". - // - // Hopefully the basic C stdlib is still functional enough - // for us to at least print an error. - // - // It gets more complicated than that, though, on some - // platforms, specifically at least Linux/gcc/libstdc++. They use - // an exception to unwind the stack when a background - // thread exits. (See comments about noexcept.) So this - // may not actually represent anything untoward. On those - // platforms we allow throws of this to propagate, or - // attempt to anyway. -# if defined(WIN32) || defined(_WIN32) - Py_FatalError( - "greenlet: Unhandled C++ exception from a greenlet run function. " - "Because memory is likely corrupted, terminating process."); - std::abort(); -#else - throw; -#endif - } - } - // These lines may run arbitrary code - args.CLEAR(); - Py_CLEAR(run); - - if (!result - && mod_globs->PyExc_GreenletExit.PyExceptionMatches() - && (this->args())) { - // This can happen, for example, if our only reference - // goes away after we switch back to the parent. - // See test_dealloc_switch_args_not_lost - PyErrPieces clear_error; - result <<= this->args(); - result = single_result(result); - } - this->release_args(); - this->python_state.did_finish(PyThreadState_GET()); - - result = g_handle_exit(result); - assert(this->thread_state()->borrow_current() == this->_self); - - /* jump back to parent */ - this->stack_state.set_inactive(); /* dead */ - - - // TODO: Can we decref some things here? Release our main greenlet - // and maybe parent? - for (Greenlet* parent = this->_parent; - parent; - parent = parent->parent()) { - // We need to somewhere consume a reference to - // the result; in most cases we'll never have control - // back in this stack frame again. Calling - // green_switch actually adds another reference! - // This would probably be clearer with a specific API - // to hand results to the parent. - parent->args() <<= result; - assert(!result); - // The parent greenlet now owns the result; in the - // typical case we'll never get back here to assign to - // result and thus release the reference. - try { - result = parent->g_switch(); - } - catch (const PyErrOccurred&) { - // Ignore, keep passing the error on up. - } - - /* Return here means switch to parent failed, - * in which case we throw *current* exception - * to the next parent in chain. - */ - assert(!result); - } - /* We ran out of parents, cannot continue */ - PyErr_WriteUnraisable(this->self().borrow_o()); - Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; " - "cannot continue"); - std::abort(); -} - -void -UserGreenlet::run(const BorrowedObject nrun) -{ - if (this->started()) { - throw AttributeError( - "run cannot be set " - "after the start of the greenlet"); - } - this->_run_callable = nrun; -} - -const OwnedGreenlet -UserGreenlet::parent() const -{ - return this->_parent; -} - -void -UserGreenlet::parent(const BorrowedObject raw_new_parent) -{ - if (!raw_new_parent) { - throw AttributeError("can't delete attribute"); - } - - BorrowedMainGreenlet main_greenlet_of_new_parent; - BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could - // throw - // TypeError! - for (BorrowedGreenlet p = new_parent; p; p = p->parent()) { - if (p == this->self()) { - throw ValueError("cyclic parent chain"); - } - main_greenlet_of_new_parent = p->main_greenlet(); - } - - if (!main_greenlet_of_new_parent) { - throw ValueError("parent must not be garbage collected"); - } - - if (this->started() - && this->_main_greenlet != main_greenlet_of_new_parent) { - throw ValueError("parent cannot be on a different thread"); - } - - this->_parent = new_parent; -} - -void -UserGreenlet::murder_in_place() -{ - this->_main_greenlet.CLEAR(); - Greenlet::murder_in_place(); -} - -bool -UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const -{ - return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet(); -} - - -int -UserGreenlet::tp_traverse(visitproc visit, void* arg) -{ - Py_VISIT(this->_parent.borrow_o()); - Py_VISIT(this->_main_greenlet.borrow_o()); - Py_VISIT(this->_run_callable.borrow_o()); - - return Greenlet::tp_traverse(visit, arg); -} - -int -UserGreenlet::tp_clear() -{ - Greenlet::tp_clear(); - this->_parent.CLEAR(); - this->_main_greenlet.CLEAR(); - this->_run_callable.CLEAR(); - return 0; -} - -UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p, - const ThreadState& thread_state) - : oldparent(p->_parent), - greenlet(p) -{ - p->_parent = thread_state.get_current(); -} - -UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard() -{ - this->greenlet->_parent = oldparent; - oldparent.CLEAR(); -} - -}; //namespace greenlet -#endif diff --git a/.venv/Lib/site-packages/greenlet/__init__.py b/.venv/Lib/site-packages/greenlet/__init__.py deleted file mode 100644 index b2dcc9b..0000000 --- a/.venv/Lib/site-packages/greenlet/__init__.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -""" -The root of the greenlet package. -""" -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function - -__all__ = [ - '__version__', - '_C_API', - - 'GreenletExit', - 'error', - - 'getcurrent', - 'greenlet', - - 'gettrace', - 'settrace', -] - -# pylint:disable=no-name-in-module - -### -# Metadata -### -__version__ = '3.1.1' -from ._greenlet import _C_API # pylint:disable=no-name-in-module - -### -# Exceptions -### -from ._greenlet import GreenletExit -from ._greenlet import error - -### -# greenlets -### -from ._greenlet import getcurrent -from ._greenlet import greenlet - -### -# tracing -### -try: - from ._greenlet import gettrace - from ._greenlet import settrace -except ImportError: - # Tracing wasn't supported. - # XXX: The option to disable it was removed in 1.0, - # so this branch should be dead code. - pass - -### -# Constants -# These constants aren't documented and aren't recommended. -# In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS -# is the same as ``sys.version_info[:2] >= 3.7`` -### -from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import -from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import -from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import - -# Controlling the use of the gc module. Provisional API for this greenlet -# implementation in 2.0. -from ._greenlet import CLOCKS_PER_SEC # pylint:disable=unused-import -from ._greenlet import enable_optional_cleanup # pylint:disable=unused-import -from ._greenlet import get_clocks_used_doing_optional_cleanup # pylint:disable=unused-import - -# Other APIS in the _greenlet module are for test support. diff --git a/.venv/Lib/site-packages/greenlet/_greenlet.cp311-win_amd64.pyd b/.venv/Lib/site-packages/greenlet/_greenlet.cp311-win_amd64.pyd deleted file mode 100644 index ad9a3380f0394b1b3031048efda4ec7198169f1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 218112 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 diff --git a/.venv/Lib/site-packages/greenlet/greenlet.cpp b/.venv/Lib/site-packages/greenlet/greenlet.cpp deleted file mode 100644 index e8d92a0..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -/* Format with: - * clang-format -i --style=file src/greenlet/greenlet.c - * - * - * Fix missing braces with: - * clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements" -*/ -#include -#include -#include -#include - - -#define PY_SSIZE_T_CLEAN -#include -#include "structmember.h" // PyMemberDef - -#include "greenlet_internal.hpp" -// Code after this point can assume access to things declared in stdint.h, -// including the fixed-width types. This goes for the platform-specific switch functions -// as well. -#include "greenlet_refs.hpp" -#include "greenlet_slp_switch.hpp" - -#include "greenlet_thread_support.hpp" -#include "TGreenlet.hpp" - -#include "TGreenletGlobals.cpp" - -#include "TGreenlet.cpp" -#include "TMainGreenlet.cpp" -#include "TUserGreenlet.cpp" -#include "TBrokenGreenlet.cpp" -#include "TExceptionState.cpp" -#include "TPythonState.cpp" -#include "TStackState.cpp" - -#include "TThreadState.hpp" -#include "TThreadStateCreator.hpp" -#include "TThreadStateDestroy.cpp" - -#include "PyGreenlet.cpp" -#include "PyGreenletUnswitchable.cpp" -#include "CObjects.cpp" - -using greenlet::LockGuard; -using greenlet::LockInitError; -using greenlet::PyErrOccurred; -using greenlet::Require; - -using greenlet::g_handle_exit; -using greenlet::single_result; - -using greenlet::Greenlet; -using greenlet::UserGreenlet; -using greenlet::MainGreenlet; -using greenlet::BrokenGreenlet; -using greenlet::ThreadState; -using greenlet::PythonState; - - - -// ******* Implementation of things from included files -template -greenlet::refs::_BorrowedGreenlet& greenlet::refs::_BorrowedGreenlet::operator=(const greenlet::refs::BorrowedObject& other) -{ - this->_set_raw_pointer(static_cast(other)); - return *this; -} - -template -inline greenlet::refs::_BorrowedGreenlet::operator Greenlet*() const noexcept -{ - if (!this->p) { - return nullptr; - } - return reinterpret_cast(this->p)->pimpl; -} - -template -greenlet::refs::_BorrowedGreenlet::_BorrowedGreenlet(const BorrowedObject& p) - : BorrowedReference(nullptr) -{ - - this->_set_raw_pointer(p.borrow()); -} - -template -inline greenlet::refs::_OwnedGreenlet::operator Greenlet*() const noexcept -{ - if (!this->p) { - return nullptr; - } - return reinterpret_cast(this->p)->pimpl; -} - - - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wmissing-field-initializers" -# pragma clang diagnostic ignored "-Wwritable-strings" -#elif defined(__GNUC__) -# pragma GCC diagnostic push -// warning: ISO C++ forbids converting a string constant to ‘char*’ -// (The python APIs aren't const correct and accept writable char*) -# pragma GCC diagnostic ignored "-Wwrite-strings" -#endif - - -/*********************************************************** - -A PyGreenlet is a range of C stack addresses that must be -saved and restored in such a way that the full range of the -stack contains valid data when we switch to it. - -Stack layout for a greenlet: - - | ^^^ | - | older data | - | | - stack_stop . |_______________| - . | | - . | greenlet data | - . | in stack | - . * |_______________| . . _____________ stack_copy + stack_saved - . | | | | - . | data | |greenlet data| - . | unrelated | | saved | - . | to | | in heap | - stack_start . | this | . . |_____________| stack_copy - | greenlet | - | | - | newer data | - | vvv | - - -Note that a greenlet's stack data is typically partly at its correct -place in the stack, and partly saved away in the heap, but always in -the above configuration: two blocks, the more recent one in the heap -and the older one still in the stack (either block may be empty). - -Greenlets are chained: each points to the previous greenlet, which is -the one that owns the data currently in the C stack above my -stack_stop. The currently running greenlet is the first element of -this chain. The main (initial) greenlet is the last one. Greenlets -whose stack is entirely in the heap can be skipped from the chain. - -The chain is not related to execution order, but only to the order -in which bits of C stack happen to belong to greenlets at a particular -point in time. - -The main greenlet doesn't have a stack_stop: it is responsible for the -complete rest of the C stack, and we don't know where it begins. We -use (char*) -1, the largest possible address. - -States: - stack_stop == NULL && stack_start == NULL: did not start yet - stack_stop != NULL && stack_start == NULL: already finished - stack_stop != NULL && stack_start != NULL: active - -The running greenlet's stack_start is undefined but not NULL. - - ***********************************************************/ - - - - -/***********************************************************/ - -/* Some functions must not be inlined: - * slp_restore_state, when inlined into slp_switch might cause - it to restore stack over its own local variables - * slp_save_state, when inlined would add its own local - variables to the saved stack, wasting space - * slp_switch, cannot be inlined for obvious reasons - * g_initialstub, when inlined would receive a pointer into its - own stack frame, leading to incomplete stack save/restore - -g_initialstub is a member function and declared virtual so that the -compiler always calls it through a vtable. - -slp_save_state and slp_restore_state are also member functions. They -are called from trampoline functions that themselves are declared as -not eligible for inlining. -*/ - -extern "C" { -static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref) -{ - return switching_thread_state->slp_save_state(stackref); -} -static void GREENLET_NOINLINE(slp_restore_state_trampoline)() -{ - switching_thread_state->slp_restore_state(); -} -} - - -/***********************************************************/ - - -#include "PyModule.cpp" - - - -static PyObject* -greenlet_internal_mod_init() noexcept -{ - static void* _PyGreenlet_API[PyGreenlet_API_pointers]; - - try { - CreatedModule m(greenlet_module_def); - - Require(PyType_Ready(&PyGreenlet_Type)); - Require(PyType_Ready(&PyGreenletUnswitchable_Type)); - - mod_globs = new greenlet::GreenletGlobals; - ThreadState::init(); - - m.PyAddObject("greenlet", PyGreenlet_Type); - m.PyAddObject("UnswitchableGreenlet", PyGreenletUnswitchable_Type); - m.PyAddObject("error", mod_globs->PyExc_GreenletError); - m.PyAddObject("GreenletExit", mod_globs->PyExc_GreenletExit); - - m.PyAddObject("GREENLET_USE_GC", 1); - m.PyAddObject("GREENLET_USE_TRACING", 1); - m.PyAddObject("GREENLET_USE_CONTEXT_VARS", 1L); - m.PyAddObject("GREENLET_USE_STANDARD_THREADING", 1L); - - OwnedObject clocks_per_sec = OwnedObject::consuming(PyLong_FromSsize_t(CLOCKS_PER_SEC)); - m.PyAddObject("CLOCKS_PER_SEC", clocks_per_sec); - - /* also publish module-level data as attributes of the greentype. */ - // XXX: This is weird, and enables a strange pattern of - // confusing the class greenlet with the module greenlet; with - // the exception of (possibly) ``getcurrent()``, this - // shouldn't be encouraged so don't add new items here. - for (const char* const* p = copy_on_greentype; *p; p++) { - OwnedObject o = m.PyRequireAttr(*p); - PyDict_SetItemString(PyGreenlet_Type.tp_dict, *p, o.borrow()); - } - - /* - * Expose C API - */ - - /* types */ - _PyGreenlet_API[PyGreenlet_Type_NUM] = (void*)&PyGreenlet_Type; - - /* exceptions */ - _PyGreenlet_API[PyExc_GreenletError_NUM] = (void*)mod_globs->PyExc_GreenletError; - _PyGreenlet_API[PyExc_GreenletExit_NUM] = (void*)mod_globs->PyExc_GreenletExit; - - /* methods */ - _PyGreenlet_API[PyGreenlet_New_NUM] = (void*)PyGreenlet_New; - _PyGreenlet_API[PyGreenlet_GetCurrent_NUM] = (void*)PyGreenlet_GetCurrent; - _PyGreenlet_API[PyGreenlet_Throw_NUM] = (void*)PyGreenlet_Throw; - _PyGreenlet_API[PyGreenlet_Switch_NUM] = (void*)PyGreenlet_Switch; - _PyGreenlet_API[PyGreenlet_SetParent_NUM] = (void*)PyGreenlet_SetParent; - - /* Previously macros, but now need to be functions externally. */ - _PyGreenlet_API[PyGreenlet_MAIN_NUM] = (void*)Extern_PyGreenlet_MAIN; - _PyGreenlet_API[PyGreenlet_STARTED_NUM] = (void*)Extern_PyGreenlet_STARTED; - _PyGreenlet_API[PyGreenlet_ACTIVE_NUM] = (void*)Extern_PyGreenlet_ACTIVE; - _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM] = (void*)Extern_PyGreenlet_GET_PARENT; - - /* XXX: Note that our module name is ``greenlet._greenlet``, but for - backwards compatibility with existing C code, we need the _C_API to - be directly in greenlet. - */ - const NewReference c_api_object(Require( - PyCapsule_New( - (void*)_PyGreenlet_API, - "greenlet._C_API", - NULL))); - m.PyAddObject("_C_API", c_api_object); - assert(c_api_object.REFCNT() == 2); - - // cerr << "Sizes:" - // << "\n\tGreenlet : " << sizeof(Greenlet) - // << "\n\tUserGreenlet : " << sizeof(UserGreenlet) - // << "\n\tMainGreenlet : " << sizeof(MainGreenlet) - // << "\n\tExceptionState : " << sizeof(greenlet::ExceptionState) - // << "\n\tPythonState : " << sizeof(greenlet::PythonState) - // << "\n\tStackState : " << sizeof(greenlet::StackState) - // << "\n\tSwitchingArgs : " << sizeof(greenlet::SwitchingArgs) - // << "\n\tOwnedObject : " << sizeof(greenlet::refs::OwnedObject) - // << "\n\tBorrowedObject : " << sizeof(greenlet::refs::BorrowedObject) - // << "\n\tPyGreenlet : " << sizeof(PyGreenlet) - // << endl; - - return m.borrow(); // But really it's the main reference. - } - catch (const LockInitError& e) { - PyErr_SetString(PyExc_MemoryError, e.what()); - return NULL; - } - catch (const PyErrOccurred&) { - return NULL; - } - -} - -extern "C" { - -PyMODINIT_FUNC -PyInit__greenlet(void) -{ - return greenlet_internal_mod_init(); -} - -}; // extern C - -#ifdef __clang__ -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet.h b/.venv/Lib/site-packages/greenlet/greenlet.h deleted file mode 100644 index d02a16e..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ - -/* Greenlet object interface */ - -#ifndef Py_GREENLETOBJECT_H -#define Py_GREENLETOBJECT_H - - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is deprecated and undocumented. It does not change. */ -#define GREENLET_VERSION "1.0.0" - -#ifndef GREENLET_MODULE -#define implementation_ptr_t void* -#endif - -typedef struct _greenlet { - PyObject_HEAD - PyObject* weakreflist; - PyObject* dict; - implementation_ptr_t pimpl; -} PyGreenlet; - -#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) - - -/* C API functions */ - -/* Total number of symbols that are exported */ -#define PyGreenlet_API_pointers 12 - -#define PyGreenlet_Type_NUM 0 -#define PyExc_GreenletError_NUM 1 -#define PyExc_GreenletExit_NUM 2 - -#define PyGreenlet_New_NUM 3 -#define PyGreenlet_GetCurrent_NUM 4 -#define PyGreenlet_Throw_NUM 5 -#define PyGreenlet_Switch_NUM 6 -#define PyGreenlet_SetParent_NUM 7 - -#define PyGreenlet_MAIN_NUM 8 -#define PyGreenlet_STARTED_NUM 9 -#define PyGreenlet_ACTIVE_NUM 10 -#define PyGreenlet_GET_PARENT_NUM 11 - -#ifndef GREENLET_MODULE -/* This section is used by modules that uses the greenlet C API */ -static void** _PyGreenlet_API = NULL; - -# define PyGreenlet_Type \ - (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) - -# define PyExc_GreenletError \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) - -# define PyExc_GreenletExit \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) - -/* - * PyGreenlet_New(PyObject *args) - * - * greenlet.greenlet(run, parent=None) - */ -# define PyGreenlet_New \ - (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ - _PyGreenlet_API[PyGreenlet_New_NUM]) - -/* - * PyGreenlet_GetCurrent(void) - * - * greenlet.getcurrent() - */ -# define PyGreenlet_GetCurrent \ - (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) - -/* - * PyGreenlet_Throw( - * PyGreenlet *greenlet, - * PyObject *typ, - * PyObject *val, - * PyObject *tb) - * - * g.throw(...) - */ -# define PyGreenlet_Throw \ - (*(PyObject * (*)(PyGreenlet * self, \ - PyObject * typ, \ - PyObject * val, \ - PyObject * tb)) \ - _PyGreenlet_API[PyGreenlet_Throw_NUM]) - -/* - * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) - * - * g.switch(*args, **kwargs) - */ -# define PyGreenlet_Switch \ - (*(PyObject * \ - (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ - _PyGreenlet_API[PyGreenlet_Switch_NUM]) - -/* - * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) - * - * g.parent = new_parent - */ -# define PyGreenlet_SetParent \ - (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ - _PyGreenlet_API[PyGreenlet_SetParent_NUM]) - -/* - * PyGreenlet_GetParent(PyObject* greenlet) - * - * return greenlet.parent; - * - * This could return NULL even if there is no exception active. - * If it does not return NULL, you are responsible for decrementing the - * reference count. - */ -# define PyGreenlet_GetParent \ - (*(PyGreenlet* (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) - -/* - * deprecated, undocumented alias. - */ -# define PyGreenlet_GET_PARENT PyGreenlet_GetParent - -# define PyGreenlet_MAIN \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_MAIN_NUM]) - -# define PyGreenlet_STARTED \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_STARTED_NUM]) - -# define PyGreenlet_ACTIVE \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) - - - - -/* Macro that imports greenlet and initializes C API */ -/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we - keep the older definition to be sure older code that might have a copy of - the header still works. */ -# define PyGreenlet_Import() \ - { \ - _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ - } - -#endif /* GREENLET_MODULE */ - -#ifdef __cplusplus -} -#endif -#endif /* !Py_GREENLETOBJECT_H */ diff --git a/.venv/Lib/site-packages/greenlet/greenlet_allocator.hpp b/.venv/Lib/site-packages/greenlet/greenlet_allocator.hpp deleted file mode 100644 index b452f54..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_allocator.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef GREENLET_ALLOCATOR_HPP -#define GREENLET_ALLOCATOR_HPP - -#define PY_SSIZE_T_CLEAN -#include -#include -#include "greenlet_compiler_compat.hpp" - - -namespace greenlet -{ - // This allocator is stateless; all instances are identical. - // It can *ONLY* be used when we're sure we're holding the GIL - // (Python's allocators require the GIL). - template - struct PythonAllocator : public std::allocator { - - PythonAllocator(const PythonAllocator& UNUSED(other)) - : std::allocator() - { - } - - PythonAllocator(const std::allocator other) - : std::allocator(other) - {} - - template - PythonAllocator(const std::allocator& other) - : std::allocator(other) - { - } - - PythonAllocator() : std::allocator() {} - - T* allocate(size_t number_objects, const void* UNUSED(hint)=0) - { - void* p; - if (number_objects == 1) - p = PyObject_Malloc(sizeof(T)); - else - p = PyMem_Malloc(sizeof(T) * number_objects); - return static_cast(p); - } - - void deallocate(T* t, size_t n) - { - void* p = t; - if (n == 1) { - PyObject_Free(p); - } - else - PyMem_Free(p); - } - // This member is deprecated in C++17 and removed in C++20 - template< class U > - struct rebind { - typedef PythonAllocator other; - }; - - }; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp b/.venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp deleted file mode 100644 index af24bd8..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_compiler_compat.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -#ifndef GREENLET_COMPILER_COMPAT_HPP -#define GREENLET_COMPILER_COMPAT_HPP - -/** - * Definitions to aid with compatibility with different compilers. - * - * .. caution:: Use extreme care with noexcept. - * Some compilers and runtimes, specifically gcc/libgcc/libstdc++ on - * Linux, implement stack unwinding by throwing an uncatchable - * exception, one that specifically does not appear to be an active - * exception to the rest of the runtime. If this happens while we're in a noexcept function, - * we have violated our dynamic exception contract, and so the runtime - * will call std::terminate(), which kills the process with the - * unhelpful message "terminate called without an active exception". - * - * This has happened in this scenario: A background thread is running - * a greenlet that has made a native call and released the GIL. - * Meanwhile, the main thread finishes and starts shutting down the - * interpreter. When the background thread is scheduled again and - * attempts to obtain the GIL, it notices that the interpreter is - * exiting and calls ``pthread_exit()``. This in turn starts to unwind - * the stack by throwing that exception. But we had the ``PyCall`` - * functions annotated as noexcept, so the runtime terminated us. - * - * #2 0x00007fab26fec2b7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6 - * #3 0x00007fab26febb3c in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6 - * #4 0x00007fab26f34de6 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1 - * #6 0x00007fab276a34c6 in __GI___pthread_unwind at ./nptl/unwind.c:130 - * #7 0x00007fab2769bd3a in __do_cancel () at ../sysdeps/nptl/pthreadP.h:280 - * #8 __GI___pthread_exit (value=value@entry=0x0) at ./nptl/pthread_exit.c:36 - * #9 0x000000000052e567 in PyThread_exit_thread () at ../Python/thread_pthread.h:370 - * #10 0x00000000004d60b5 in take_gil at ../Python/ceval_gil.h:224 - * #11 0x00000000004d65f9 in PyEval_RestoreThread at ../Python/ceval.c:467 - * #12 0x000000000060cce3 in setipaddr at ../Modules/socketmodule.c:1203 - * #13 0x00000000006101cd in socket_gethostbyname - */ - -#include - -# define G_NO_COPIES_OF_CLS(Cls) private: \ - Cls(const Cls& other) = delete; \ - Cls& operator=(const Cls& other) = delete - -# define G_NO_ASSIGNMENT_OF_CLS(Cls) private: \ - Cls& operator=(const Cls& other) = delete - -# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \ - Cls(const Cls& other) = delete; - - -// CAUTION: MSVC is stupidly picky: -// -// "The compiler ignores, without warning, any __declspec keywords -// placed after * or & and in front of the variable identifier in a -// declaration." -// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160) -// -// So pointer return types must be handled differently (because of the -// trailing *), or you get inscrutable compiler warnings like "error -// C2059: syntax error: ''" -// -// In C++ 11, there is a standard syntax for attributes, and -// GCC defines an attribute to use with this: [[gnu:noinline]]. -// In the future, this is expected to become standard. - -#if defined(__GNUC__) || defined(__clang__) -/* We used to check for GCC 4+ or 3.4+, but those compilers are - laughably out of date. Just assume they support it. */ -# define GREENLET_NOINLINE(name) __attribute__((noinline)) name -# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name -# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) -#elif defined(_MSC_VER) -/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */ -# define GREENLET_NOINLINE(name) __declspec(noinline) name -# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name -# define UNUSED(x) UNUSED_ ## x -#endif - -#if defined(_MSC_VER) -# define G_NOEXCEPT_WIN32 noexcept -#else -# define G_NOEXCEPT_WIN32 -#endif - -#if defined(__GNUC__) && defined(__POWERPC__) && defined(__APPLE__) -// 32-bit PPC/MacOSX. Only known to be tested on unreleased versions -// of macOS 10.6 using a macports build gcc 14. It appears that -// running C++ destructors of thread-local variables is broken. - -// See https://github.com/python-greenlet/greenlet/pull/419 -# define GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK 1 -#else -# define GREENLET_BROKEN_THREAD_LOCAL_CLEANUP_JUST_LEAK 0 -#endif - - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_cpython_add_pending.hpp b/.venv/Lib/site-packages/greenlet/greenlet_cpython_add_pending.hpp deleted file mode 100644 index 0d28efd..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_cpython_add_pending.hpp +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef GREENLET_CPYTHON_ADD_PENDING_HPP -#define GREENLET_CPYTHON_ADD_PENDING_HPP - -#if (PY_VERSION_HEX >= 0x30800A0 && PY_VERSION_HEX < 0x3090000) && !(defined(_WIN32) || defined(WIN32)) -// XXX: From Python 3.8a3 [1] up until Python 3.9a6 [2][3], -// ``Py_AddPendingCall`` would try to produce a Python exception if -// the interpreter was in the beginning of shutting down when this -// function is called. However, ``Py_AddPendingCall`` doesn't require -// the GIL, and we are absolutely not holding it when we make that -// call. That means that trying to create the Python exception is -// using the C API in an undefined state; here the C API detects this -// and aborts the process with an error ("Fatal Python error: Python -// memory allocator called without holding the GIL": Add -> -// PyErr_SetString -> PyUnicode_New -> PyObject_Malloc). This arises -// (obviously) in multi-threaded programs and happens if one thread is -// exiting and cleaning up its thread-local data while the other -// thread is trying to shut down the interpreter. A crash on shutdown -// is still a crash and could result in data loss (e.g., daemon -// threads are still running, pending signal handlers may be present, -// buffers may not be flushed, there may be __del__ that need run, -// etc), so we have to work around it. -// -// Of course, we can (and do) check for whether the interpreter is -// shutting down before calling ``Py_AddPendingCall``, but that's a -// race condition since we don't hold the GIL, and so we may not -// actually get the right answer. Plus, ``Py_FinalizeEx`` actually -// calls ``_Py_FinishPendingCalls`` (which sets the pending->finishing -// flag, which is used to gate creating the exceptioen) *before* -// publishing any other data that would let us detect the shutdown -// (such as runtime->finalizing). So that point is moot. -// -// Our solution for those versions is to inline the same code, without -// the problematic bit that sets the exception. Unfortunately, all of -// the structure definitions are private/opaque, *and* we can't -// actually count on being able to include their definitions from -// ``internal/pycore_*``, because on some platforms those header files -// are incomplete (i.e., on macOS with macports 3.8, the includes are -// fine, but on Ubuntu jammy with 3.8 from ppa:deadsnakes or GitHub -// Actions 3.8 (I think it's Ubuntu 18.04), they con't be used; at -// least, I couldn't get them to work). So we need to define the -// structures and _PyRuntime data member ourself. Yet more -// unfortunately, _PyRuntime won't link on Windows, so we can only do -// this on other platforms. -// -// [1] https://github.com/python/cpython/commit/842a2f07f2f08a935ef470bfdaeef40f87490cfc -// [2] https://github.com/python/cpython/commit/cfc3c2f8b34d3864717ab584c5b6c260014ba55a -// [3] https://github.com/python/cpython/issues/81308 -# define GREENLET_BROKEN_PY_ADD_PENDING 1 - -// When defining these structures, the important thing is to get -// binary compatibility, i.e., structure layout. For that, we only -// need to define fields up to the ones we use; after that they're -// irrelevant UNLESS the structure is included in another structure -// *before* the structure we're interested in --- in that case, it -// must be complete. Ellipsis indicate elided trailing members. -// Pointer types are changed to void* to keep from having to define -// more structures. - -// From "internal/pycore_atomic.h" - -// There are several different definitions of this, including the -// plain ``int`` version, a ``volatile int`` and an ``_Atomic int`` -// I don't think any of those change the size/layout. -typedef struct _Py_atomic_int { - volatile int _value; -} _Py_atomic_int; - -// This needs too much infrastructure, so we just do a regular store. -#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \ - (ATOMIC_VAL)->_value = NEW_VAL - - - -// From "internal/pycore_pymem.h" -#define NUM_GENERATIONS 3 - - -struct gc_generation { - PyGC_Head head; // We already have this defined. - int threshold; - int count; -}; -struct gc_generation_stats { - Py_ssize_t collections; - Py_ssize_t collected; - Py_ssize_t uncollectable; -}; - -struct _gc_runtime_state { - void *trash_delete_later; - int trash_delete_nesting; - int enabled; - int debug; - struct gc_generation generations[NUM_GENERATIONS]; - void *generation0; - struct gc_generation permanent_generation; - struct gc_generation_stats generation_stats[NUM_GENERATIONS]; - int collecting; - void *garbage; - void *callbacks; - Py_ssize_t long_lived_total; - Py_ssize_t long_lived_pending; -}; - -// From "internal/pycore_pystate.h" -struct _pending_calls { - int finishing; - PyThread_type_lock lock; - _Py_atomic_int calls_to_do; - int async_exc; -#define NPENDINGCALLS 32 - struct { - int (*func)(void *); - void *arg; - } calls[NPENDINGCALLS]; - int first; - int last; -}; - -struct _ceval_runtime_state { - int recursion_limit; - int tracing_possible; - _Py_atomic_int eval_breaker; - _Py_atomic_int gil_drop_request; - struct _pending_calls pending; - // ... -}; - -typedef struct pyruntimestate { - int preinitializing; - int preinitialized; - int core_initialized; - int initialized; - void *finalizing; - - struct pyinterpreters { - PyThread_type_lock mutex; - void *head; - void *main; - int64_t next_id; - } interpreters; - // XXX Remove this field once we have a tp_* slot. - struct _xidregistry { - PyThread_type_lock mutex; - void *head; - } xidregistry; - - unsigned long main_thread; - -#define NEXITFUNCS 32 - void (*exitfuncs[NEXITFUNCS])(void); - int nexitfuncs; - - struct _gc_runtime_state gc; - struct _ceval_runtime_state ceval; - // ... -} _PyRuntimeState; - -#define SIGNAL_PENDING_CALLS(ceval) \ - do { \ - _Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \ - _Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \ - } while (0) - -extern _PyRuntimeState _PyRuntime; - -#else -# define GREENLET_BROKEN_PY_ADD_PENDING 0 -#endif - - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp b/.venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp deleted file mode 100644 index ce5fd88..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_cpython_compat.hpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -#ifndef GREENLET_CPYTHON_COMPAT_H -#define GREENLET_CPYTHON_COMPAT_H - -/** - * Helpers for compatibility with multiple versions of CPython. - */ - -#define PY_SSIZE_T_CLEAN -#include "Python.h" - - -#if PY_VERSION_HEX >= 0x30A00B1 -# define GREENLET_PY310 1 -#else -# define GREENLET_PY310 0 -#endif - -/* -Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member. -See https://github.com/python/cpython/pull/25276 -We have to save and restore this as well. - -Python 3.13 removed PyThreadState.cframe (GH-108035). -*/ -#if GREENLET_PY310 && PY_VERSION_HEX < 0x30D0000 -# define GREENLET_USE_CFRAME 1 -#else -# define GREENLET_USE_CFRAME 0 -#endif - - -#if PY_VERSION_HEX >= 0x30B00A4 -/* -Greenlet won't compile on anything older than Python 3.11 alpha 4 (see -https://bugs.python.org/issue46090). Summary of breaking internal changes: -- Python 3.11 alpha 1 changed how frame objects are represented internally. - - https://github.com/python/cpython/pull/30122 -- Python 3.11 alpha 3 changed how recursion limits are stored. - - https://github.com/python/cpython/pull/29524 -- Python 3.11 alpha 4 changed how exception state is stored. It also includes a - change to help greenlet save and restore the interpreter frame "data stack". - - https://github.com/python/cpython/pull/30122 - - https://github.com/python/cpython/pull/30234 -*/ -# define GREENLET_PY311 1 -#else -# define GREENLET_PY311 0 -#endif - - -#if PY_VERSION_HEX >= 0x30C0000 -# define GREENLET_PY312 1 -#else -# define GREENLET_PY312 0 -#endif - -#if PY_VERSION_HEX >= 0x30D0000 -# define GREENLET_PY313 1 -#else -# define GREENLET_PY313 0 -#endif - -#ifndef Py_SET_REFCNT -/* Py_REFCNT and Py_SIZE macros are converted to functions -https://bugs.python.org/issue39573 */ -# define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) -#endif - -#ifndef _Py_DEC_REFTOTAL -/* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by: - https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924 - - The symbol we use to replace it was removed by at least 3.12. -*/ -# ifdef Py_REF_DEBUG -# if GREENLET_PY312 -# define _Py_DEC_REFTOTAL -# else -# define _Py_DEC_REFTOTAL _Py_RefTotal-- -# endif -# else -# define _Py_DEC_REFTOTAL -# endif -#endif -// Define these flags like Cython does if we're on an old version. -#ifndef Py_TPFLAGS_CHECKTYPES - #define Py_TPFLAGS_CHECKTYPES 0 -#endif -#ifndef Py_TPFLAGS_HAVE_INDEX - #define Py_TPFLAGS_HAVE_INDEX 0 -#endif -#ifndef Py_TPFLAGS_HAVE_NEWBUFFER - #define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif - -#ifndef Py_TPFLAGS_HAVE_VERSION_TAG - #define Py_TPFLAGS_HAVE_VERSION_TAG 0 -#endif - -#define G_TPFLAGS_DEFAULT Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VERSION_TAG | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_HAVE_GC - - -#if PY_VERSION_HEX < 0x03090000 -// The official version only became available in 3.9 -# define PyObject_GC_IsTracked(o) _PyObject_GC_IS_TRACKED(o) -#endif - - -// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 -#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -static inline void PyThreadState_EnterTracing(PyThreadState *tstate) -{ - tstate->tracing++; -#if PY_VERSION_HEX >= 0x030A00A1 - tstate->cframe->use_tracing = 0; -#else - tstate->use_tracing = 0; -#endif -} -#endif - -// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 -#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) -{ - tstate->tracing--; - int use_tracing = (tstate->c_tracefunc != NULL - || tstate->c_profilefunc != NULL); -#if PY_VERSION_HEX >= 0x030A00A1 - tstate->cframe->use_tracing = use_tracing; -#else - tstate->use_tracing = use_tracing; -#endif -} -#endif - -#if !defined(Py_C_RECURSION_LIMIT) && defined(C_RECURSION_LIMIT) -# define Py_C_RECURSION_LIMIT C_RECURSION_LIMIT -#endif - -#endif /* GREENLET_CPYTHON_COMPAT_H */ diff --git a/.venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp b/.venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp deleted file mode 100644 index 617f07c..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_exceptions.hpp +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef GREENLET_EXCEPTIONS_HPP -#define GREENLET_EXCEPTIONS_HPP - -#define PY_SSIZE_T_CLEAN -#include -#include -#include - -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-function" -#endif - -namespace greenlet { - - class PyErrOccurred : public std::runtime_error - { - public: - - // CAUTION: In debug builds, may run arbitrary Python code. - static const PyErrOccurred - from_current() - { - assert(PyErr_Occurred()); -#ifndef NDEBUG - // This is not exception safe, and - // not necessarily safe in general (what if it switches?) - // But we only do this in debug mode, where we are in - // tight control of what exceptions are getting raised and - // can prevent those issues. - - // You can't call PyObject_Str with a pending exception. - PyObject* typ; - PyObject* val; - PyObject* tb; - - PyErr_Fetch(&typ, &val, &tb); - PyObject* typs = PyObject_Str(typ); - PyObject* vals = PyObject_Str(val ? val : typ); - const char* typ_msg = PyUnicode_AsUTF8(typs); - const char* val_msg = PyUnicode_AsUTF8(vals); - PyErr_Restore(typ, val, tb); - - std::string msg(typ_msg); - msg += ": "; - msg += val_msg; - PyErrOccurred ex(msg); - Py_XDECREF(typs); - Py_XDECREF(vals); - - return ex; -#else - return PyErrOccurred(); -#endif - } - - PyErrOccurred() : std::runtime_error("") - { - assert(PyErr_Occurred()); - } - - PyErrOccurred(const std::string& msg) : std::runtime_error(msg) - { - assert(PyErr_Occurred()); - } - - PyErrOccurred(PyObject* exc_kind, const char* const msg) - : std::runtime_error(msg) - { - PyErr_SetString(exc_kind, msg); - } - - PyErrOccurred(PyObject* exc_kind, const std::string msg) - : std::runtime_error(msg) - { - // This copies the c_str, so we don't have any lifetime - // issues to worry about. - PyErr_SetString(exc_kind, msg.c_str()); - } - - PyErrOccurred(PyObject* exc_kind, - const std::string msg, //This is the format - //string; that's not - //usually safe! - - PyObject* borrowed_obj_one, PyObject* borrowed_obj_two) - : std::runtime_error(msg) - { - - //This is designed specifically for the - //``check_switch_allowed`` function. - - // PyObject_Str and PyObject_Repr are safe to call with - // NULL pointers; they return the string "" in that - // case. - // This function always returns null. - PyErr_Format(exc_kind, - msg.c_str(), - borrowed_obj_one, borrowed_obj_two); - } - }; - - class TypeError : public PyErrOccurred - { - public: - TypeError(const char* const what) - : PyErrOccurred(PyExc_TypeError, what) - { - } - TypeError(const std::string what) - : PyErrOccurred(PyExc_TypeError, what) - { - } - }; - - class ValueError : public PyErrOccurred - { - public: - ValueError(const char* const what) - : PyErrOccurred(PyExc_ValueError, what) - { - } - }; - - class AttributeError : public PyErrOccurred - { - public: - AttributeError(const char* const what) - : PyErrOccurred(PyExc_AttributeError, what) - { - } - }; - - /** - * Calls `Py_FatalError` when constructed, so you can't actually - * throw this. It just makes static analysis easier. - */ - class PyFatalError : public std::runtime_error - { - public: - PyFatalError(const char* const msg) - : std::runtime_error(msg) - { - Py_FatalError(msg); - } - }; - - static inline PyObject* - Require(PyObject* p, const std::string& msg="") - { - if (!p) { - throw PyErrOccurred(msg); - } - return p; - }; - - static inline void - Require(const int retval) - { - if (retval < 0) { - throw PyErrOccurred(); - } - }; - - -}; -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_internal.hpp b/.venv/Lib/site-packages/greenlet/greenlet_internal.hpp deleted file mode 100644 index f2b15d5..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_internal.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ -#ifndef GREENLET_INTERNAL_H -#define GREENLET_INTERNAL_H -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-function" -#endif - -/** - * Implementation helpers. - * - * C++ templates and inline functions should go here. - */ -#define PY_SSIZE_T_CLEAN -#include "greenlet_compiler_compat.hpp" -#include "greenlet_cpython_compat.hpp" -#include "greenlet_exceptions.hpp" -#include "TGreenlet.hpp" -#include "greenlet_allocator.hpp" - -#include -#include - -#define GREENLET_MODULE -struct _greenlet; -typedef struct _greenlet PyGreenlet; -namespace greenlet { - - class ThreadState; - // We can't use the PythonAllocator for this, because we push to it - // from the thread state destructor, which doesn't have the GIL, - // and Python's allocators can only be called with the GIL. - typedef std::vector cleanup_queue_t; - -}; - - -#define implementation_ptr_t greenlet::Greenlet* - - -#include "greenlet.h" - -void -greenlet::refs::MainGreenletExactChecker(void *p) -{ - if (!p) { - return; - } - // We control the class of the main greenlet exactly. - if (Py_TYPE(p) != &PyGreenlet_Type) { - std::string err("MainGreenlet: Expected exactly a greenlet, not a "); - err += Py_TYPE(p)->tp_name; - throw greenlet::TypeError(err); - } - - // Greenlets from dead threads no longer respond to main() with a - // true value; so in that case we need to perform an additional - // check. - Greenlet* g = static_cast(p)->pimpl; - if (g->main()) { - return; - } - if (!dynamic_cast(g)) { - std::string err("MainGreenlet: Expected exactly a main greenlet, not a "); - err += Py_TYPE(p)->tp_name; - throw greenlet::TypeError(err); - } -} - - - -template -inline greenlet::Greenlet* greenlet::refs::_OwnedGreenlet::operator->() const noexcept -{ - return reinterpret_cast(this->p)->pimpl; -} - -template -inline greenlet::Greenlet* greenlet::refs::_BorrowedGreenlet::operator->() const noexcept -{ - return reinterpret_cast(this->p)->pimpl; -} - -#include -#include - - -extern PyTypeObject PyGreenlet_Type; - - - -/** - * Forward declarations needed in multiple files. - */ -static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs); - - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - - -#endif - -// Local Variables: -// flycheck-clang-include-path: ("../../include" "/opt/local/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10") -// End: diff --git a/.venv/Lib/site-packages/greenlet/greenlet_refs.hpp b/.venv/Lib/site-packages/greenlet/greenlet_refs.hpp deleted file mode 100644 index b7e5e3f..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_refs.hpp +++ /dev/null @@ -1,1118 +0,0 @@ -#ifndef GREENLET_REFS_HPP -#define GREENLET_REFS_HPP - -#define PY_SSIZE_T_CLEAN -#include - -#include - -//#include "greenlet_internal.hpp" -#include "greenlet_compiler_compat.hpp" -#include "greenlet_cpython_compat.hpp" -#include "greenlet_exceptions.hpp" - -struct _greenlet; -struct _PyMainGreenlet; - -typedef struct _greenlet PyGreenlet; -extern PyTypeObject PyGreenlet_Type; - - -#ifdef GREENLET_USE_STDIO -#include -using std::cerr; -using std::endl; -#endif - -namespace greenlet -{ - class Greenlet; - - namespace refs - { - // Type checkers throw a TypeError if the argument is not - // null, and isn't of the required Python type. - // (We can't use most of the defined type checkers - // like PyList_Check, etc, directly, because they are - // implemented as macros.) - typedef void (*TypeChecker)(void*); - - void - NoOpChecker(void*) - { - return; - } - - void - GreenletChecker(void *p) - { - if (!p) { - return; - } - - PyTypeObject* typ = Py_TYPE(p); - // fast, common path. (PyObject_TypeCheck is a macro or - // static inline function, and it also does a - // direct comparison of the type pointers, but its fast - // path only handles one type) - if (typ == &PyGreenlet_Type) { - return; - } - - if (!PyObject_TypeCheck(p, &PyGreenlet_Type)) { - std::string err("GreenletChecker: Expected any type of greenlet, not "); - err += Py_TYPE(p)->tp_name; - throw TypeError(err); - } - } - - void - MainGreenletExactChecker(void *p); - - template - class PyObjectPointer; - - template - class OwnedReference; - - - template - class BorrowedReference; - - typedef BorrowedReference BorrowedObject; - typedef OwnedReference OwnedObject; - - class ImmortalObject; - class ImmortalString; - - template - class _OwnedGreenlet; - - typedef _OwnedGreenlet OwnedGreenlet; - typedef _OwnedGreenlet OwnedMainGreenlet; - - template - class _BorrowedGreenlet; - - typedef _BorrowedGreenlet BorrowedGreenlet; - - void - ContextExactChecker(void *p) - { - if (!p) { - return; - } - if (!PyContext_CheckExact(p)) { - throw TypeError( - "greenlet context must be a contextvars.Context or None" - ); - } - } - - typedef OwnedReference OwnedContext; - } -} - -namespace greenlet { - - - namespace refs { - // A set of classes to make reference counting rules in python - // code explicit. - // - // Rules of use: - // (1) Functions returning a new reference that the caller of the - // function is expected to dispose of should return a - // ``OwnedObject`` object. This object automatically releases its - // reference when it goes out of scope. It works like a ``std::shared_ptr`` - // and can be copied or used as a function parameter (but don't do - // that). Note that constructing a ``OwnedObject`` from a - // PyObject* steals the reference. - // (2) Parameters to functions should be either a - // ``OwnedObject&``, or, more generally, a ``PyObjectPointer&``. - // If the function needs to create its own new reference, it can - // do so by copying to a local ``OwnedObject``. - // (3) Functions returning an existing pointer that is NOT - // incref'd, and which the caller MUST NOT decref, - // should return a ``BorrowedObject``. - - // XXX: The following two paragraphs do not hold for all platforms. - // Notably, 32-bit PPC Linux passes structs by reference, not by - // value, so this actually doesn't work. (Although that's the only - // platform that doesn't work on.) DO NOT ATTEMPT IT. The - // unfortunate consequence of that is that the slots which we - // *know* are already type safe will wind up calling the type - // checker function (when we had the slots accepting - // BorrowedGreenlet, this was bypassed), so this slows us down. - // TODO: Optimize this again. - - // For a class with a single pointer member, whose constructor - // does nothing but copy a pointer parameter into the member, and - // which can then be converted back to the pointer type, compilers - // generate code that's the same as just passing the pointer. - // That is, func(BorrowedObject x) called like ``PyObject* p = - // ...; f(p)`` has 0 overhead. Similarly, they "unpack" to the - // pointer type with 0 overhead. - // - // If there are no virtual functions, no complex inheritance (maybe?) and - // no destructor, these can be directly used as parameters in - // Python callbacks like tp_init: the layout is the same as a - // single pointer. Only subclasses with trivial constructors that - // do nothing but set the single pointer member are safe to use - // that way. - - - // This is the base class for things that can be done with a - // PyObject pointer. It assumes nothing about memory management. - // NOTE: Nothing is virtual, so subclasses shouldn't add new - // storage fields or try to override these methods. - template - class PyObjectPointer - { - public: - typedef T PyType; - protected: - T* p; - public: - PyObjectPointer(T* it=nullptr) : p(it) - { - TC(p); - } - - // We don't allow automatic casting to PyObject* at this - // level, because then we could be passed to Py_DECREF/INCREF, - // but we want nothing to do with memory management. If you - // know better, then you can use the get() method, like on a - // std::shared_ptr. Except we name it borrow() to clarify that - // if this is a reference-tracked object, the pointer you get - // back will go away when the object does. - // TODO: This should probably not exist here, but be moved - // down to relevant sub-types. - - T* borrow() const noexcept - { - return this->p; - } - - PyObject* borrow_o() const noexcept - { - return reinterpret_cast(this->p); - } - - T* operator->() const noexcept - { - return this->p; - } - - bool is_None() const noexcept - { - return this->p == Py_None; - } - - PyObject* acquire_or_None() const noexcept - { - PyObject* result = this->p ? reinterpret_cast(this->p) : Py_None; - Py_INCREF(result); - return result; - } - - explicit operator bool() const noexcept - { - return this->p != nullptr; - } - - bool operator!() const noexcept - { - return this->p == nullptr; - } - - Py_ssize_t REFCNT() const noexcept - { - return p ? Py_REFCNT(p) : -42; - } - - PyTypeObject* TYPE() const noexcept - { - return p ? Py_TYPE(p) : nullptr; - } - - inline OwnedObject PyStr() const noexcept; - inline const std::string as_str() const noexcept; - inline OwnedObject PyGetAttr(const ImmortalObject& name) const noexcept; - inline OwnedObject PyRequireAttr(const char* const name) const; - inline OwnedObject PyRequireAttr(const ImmortalString& name) const; - inline OwnedObject PyCall(const BorrowedObject& arg) const; - inline OwnedObject PyCall(PyGreenlet* arg) const ; - inline OwnedObject PyCall(PyObject* arg) const ; - // PyObject_Call(this, args, kwargs); - inline OwnedObject PyCall(const BorrowedObject args, - const BorrowedObject kwargs) const; - inline OwnedObject PyCall(const OwnedObject& args, - const OwnedObject& kwargs) const; - - protected: - void _set_raw_pointer(void* t) - { - TC(t); - p = reinterpret_cast(t); - } - void* _get_raw_pointer() const - { - return p; - } - }; - -#ifdef GREENLET_USE_STDIO - template - std::ostream& operator<<(std::ostream& os, const PyObjectPointer& s) - { - const std::type_info& t = typeid(s); - os << t.name() - << "(addr=" << s.borrow() - << ", refcnt=" << s.REFCNT() - << ", value=" << s.as_str() - << ")"; - - return os; - } -#endif - - template - inline bool operator==(const PyObjectPointer& lhs, const PyObject* const rhs) noexcept - { - return static_cast(lhs.borrow_o()) == static_cast(rhs); - } - - template - inline bool operator==(const PyObjectPointer& lhs, const PyObjectPointer& rhs) noexcept - { - return lhs.borrow_o() == rhs.borrow_o(); - } - - template - inline bool operator!=(const PyObjectPointer& lhs, - const PyObjectPointer& rhs) noexcept - { - return lhs.borrow_o() != rhs.borrow_o(); - } - - template - class OwnedReference : public PyObjectPointer - { - private: - friend class OwnedList; - - protected: - explicit OwnedReference(T* it) : PyObjectPointer(it) - { - } - - public: - - // Constructors - - static OwnedReference consuming(PyObject* p) - { - return OwnedReference(reinterpret_cast(p)); - } - - static OwnedReference owning(T* p) - { - OwnedReference result(p); - Py_XINCREF(result.p); - return result; - } - - OwnedReference() : PyObjectPointer(nullptr) - {} - - explicit OwnedReference(const PyObjectPointer<>& other) - : PyObjectPointer(nullptr) - { - T* op = other.borrow(); - TC(op); - this->p = other.borrow(); - Py_XINCREF(this->p); - } - - // It would be good to make use of the C++11 distinction - // between move and copy operations, e.g., constructing from a - // pointer should be a move operation. - // In the common case of ``OwnedObject x = Py_SomeFunction()``, - // the call to the copy constructor will be elided completely. - OwnedReference(const OwnedReference& other) - : PyObjectPointer(other.p) - { - Py_XINCREF(this->p); - } - - static OwnedReference None() - { - Py_INCREF(Py_None); - return OwnedReference(Py_None); - } - - // We can assign from exactly our type without any extra checking - OwnedReference& operator=(const OwnedReference& other) - { - Py_XINCREF(other.p); - const T* tmp = this->p; - this->p = other.p; - Py_XDECREF(tmp); - return *this; - } - - OwnedReference& operator=(const BorrowedReference other) - { - return this->operator=(other.borrow()); - } - - OwnedReference& operator=(T* const other) - { - TC(other); - Py_XINCREF(other); - T* tmp = this->p; - this->p = other; - Py_XDECREF(tmp); - return *this; - } - - // We can assign from an arbitrary reference type - // if it passes our check. - template - OwnedReference& operator=(const OwnedReference& other) - { - X* op = other.borrow(); - TC(op); - return this->operator=(reinterpret_cast(op)); - } - - inline void steal(T* other) - { - assert(this->p == nullptr); - TC(other); - this->p = other; - } - - T* relinquish_ownership() - { - T* result = this->p; - this->p = nullptr; - return result; - } - - T* acquire() const - { - // Return a new reference. - // TODO: This may go away when we have reference objects - // throughout the code. - Py_XINCREF(this->p); - return this->p; - } - - // Nothing else declares a destructor, we're the leaf, so we - // should be able to get away without virtual. - ~OwnedReference() - { - Py_CLEAR(this->p); - } - - void CLEAR() - { - Py_CLEAR(this->p); - assert(this->p == nullptr); - } - }; - - static inline - void operator<<=(PyObject*& target, OwnedObject& o) - { - target = o.relinquish_ownership(); - } - - - class NewReference : public OwnedObject - { - private: - G_NO_COPIES_OF_CLS(NewReference); - public: - // Consumes the reference. Only use this - // for API return values. - NewReference(PyObject* it) : OwnedObject(it) - { - } - }; - - class NewDictReference : public NewReference - { - private: - G_NO_COPIES_OF_CLS(NewDictReference); - public: - NewDictReference() : NewReference(PyDict_New()) - { - if (!this->p) { - throw PyErrOccurred(); - } - } - - void SetItem(const char* const key, PyObject* value) - { - Require(PyDict_SetItemString(this->p, key, value)); - } - - void SetItem(const PyObjectPointer<>& key, PyObject* value) - { - Require(PyDict_SetItem(this->p, key.borrow_o(), value)); - } - }; - - template - class _OwnedGreenlet: public OwnedReference - { - private: - protected: - _OwnedGreenlet(T* it) : OwnedReference(it) - {} - - public: - _OwnedGreenlet() : OwnedReference() - {} - - _OwnedGreenlet(const _OwnedGreenlet& other) : OwnedReference(other) - { - } - _OwnedGreenlet(OwnedMainGreenlet& other) : - OwnedReference(reinterpret_cast(other.acquire())) - { - } - _OwnedGreenlet(const BorrowedGreenlet& other); - // Steals a reference. - static _OwnedGreenlet consuming(PyGreenlet* it) - { - return _OwnedGreenlet(reinterpret_cast(it)); - } - - inline _OwnedGreenlet& operator=(const OwnedGreenlet& other) - { - return this->operator=(other.borrow()); - } - - inline _OwnedGreenlet& operator=(const BorrowedGreenlet& other); - - _OwnedGreenlet& operator=(const OwnedMainGreenlet& other) - { - PyGreenlet* owned = other.acquire(); - Py_XDECREF(this->p); - this->p = reinterpret_cast(owned); - return *this; - } - - _OwnedGreenlet& operator=(T* const other) - { - OwnedReference::operator=(other); - return *this; - } - - T* relinquish_ownership() - { - T* result = this->p; - this->p = nullptr; - return result; - } - - PyObject* relinquish_ownership_o() - { - return reinterpret_cast(relinquish_ownership()); - } - - inline Greenlet* operator->() const noexcept; - inline operator Greenlet*() const noexcept; - }; - - template - class BorrowedReference : public PyObjectPointer - { - public: - // Allow implicit creation from PyObject* pointers as we - // transition to using these classes. Also allow automatic - // conversion to PyObject* for passing to C API calls and even - // for Py_INCREF/DECREF, because we ourselves do no memory management. - BorrowedReference(T* it) : PyObjectPointer(it) - {} - - BorrowedReference(const PyObjectPointer& ref) : PyObjectPointer(ref.borrow()) - {} - - BorrowedReference() : PyObjectPointer(nullptr) - {} - - operator T*() const - { - return this->p; - } - }; - - typedef BorrowedReference BorrowedObject; - //typedef BorrowedReference BorrowedGreenlet; - - template - class _BorrowedGreenlet : public BorrowedReference - { - public: - _BorrowedGreenlet() : - BorrowedReference(nullptr) - {} - - _BorrowedGreenlet(T* it) : - BorrowedReference(it) - {} - - _BorrowedGreenlet(const BorrowedObject& it); - - _BorrowedGreenlet(const OwnedGreenlet& it) : - BorrowedReference(it.borrow()) - {} - - _BorrowedGreenlet& operator=(const BorrowedObject& other); - - // We get one of these for PyGreenlet, but one for PyObject - // is handy as well - operator PyObject*() const - { - return reinterpret_cast(this->p); - } - Greenlet* operator->() const noexcept; - operator Greenlet*() const noexcept; - }; - - typedef _BorrowedGreenlet BorrowedGreenlet; - - template - _OwnedGreenlet::_OwnedGreenlet(const BorrowedGreenlet& other) - : OwnedReference(reinterpret_cast(other.borrow())) - { - Py_XINCREF(this->p); - } - - - class BorrowedMainGreenlet - : public _BorrowedGreenlet - { - public: - BorrowedMainGreenlet(const OwnedMainGreenlet& it) : - _BorrowedGreenlet(it.borrow()) - {} - BorrowedMainGreenlet(PyGreenlet* it=nullptr) - : _BorrowedGreenlet(it) - {} - }; - - template - _OwnedGreenlet& _OwnedGreenlet::operator=(const BorrowedGreenlet& other) - { - return this->operator=(other.borrow()); - } - - - class ImmortalObject : public PyObjectPointer<> - { - private: - G_NO_ASSIGNMENT_OF_CLS(ImmortalObject); - public: - explicit ImmortalObject(PyObject* it) : PyObjectPointer<>(it) - { - } - - ImmortalObject(const ImmortalObject& other) - : PyObjectPointer<>(other.p) - { - - } - - /** - * Become the new owner of the object. Does not change the - * reference count. - */ - ImmortalObject& operator=(PyObject* it) - { - assert(this->p == nullptr); - this->p = it; - return *this; - } - - static ImmortalObject consuming(PyObject* it) - { - return ImmortalObject(it); - } - - inline operator PyObject*() const - { - return this->p; - } - }; - - class ImmortalString : public ImmortalObject - { - private: - G_NO_COPIES_OF_CLS(ImmortalString); - const char* str; - public: - ImmortalString(const char* const str) : - ImmortalObject(str ? Require(PyUnicode_InternFromString(str)) : nullptr) - { - this->str = str; - } - - inline ImmortalString& operator=(const char* const str) - { - if (!this->p) { - this->p = Require(PyUnicode_InternFromString(str)); - this->str = str; - } - else { - assert(this->str == str); - } - return *this; - } - - inline operator std::string() const - { - return this->str; - } - - }; - - class ImmortalEventName : public ImmortalString - { - private: - G_NO_COPIES_OF_CLS(ImmortalEventName); - public: - ImmortalEventName(const char* const str) : ImmortalString(str) - {} - }; - - class ImmortalException : public ImmortalObject - { - private: - G_NO_COPIES_OF_CLS(ImmortalException); - public: - ImmortalException(const char* const name, PyObject* base=nullptr) : - ImmortalObject(name - // Python 2.7 isn't const correct - ? Require(PyErr_NewException((char*)name, base, nullptr)) - : nullptr) - {} - - inline bool PyExceptionMatches() const - { - return PyErr_ExceptionMatches(this->p) > 0; - } - - }; - - template - inline OwnedObject PyObjectPointer::PyStr() const noexcept - { - if (!this->p) { - return OwnedObject(); - } - return OwnedObject::consuming(PyObject_Str(reinterpret_cast(this->p))); - } - - template - inline const std::string PyObjectPointer::as_str() const noexcept - { - // NOTE: This is not Python exception safe. - if (this->p) { - // The Python APIs return a cached char* value that's only valid - // as long as the original object stays around, and we're - // about to (probably) toss it. Hence the copy to std::string. - OwnedObject py_str = this->PyStr(); - if (!py_str) { - return "(nil)"; - } - return PyUnicode_AsUTF8(py_str.borrow()); - } - return "(nil)"; - } - - template - inline OwnedObject PyObjectPointer::PyGetAttr(const ImmortalObject& name) const noexcept - { - assert(this->p); - return OwnedObject::consuming(PyObject_GetAttr(reinterpret_cast(this->p), name)); - } - - template - inline OwnedObject PyObjectPointer::PyRequireAttr(const char* const name) const - { - assert(this->p); - return OwnedObject::consuming(Require(PyObject_GetAttrString(this->p, name), name)); - } - - template - inline OwnedObject PyObjectPointer::PyRequireAttr(const ImmortalString& name) const - { - assert(this->p); - return OwnedObject::consuming(Require( - PyObject_GetAttr( - reinterpret_cast(this->p), - name - ), - name - )); - } - - template - inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject& arg) const - { - return this->PyCall(arg.borrow()); - } - - template - inline OwnedObject PyObjectPointer::PyCall(PyGreenlet* arg) const - { - return this->PyCall(reinterpret_cast(arg)); - } - - template - inline OwnedObject PyObjectPointer::PyCall(PyObject* arg) const - { - assert(this->p); - return OwnedObject::consuming(PyObject_CallFunctionObjArgs(this->p, arg, NULL)); - } - - template - inline OwnedObject PyObjectPointer::PyCall(const BorrowedObject args, - const BorrowedObject kwargs) const - { - assert(this->p); - return OwnedObject::consuming(PyObject_Call(this->p, args, kwargs)); - } - - template - inline OwnedObject PyObjectPointer::PyCall(const OwnedObject& args, - const OwnedObject& kwargs) const - { - assert(this->p); - return OwnedObject::consuming(PyObject_Call(this->p, args.borrow(), kwargs.borrow())); - } - - inline void - ListChecker(void * p) - { - if (!p) { - return; - } - if (!PyList_Check(p)) { - throw TypeError("Expected a list"); - } - } - - class OwnedList : public OwnedReference - { - private: - G_NO_ASSIGNMENT_OF_CLS(OwnedList); - public: - // TODO: Would like to use move. - explicit OwnedList(const OwnedObject& other) - : OwnedReference(other) - { - } - - OwnedList& operator=(const OwnedObject& other) - { - if (other && PyList_Check(other.p)) { - // Valid list. Own a new reference to it, discard the - // reference to what we did own. - PyObject* new_ptr = other.p; - Py_INCREF(new_ptr); - Py_XDECREF(this->p); - this->p = new_ptr; - } - else { - // Either the other object was NULL (an error) or it - // wasn't a list. Either way, we're now invalidated. - Py_XDECREF(this->p); - this->p = nullptr; - } - return *this; - } - - inline bool empty() const - { - return PyList_GET_SIZE(p) == 0; - } - - inline Py_ssize_t size() const - { - return PyList_GET_SIZE(p); - } - - inline BorrowedObject at(const Py_ssize_t index) const - { - return PyList_GET_ITEM(p, index); - } - - inline void clear() - { - PyList_SetSlice(p, 0, PyList_GET_SIZE(p), NULL); - } - }; - - // Use this to represent the module object used at module init - // time. - // This could either be a borrowed (Py2) or new (Py3) reference; - // either way, we don't want to do any memory management - // on it here, Python itself will handle that. - // XXX: Actually, that's not quite right. On Python 3, if an - // exception occurs before we return to the interpreter, this will - // leak; but all previous versions also had that problem. - class CreatedModule : public PyObjectPointer<> - { - private: - G_NO_COPIES_OF_CLS(CreatedModule); - public: - CreatedModule(PyModuleDef& mod_def) : PyObjectPointer<>( - Require(PyModule_Create(&mod_def))) - { - } - - // PyAddObject(): Add a reference to the object to the module. - // On return, the reference count of the object is unchanged. - // - // The docs warn that PyModule_AddObject only steals the - // reference on success, so if it fails after we've incref'd - // or allocated, we're responsible for the decref. - void PyAddObject(const char* name, const long new_bool) - { - OwnedObject p = OwnedObject::consuming(Require(PyBool_FromLong(new_bool))); - this->PyAddObject(name, p); - } - - void PyAddObject(const char* name, const OwnedObject& new_object) - { - // The caller already owns a reference they will decref - // when their variable goes out of scope, we still need to - // incref/decref. - this->PyAddObject(name, new_object.borrow()); - } - - void PyAddObject(const char* name, const ImmortalObject& new_object) - { - this->PyAddObject(name, new_object.borrow()); - } - - void PyAddObject(const char* name, PyTypeObject& type) - { - this->PyAddObject(name, reinterpret_cast(&type)); - } - - void PyAddObject(const char* name, PyObject* new_object) - { - Py_INCREF(new_object); - try { - Require(PyModule_AddObject(this->p, name, new_object)); - } - catch (const PyErrOccurred&) { - Py_DECREF(p); - throw; - } - } - }; - - class PyErrFetchParam : public PyObjectPointer<> - { - // Not an owned object, because we can't be initialized with - // one, and we only sometimes acquire ownership. - private: - G_NO_COPIES_OF_CLS(PyErrFetchParam); - public: - // To allow declaring these and passing them to - // PyErr_Fetch we implement the empty constructor, - // and the address operator. - PyErrFetchParam() : PyObjectPointer<>(nullptr) - { - } - - PyObject** operator&() - { - return &this->p; - } - - // This allows us to pass one directly without the &, - // BUT it has higher precedence than the bool operator - // if it's not explicit. - operator PyObject**() - { - return &this->p; - } - - // We don't want to be able to pass these to Py_DECREF and - // such so we don't have the implicit PyObject* conversion. - - inline PyObject* relinquish_ownership() - { - PyObject* result = this->p; - this->p = nullptr; - return result; - } - - ~PyErrFetchParam() - { - Py_XDECREF(p); - } - }; - - class OwnedErrPiece : public OwnedObject - { - private: - - public: - // Unlike OwnedObject, this increments the refcount. - OwnedErrPiece(PyObject* p=nullptr) : OwnedObject(p) - { - this->acquire(); - } - - PyObject** operator&() - { - return &this->p; - } - - inline operator PyObject*() const - { - return this->p; - } - - operator PyTypeObject*() const - { - return reinterpret_cast(this->p); - } - }; - - class PyErrPieces - { - private: - OwnedErrPiece type; - OwnedErrPiece instance; - OwnedErrPiece traceback; - bool restored; - public: - // Takes new references; if we're destroyed before - // restoring the error, we drop the references. - PyErrPieces(PyObject* t, PyObject* v, PyObject* tb) : - type(t), - instance(v), - traceback(tb), - restored(0) - { - this->normalize(); - } - - PyErrPieces() : - restored(0) - { - // PyErr_Fetch transfers ownership to us, so - // we don't actually need to INCREF; but we *do* - // need to DECREF if we're not restored. - PyErrFetchParam t, v, tb; - PyErr_Fetch(&t, &v, &tb); - type.steal(t.relinquish_ownership()); - instance.steal(v.relinquish_ownership()); - traceback.steal(tb.relinquish_ownership()); - } - - void PyErrRestore() - { - // can only do this once - assert(!this->restored); - this->restored = true; - PyErr_Restore( - this->type.relinquish_ownership(), - this->instance.relinquish_ownership(), - this->traceback.relinquish_ownership()); - assert(!this->type && !this->instance && !this->traceback); - } - - private: - void normalize() - { - // First, check the traceback argument, replacing None, - // with NULL - if (traceback.is_None()) { - traceback = nullptr; - } - - if (traceback && !PyTraceBack_Check(traceback.borrow())) { - throw PyErrOccurred(PyExc_TypeError, - "throw() third argument must be a traceback object"); - } - - if (PyExceptionClass_Check(type)) { - // If we just had a type, we'll now have a type and - // instance. - // The type's refcount will have gone up by one - // because of the instance and the instance will have - // a refcount of one. Either way, we owned, and still - // do own, exactly one reference. - PyErr_NormalizeException(&type, &instance, &traceback); - - } - else if (PyExceptionInstance_Check(type)) { - /* Raising an instance --- usually that means an - object that is a subclass of BaseException, but on - Python 2, that can also mean an arbitrary old-style - object. The value should be a dummy. */ - if (instance && !instance.is_None()) { - throw PyErrOccurred( - PyExc_TypeError, - "instance exception may not have a separate value"); - } - /* Normalize to raise , */ - this->instance = this->type; - this->type = PyExceptionInstance_Class(instance.borrow()); - - /* - It would be tempting to do this: - - Py_ssize_t type_count = Py_REFCNT(Py_TYPE(instance.borrow())); - this->type = PyExceptionInstance_Class(instance.borrow()); - assert(this->type.REFCNT() == type_count + 1); - - But that doesn't work on Python 2 in the case of - old-style instances: The result of Py_TYPE is going to - be the global shared that all - old-style classes have, while the return of Instance_Class() - will be the Python-level class object. The two are unrelated. - */ - } - else { - /* Not something you can raise. throw() fails. */ - PyErr_Format(PyExc_TypeError, - "exceptions must be classes, or instances, not %s", - Py_TYPE(type.borrow())->tp_name); - throw PyErrOccurred(); - } - } - }; - - // PyArg_Parse's O argument returns a borrowed reference. - class PyArgParseParam : public BorrowedObject - { - private: - G_NO_COPIES_OF_CLS(PyArgParseParam); - public: - explicit PyArgParseParam(PyObject* p=nullptr) : BorrowedObject(p) - { - } - - inline PyObject** operator&() - { - return &this->p; - } - }; - -};}; - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp b/.venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp deleted file mode 100644 index bd4b7ae..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_slp_switch.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef GREENLET_SLP_SWITCH_HPP -#define GREENLET_SLP_SWITCH_HPP - -#include "greenlet_compiler_compat.hpp" -#include "greenlet_refs.hpp" - -/* - * the following macros are spliced into the OS/compiler - * specific code, in order to simplify maintenance. - */ -// We can save about 10% of the time it takes to switch greenlets if -// we thread the thread state through the slp_save_state() and the -// following slp_restore_state() calls from -// slp_switch()->g_switchstack() (which already needs to access it). -// -// However: -// -// that requires changing the prototypes and implementations of the -// switching functions. If we just change the prototype of -// slp_switch() to accept the argument and update the macros, without -// changing the implementation of slp_switch(), we get crashes on -// 64-bit Linux and 32-bit x86 (for reasons that aren't 100% clear); -// on the other hand, 64-bit macOS seems to be fine. Also, 64-bit -// windows is an issue because slp_switch is written fully in assembly -// and currently ignores its argument so some code would have to be -// adjusted there to pass the argument on to the -// ``slp_save_state_asm()`` function (but interestingly, because of -// the calling convention, the extra argument is just ignored and -// things function fine, albeit slower, if we just modify -// ``slp_save_state_asm`()` to fetch the pointer to pass to the -// macro.) -// -// Our compromise is to use a *glabal*, untracked, weak, pointer -// to the necessary thread state during the process of switching only. -// This is safe because we're protected by the GIL, and if we're -// running this code, the thread isn't exiting. This also nets us a -// 10-12% speed improvement. - -static greenlet::Greenlet* volatile switching_thread_state = nullptr; - - -extern "C" { -static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref); -static void GREENLET_NOINLINE(slp_restore_state_trampoline)(); -} - - -#define SLP_SAVE_STATE(stackref, stsizediff) \ -do { \ - assert(switching_thread_state); \ - stackref += STACK_MAGIC; \ - if (slp_save_state_trampoline((char*)stackref)) \ - return -1; \ - if (!switching_thread_state->active()) \ - return 1; \ - stsizediff = switching_thread_state->stack_start() - (char*)stackref; \ -} while (0) - -#define SLP_RESTORE_STATE() slp_restore_state_trampoline() - -#define SLP_EVAL -extern "C" { -#define slp_switch GREENLET_NOINLINE(slp_switch) -#include "slp_platformselect.h" -} -#undef slp_switch - -#ifndef STACK_MAGIC -# error \ - "greenlet needs to be ported to this platform, or taught how to detect your compiler properly." -#endif /* !STACK_MAGIC */ - - - -#ifdef EXTERNAL_ASM -/* CCP addition: Make these functions, to be called from assembler. - * The token include file for the given platform should enable the - * EXTERNAL_ASM define so that this is included. - */ -extern "C" { -intptr_t -slp_save_state_asm(intptr_t* ref) -{ - intptr_t diff; - SLP_SAVE_STATE(ref, diff); - return diff; -} - -void -slp_restore_state_asm(void) -{ - SLP_RESTORE_STATE(); -} - -extern int slp_switch(void); -}; -#endif - -#endif diff --git a/.venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp b/.venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp deleted file mode 100644 index 3ded7d2..0000000 --- a/.venv/Lib/site-packages/greenlet/greenlet_thread_support.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef GREENLET_THREAD_SUPPORT_HPP -#define GREENLET_THREAD_SUPPORT_HPP - -/** - * Defines various utility functions to help greenlet integrate well - * with threads. This used to be needed when we supported Python - * 2.7 on Windows, which used a very old compiler. We wrote an - * alternative implementation using Python APIs and POSIX or Windows - * APIs, but that's no longer needed. So this file is a shadow of its - * former self --- but may be needed in the future. - */ - -#include -#include -#include - -#include "greenlet_compiler_compat.hpp" - -namespace greenlet { - typedef std::mutex Mutex; - typedef std::lock_guard LockGuard; - class LockInitError : public std::runtime_error - { - public: - LockInitError(const char* what) : std::runtime_error(what) - {}; - }; -}; - - -#endif /* GREENLET_THREAD_SUPPORT_HPP */ diff --git a/.venv/Lib/site-packages/greenlet/platform/__init__.py b/.venv/Lib/site-packages/greenlet/platform/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd b/.venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd deleted file mode 100644 index 038ced2..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/setup_switch_x64_masm.cmd +++ /dev/null @@ -1,2 +0,0 @@ -call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64 -ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h deleted file mode 100644 index 058617c..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_aarch64_gcc.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall - * 13-Apr-13 Add support for strange GCC caller-save decisions - * 08-Apr-13 File creation. Michael Matz - * - * NOTES - * - * Simply save all callee saved registers - * - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 -#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \ - "x27", "x28", "x30" /* aka lr */, \ - "v8", "v9", "v10", "v11", \ - "v12", "v13", "v14", "v15" - -/* - * Recall: - asm asm-qualifiers ( AssemblerTemplate - : OutputOperands - [ : InputOperands - [ : Clobbers ] ]) - - or (if asm-qualifiers contains 'goto') - - asm asm-qualifiers ( AssemblerTemplate - : OutputOperands - : InputOperands - : Clobbers - : GotoLabels) - - and OutputOperands are - - [ [asmSymbolicName] ] constraint (cvariablename) - - When a name is given, refer to it as ``%[the name]``. - When not given, ``%i`` where ``i`` is the zero-based index. - - constraints starting with ``=`` means only writing; ``+`` means - reading and writing. - - This is followed by ``r`` (must be register) or ``m`` (must be memory) - and these can be combined. - - The ``cvariablename`` is actually an lvalue expression. - - In AArch65, 31 general purpose registers. If named X0... they are - 64-bit. If named W0... they are the bottom 32 bits of the - corresponding 64 bit register. - - XZR and WZR are hardcoded to 0, and ignore writes. - - Arguments are in X0..X7. C++ uses X0 for ``this``. X0 holds simple return - values (?) - - Whenever a W register is written, the top half of the X register is zeroed. - */ - -static int -slp_switch(void) -{ - int err; - void *fp; - /* Windowz uses a 32-bit long on a 64-bit platform, unlike the rest of - the world, and in theory we can be compiled with GCC/llvm on 64-bit - windows. So we need a fixed-width type. - */ - int64_t *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("str x29, %0" : "=m"(fp) : : ); - __asm__ ("mov %0, sp" : "=r" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add sp,sp,%0\n" - "add x29,x29,%0\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - /* SLP_SAVE_STATE macro contains some return statements - (of -1 and 1). It falls through only when - the return value of slp_save_state() is zero, which - is placed in x0. - In that case we (slp_switch) also want to return zero - (also in x0 of course). - Now, some GCC versions (seen with 4.8) think it's a - good idea to save/restore x0 around the call to - slp_restore_state(), instead of simply zeroing it - at the return below. But slp_restore_state - writes random values to the stack slot used for this - save/restore (from when it once was saved above in - SLP_SAVE_STATE, when it was still uninitialized), so - "restoring" that precious zero actually makes us - return random values. There are some ways to make - GCC not use that zero value in the normal return path - (e.g. making err volatile, but that costs a little - stack space), and the simplest is to call a function - that returns an unknown value (which happens to be zero), - so the saved/restored value is unused. - - Thus, this line stores a 0 into the ``err`` variable - (which must be held in a register for this instruction, - of course). The ``w`` qualifier causes the instruction - to use W0 instead of X0, otherwise we get a warning - about a value size mismatch (because err is an int, - and aarch64 platforms are LP64: 32-bit int, 64 bit long - and pointer). - */ - __asm__ volatile ("mov %w0, #0" : "=r" (err)); - } - __asm__ volatile ("ldr x29, %0" : : "m" (fp) :); - __asm__ volatile ("" : : : REGS_TO_SAVE); - return err; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h deleted file mode 100644 index 7e07abf..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_alpha_unix.h +++ /dev/null @@ -1,30 +0,0 @@ -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \ - "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9" - -static int -slp_switch(void) -{ - int ret; - long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("mov $30, %0" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addq $30, %0, $30\n\t" - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("mov $31, %0" : "=r" (ret) : ); - return ret; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h deleted file mode 100644 index d470110..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_amd64_unix.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 3-May-13 Ralf Schmitt - * Add support for strange GCC caller-save decisions - * (ported from switch_aarch64_gcc.h) - * 18-Aug-11 Alexey Borzenkov - * Correctly save rbp, csr and cw - * 01-Apr-04 Hye-Shik Chang - * Ported from i386 to amd64. - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "r12", "r13", "r14", "r15" - -static int -slp_switch(void) -{ - int err; - void* rbp; - void* rbx; - unsigned int csr; - unsigned short cw; - /* This used to be declared 'register', but that does nothing in - modern compilers and is explicitly forbidden in some new - standards. */ - long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("fstcw %0" : "=m" (cw)); - __asm__ volatile ("stmxcsr %0" : "=m" (csr)); - __asm__ volatile ("movq %%rbp, %0" : "=m" (rbp)); - __asm__ volatile ("movq %%rbx, %0" : "=m" (rbx)); - __asm__ ("movq %%rsp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addq %0, %%rsp\n" - "addq %0, %%rbp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - __asm__ volatile ("xorq %%rax, %%rax" : "=a" (err)); - } - __asm__ volatile ("movq %0, %%rbx" : : "m" (rbx)); - __asm__ volatile ("movq %0, %%rbp" : : "m" (rbp)); - __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); - __asm__ volatile ("fldcw %0" : : "m" (cw)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h deleted file mode 100644 index 655003a..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_arm32_gcc.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro - * 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I - * read that these do not need to be saved. Also added notes and - * errors related to the frame pointer. Richard Tew. - * - * NOTES - * - * It is not possible to detect if fp is used or not, so the supplied - * switch function needs to support it, so that you can remove it if - * it does not apply to you. - * - * POSSIBLE ERRORS - * - * "fp cannot be used in asm here" - * - * - Try commenting out "fp" in REGS_TO_SAVE. - * - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 -#define REG_SP "sp" -#define REG_SPSP "sp,sp" -#ifdef __thumb__ -#define REG_FP "r7" -#define REG_FPFP "r7,r7" -#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr" -#else -#define REG_FP "fp" -#define REG_FPFP "fp,fp" -#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr" -#endif -#if defined(__SOFTFP__) -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL -#elif defined(__VFP_FP__) -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ - "d12", "d13", "d14", "d15" -#elif defined(__MAVERICK__) -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \ - "mvf8", "mvf9", "mvf10", "mvf11", \ - "mvf12", "mvf13", "mvf14", "mvf15" -#else -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7" -#endif - -static int -#ifdef __GNUC__ -__attribute__((optimize("no-omit-frame-pointer"))) -#endif -slp_switch(void) -{ - void *fp; - int *stackref, stsizediff; - int result; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0"); - __asm__ ("mov %0," REG_SP : "=r" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add " REG_SPSP ",%0\n" - "add " REG_FPFP ",%0\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0"); - __asm__ volatile ("" : : : REGS_TO_SAVE); - return result; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h b/.venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h deleted file mode 100644 index 9e640e1..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_arm32_ios.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 31-May-15 iOS support. Ported from arm32. Proton - * - * NOTES - * - * It is not possible to detect if fp is used or not, so the supplied - * switch function needs to support it, so that you can remove it if - * it does not apply to you. - * - * POSSIBLE ERRORS - * - * "fp cannot be used in asm here" - * - * - Try commenting out "fp" in REGS_TO_SAVE. - * - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 -#define REG_SP "sp" -#define REG_SPSP "sp,sp" -#define REG_FP "r7" -#define REG_FPFP "r7,r7" -#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr" -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \ - "d12", "d13", "d14", "d15" - -static int -#ifdef __GNUC__ -__attribute__((optimize("no-omit-frame-pointer"))) -#endif -slp_switch(void) -{ - void *fp; - int *stackref, stsizediff, result; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("str " REG_FP ",%0" : "=m" (fp)); - __asm__ ("mov %0," REG_SP : "=r" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add " REG_SPSP ",%0\n" - "add " REG_FPFP ",%0\n" - : - : "r" (stsizediff) - : REGS_TO_SAVE /* Clobber registers, force compiler to - * recalculate address of void *fp from REG_SP or REG_FP */ - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ( - "ldr " REG_FP ", %1\n\t" - "mov %0, #0" - : "=r" (result) - : "m" (fp) - : REGS_TO_SAVE /* Force compiler to restore saved registers after this */ - ); - return result; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm b/.venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm deleted file mode 100644 index 29f9c22..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.asm +++ /dev/null @@ -1,53 +0,0 @@ - AREA switch_arm64_masm, CODE, READONLY; - GLOBAL slp_switch [FUNC] - EXTERN slp_save_state_asm - EXTERN slp_restore_state_asm - -slp_switch - ; push callee saved registers to stack - stp x19, x20, [sp, #-16]! - stp x21, x22, [sp, #-16]! - stp x23, x24, [sp, #-16]! - stp x25, x26, [sp, #-16]! - stp x27, x28, [sp, #-16]! - stp x29, x30, [sp, #-16]! - stp d8, d9, [sp, #-16]! - stp d10, d11, [sp, #-16]! - stp d12, d13, [sp, #-16]! - stp d14, d15, [sp, #-16]! - - ; call slp_save_state_asm with stack pointer - mov x0, sp - bl slp_save_state_asm - - ; early return for return value of 1 and -1 - cmp x0, #-1 - b.eq RETURN - cmp x0, #1 - b.eq RETURN - - ; increment stack and frame pointer - add sp, sp, x0 - add x29, x29, x0 - - bl slp_restore_state_asm - - ; store return value for successful completion of routine - mov x0, #0 - -RETURN - ; pop registers from stack - ldp d14, d15, [sp], #16 - ldp d12, d13, [sp], #16 - ldp d10, d11, [sp], #16 - ldp d8, d9, [sp], #16 - ldp x29, x30, [sp], #16 - ldp x27, x28, [sp], #16 - ldp x25, x26, [sp], #16 - ldp x23, x24, [sp], #16 - ldp x21, x22, [sp], #16 - ldp x19, x20, [sp], #16 - - ret - - END diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj b/.venv/Lib/site-packages/greenlet/platform/switch_arm64_masm.obj deleted file mode 100644 index f6f220e4310baaa9756110685ce7d6a2bdf90c37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 746 zcma)4PiqrF6n~qoo~*PNZ{i+=wji4b#Xu2~wiJpGk)*AMF07NyB(9n1#+i+!)I;v| zBKQG3?t1eB$T(lYgGb4+lu{_QmQrebldQB_4?cMF-uu0IZ{DA2e8@rZ+e^~30488W z`B{KPh%yV{HEIpyeum^wI#7P*HfX)ux?9U&c!SFK-$o|OFtKn{Q|a-#N>2inp0-tb zCRKXAtg2SolaoLv$Ll&ds_Epj?SH+8K{t?XSjKaFsEy%yh}=V70BaHj zEY5kWk_zcJo{$SSUL~=K(zW|tnhm$rA z<%dZ$q?>RX*18r{!azhaYR1lVb;g;mR-6h!#F>|p@;aje%0a|CZrE7s+SXuT>MS=Y ziQPiMY-5DD&5+S7^H03fy7qt7ir}L34kK|h68s-MU>{lXOqlr?!Y=`~WwviNenFS_ zZalVSHh-0FU4lj#X8u5`ODn6@#{f?dHE&%XdP~_I3;$RS9-(z*>>ydkm*f@oWlUn~ Qn+^;lsEi}=H#!Q3U&UU-WdHyG diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h b/.venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h deleted file mode 100644 index 7ab7f45..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_arm64_msvc.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 21-Oct-21 Niyas Sait - * First version to enable win/arm64 support. - */ - -#define STACK_REFPLUS 1 -#define STACK_MAGIC 0 - -/* Use the generic support for an external assembly language slp_switch function. */ -#define EXTERNAL_ASM - -#ifdef SLP_EVAL -/* This always uses the external masm assembly file. */ -#endif \ No newline at end of file diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h deleted file mode 100644 index ac469d3..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_csky_gcc.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifdef SLP_EVAL -#define STACK_MAGIC 0 -#define REG_FP "r8" -#ifdef __CSKYABIV2__ -#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r9", "r10", "r11", "r15",\ - "r16", "r17", "r18", "r19", "r20", "r21", "r22",\ - "r23", "r24", "r25" - -#if defined (__CSKY_HARD_FLOAT__) || (__CSKY_VDSP__) -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "vr8", "vr9", "vr10", "vr11", "vr12",\ - "vr13", "vr14", "vr15" -#else -#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL -#endif -#else -#define REGS_TO_SAVE "r9", "r10", "r11", "r12", "r13", "r15" -#endif - - -static int -#ifdef __GNUC__ -__attribute__((optimize("no-omit-frame-pointer"))) -#endif -slp_switch(void) -{ - int *stackref, stsizediff; - int result; - - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mov %0, sp" : "=r" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addu sp,%0\n" - "addu "REG_FP",%0\n" - : - : "r" (stsizediff) - ); - - SLP_RESTORE_STATE(); - } - __asm__ volatile ("movi %0, 0" : "=r" (result)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - - return result; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h b/.venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h deleted file mode 100644 index 9eaf34e..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_loongarch64_linux.h +++ /dev/null @@ -1,31 +0,0 @@ -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "s0", "s1", "s2", "s3", "s4", "s5", \ - "s6", "s7", "s8", "fp", \ - "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" - -static int -slp_switch(void) -{ - int ret; - long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("move %0, $sp" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add.d $sp, $sp, %0\n\t" - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("move %0, $zero" : "=r" (ret) : ); - return ret; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h deleted file mode 100644 index da761c2..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_m68k_gcc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 2014-01-06 Andreas Schwab - * File created. - */ - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "%d2", "%d3", "%d4", "%d5", "%d6", "%d7", \ - "%a2", "%a3", "%a4" - -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - void *fp, *a5; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("move.l %%fp, %0" : "=m"(fp)); - __asm__ volatile ("move.l %%a5, %0" : "=m"(a5)); - __asm__ ("move.l %%sp, %0" : "=r"(stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ("add.l %0, %%sp; add.l %0, %%fp" : : "r"(stsizediff)); - SLP_RESTORE_STATE(); - __asm__ volatile ("clr.l %0" : "=g" (err)); - } - __asm__ volatile ("move.l %0, %%a5" : : "m"(a5)); - __asm__ volatile ("move.l %0, %%fp" : : "m"(fp)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - return err; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h deleted file mode 100644 index b9003e9..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_mips_unix.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 20-Sep-14 Matt Madison - * Re-code the saving of the gp register for MIPS64. - * 05-Jan-08 Thiemo Seufer - * Ported from ppc. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ - "$23", "$30" -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; -#ifdef __mips64 - uint64_t gpsave; -#endif - __asm__ __volatile__ ("" : : : REGS_TO_SAVE); -#ifdef __mips64 - __asm__ __volatile__ ("sd $28,%0" : "=m" (gpsave) : : ); -#endif - __asm__ ("move %0, $29" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ __volatile__ ( -#ifdef __mips64 - "daddu $29, %0\n" -#else - "addu $29, %0\n" -#endif - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } -#ifdef __mips64 - __asm__ __volatile__ ("ld $28,%0" : : "m" (gpsave) : ); -#endif - __asm__ __volatile__ ("" : : : REGS_TO_SAVE); - __asm__ __volatile__ ("move %0, $0" : "=r" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h deleted file mode 100644 index e7e0b87..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_aix.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 16-Oct-20 Jesse Gorzinski - * Copied from Linux PPC64 implementation - * 04-Sep-18 Alexey Borzenkov - * Workaround a gcc bug using manual save/restore of r30 - * 21-Mar-18 Tulio Magno Quites Machado Filho - * Added r30 to the list of saved registers in order to fully comply with - * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this - * register as a nonvolatile register used for local variables. - * 21-Mar-18 Laszlo Boszormenyi - * Save r2 (TOC pointer) manually. - * 10-Dec-13 Ulrich Weigand - * Support ELFv2 ABI. Save float/vector registers. - * 09-Mar-12 Michael Ellerman - * 64-bit implementation, copied from 32-bit. - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - * 31-Jul-12 Trevor Bowen - * Changed memory constraints to register only. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 6 - -#if defined(__ALTIVEC__) -#define ALTIVEC_REGS \ - "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ - "v28", "v29", "v30", "v31", -#else -#define ALTIVEC_REGS -#endif - -#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "r31", \ - "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ - "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ - "fr30", "fr31", \ - ALTIVEC_REGS \ - "cr2", "cr3", "cr4" - -static int -slp_switch(void) -{ - int err; - long *stackref, stsizediff; - void * toc; - void * r30; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("std 2, %0" : "=m" (toc)); - __asm__ volatile ("std 30, %0" : "=m" (r30)); - __asm__ ("mr %0, 1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - : /* no outputs */ - : "r" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("ld 30, %0" : : "m" (r30)); - __asm__ volatile ("ld 2, %0" : : "m" (toc)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_linux.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_linux.h deleted file mode 100644 index 3c324d0..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc64_linux.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 04-Sep-18 Alexey Borzenkov - * Workaround a gcc bug using manual save/restore of r30 - * 21-Mar-18 Tulio Magno Quites Machado Filho - * Added r30 to the list of saved registers in order to fully comply with - * both ppc64 ELFv1 ABI and the ppc64le ELFv2 ABI, that classify this - * register as a nonvolatile register used for local variables. - * 21-Mar-18 Laszlo Boszormenyi - * Save r2 (TOC pointer) manually. - * 10-Dec-13 Ulrich Weigand - * Support ELFv2 ABI. Save float/vector registers. - * 09-Mar-12 Michael Ellerman - * 64-bit implementation, copied from 32-bit. - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - * 31-Jul-12 Trevor Bowen - * Changed memory constraints to register only. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#if _CALL_ELF == 2 -#define STACK_MAGIC 4 -#else -#define STACK_MAGIC 6 -#endif - -#if defined(__ALTIVEC__) -#define ALTIVEC_REGS \ - "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", \ - "v28", "v29", "v30", "v31", -#else -#define ALTIVEC_REGS -#endif - -#define REGS_TO_SAVE "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "r31", \ - "fr14", "fr15", "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", \ - "fr22", "fr23", "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", \ - "fr30", "fr31", \ - ALTIVEC_REGS \ - "cr2", "cr3", "cr4" - -static int -slp_switch(void) -{ - int err; - long *stackref, stsizediff; - void * toc; - void * r30; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("std 2, %0" : "=m" (toc)); - __asm__ volatile ("std 30, %0" : "=m" (r30)); - __asm__ ("mr %0, 1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - : /* no outputs */ - : "r" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("ld 30, %0" : : "m" (r30)); - __asm__ volatile ("ld 2, %0" : : "m" (toc)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_aix.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc_aix.h deleted file mode 100644 index 6d93c13..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_aix.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Mar-11 Floris Bruynooghe - * Do not add stsizediff to general purpose - * register (GPR) 30 as this is a non-volatile and - * unused by the PowerOpen Environment, therefore - * this was modifying a user register instead of the - * frame pointer (which does not seem to exist). - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - : /* no outputs */ - : "r" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_linux.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc_linux.h deleted file mode 100644 index e83ad70..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_linux.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - * 31-Jul-12 Trevor Bowen - * Changed memory constraints to register only. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - "add 30, 30, 11\n" - : /* no outputs */ - : "r" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_macosx.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc_macosx.h deleted file mode 100644 index bd414c6..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_macosx.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" - -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("; asm block 2\n\tmr %0, r1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "; asm block 3\n" - "\tmr r11, %0\n" - "\tadd r1, r1, r11\n" - "\tadd r30, r30, r11\n" - : /* no outputs */ - : "r" (stsizediff) - : "r11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_ppc_unix.h deleted file mode 100644 index bb18808..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_ppc_unix.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - "add 30, 30, 11\n" - : /* no outputs */ - : "r" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("li %0, 0" : "=r" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_riscv_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_riscv_unix.h deleted file mode 100644 index e74f37a..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_riscv_unix.h +++ /dev/null @@ -1,36 +0,0 @@ -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "s1", "s2", "s3", "s4", "s5", \ - "s6", "s7", "s8", "s9", "s10", "s11", "fs0", "fs1", \ - "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", \ - "fs10", "fs11" - -static int -slp_switch(void) -{ - long fp; - int ret; - long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("mv %0, fp" : "=r" (fp) : ); - __asm__ volatile ("mv %0, sp" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "add sp, sp, %0\n\t" - "add fp, fp, %0\n\t" - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("ld fp, %0" : : "m" (fp)); - __asm__ volatile ("mv %0, zero" : "=r" (ret) : ); - return ret; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_s390_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_s390_unix.h deleted file mode 100644 index 9199367..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_s390_unix.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 25-Jan-12 Alexey Borzenkov - * Fixed Linux/S390 port to work correctly with - * different optimization options both on 31-bit - * and 64-bit. Thanks to Stefan Raabe for lots - * of testing. - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 06-Oct-02 Gustavo Niemeyer - * Ported to Linux/S390. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#ifdef __s390x__ -#define STACK_MAGIC 20 /* 20 * 8 = 160 bytes of function call area */ -#else -#define STACK_MAGIC 24 /* 24 * 4 = 96 bytes of function call area */ -#endif - -/* Technically, r11-r13 also need saving, but function prolog starts - with stm(g) and since there are so many saved registers already - it won't be optimized, resulting in all r6-r15 being saved */ -#define REGS_TO_SAVE "r6", "r7", "r8", "r9", "r10", "r14", \ - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ - "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" - -static int -slp_switch(void) -{ - int ret; - long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); -#ifdef __s390x__ - __asm__ volatile ("lgr %0, 15" : "=r" (stackref) : ); -#else - __asm__ volatile ("lr %0, 15" : "=r" (stackref) : ); -#endif - { - SLP_SAVE_STATE(stackref, stsizediff); -/* N.B. - r11 may be used as the frame pointer, and in that case it cannot be - clobbered and needs offsetting just like the stack pointer (but in cases - where frame pointer isn't used we might clobber it accidentally). What's - scary is that r11 is 2nd (and even 1st when GOT is used) callee saved - register that gcc would chose for surviving function calls. However, - since r6-r10 are clobbered above, their cost for reuse is reduced, so - gcc IRA will chose them over r11 (not seeing r11 is implicitly saved), - making it relatively safe to offset in all cases. :) */ - __asm__ volatile ( -#ifdef __s390x__ - "agr 15, %0\n\t" - "agr 11, %0" -#else - "ar 15, %0\n\t" - "ar 11, %0" -#endif - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("lhi %0, 0" : "=r" (ret) : ); - return ret; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_sh_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_sh_gcc.h deleted file mode 100644 index 5ecc3b3..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_sh_gcc.h +++ /dev/null @@ -1,36 +0,0 @@ -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL -#define STACK_MAGIC 0 -#define REGS_TO_SAVE "r8", "r9", "r10", "r11", "r13", \ - "fr12", "fr13", "fr14", "fr15" - -// r12 Global context pointer, GP -// r14 Frame pointer, FP -// r15 Stack pointer, SP - -static int -slp_switch(void) -{ - int err; - void* fp; - int *stackref, stsizediff; - __asm__ volatile("" : : : REGS_TO_SAVE); - __asm__ volatile("mov.l r14, %0" : "=m"(fp) : :); - __asm__("mov r15, %0" : "=r"(stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile( - "add %0, r15\n" - "add %0, r14\n" - : /* no outputs */ - : "r"(stsizediff)); - SLP_RESTORE_STATE(); - __asm__ volatile("mov r0, %0" : "=r"(err) : :); - } - __asm__ volatile("mov.l %0, r14" : : "m"(fp) :); - __asm__ volatile("" : : : REGS_TO_SAVE); - return err; -} - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_sparc_sun_gcc.h b/.venv/Lib/site-packages/greenlet/platform/switch_sparc_sun_gcc.h deleted file mode 100644 index 96990c3..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_sparc_sun_gcc.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 16-May-15 Alexey Borzenkov - * Move stack spilling code inside save/restore functions - * 30-Aug-13 Floris Bruynooghe - Clean the register windows again before returning. - This does not clobber the PIC register as it leaves - the current window intact and is required for multi- - threaded code to work correctly. - * 08-Mar-11 Floris Bruynooghe - * No need to set return value register explicitly - * before the stack and framepointer are adjusted - * as none of the other registers are influenced by - * this. Also don't needlessly clean the windows - * ('ta %0" :: "i" (ST_CLEAN_WINDOWS)') as that - * clobbers the gcc PIC register (%l7). - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * added support for SunOS sparc with gcc - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - - -#define STACK_MAGIC 0 - - -#if defined(__sparcv9) -#define SLP_FLUSHW __asm__ volatile ("flushw") -#else -#define SLP_FLUSHW __asm__ volatile ("ta 3") /* ST_FLUSH_WINDOWS */ -#endif - -/* On sparc we need to spill register windows inside save/restore functions */ -#define SLP_BEFORE_SAVE_STATE() SLP_FLUSHW -#define SLP_BEFORE_RESTORE_STATE() SLP_FLUSHW - - -static int -slp_switch(void) -{ - int err; - int *stackref, stsizediff; - - /* Put current stack pointer into stackref. - * Register spilling is done in save/restore. - */ - __asm__ volatile ("mov %%sp, %0" : "=r" (stackref)); - - { - /* Thou shalt put SLP_SAVE_STATE into a local block */ - /* Copy the current stack onto the heap */ - SLP_SAVE_STATE(stackref, stsizediff); - - /* Increment stack and frame pointer by stsizediff */ - __asm__ volatile ( - "add %0, %%sp, %%sp\n\t" - "add %0, %%fp, %%fp" - : : "r" (stsizediff)); - - /* Copy new stack from it's save store on the heap */ - SLP_RESTORE_STATE(); - - __asm__ volatile ("mov %1, %0" : "=r" (err) : "i" (0)); - return err; - } -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x32_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_x32_unix.h deleted file mode 100644 index 893369c..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_x32_unix.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 17-Aug-12 Fantix King - * Ported from amd64. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "r12", "r13", "r14", "r15" - - -static int -slp_switch(void) -{ - void* ebp; - void* ebx; - unsigned int csr; - unsigned short cw; - int err; - int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("fstcw %0" : "=m" (cw)); - __asm__ volatile ("stmxcsr %0" : "=m" (csr)); - __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); - __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); - __asm__ ("movl %%esp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addl %0, %%esp\n" - "addl %0, %%ebp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); - __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); - __asm__ volatile ("ldmxcsr %0" : : "m" (csr)); - __asm__ volatile ("fldcw %0" : : "m" (cw)); - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x64_masm.asm b/.venv/Lib/site-packages/greenlet/platform/switch_x64_masm.asm deleted file mode 100644 index f5c72a2..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_x64_masm.asm +++ /dev/null @@ -1,111 +0,0 @@ -; -; stack switching code for MASM on x641 -; Kristjan Valur Jonsson, sept 2005 -; - - -;prototypes for our calls -slp_save_state_asm PROTO -slp_restore_state_asm PROTO - - -pushxmm MACRO reg - sub rsp, 16 - .allocstack 16 - movaps [rsp], reg ; faster than movups, but we must be aligned - ; .savexmm128 reg, offset (don't know what offset is, no documentation) -ENDM -popxmm MACRO reg - movaps reg, [rsp] ; faster than movups, but we must be aligned - add rsp, 16 -ENDM - -pushreg MACRO reg - push reg - .pushreg reg -ENDM -popreg MACRO reg - pop reg -ENDM - - -.code -slp_switch PROC FRAME - ;realign stack to 16 bytes after return address push, makes the following faster - sub rsp,8 - .allocstack 8 - - pushxmm xmm15 - pushxmm xmm14 - pushxmm xmm13 - pushxmm xmm12 - pushxmm xmm11 - pushxmm xmm10 - pushxmm xmm9 - pushxmm xmm8 - pushxmm xmm7 - pushxmm xmm6 - - pushreg r15 - pushreg r14 - pushreg r13 - pushreg r12 - - pushreg rbp - pushreg rbx - pushreg rdi - pushreg rsi - - sub rsp, 10h ;allocate the singlefunction argument (must be multiple of 16) - .allocstack 10h -.endprolog - - lea rcx, [rsp+10h] ;load stack base that we are saving - call slp_save_state_asm ;pass stackpointer, return offset in eax - cmp rax, 1 - je EXIT1 - cmp rax, -1 - je EXIT2 - ;actual stack switch: - add rsp, rax - call slp_restore_state_asm - xor rax, rax ;return 0 - -EXIT: - - add rsp, 10h - popreg rsi - popreg rdi - popreg rbx - popreg rbp - - popreg r12 - popreg r13 - popreg r14 - popreg r15 - - popxmm xmm6 - popxmm xmm7 - popxmm xmm8 - popxmm xmm9 - popxmm xmm10 - popxmm xmm11 - popxmm xmm12 - popxmm xmm13 - popxmm xmm14 - popxmm xmm15 - - add rsp, 8 - ret - -EXIT1: - mov rax, 1 - jmp EXIT - -EXIT2: - sar rax, 1 - jmp EXIT - -slp_switch ENDP - -END \ No newline at end of file diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x64_masm.obj b/.venv/Lib/site-packages/greenlet/platform/switch_x64_masm.obj deleted file mode 100644 index 64e3e6b898ec765d4e37075f7b1635ad24c9efa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1078 zcmZ{j&ubG=5XWb`DJB@*%~BA=L%=;Gk}d_~52VO$4J4q2U~MY6&1RFl{E&?scGnt@ zn(9GNy!ihFEO@PV4?T&H9`x2*oO!!jlNJZwd!P4xlX&_;U$Bg3z>p zje>}2kp+MsxE|w5hOUr>YC~(=fz6fwPdZd5+Hlb^P3{;ZO@Yuv96GG&+Gx?QfclNd zhy2KN&~>fNnlHQRR;U1cMyQ?hlQ$~k<0KBbB<0uD2#PTjVo+na7Q;#m=@=3m;xJOa zs2V#)&Db`cY;WzTF9)11;SjkVQWE!?bPTC%x3h3^F2;aBns5!i%m4&-*h69;~AUpZR%rDpm!zuXY+kc zFCz-n*^4&c)5~}y3e?r-Evy*n*(lp9r%ti58Y#l5&)rDjx5EbRd}nC+_8znRzz&#& XZ_Fi+`GM=5Rl{n4%KxAK>jC@)Nz=zi diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x64_msvc.h b/.venv/Lib/site-packages/greenlet/platform/switch_x64_msvc.h deleted file mode 100644 index 601ea56..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_x64_msvc.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 26-Sep-02 Christian Tismer - * again as a result of virtualized stack access, - * the compiler used less registers. Needed to - * explicit mention registers in order to get them saved. - * Thanks to Jeff Senn for pointing this out and help. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 01-Mar-02 Christian Tismer - * Initial final version after lots of iterations for i386. - */ - -/* Avoid alloca redefined warning on mingw64 */ -#ifndef alloca -#define alloca _alloca -#endif - -#define STACK_REFPLUS 1 -#define STACK_MAGIC 0 - -/* Use the generic support for an external assembly language slp_switch function. */ -#define EXTERNAL_ASM - -#ifdef SLP_EVAL -/* This always uses the external masm assembly file. */ -#endif - -/* - * further self-processing support - */ - -/* we have IsBadReadPtr available, so we can peek at objects */ -/* -#define STACKLESS_SPY - -#ifdef IMPLEMENT_STACKLESSMODULE -#include "Windows.h" -#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) - -static int IS_ON_STACK(void*p) -{ - int stackref; - intptr_t stackbase = ((intptr_t)&stackref) & 0xfffff000; - return (intptr_t)p >= stackbase && (intptr_t)p < stackbase + 0x00100000; -} - -#endif -*/ \ No newline at end of file diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x86_msvc.h b/.venv/Lib/site-packages/greenlet/platform/switch_x86_msvc.h deleted file mode 100644 index 0f3a59f..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_x86_msvc.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 26-Sep-02 Christian Tismer - * again as a result of virtualized stack access, - * the compiler used less registers. Needed to - * explicit mention registers in order to get them saved. - * Thanks to Jeff Senn for pointing this out and help. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 01-Mar-02 Christian Tismer - * Initial final version after lots of iterations for i386. - */ - -#define alloca _alloca - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -/* Some magic to quell warnings and keep slp_switch() from crashing when built - with VC90. Disable global optimizations, and the warning: frame pointer - register 'ebp' modified by inline assembly code. - - We used to just disable global optimizations ("g") but upstream stackless - Python, as well as stackman, turn off all optimizations. - -References: -https://github.com/stackless-dev/stackman/blob/dbc72fe5207a2055e658c819fdeab9731dee78b9/stackman/platforms/switch_x86_msvc.h -https://github.com/stackless-dev/stackless/blob/main-slp/Stackless/platf/switch_x86_msvc.h -*/ -#define WIN32_LEAN_AND_MEAN -#include - -#pragma optimize("", off) /* so that autos are stored on the stack */ -#pragma warning(disable:4731) -#pragma warning(disable:4733) /* disable warning about modifying FS[0] */ - -/** - * Most modern compilers and environments handle C++ exceptions without any - * special help from us. MSVC on 32-bit windows is an exception. There, C++ - * exceptions are dealt with using Windows' Structured Exception Handling - * (SEH). - * - * SEH is implemented as a singly linked list of nodes. The - * head of this list is stored in the Thread Information Block, which itself - * is pointed to from the FS register. It's the first field in the structure, - * or offset 0, so we can access it using assembly FS:[0], or the compiler - * intrinsics and field offset information from the headers (as we do below). - * Somewhat unusually, the tail of the list doesn't have prev == NULL, it has - * prev == 0xFFFFFFFF. - * - * SEH was designed for C, and traditionally uses the MSVC compiler - * intrinsincs __try{}/__except{}. It is also utilized for C++ exceptions by - * MSVC; there, every throw of a C++ exception raises a SEH error with the - * ExceptionCode 0xE06D7363; the SEH handler list is then traversed to - * deal with the exception. - * - * If the SEH list is corrupt, then when a C++ exception is thrown the program - * will abruptly exit with exit code 1. This does not use std::terminate(), so - * std::set_terminate() is useless to debug this. - * - * The SEH list is closely tied to the call stack; entering a function that - * uses __try{} or most C++ functions will push a new handler onto the front - * of the list. Returning from the function will remove the handler. Saving - * and restoring the head node of the SEH list (FS:[0]) per-greenlet is NOT - * ENOUGH to make SEH or exceptions work. - * - * Stack switching breaks SEH because the call stack no longer necessarily - * matches the SEH list. For example, given greenlet A that switches to - * greenlet B, at the moment of entering greenlet B, we will have any SEH - * handlers from greenlet A on the SEH list; greenlet B can then add its own - * handlers to the SEH list. When greenlet B switches back to greenlet A, - * greenlet B's handlers would still be on the SEH stack, but when switch() - * returns control to greenlet A, we have replaced the contents of the stack - * in memory, so all the address that greenlet B added to the SEH list are now - * invalid: part of the call stack has been unwound, but the SEH list was out - * of sync with the call stack. The net effect is that exception handling - * stops working. - * - * Thus, when switching greenlets, we need to be sure that the SEH list - * matches the effective call stack, "cutting out" any handlers that were - * pushed by the greenlet that switched out and which are no longer valid. - * - * The easiest way to do this is to capture the SEH list at the time the main - * greenlet for a thread is created, and, when initially starting a greenlet, - * start a new SEH list for it, which contains nothing but the handler - * established for the new greenlet itself, with the tail being the handlers - * for the main greenlet. If we then save and restore the SEH per-greenlet, - * they won't interfere with each others SEH lists. (No greenlet can unwind - * the call stack past the handlers established by the main greenlet). - * - * By observation, a new thread starts with three SEH handlers on the list. By - * the time we get around to creating the main greenlet, though, there can be - * many more, established by transient calls that lead to the creation of the - * main greenlet. Therefore, 3 is a magic constant telling us when to perform - * the initial slice. - * - * All of this can be debugged using a vectored exception handler, which - * operates independently of the SEH handler list, and is called first. - * Walking the SEH list at key points can also be helpful. - * - * References: - * https://en.wikipedia.org/wiki/Win32_Thread_Information_Block - * https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 - * https://docs.microsoft.com/en-us/cpp/cpp/try-except-statement?view=msvc-160 - * https://docs.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160 - * https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling - * https://docs.microsoft.com/en-us/windows/win32/debug/using-a-vectored-exception-handler - * https://bytepointer.com/resources/pietrek_crash_course_depths_of_win32_seh.htm - */ -#define GREENLET_NEEDS_EXCEPTION_STATE_SAVED - - -typedef struct _GExceptionRegistration { - struct _GExceptionRegistration* prev; - void* handler_f; -} GExceptionRegistration; - -static void -slp_set_exception_state(const void *const seh_state) -{ - // Because the stack from from which we do this is ALSO a handler, and - // that one we want to keep, we need to relink the current SEH handler - // frame to point to this one, cutting out the middle men, as it were. - // - // Entering a try block doesn't change the SEH frame, but entering a - // function containing a try block does. - GExceptionRegistration* current_seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); - current_seh_state->prev = (GExceptionRegistration*)seh_state; -} - - -static GExceptionRegistration* -x86_slp_get_third_oldest_handler() -{ - GExceptionRegistration* a = NULL; /* Closest to the top */ - GExceptionRegistration* b = NULL; /* second */ - GExceptionRegistration* c = NULL; - GExceptionRegistration* seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); - a = b = c = seh_state; - - while (seh_state && seh_state != (GExceptionRegistration*)0xFFFFFFFF) { - if ((void*)seh_state->prev < (void*)100) { - fprintf(stderr, "\tERROR: Broken SEH chain.\n"); - return NULL; - } - a = b; - b = c; - c = seh_state; - - seh_state = seh_state->prev; - } - return a ? a : (b ? b : c); -} - - -static void* -slp_get_exception_state() -{ - // XXX: There appear to be three SEH handlers on the stack already at the - // start of the thread. Is that a guarantee? Almost certainly not. Yet in - // all observed cases it has been three. This is consistent with - // faulthandler off or on, and optimizations off or on. It may not be - // consistent with other operating system versions, though: we only have - // CI on one or two versions (don't ask what there are). - // In theory we could capture the number of handlers on the chain when - // PyInit__greenlet is called: there are probably only the default - // handlers at that point (unless we're embedded and people have used - // __try/__except or a C++ handler)? - return x86_slp_get_third_oldest_handler(); -} - -static int -slp_switch(void) -{ - /* MASM syntax is typically reversed from other assemblers. - It is usually - */ - int *stackref, stsizediff; - /* store the structured exception state for this stack */ - DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); - __asm mov stackref, esp; - /* modify EBX, ESI and EDI in order to get them preserved */ - __asm mov ebx, ebx; - __asm xchg esi, edi; - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm { - mov eax, stsizediff - add esp, eax - add ebp, eax - } - SLP_RESTORE_STATE(); - } - __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state); - return 0; -} - -/* re-enable ebp warning and global optimizations. */ -#pragma optimize("", on) -#pragma warning(default:4731) -#pragma warning(default:4733) /* disable warning about modifying FS[0] */ - - -#endif - -/* - * further self-processing support - */ - -/* we have IsBadReadPtr available, so we can peek at objects */ -#define STACKLESS_SPY - -#ifdef GREENLET_DEBUG - -#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) - -static int IS_ON_STACK(void*p) -{ - int stackref; - int stackbase = ((int)&stackref) & 0xfffff000; - return (int)p >= stackbase && (int)p < stackbase + 0x00100000; -} - -static void -x86_slp_show_seh_chain() -{ - GExceptionRegistration* seh_state = (GExceptionRegistration*)__readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList)); - fprintf(stderr, "====== SEH Chain ======\n"); - while (seh_state && seh_state != (GExceptionRegistration*)0xFFFFFFFF) { - fprintf(stderr, "\tSEH_chain addr: %p handler: %p prev: %p\n", - seh_state, - seh_state->handler_f, seh_state->prev); - if ((void*)seh_state->prev < (void*)100) { - fprintf(stderr, "\tERROR: Broken chain.\n"); - break; - } - seh_state = seh_state->prev; - } - fprintf(stderr, "====== End SEH Chain ======\n"); - fflush(NULL); - return; -} - -//addVectoredExceptionHandler constants: -//CALL_FIRST means call this exception handler first; -//CALL_LAST means call this exception handler last -#define CALL_FIRST 1 -#define CALL_LAST 0 - -LONG WINAPI -GreenletVectorHandler(PEXCEPTION_POINTERS ExceptionInfo) -{ - // We get one of these for every C++ exception, with code - // E06D7363 - // This is a special value that means "C++ exception from MSVC" - // https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 - // - // Install in the module init function with: - // AddVectoredExceptionHandler(CALL_FIRST, GreenletVectorHandler); - PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord; - - fprintf(stderr, - "GOT VECTORED EXCEPTION:\n" - "\tExceptionCode : %p\n" - "\tExceptionFlags : %p\n" - "\tExceptionAddr : %p\n" - "\tNumberparams : %ld\n", - ExceptionRecord->ExceptionCode, - ExceptionRecord->ExceptionFlags, - ExceptionRecord->ExceptionAddress, - ExceptionRecord->NumberParameters - ); - if (ExceptionRecord->ExceptionFlags & 1) { - fprintf(stderr, "\t\tEH_NONCONTINUABLE\n" ); - } - if (ExceptionRecord->ExceptionFlags & 2) { - fprintf(stderr, "\t\tEH_UNWINDING\n" ); - } - if (ExceptionRecord->ExceptionFlags & 4) { - fprintf(stderr, "\t\tEH_EXIT_UNWIND\n" ); - } - if (ExceptionRecord->ExceptionFlags & 8) { - fprintf(stderr, "\t\tEH_STACK_INVALID\n" ); - } - if (ExceptionRecord->ExceptionFlags & 0x10) { - fprintf(stderr, "\t\tEH_NESTED_CALL\n" ); - } - if (ExceptionRecord->ExceptionFlags & 0x20) { - fprintf(stderr, "\t\tEH_TARGET_UNWIND\n" ); - } - if (ExceptionRecord->ExceptionFlags & 0x40) { - fprintf(stderr, "\t\tEH_COLLIDED_UNWIND\n" ); - } - fprintf(stderr, "\n"); - fflush(NULL); - for(DWORD i = 0; i < ExceptionRecord->NumberParameters; i++) { - fprintf(stderr, "\t\t\tParam %ld: %lX\n", i, ExceptionRecord->ExceptionInformation[i]); - } - - if (ExceptionRecord->NumberParameters == 3) { - fprintf(stderr, "\tAbout to traverse SEH chain\n"); - // C++ Exception records have 3 params. - x86_slp_show_seh_chain(); - } - - return EXCEPTION_CONTINUE_SEARCH; -} - - - - -#endif diff --git a/.venv/Lib/site-packages/greenlet/platform/switch_x86_unix.h b/.venv/Lib/site-packages/greenlet/platform/switch_x86_unix.h deleted file mode 100644 index 493fa6b..0000000 --- a/.venv/Lib/site-packages/greenlet/platform/switch_x86_unix.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 3-May-13 Ralf Schmitt - * Add support for strange GCC caller-save decisions - * (ported from switch_aarch64_gcc.h) - * 19-Aug-11 Alexey Borzenkov - * Correctly save ebp, ebx and cw - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'ebx' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -# define ATTR_NOCLONE __attribute__((noclone)) -#else -# define ATTR_NOCLONE -#endif - -static int -slp_switch(void) -{ - int err; -#ifdef _WIN32 - void *seh; -#endif - void *ebp, *ebx; - unsigned short cw; - int *stackref, stsizediff; - __asm__ volatile ("" : : : "esi", "edi"); - __asm__ volatile ("fstcw %0" : "=m" (cw)); - __asm__ volatile ("movl %%ebp, %0" : "=m" (ebp)); - __asm__ volatile ("movl %%ebx, %0" : "=m" (ebx)); -#ifdef _WIN32 - __asm__ volatile ( - "movl %%fs:0x0, %%eax\n" - "movl %%eax, %0\n" - : "=m" (seh) - : - : "eax"); -#endif - __asm__ ("movl %%esp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addl %0, %%esp\n" - "addl %0, %%ebp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - __asm__ volatile ("xorl %%eax, %%eax" : "=a" (err)); - } -#ifdef _WIN32 - __asm__ volatile ( - "movl %0, %%eax\n" - "movl %%eax, %%fs:0x0\n" - : - : "m" (seh) - : "eax"); -#endif - __asm__ volatile ("movl %0, %%ebx" : : "m" (ebx)); - __asm__ volatile ("movl %0, %%ebp" : : "m" (ebp)); - __asm__ volatile ("fldcw %0" : : "m" (cw)); - __asm__ volatile ("" : : : "esi", "edi"); - return err; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/.venv/Lib/site-packages/greenlet/slp_platformselect.h b/.venv/Lib/site-packages/greenlet/slp_platformselect.h deleted file mode 100644 index 4945648..0000000 --- a/.venv/Lib/site-packages/greenlet/slp_platformselect.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Platform Selection for Stackless Python - */ -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) && defined(_MSC_VER) -# include "platform/switch_x86_msvc.h" /* MS Visual Studio on X86 */ -#elif defined(MS_WIN64) && defined(_M_X64) && defined(_MSC_VER) || defined(__MINGW64__) -# include "platform/switch_x64_msvc.h" /* MS Visual Studio on X64 */ -#elif defined(MS_WIN64) && defined(_M_ARM64) -# include "platform/switch_arm64_msvc.h" /* MS Visual Studio on ARM64 */ -#elif defined(__GNUC__) && defined(__amd64__) && defined(__ILP32__) -# include "platform/switch_x32_unix.h" /* gcc on amd64 with x32 ABI */ -#elif defined(__GNUC__) && defined(__amd64__) -# include "platform/switch_amd64_unix.h" /* gcc on amd64 */ -#elif defined(__GNUC__) && defined(__i386__) -# include "platform/switch_x86_unix.h" /* gcc on X86 */ -#elif defined(__GNUC__) && defined(__powerpc64__) && (defined(__linux__) || defined(__FreeBSD__)) -# include "platform/switch_ppc64_linux.h" /* gcc on PowerPC 64-bit */ -#elif defined(__GNUC__) && defined(__PPC__) && (defined(__linux__) || defined(__FreeBSD__)) -# include "platform/switch_ppc_linux.h" /* gcc on PowerPC */ -#elif defined(__GNUC__) && defined(__POWERPC__) && defined(__APPLE__) -# include "platform/switch_ppc_macosx.h" /* Apple MacOS X on 32-bit PowerPC */ -#elif defined(__GNUC__) && defined(__powerpc64__) && defined(_AIX) -# include "platform/switch_ppc64_aix.h" /* gcc on AIX/PowerPC 64-bit */ -#elif defined(__GNUC__) && defined(_ARCH_PPC) && defined(_AIX) -# include "platform/switch_ppc_aix.h" /* gcc on AIX/PowerPC */ -#elif defined(__GNUC__) && defined(__powerpc__) && defined(__NetBSD__) -#include "platform/switch_ppc_unix.h" /* gcc on NetBSD/powerpc */ -#elif defined(__GNUC__) && defined(sparc) -# include "platform/switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ -#elif defined(__SUNPRO_C) && defined(sparc) && defined(sun) -# iiclude "platform/switch_sparc_sun_gcc.h" /* SunStudio on amd64 */ -#elif defined(__SUNPRO_C) && defined(__amd64__) && defined(sun) -# include "platform/switch_amd64_unix.h" /* SunStudio on amd64 */ -#elif defined(__SUNPRO_C) && defined(__i386__) && defined(sun) -# include "platform/switch_x86_unix.h" /* SunStudio on x86 */ -#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) -# include "platform/switch_s390_unix.h" /* Linux/S390 */ -#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) -# include "platform/switch_s390_unix.h" /* Linux/S390 zSeries (64-bit) */ -#elif defined(__GNUC__) && defined(__arm__) -# ifdef __APPLE__ -# include -# endif -# if TARGET_OS_IPHONE -# include "platform/switch_arm32_ios.h" /* iPhone OS on arm32 */ -# else -# include "platform/switch_arm32_gcc.h" /* gcc using arm32 */ -# endif -#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) -# include "platform/switch_mips_unix.h" /* Linux/MIPS */ -#elif defined(__GNUC__) && defined(__aarch64__) -# include "platform/switch_aarch64_gcc.h" /* Aarch64 ABI */ -#elif defined(__GNUC__) && defined(__mc68000__) -# include "platform/switch_m68k_gcc.h" /* gcc on m68k */ -#elif defined(__GNUC__) && defined(__csky__) -#include "platform/switch_csky_gcc.h" /* gcc on csky */ -# elif defined(__GNUC__) && defined(__riscv) -# include "platform/switch_riscv_unix.h" /* gcc on RISC-V */ -#elif defined(__GNUC__) && defined(__alpha__) -# include "platform/switch_alpha_unix.h" /* gcc on DEC Alpha */ -#elif defined(MS_WIN32) && defined(__llvm__) && defined(__aarch64__) -# include "platform/switch_aarch64_gcc.h" /* LLVM Aarch64 ABI for Windows */ -#elif defined(__GNUC__) && defined(__loongarch64) && defined(__linux__) -# include "platform/switch_loongarch64_linux.h" /* LoongArch64 */ -#elif defined(__GNUC__) && defined(__sh__) -# include "platform/switch_sh_gcc.h" /* SuperH */ -#endif - -#ifdef __cplusplus -}; -#endif diff --git a/.venv/Lib/site-packages/greenlet/tests/__init__.py b/.venv/Lib/site-packages/greenlet/tests/__init__.py deleted file mode 100644 index e69392e..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/__init__.py +++ /dev/null @@ -1,240 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tests for greenlet. - -""" -import os -import sys -import unittest - -from gc import collect -from gc import get_objects -from threading import active_count as active_thread_count -from time import sleep -from time import time - -import psutil - -from greenlet import greenlet as RawGreenlet -from greenlet import getcurrent - -from greenlet._greenlet import get_pending_cleanup_count -from greenlet._greenlet import get_total_main_greenlets - -from . import leakcheck - -PY312 = sys.version_info[:2] >= (3, 12) -PY313 = sys.version_info[:2] >= (3, 13) - -WIN = sys.platform.startswith("win") -RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS') -RUNNING_ON_TRAVIS = os.environ.get('TRAVIS') or RUNNING_ON_GITHUB_ACTIONS -RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR') -RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR -RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX') - -class TestCaseMetaClass(type): - # wrap each test method with - # a) leak checks - def __new__(cls, classname, bases, classDict): - # pylint and pep8 fight over what this should be called (mcs or cls). - # pylint gets it right, but we can't scope disable pep8, so we go with - # its convention. - # pylint: disable=bad-mcs-classmethod-argument - check_totalrefcount = True - - # Python 3: must copy, we mutate the classDict. Interestingly enough, - # it doesn't actually error out, but under 3.6 we wind up wrapping - # and re-wrapping the same items over and over and over. - for key, value in list(classDict.items()): - if key.startswith('test') and callable(value): - classDict.pop(key) - if check_totalrefcount: - value = leakcheck.wrap_refcount(value) - classDict[key] = value - return type.__new__(cls, classname, bases, classDict) - - -class TestCase(TestCaseMetaClass( - "NewBase", - (unittest.TestCase,), - {})): - - cleanup_attempt_sleep_duration = 0.001 - cleanup_max_sleep_seconds = 1 - - def wait_for_pending_cleanups(self, - initial_active_threads=None, - initial_main_greenlets=None): - initial_active_threads = initial_active_threads or self.threads_before_test - initial_main_greenlets = initial_main_greenlets or self.main_greenlets_before_test - sleep_time = self.cleanup_attempt_sleep_duration - # NOTE: This is racy! A Python-level thread object may be dead - # and gone, but the C thread may not yet have fired its - # destructors and added to the queue. There's no particular - # way to know that's about to happen. We try to watch the - # Python threads to make sure they, at least, have gone away. - # Counting the main greenlets, which we can easily do deterministically, - # also helps. - - # Always sleep at least once to let other threads run - sleep(sleep_time) - quit_after = time() + self.cleanup_max_sleep_seconds - # TODO: We could add an API that calls us back when a particular main greenlet is deleted? - # It would have to drop the GIL - while ( - get_pending_cleanup_count() - or active_thread_count() > initial_active_threads - or (not self.expect_greenlet_leak - and get_total_main_greenlets() > initial_main_greenlets)): - sleep(sleep_time) - if time() > quit_after: - print("Time limit exceeded.") - print("Threads: Waiting for only", initial_active_threads, - "-->", active_thread_count()) - print("MGlets : Waiting for only", initial_main_greenlets, - "-->", get_total_main_greenlets()) - break - collect() - - def count_objects(self, kind=list, exact_kind=True): - # pylint:disable=unidiomatic-typecheck - # Collect the garbage. - for _ in range(3): - collect() - if exact_kind: - return sum( - 1 - for x in get_objects() - if type(x) is kind - ) - # instances - return sum( - 1 - for x in get_objects() - if isinstance(x, kind) - ) - - greenlets_before_test = 0 - threads_before_test = 0 - main_greenlets_before_test = 0 - expect_greenlet_leak = False - - def count_greenlets(self): - """ - Find all the greenlets and subclasses tracked by the GC. - """ - return self.count_objects(RawGreenlet, False) - - def setUp(self): - # Ensure the main greenlet exists, otherwise the first test - # gets a false positive leak - super().setUp() - getcurrent() - self.threads_before_test = active_thread_count() - self.main_greenlets_before_test = get_total_main_greenlets() - self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) - self.greenlets_before_test = self.count_greenlets() - - def tearDown(self): - if getattr(self, 'skipTearDown', False): - return - - self.wait_for_pending_cleanups(self.threads_before_test, self.main_greenlets_before_test) - super().tearDown() - - def get_expected_returncodes_for_aborted_process(self): - import signal - # The child should be aborted in an unusual way. On POSIX - # platforms, this is done with abort() and signal.SIGABRT, - # which is reflected in a negative return value; however, on - # Windows, even though we observe the child print "Fatal - # Python error: Aborted" and in older versions of the C - # runtime "This application has requested the Runtime to - # terminate it in an unusual way," it always has an exit code - # of 3. This is interesting because 3 is the error code for - # ERROR_PATH_NOT_FOUND; BUT: the C runtime abort() function - # also uses this code. - # - # If we link to the static C library on Windows, the error - # code changes to '0xc0000409' (hex(3221226505)), which - # apparently is STATUS_STACK_BUFFER_OVERRUN; but "What this - # means is that nowadays when you get a - # STATUS_STACK_BUFFER_OVERRUN, it doesn’t actually mean that - # there is a stack buffer overrun. It just means that the - # application decided to terminate itself with great haste." - # - # - # On windows, we've also seen '0xc0000005' (hex(3221225477)). - # That's "Access Violation" - # - # See - # https://devblogs.microsoft.com/oldnewthing/20110519-00/?p=10623 - # and - # https://docs.microsoft.com/en-us/previous-versions/k089yyh0(v=vs.140)?redirectedfrom=MSDN - # and - # https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655 - expected_exit = ( - -signal.SIGABRT, - # But beginning on Python 3.11, the faulthandler - # that prints the C backtraces sometimes segfaults after - # reporting the exception but before printing the stack. - # This has only been seen on linux/gcc. - -signal.SIGSEGV, - ) if not WIN else ( - 3, - 0xc0000409, - 0xc0000005, - ) - return expected_exit - - def get_process_uss(self): - """ - Return the current process's USS in bytes. - - uss is available on Linux, macOS, Windows. Also known as - "Unique Set Size", this is the memory which is unique to a - process and which would be freed if the process was terminated - right now. - - If this is not supported by ``psutil``, this raises the - :exc:`unittest.SkipTest` exception. - """ - try: - return psutil.Process().memory_full_info().uss - except AttributeError as e: - raise unittest.SkipTest("uss not supported") from e - - def run_script(self, script_name, show_output=True): - import subprocess - script = os.path.join( - os.path.dirname(__file__), - script_name, - ) - - try: - return subprocess.check_output([sys.executable, script], - encoding='utf-8', - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as ex: - if show_output: - print('-----') - print('Failed to run script', script) - print('~~~~~') - print(ex.output) - print('------') - raise - - - def assertScriptRaises(self, script_name, exitcodes=None): - import subprocess - with self.assertRaises(subprocess.CalledProcessError) as exc: - output = self.run_script(script_name, show_output=False) - __traceback_info__ = output - # We're going to fail the assertion if we get here, at least - # preserve the output in the traceback. - - if exitcodes is None: - exitcodes = self.get_expected_returncodes_for_aborted_process() - self.assertIn(exc.exception.returncode, exitcodes) - return exc.exception diff --git a/.venv/Lib/site-packages/greenlet/tests/_test_extension.c b/.venv/Lib/site-packages/greenlet/tests/_test_extension.c deleted file mode 100644 index 05e81c0..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/_test_extension.c +++ /dev/null @@ -1,231 +0,0 @@ -/* This is a set of functions used by test_extension_interface.py to test the - * Greenlet C API. - */ - -#include "../greenlet.h" - -#ifndef Py_RETURN_NONE -# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None -#endif - -#define TEST_MODULE_NAME "_test_extension" - -static PyObject* -test_switch(PyObject* self, PyObject* greenlet) -{ - PyObject* result = NULL; - - if (greenlet == NULL || !PyGreenlet_Check(greenlet)) { - PyErr_BadArgument(); - return NULL; - } - - result = PyGreenlet_Switch((PyGreenlet*)greenlet, NULL, NULL); - if (result == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_AssertionError, - "greenlet.switch() failed for some reason."); - } - return NULL; - } - Py_INCREF(result); - return result; -} - -static PyObject* -test_switch_kwargs(PyObject* self, PyObject* args, PyObject* kwargs) -{ - PyGreenlet* g = NULL; - PyObject* result = NULL; - - PyArg_ParseTuple(args, "O!", &PyGreenlet_Type, &g); - - if (g == NULL || !PyGreenlet_Check(g)) { - PyErr_BadArgument(); - return NULL; - } - - result = PyGreenlet_Switch(g, NULL, kwargs); - if (result == NULL) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_AssertionError, - "greenlet.switch() failed for some reason."); - } - return NULL; - } - Py_XINCREF(result); - return result; -} - -static PyObject* -test_getcurrent(PyObject* self) -{ - PyGreenlet* g = PyGreenlet_GetCurrent(); - if (g == NULL || !PyGreenlet_Check(g) || !PyGreenlet_ACTIVE(g)) { - PyErr_SetString(PyExc_AssertionError, - "getcurrent() returned an invalid greenlet"); - Py_XDECREF(g); - return NULL; - } - Py_DECREF(g); - Py_RETURN_NONE; -} - -static PyObject* -test_setparent(PyObject* self, PyObject* arg) -{ - PyGreenlet* current; - PyGreenlet* greenlet = NULL; - - if (arg == NULL || !PyGreenlet_Check(arg)) { - PyErr_BadArgument(); - return NULL; - } - if ((current = PyGreenlet_GetCurrent()) == NULL) { - return NULL; - } - greenlet = (PyGreenlet*)arg; - if (PyGreenlet_SetParent(greenlet, current)) { - Py_DECREF(current); - return NULL; - } - Py_DECREF(current); - if (PyGreenlet_Switch(greenlet, NULL, NULL) == NULL) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject* -test_new_greenlet(PyObject* self, PyObject* callable) -{ - PyObject* result = NULL; - PyGreenlet* greenlet = PyGreenlet_New(callable, NULL); - - if (!greenlet) { - return NULL; - } - - result = PyGreenlet_Switch(greenlet, NULL, NULL); - Py_CLEAR(greenlet); - if (result == NULL) { - return NULL; - } - - Py_INCREF(result); - return result; -} - -static PyObject* -test_raise_dead_greenlet(PyObject* self) -{ - PyErr_SetString(PyExc_GreenletExit, "test GreenletExit exception."); - return NULL; -} - -static PyObject* -test_raise_greenlet_error(PyObject* self) -{ - PyErr_SetString(PyExc_GreenletError, "test greenlet.error exception"); - return NULL; -} - -static PyObject* -test_throw(PyObject* self, PyGreenlet* g) -{ - const char msg[] = "take that sucka!"; - PyObject* msg_obj = Py_BuildValue("s", msg); - PyGreenlet_Throw(g, PyExc_ValueError, msg_obj, NULL); - Py_DECREF(msg_obj); - if (PyErr_Occurred()) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject* -test_throw_exact(PyObject* self, PyObject* args) -{ - PyGreenlet* g = NULL; - PyObject* typ = NULL; - PyObject* val = NULL; - PyObject* tb = NULL; - - if (!PyArg_ParseTuple(args, "OOOO:throw", &g, &typ, &val, &tb)) { - return NULL; - } - - PyGreenlet_Throw(g, typ, val, tb); - if (PyErr_Occurred()) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyMethodDef test_methods[] = { - {"test_switch", - (PyCFunction)test_switch, - METH_O, - "Switch to the provided greenlet sending provided arguments, and \n" - "return the results."}, - {"test_switch_kwargs", - (PyCFunction)test_switch_kwargs, - METH_VARARGS | METH_KEYWORDS, - "Switch to the provided greenlet sending the provided keyword args."}, - {"test_getcurrent", - (PyCFunction)test_getcurrent, - METH_NOARGS, - "Test PyGreenlet_GetCurrent()"}, - {"test_setparent", - (PyCFunction)test_setparent, - METH_O, - "Se the parent of the provided greenlet and switch to it."}, - {"test_new_greenlet", - (PyCFunction)test_new_greenlet, - METH_O, - "Test PyGreenlet_New()"}, - {"test_raise_dead_greenlet", - (PyCFunction)test_raise_dead_greenlet, - METH_NOARGS, - "Just raise greenlet.GreenletExit"}, - {"test_raise_greenlet_error", - (PyCFunction)test_raise_greenlet_error, - METH_NOARGS, - "Just raise greenlet.error"}, - {"test_throw", - (PyCFunction)test_throw, - METH_O, - "Throw a ValueError at the provided greenlet"}, - {"test_throw_exact", - (PyCFunction)test_throw_exact, - METH_VARARGS, - "Throw exactly the arguments given at the provided greenlet"}, - {NULL, NULL, 0, NULL} -}; - - -#define INITERROR return NULL - -static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, - TEST_MODULE_NAME, - NULL, - 0, - test_methods, - NULL, - NULL, - NULL, - NULL}; - -PyMODINIT_FUNC -PyInit__test_extension(void) -{ - PyObject* module = NULL; - module = PyModule_Create(&moduledef); - - if (module == NULL) { - return NULL; - } - - PyGreenlet_Import(); - return module; -} diff --git a/.venv/Lib/site-packages/greenlet/tests/_test_extension.cp311-win_amd64.pyd b/.venv/Lib/site-packages/greenlet/tests/_test_extension.cp311-win_amd64.pyd deleted file mode 100644 index 97025b13334e696a50eec4303a298524b52572ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14336 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 If2HyN33HL{@&Et; diff --git a/.venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cp311-win_amd64.pyd b/.venv/Lib/site-packages/greenlet/tests/_test_extension_cpp.cp311-win_amd64.pyd deleted file mode 100644 index a9aa58aeb3321740c094a766af1121c2ae77e3e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15872 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 -#include - -struct exception_t { - int depth; - exception_t(int depth) : depth(depth) {} -}; - -/* Functions are called via pointers to prevent inlining */ -static void (*p_test_exception_throw_nonstd)(int depth); -static void (*p_test_exception_throw_std)(); -static PyObject* (*p_test_exception_switch_recurse)(int depth, int left); - -static void -test_exception_throw_nonstd(int depth) -{ - throw exception_t(depth); -} - -static void -test_exception_throw_std() -{ - throw std::runtime_error("Thrown from an extension."); -} - -static PyObject* -test_exception_switch_recurse(int depth, int left) -{ - if (left > 0) { - return p_test_exception_switch_recurse(depth, left - 1); - } - - PyObject* result = NULL; - PyGreenlet* self = PyGreenlet_GetCurrent(); - if (self == NULL) - return NULL; - - try { - if (PyGreenlet_Switch(PyGreenlet_GET_PARENT(self), NULL, NULL) == NULL) { - Py_DECREF(self); - return NULL; - } - p_test_exception_throw_nonstd(depth); - PyErr_SetString(PyExc_RuntimeError, - "throwing C++ exception didn't work"); - } - catch (const exception_t& e) { - if (e.depth != depth) - PyErr_SetString(PyExc_AssertionError, "depth mismatch"); - else - result = PyLong_FromLong(depth); - } - catch (...) { - PyErr_SetString(PyExc_RuntimeError, "unexpected C++ exception"); - } - - Py_DECREF(self); - return result; -} - -/* test_exception_switch(int depth) - * - recurses depth times - * - switches to parent inside try/catch block - * - throws an exception that (expected to be caught in the same function) - * - verifies depth matches (exceptions shouldn't be caught in other greenlets) - */ -static PyObject* -test_exception_switch(PyObject* UNUSED(self), PyObject* args) -{ - int depth; - if (!PyArg_ParseTuple(args, "i", &depth)) - return NULL; - return p_test_exception_switch_recurse(depth, depth); -} - - -static PyObject* -py_test_exception_throw_nonstd(PyObject* self, PyObject* args) -{ - if (!PyArg_ParseTuple(args, "")) - return NULL; - p_test_exception_throw_nonstd(0); - PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); - return NULL; -} - -static PyObject* -py_test_exception_throw_std(PyObject* self, PyObject* args) -{ - if (!PyArg_ParseTuple(args, "")) - return NULL; - p_test_exception_throw_std(); - PyErr_SetString(PyExc_AssertionError, "unreachable code running after throw"); - return NULL; -} - -static PyObject* -py_test_call(PyObject* self, PyObject* arg) -{ - PyObject* noargs = PyTuple_New(0); - PyObject* ret = PyObject_Call(arg, noargs, nullptr); - Py_DECREF(noargs); - return ret; -} - - - -/* test_exception_switch_and_do_in_g2(g2func) - * - creates new greenlet g2 to run g2func - * - switches to g2 inside try/catch block - * - verifies that no exception has been caught - * - * it is used together with test_exception_throw to verify that unhandled - * exceptions thrown in one greenlet do not propagate to other greenlet nor - * segfault the process. - */ -static PyObject* -test_exception_switch_and_do_in_g2(PyObject* self, PyObject* args) -{ - PyObject* g2func = NULL; - PyObject* result = NULL; - - if (!PyArg_ParseTuple(args, "O", &g2func)) - return NULL; - PyGreenlet* g2 = PyGreenlet_New(g2func, NULL); - if (!g2) { - return NULL; - } - - try { - result = PyGreenlet_Switch(g2, NULL, NULL); - if (!result) { - return NULL; - } - } - catch (const exception_t& e) { - /* if we are here the memory can be already corrupted and the program - * might crash before below py-level exception might become printed. - * -> print something to stderr to make it clear that we had entered - * this catch block. - * See comments in inner_bootstrap() - */ -#if defined(WIN32) || defined(_WIN32) - fprintf(stderr, "C++ exception unexpectedly caught in g1\n"); - PyErr_SetString(PyExc_AssertionError, "C++ exception unexpectedly caught in g1"); - Py_XDECREF(result); - return NULL; -#else - throw; -#endif - } - - Py_XDECREF(result); - Py_RETURN_NONE; -} - -static PyMethodDef test_methods[] = { - {"test_exception_switch", - (PyCFunction)&test_exception_switch, - METH_VARARGS, - "Switches to parent twice, to test exception handling and greenlet " - "switching."}, - {"test_exception_switch_and_do_in_g2", - (PyCFunction)&test_exception_switch_and_do_in_g2, - METH_VARARGS, - "Creates new greenlet g2 to run g2func and switches to it inside try/catch " - "block. Used together with test_exception_throw to verify that unhandled " - "C++ exceptions thrown in a greenlet doe not corrupt memory."}, - {"test_exception_throw_nonstd", - (PyCFunction)&py_test_exception_throw_nonstd, - METH_VARARGS, - "Throws non-standard C++ exception. Calling this function directly should abort the process." - }, - {"test_exception_throw_std", - (PyCFunction)&py_test_exception_throw_std, - METH_VARARGS, - "Throws standard C++ exception. Calling this function directly should abort the process." - }, - {"test_call", - (PyCFunction)&py_test_call, - METH_O, - "Call the given callable. Unlike calling it directly, this creates a " - "new C-level stack frame, which may be helpful in testing." - }, - {NULL, NULL, 0, NULL} -}; - - -static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, - "greenlet.tests._test_extension_cpp", - NULL, - 0, - test_methods, - NULL, - NULL, - NULL, - NULL}; - -PyMODINIT_FUNC -PyInit__test_extension_cpp(void) -{ - PyObject* module = NULL; - - module = PyModule_Create(&moduledef); - - if (module == NULL) { - return NULL; - } - - PyGreenlet_Import(); - if (_PyGreenlet_API == NULL) { - return NULL; - } - - p_test_exception_throw_nonstd = test_exception_throw_nonstd; - p_test_exception_throw_std = test_exception_throw_std; - p_test_exception_switch_recurse = test_exception_switch_recurse; - - return module; -} diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_clearing_run_switches.py b/.venv/Lib/site-packages/greenlet/tests/fail_clearing_run_switches.py deleted file mode 100644 index 6dd1492..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_clearing_run_switches.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -If we have a run callable passed to the constructor or set as an -attribute, but we don't actually use that (because ``__getattribute__`` -or the like interferes), then when we clear callable before beginning -to run, there's an opportunity for Python code to run. - -""" -import greenlet - -g = None -main = greenlet.getcurrent() - -results = [] - -class RunCallable: - - def __del__(self): - results.append(('RunCallable', '__del__')) - main.switch('from RunCallable') - - -class G(greenlet.greenlet): - - def __getattribute__(self, name): - if name == 'run': - results.append(('G.__getattribute__', 'run')) - return run_func - return object.__getattribute__(self, name) - - -def run_func(): - results.append(('run_func', 'enter')) - - -g = G(RunCallable()) -# Try to start G. It will get to the point where it deletes -# its run callable C++ variable in inner_bootstrap. That triggers -# the __del__ method, which switches back to main before g -# actually even starts running. -x = g.switch() -results.append(('main: g.switch()', x)) -# In the C++ code, this results in g->g_switch() appearing to return, even though -# it has yet to run. -print('In main with', x, flush=True) -g.switch() -print('RESULTS', results) diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_cpp_exception.py b/.venv/Lib/site-packages/greenlet/tests/fail_cpp_exception.py deleted file mode 100644 index fa4dc2e..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_cpp_exception.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Helper for testing a C++ exception throw aborts the process. - -Takes one argument, the name of the function in :mod:`_test_extension_cpp` to call. -""" -import sys -import greenlet -from greenlet.tests import _test_extension_cpp -print('fail_cpp_exception is running') - -def run_unhandled_exception_in_greenlet_aborts(): - def _(): - _test_extension_cpp.test_exception_switch_and_do_in_g2( - _test_extension_cpp.test_exception_throw_nonstd - ) - g1 = greenlet.greenlet(_) - g1.switch() - - -func_name = sys.argv[1] -try: - func = getattr(_test_extension_cpp, func_name) -except AttributeError: - if func_name == run_unhandled_exception_in_greenlet_aborts.__name__: - func = run_unhandled_exception_in_greenlet_aborts - elif func_name == 'run_as_greenlet_target': - g = greenlet.greenlet(_test_extension_cpp.test_exception_throw_std) - func = g.switch - else: - raise -print('raising', func, flush=True) -func() diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_initialstub_already_started.py b/.venv/Lib/site-packages/greenlet/tests/fail_initialstub_already_started.py deleted file mode 100644 index c1a44ef..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_initialstub_already_started.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Testing initialstub throwing an already started exception. -""" - -import greenlet - -a = None -b = None -c = None -main = greenlet.getcurrent() - -# If we switch into a dead greenlet, -# we go looking for its parents. -# if a parent is not yet started, we start it. - -results = [] - -def a_run(*args): - #results.append('A') - results.append(('Begin A', args)) - - -def c_run(): - results.append('Begin C') - b.switch('From C') - results.append('C done') - -class A(greenlet.greenlet): pass - -class B(greenlet.greenlet): - doing_it = False - def __getattribute__(self, name): - if name == 'run' and not self.doing_it: - assert greenlet.getcurrent() is c - self.doing_it = True - results.append('Switch to b from B.__getattribute__ in ' - + type(greenlet.getcurrent()).__name__) - b.switch() - results.append('B.__getattribute__ back from main in ' - + type(greenlet.getcurrent()).__name__) - if name == 'run': - name = '_B_run' - return object.__getattribute__(self, name) - - def _B_run(self, *arg): - results.append(('Begin B', arg)) - results.append('_B_run switching to main') - main.switch('From B') - -class C(greenlet.greenlet): - pass -a = A(a_run) -b = B(parent=a) -c = C(c_run, b) - -# Start a child; while running, it will start B, -# but starting B will ALSO start B. -result = c.switch() -results.append(('main from c', result)) - -# Switch back to C, which was in the middle of switching -# already. This will throw the ``GreenletStartedWhileInPython`` -# exception, which results in parent A getting started (B is finished) -c.switch() - -results.append(('A dead?', a.dead, 'B dead?', b.dead, 'C dead?', c.dead)) - -# A and B should both be dead now. -assert a.dead -assert b.dead -assert not c.dead - -result = c.switch() -results.append(('main from c.2', result)) -# Now C is dead -assert c.dead - -print("RESULTS:", results) diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_slp_switch.py b/.venv/Lib/site-packages/greenlet/tests/fail_slp_switch.py deleted file mode 100644 index 0990526..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_slp_switch.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" -A test helper for seeing what happens when slp_switch() -fails. -""" -# pragma: no cover - -import greenlet - - -print('fail_slp_switch is running', flush=True) - -runs = [] -def func(): - runs.append(1) - greenlet.getcurrent().parent.switch() - runs.append(2) - greenlet.getcurrent().parent.switch() - runs.append(3) - -g = greenlet._greenlet.UnswitchableGreenlet(func) -g.switch() -assert runs == [1] -g.switch() -assert runs == [1, 2] -g.force_slp_switch_error = True - -# This should crash. -g.switch() diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets.py b/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets.py deleted file mode 100644 index e151b19..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Uses a trace function to switch greenlets at unexpected times. - -In the trace function, we switch from the current greenlet to another -greenlet, which switches -""" -import greenlet - -g1 = None -g2 = None - -switch_to_g2 = False - -def tracefunc(*args): - print('TRACE', *args) - global switch_to_g2 - if switch_to_g2: - switch_to_g2 = False - g2.switch() - print('\tLEAVE TRACE', *args) - -def g1_run(): - print('In g1_run') - global switch_to_g2 - switch_to_g2 = True - from_parent = greenlet.getcurrent().parent.switch() - print('Return to g1_run') - print('From parent', from_parent) - -def g2_run(): - #g1.switch() - greenlet.getcurrent().parent.switch() - -greenlet.settrace(tracefunc) - -g1 = greenlet.greenlet(g1_run) -g2 = greenlet.greenlet(g2_run) - -# This switch didn't actually finish! -# And if it did, it would raise TypeError -# because g1_run() doesn't take any arguments. -g1.switch(1) -print('Back in main') -g1.switch(2) diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets2.py b/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets2.py deleted file mode 100644 index 1f6b66b..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_switch_three_greenlets2.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Like fail_switch_three_greenlets, but the call into g1_run would actually be -valid. -""" -import greenlet - -g1 = None -g2 = None - -switch_to_g2 = True - -results = [] - -def tracefunc(*args): - results.append(('trace', args[0])) - print('TRACE', *args) - global switch_to_g2 - if switch_to_g2: - switch_to_g2 = False - g2.switch('g2 from tracefunc') - print('\tLEAVE TRACE', *args) - -def g1_run(arg): - results.append(('g1 arg', arg)) - print('In g1_run') - from_parent = greenlet.getcurrent().parent.switch('from g1_run') - results.append(('g1 from parent', from_parent)) - return 'g1 done' - -def g2_run(arg): - #g1.switch() - results.append(('g2 arg', arg)) - parent = greenlet.getcurrent().parent.switch('from g2_run') - global switch_to_g2 - switch_to_g2 = False - results.append(('g2 from parent', parent)) - return 'g2 done' - - -greenlet.settrace(tracefunc) - -g1 = greenlet.greenlet(g1_run) -g2 = greenlet.greenlet(g2_run) - -x = g1.switch('g1 from main') -results.append(('main g1', x)) -print('Back in main', x) -x = g1.switch('g2 from main') -results.append(('main g2', x)) -print('back in amain again', x) -x = g1.switch('g1 from main 2') -results.append(('main g1.2', x)) -x = g2.switch() -results.append(('main g2.2', x)) -print("RESULTS:", results) diff --git a/.venv/Lib/site-packages/greenlet/tests/fail_switch_two_greenlets.py b/.venv/Lib/site-packages/greenlet/tests/fail_switch_two_greenlets.py deleted file mode 100644 index 3e52345..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/fail_switch_two_greenlets.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Uses a trace function to switch greenlets at unexpected times. - -In the trace function, we switch from the current greenlet to another -greenlet, which switches -""" -import greenlet - -g1 = None -g2 = None - -switch_to_g2 = False - -def tracefunc(*args): - print('TRACE', *args) - global switch_to_g2 - if switch_to_g2: - switch_to_g2 = False - g2.switch() - print('\tLEAVE TRACE', *args) - -def g1_run(): - print('In g1_run') - global switch_to_g2 - switch_to_g2 = True - greenlet.getcurrent().parent.switch() - print('Return to g1_run') - print('Falling off end of g1_run') - -def g2_run(): - g1.switch() - print('Falling off end of g2') - -greenlet.settrace(tracefunc) - -g1 = greenlet.greenlet(g1_run) -g2 = greenlet.greenlet(g2_run) - -g1.switch() -print('Falling off end of main') -g2.switch() diff --git a/.venv/Lib/site-packages/greenlet/tests/leakcheck.py b/.venv/Lib/site-packages/greenlet/tests/leakcheck.py deleted file mode 100644 index a5152fb..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/leakcheck.py +++ /dev/null @@ -1,319 +0,0 @@ -# Copyright (c) 2018 gevent community -# Copyright (c) 2021 greenlet community -# -# This was originally part of gevent's test suite. The main author -# (Jason Madden) vendored a copy of it into greenlet. -# -# 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. -from __future__ import print_function - -import os -import sys -import gc - -from functools import wraps -import unittest - - -import objgraph - -# graphviz 0.18 (Nov 7 2021), available only on Python 3.6 and newer, -# has added type hints (sigh). It wants to use ``typing.Literal`` for -# some stuff, but that's only available on Python 3.9+. If that's not -# found, it creates a ``unittest.mock.MagicMock`` object and annotates -# with that. These are GC'able objects, and doing almost *anything* -# with them results in an explosion of objects. For example, trying to -# compare them for equality creates new objects. This causes our -# leakchecks to fail, with reports like: -# -# greenlet.tests.leakcheck.LeakCheckError: refcount increased by [337, 1333, 343, 430, 530, 643, 769] -# _Call 1820 +546 -# dict 4094 +76 -# MagicProxy 585 +73 -# tuple 2693 +66 -# _CallList 24 +3 -# weakref 1441 +1 -# function 5996 +1 -# type 736 +1 -# cell 592 +1 -# MagicMock 8 +1 -# -# To avoid this, we *could* filter this type of object out early. In -# principle it could leak, but we don't use mocks in greenlet, so it -# doesn't leak from us. However, a further issue is that ``MagicMock`` -# objects have subobjects that are also GC'able, like ``_Call``, and -# those create new mocks of their own too. So we'd have to filter them -# as well, and they're not public. That's OK, we can workaround the -# problem by being very careful to never compare by equality or other -# user-defined operators, only using object identity or other builtin -# functions. - -RUNNING_ON_GITHUB_ACTIONS = os.environ.get('GITHUB_ACTIONS') -RUNNING_ON_TRAVIS = os.environ.get('TRAVIS') or RUNNING_ON_GITHUB_ACTIONS -RUNNING_ON_APPVEYOR = os.environ.get('APPVEYOR') -RUNNING_ON_CI = RUNNING_ON_TRAVIS or RUNNING_ON_APPVEYOR -RUNNING_ON_MANYLINUX = os.environ.get('GREENLET_MANYLINUX') -SKIP_LEAKCHECKS = RUNNING_ON_MANYLINUX or os.environ.get('GREENLET_SKIP_LEAKCHECKS') -SKIP_FAILING_LEAKCHECKS = os.environ.get('GREENLET_SKIP_FAILING_LEAKCHECKS') -ONLY_FAILING_LEAKCHECKS = os.environ.get('GREENLET_ONLY_FAILING_LEAKCHECKS') - -def ignores_leakcheck(func): - """ - Ignore the given object during leakchecks. - - Can be applied to a method, in which case the method will run, but - will not be subject to leak checks. - - If applied to a class, the entire class will be skipped during leakchecks. This - is intended to be used for classes that are very slow and cause problems such as - test timeouts; typically it will be used for classes that are subclasses of a base - class and specify variants of behaviour (such as pool sizes). - """ - func.ignore_leakcheck = True - return func - -def fails_leakcheck(func): - """ - Mark that the function is known to leak. - """ - func.fails_leakcheck = True - if SKIP_FAILING_LEAKCHECKS: - func = unittest.skip("Skipping known failures")(func) - return func - -class LeakCheckError(AssertionError): - pass - -if hasattr(sys, 'getobjects'): - # In a Python build with ``--with-trace-refs``, make objgraph - # trace *all* the objects, not just those that are tracked by the - # GC - class _MockGC(object): - def get_objects(self): - return sys.getobjects(0) # pylint:disable=no-member - def __getattr__(self, name): - return getattr(gc, name) - objgraph.gc = _MockGC() - fails_strict_leakcheck = fails_leakcheck -else: - def fails_strict_leakcheck(func): - """ - Decorator for a function that is known to fail when running - strict (``sys.getobjects()``) leakchecks. - - This type of leakcheck finds all objects, even those, such as - strings, which are not tracked by the garbage collector. - """ - return func - -class ignores_types_in_strict_leakcheck(object): - def __init__(self, types): - self.types = types - def __call__(self, func): - func.leakcheck_ignore_types = self.types - return func - -class _RefCountChecker(object): - - # Some builtin things that we ignore - # XXX: Those things were ignored by gevent, but they're important here, - # presumably. - IGNORED_TYPES = () #(tuple, dict, types.FrameType, types.TracebackType) - - def __init__(self, testcase, function): - self.testcase = testcase - self.function = function - self.deltas = [] - self.peak_stats = {} - self.ignored_types = () - - # The very first time we are called, we have already been - # self.setUp() by the test runner, so we don't need to do it again. - self.needs_setUp = False - - def _include_object_p(self, obj): - # pylint:disable=too-many-return-statements - # - # See the comment block at the top. We must be careful to - # avoid invoking user-defined operations. - if obj is self: - return False - kind = type(obj) - # ``self._include_object_p == obj`` returns NotImplemented - # for non-function objects, which causes the interpreter - # to try to reverse the order of arguments...which leads - # to the explosion of mock objects. We don't want that, so we implement - # the check manually. - if kind == type(self._include_object_p): - try: - # pylint:disable=not-callable - exact_method_equals = self._include_object_p.__eq__(obj) - except AttributeError: - # Python 2.7 methods may only have __cmp__, and that raises a - # TypeError for non-method arguments - # pylint:disable=no-member - exact_method_equals = self._include_object_p.__cmp__(obj) == 0 - - if exact_method_equals is not NotImplemented and exact_method_equals: - return False - - # Similarly, we need to check identity in our __dict__ to avoid mock explosions. - for x in self.__dict__.values(): - if obj is x: - return False - - - if kind in self.ignored_types or kind in self.IGNORED_TYPES: - return False - - return True - - def _growth(self): - return objgraph.growth(limit=None, peak_stats=self.peak_stats, - filter=self._include_object_p) - - def _report_diff(self, growth): - if not growth: - return "" - - lines = [] - width = max(len(name) for name, _, _ in growth) - for name, count, delta in growth: - lines.append('%-*s%9d %+9d' % (width, name, count, delta)) - - diff = '\n'.join(lines) - return diff - - - def _run_test(self, args, kwargs): - gc_enabled = gc.isenabled() - gc.disable() - - if self.needs_setUp: - self.testcase.setUp() - self.testcase.skipTearDown = False - try: - self.function(self.testcase, *args, **kwargs) - finally: - self.testcase.tearDown() - self.testcase.doCleanups() - self.testcase.skipTearDown = True - self.needs_setUp = True - if gc_enabled: - gc.enable() - - def _growth_after(self): - # Grab post snapshot - # pylint:disable=no-member - if 'urlparse' in sys.modules: - sys.modules['urlparse'].clear_cache() - if 'urllib.parse' in sys.modules: - sys.modules['urllib.parse'].clear_cache() - - return self._growth() - - def _check_deltas(self, growth): - # Return false when we have decided there is no leak, - # true if we should keep looping, raises an assertion - # if we have decided there is a leak. - - deltas = self.deltas - if not deltas: - # We haven't run yet, no data, keep looping - return True - - if gc.garbage: - raise LeakCheckError("Generated uncollectable garbage %r" % (gc.garbage,)) - - - # the following configurations are classified as "no leak" - # [0, 0] - # [x, 0, 0] - # [... a, b, c, d] where a+b+c+d = 0 - # - # the following configurations are classified as "leak" - # [... z, z, z] where z > 0 - - if deltas[-2:] == [0, 0] and len(deltas) in (2, 3): - return False - - if deltas[-3:] == [0, 0, 0]: - return False - - if len(deltas) >= 4 and sum(deltas[-4:]) == 0: - return False - - if len(deltas) >= 3 and deltas[-1] > 0 and deltas[-1] == deltas[-2] and deltas[-2] == deltas[-3]: - diff = self._report_diff(growth) - raise LeakCheckError('refcount increased by %r\n%s' % (deltas, diff)) - - # OK, we don't know for sure yet. Let's search for more - if sum(deltas[-3:]) <= 0 or sum(deltas[-4:]) <= 0 or deltas[-4:].count(0) >= 2: - # this is suspicious, so give a few more runs - limit = 11 - else: - limit = 7 - if len(deltas) >= limit: - raise LeakCheckError('refcount increased by %r\n%s' - % (deltas, - self._report_diff(growth))) - - # We couldn't decide yet, keep going - return True - - def __call__(self, args, kwargs): - for _ in range(3): - gc.collect() - - expect_failure = getattr(self.function, 'fails_leakcheck', False) - if expect_failure: - self.testcase.expect_greenlet_leak = True - self.ignored_types = getattr(self.function, "leakcheck_ignore_types", ()) - - # Capture state before; the incremental will be - # updated by each call to _growth_after - growth = self._growth() - - try: - while self._check_deltas(growth): - self._run_test(args, kwargs) - - growth = self._growth_after() - - self.deltas.append(sum((stat[2] for stat in growth))) - except LeakCheckError: - if not expect_failure: - raise - else: - if expect_failure: - raise LeakCheckError("Expected %s to leak but it did not." % (self.function,)) - -def wrap_refcount(method): - if getattr(method, 'ignore_leakcheck', False) or SKIP_LEAKCHECKS: - return method - - @wraps(method) - def wrapper(self, *args, **kwargs): # pylint:disable=too-many-branches - if getattr(self, 'ignore_leakcheck', False): - raise unittest.SkipTest("This class ignored during leakchecks") - if ONLY_FAILING_LEAKCHECKS and not getattr(method, 'fails_leakcheck', False): - raise unittest.SkipTest("Only running tests that fail leakchecks.") - return _RefCountChecker(self, method)(args, kwargs) - - return wrapper diff --git a/.venv/Lib/site-packages/greenlet/tests/test_contextvars.py b/.venv/Lib/site-packages/greenlet/tests/test_contextvars.py deleted file mode 100644 index 9a16f67..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_contextvars.py +++ /dev/null @@ -1,310 +0,0 @@ -from __future__ import print_function - -import gc -import sys -import unittest - -from functools import partial -from unittest import skipUnless -from unittest import skipIf - -from greenlet import greenlet -from greenlet import getcurrent -from . import TestCase - - -try: - from contextvars import Context - from contextvars import ContextVar - from contextvars import copy_context - # From the documentation: - # - # Important: Context Variables should be created at the top module - # level and never in closures. Context objects hold strong - # references to context variables which prevents context variables - # from being properly garbage collected. - ID_VAR = ContextVar("id", default=None) - VAR_VAR = ContextVar("var", default=None) - ContextVar = None -except ImportError: - Context = ContextVar = copy_context = None - -# We don't support testing if greenlet's built-in context var support is disabled. -@skipUnless(Context is not None, "ContextVar not supported") -class ContextVarsTests(TestCase): - def _new_ctx_run(self, *args, **kwargs): - return copy_context().run(*args, **kwargs) - - def _increment(self, greenlet_id, callback, counts, expect): - ctx_var = ID_VAR - if expect is None: - self.assertIsNone(ctx_var.get()) - else: - self.assertEqual(ctx_var.get(), expect) - ctx_var.set(greenlet_id) - for _ in range(2): - counts[ctx_var.get()] += 1 - callback() - - def _test_context(self, propagate_by): - # pylint:disable=too-many-branches - ID_VAR.set(0) - - callback = getcurrent().switch - counts = dict((i, 0) for i in range(5)) - - lets = [ - greenlet(partial( - partial( - copy_context().run, - self._increment - ) if propagate_by == "run" else self._increment, - greenlet_id=i, - callback=callback, - counts=counts, - expect=( - i - 1 if propagate_by == "share" else - 0 if propagate_by in ("set", "run") else None - ) - )) - for i in range(1, 5) - ] - - for let in lets: - if propagate_by == "set": - let.gr_context = copy_context() - elif propagate_by == "share": - let.gr_context = getcurrent().gr_context - - for i in range(2): - counts[ID_VAR.get()] += 1 - for let in lets: - let.switch() - - if propagate_by == "run": - # Must leave each context.run() in reverse order of entry - for let in reversed(lets): - let.switch() - else: - # No context.run(), so fine to exit in any order. - for let in lets: - let.switch() - - for let in lets: - self.assertTrue(let.dead) - # When using run(), we leave the run() as the greenlet dies, - # and there's no context "underneath". When not using run(), - # gr_context still reflects the context the greenlet was - # running in. - if propagate_by == 'run': - self.assertIsNone(let.gr_context) - else: - self.assertIsNotNone(let.gr_context) - - - if propagate_by == "share": - self.assertEqual(counts, {0: 1, 1: 1, 2: 1, 3: 1, 4: 6}) - else: - self.assertEqual(set(counts.values()), set([2])) - - def test_context_propagated_by_context_run(self): - self._new_ctx_run(self._test_context, "run") - - def test_context_propagated_by_setting_attribute(self): - self._new_ctx_run(self._test_context, "set") - - def test_context_not_propagated(self): - self._new_ctx_run(self._test_context, None) - - def test_context_shared(self): - self._new_ctx_run(self._test_context, "share") - - def test_break_ctxvars(self): - let1 = greenlet(copy_context().run) - let2 = greenlet(copy_context().run) - let1.switch(getcurrent().switch) - let2.switch(getcurrent().switch) - # Since let2 entered the current context and let1 exits its own, the - # interpreter emits: - # RuntimeError: cannot exit context: thread state references a different context object - let1.switch() - - def test_not_broken_if_using_attribute_instead_of_context_run(self): - let1 = greenlet(getcurrent().switch) - let2 = greenlet(getcurrent().switch) - let1.gr_context = copy_context() - let2.gr_context = copy_context() - let1.switch() - let2.switch() - let1.switch() - let2.switch() - - def test_context_assignment_while_running(self): - # pylint:disable=too-many-statements - ID_VAR.set(None) - - def target(): - self.assertIsNone(ID_VAR.get()) - self.assertIsNone(gr.gr_context) - - # Context is created on first use - ID_VAR.set(1) - self.assertIsInstance(gr.gr_context, Context) - self.assertEqual(ID_VAR.get(), 1) - self.assertEqual(gr.gr_context[ID_VAR], 1) - - # Clearing the context makes it get re-created as another - # empty context when next used - old_context = gr.gr_context - gr.gr_context = None # assign None while running - self.assertIsNone(ID_VAR.get()) - self.assertIsNone(gr.gr_context) - ID_VAR.set(2) - self.assertIsInstance(gr.gr_context, Context) - self.assertEqual(ID_VAR.get(), 2) - self.assertEqual(gr.gr_context[ID_VAR], 2) - - new_context = gr.gr_context - getcurrent().parent.switch((old_context, new_context)) - # parent switches us back to old_context - - self.assertEqual(ID_VAR.get(), 1) - gr.gr_context = new_context # assign non-None while running - self.assertEqual(ID_VAR.get(), 2) - - getcurrent().parent.switch() - # parent switches us back to no context - self.assertIsNone(ID_VAR.get()) - self.assertIsNone(gr.gr_context) - gr.gr_context = old_context - self.assertEqual(ID_VAR.get(), 1) - - getcurrent().parent.switch() - # parent switches us back to no context - self.assertIsNone(ID_VAR.get()) - self.assertIsNone(gr.gr_context) - - gr = greenlet(target) - - with self.assertRaisesRegex(AttributeError, "can't delete context attribute"): - del gr.gr_context - - self.assertIsNone(gr.gr_context) - old_context, new_context = gr.switch() - self.assertIs(new_context, gr.gr_context) - self.assertEqual(old_context[ID_VAR], 1) - self.assertEqual(new_context[ID_VAR], 2) - self.assertEqual(new_context.run(ID_VAR.get), 2) - gr.gr_context = old_context # assign non-None while suspended - gr.switch() - self.assertIs(gr.gr_context, new_context) - gr.gr_context = None # assign None while suspended - gr.switch() - self.assertIs(gr.gr_context, old_context) - gr.gr_context = None - gr.switch() - self.assertIsNone(gr.gr_context) - - # Make sure there are no reference leaks - gr = None - gc.collect() - self.assertEqual(sys.getrefcount(old_context), 2) - self.assertEqual(sys.getrefcount(new_context), 2) - - def test_context_assignment_different_thread(self): - import threading - VAR_VAR.set(None) - ctx = Context() - - is_running = threading.Event() - should_suspend = threading.Event() - did_suspend = threading.Event() - should_exit = threading.Event() - holder = [] - - def greenlet_in_thread_fn(): - VAR_VAR.set(1) - is_running.set() - should_suspend.wait(10) - VAR_VAR.set(2) - getcurrent().parent.switch() - holder.append(VAR_VAR.get()) - - def thread_fn(): - gr = greenlet(greenlet_in_thread_fn) - gr.gr_context = ctx - holder.append(gr) - gr.switch() - did_suspend.set() - should_exit.wait(10) - gr.switch() - del gr - greenlet() # trigger cleanup - - thread = threading.Thread(target=thread_fn, daemon=True) - thread.start() - is_running.wait(10) - gr = holder[0] - - # Can't access or modify context if the greenlet is running - # in a different thread - with self.assertRaisesRegex(ValueError, "running in a different"): - getattr(gr, 'gr_context') - with self.assertRaisesRegex(ValueError, "running in a different"): - gr.gr_context = None - - should_suspend.set() - did_suspend.wait(10) - - # OK to access and modify context if greenlet is suspended - self.assertIs(gr.gr_context, ctx) - self.assertEqual(gr.gr_context[VAR_VAR], 2) - gr.gr_context = None - - should_exit.set() - thread.join(10) - - self.assertEqual(holder, [gr, None]) - - # Context can still be accessed/modified when greenlet is dead: - self.assertIsNone(gr.gr_context) - gr.gr_context = ctx - self.assertIs(gr.gr_context, ctx) - - # Otherwise we leak greenlets on some platforms. - # XXX: Should be able to do this automatically - del holder[:] - gr = None - thread = None - - def test_context_assignment_wrong_type(self): - g = greenlet() - with self.assertRaisesRegex(TypeError, - "greenlet context must be a contextvars.Context or None"): - g.gr_context = self - - -@skipIf(Context is not None, "ContextVar supported") -class NoContextVarsTests(TestCase): - def test_contextvars_errors(self): - let1 = greenlet(getcurrent().switch) - self.assertFalse(hasattr(let1, 'gr_context')) - with self.assertRaises(AttributeError): - getattr(let1, 'gr_context') - - with self.assertRaises(AttributeError): - let1.gr_context = None - - let1.switch() - - with self.assertRaises(AttributeError): - getattr(let1, 'gr_context') - - with self.assertRaises(AttributeError): - let1.gr_context = None - - del let1 - - -if __name__ == '__main__': - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_cpp.py b/.venv/Lib/site-packages/greenlet/tests/test_cpp.py deleted file mode 100644 index 2d0cc9c..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_cpp.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import print_function -from __future__ import absolute_import - -import subprocess -import unittest - -import greenlet -from . import _test_extension_cpp -from . import TestCase -from . import WIN - -class CPPTests(TestCase): - def test_exception_switch(self): - greenlets = [] - for i in range(4): - g = greenlet.greenlet(_test_extension_cpp.test_exception_switch) - g.switch(i) - greenlets.append(g) - for i, g in enumerate(greenlets): - self.assertEqual(g.switch(), i) - - def _do_test_unhandled_exception(self, target): - import os - import sys - script = os.path.join( - os.path.dirname(__file__), - 'fail_cpp_exception.py', - ) - args = [sys.executable, script, target.__name__ if not isinstance(target, str) else target] - __traceback_info__ = args - with self.assertRaises(subprocess.CalledProcessError) as exc: - subprocess.check_output( - args, - encoding='utf-8', - stderr=subprocess.STDOUT - ) - - ex = exc.exception - expected_exit = self.get_expected_returncodes_for_aborted_process() - self.assertIn(ex.returncode, expected_exit) - self.assertIn('fail_cpp_exception is running', ex.output) - return ex.output - - - def test_unhandled_nonstd_exception_aborts(self): - # verify that plain unhandled throw aborts - self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_nonstd) - - def test_unhandled_std_exception_aborts(self): - # verify that plain unhandled throw aborts - self._do_test_unhandled_exception(_test_extension_cpp.test_exception_throw_std) - - @unittest.skipIf(WIN, "XXX: This does not crash on Windows") - # Meaning the exception is getting lost somewhere... - def test_unhandled_std_exception_as_greenlet_function_aborts(self): - # verify that plain unhandled throw aborts - output = self._do_test_unhandled_exception('run_as_greenlet_target') - self.assertIn( - # We really expect this to be prefixed with "greenlet: Unhandled C++ exception:" - # as added by our handler for std::exception (see TUserGreenlet.cpp), but - # that's not correct everywhere --- our handler never runs before std::terminate - # gets called (for example, on arm32). - 'Thrown from an extension.', - output - ) - - def test_unhandled_exception_in_greenlet_aborts(self): - # verify that unhandled throw called in greenlet aborts too - self._do_test_unhandled_exception('run_unhandled_exception_in_greenlet_aborts') - - -if __name__ == '__main__': - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_extension_interface.py b/.venv/Lib/site-packages/greenlet/tests/test_extension_interface.py deleted file mode 100644 index 34b6656..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_extension_interface.py +++ /dev/null @@ -1,115 +0,0 @@ -from __future__ import print_function -from __future__ import absolute_import - -import sys - -import greenlet -from . import _test_extension -from . import TestCase - -# pylint:disable=c-extension-no-member - -class CAPITests(TestCase): - def test_switch(self): - self.assertEqual( - 50, _test_extension.test_switch(greenlet.greenlet(lambda: 50))) - - def test_switch_kwargs(self): - def adder(x, y): - return x * y - g = greenlet.greenlet(adder) - self.assertEqual(6, _test_extension.test_switch_kwargs(g, x=3, y=2)) - - def test_setparent(self): - # pylint:disable=disallowed-name - def foo(): - def bar(): - greenlet.getcurrent().parent.switch() - - # This final switch should go back to the main greenlet, since - # the test_setparent() function in the C extension should have - # reparented this greenlet. - greenlet.getcurrent().parent.switch() - raise AssertionError("Should never have reached this code") - child = greenlet.greenlet(bar) - child.switch() - greenlet.getcurrent().parent.switch(child) - greenlet.getcurrent().parent.throw( - AssertionError("Should never reach this code")) - foo_child = greenlet.greenlet(foo).switch() - self.assertEqual(None, _test_extension.test_setparent(foo_child)) - - def test_getcurrent(self): - _test_extension.test_getcurrent() - - def test_new_greenlet(self): - self.assertEqual(-15, _test_extension.test_new_greenlet(lambda: -15)) - - def test_raise_greenlet_dead(self): - self.assertRaises( - greenlet.GreenletExit, _test_extension.test_raise_dead_greenlet) - - def test_raise_greenlet_error(self): - self.assertRaises( - greenlet.error, _test_extension.test_raise_greenlet_error) - - def test_throw(self): - seen = [] - - def foo(): # pylint:disable=disallowed-name - try: - greenlet.getcurrent().parent.switch() - except ValueError: - seen.append(sys.exc_info()[1]) - except greenlet.GreenletExit: - raise AssertionError - g = greenlet.greenlet(foo) - g.switch() - _test_extension.test_throw(g) - self.assertEqual(len(seen), 1) - self.assertTrue( - isinstance(seen[0], ValueError), - "ValueError was not raised in foo()") - self.assertEqual( - str(seen[0]), - 'take that sucka!', - "message doesn't match") - - def test_non_traceback_param(self): - with self.assertRaises(TypeError) as exc: - _test_extension.test_throw_exact( - greenlet.getcurrent(), - Exception, - Exception(), - self - ) - self.assertEqual(str(exc.exception), - "throw() third argument must be a traceback object") - - def test_instance_of_wrong_type(self): - with self.assertRaises(TypeError) as exc: - _test_extension.test_throw_exact( - greenlet.getcurrent(), - Exception(), - BaseException(), - None, - ) - - self.assertEqual(str(exc.exception), - "instance exception may not have a separate value") - - def test_not_throwable(self): - with self.assertRaises(TypeError) as exc: - _test_extension.test_throw_exact( - greenlet.getcurrent(), - "abc", - None, - None, - ) - self.assertEqual(str(exc.exception), - "exceptions must be classes, or instances, not str") - - -if __name__ == '__main__': - import unittest - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_gc.py b/.venv/Lib/site-packages/greenlet/tests/test_gc.py deleted file mode 100644 index 994addb..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_gc.py +++ /dev/null @@ -1,86 +0,0 @@ -import gc - -import weakref - -import greenlet - - -from . import TestCase -from .leakcheck import fails_leakcheck -# These only work with greenlet gc support -# which is no longer optional. -assert greenlet.GREENLET_USE_GC - -class GCTests(TestCase): - def test_dead_circular_ref(self): - o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch()) - gc.collect() - if o() is not None: - import sys - print("O IS NOT NONE.", sys.getrefcount(o())) - self.assertIsNone(o()) - self.assertFalse(gc.garbage, gc.garbage) - - def test_circular_greenlet(self): - class circular_greenlet(greenlet.greenlet): - self = None - o = circular_greenlet() - o.self = o - o = weakref.ref(o) - gc.collect() - self.assertIsNone(o()) - self.assertFalse(gc.garbage, gc.garbage) - - def test_inactive_ref(self): - class inactive_greenlet(greenlet.greenlet): - def __init__(self): - greenlet.greenlet.__init__(self, run=self.run) - - def run(self): - pass - o = inactive_greenlet() - o = weakref.ref(o) - gc.collect() - self.assertIsNone(o()) - self.assertFalse(gc.garbage, gc.garbage) - - @fails_leakcheck - def test_finalizer_crash(self): - # This test is designed to crash when active greenlets - # are made garbage collectable, until the underlying - # problem is resolved. How does it work: - # - order of object creation is important - # - array is created first, so it is moved to unreachable first - # - we create a cycle between a greenlet and this array - # - we create an object that participates in gc, is only - # referenced by a greenlet, and would corrupt gc lists - # on destruction, the easiest is to use an object with - # a finalizer - # - because array is the first object in unreachable it is - # cleared first, which causes all references to greenlet - # to disappear and causes greenlet to be destroyed, but since - # it is still live it causes a switch during gc, which causes - # an object with finalizer to be destroyed, which causes stack - # corruption and then a crash - - class object_with_finalizer(object): - def __del__(self): - pass - array = [] - parent = greenlet.getcurrent() - def greenlet_body(): - greenlet.getcurrent().object = object_with_finalizer() - try: - parent.switch() - except greenlet.GreenletExit: - print("Got greenlet exit!") - finally: - del greenlet.getcurrent().object - g = greenlet.greenlet(greenlet_body) - g.array = array - array.append(g) - g.switch() - del array - del g - greenlet.getcurrent() - gc.collect() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_generator.py b/.venv/Lib/site-packages/greenlet/tests/test_generator.py deleted file mode 100644 index ca4a644..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_generator.py +++ /dev/null @@ -1,59 +0,0 @@ - -from greenlet import greenlet - -from . import TestCase - -class genlet(greenlet): - parent = None - def __init__(self, *args, **kwds): - self.args = args - self.kwds = kwds - - def run(self): - fn, = self.fn - fn(*self.args, **self.kwds) - - def __iter__(self): - return self - - def __next__(self): - self.parent = greenlet.getcurrent() - result = self.switch() - if self: - return result - - raise StopIteration - - next = __next__ - - -def Yield(value): - g = greenlet.getcurrent() - while not isinstance(g, genlet): - if g is None: - raise RuntimeError('yield outside a genlet') - g = g.parent - g.parent.switch(value) - - -def generator(func): - class Generator(genlet): - fn = (func,) - return Generator - -# ____________________________________________________________ - - -class GeneratorTests(TestCase): - def test_generator(self): - seen = [] - - def g(n): - for i in range(n): - seen.append(i) - Yield(i) - g = generator(g) - for _ in range(3): - for j in g(5): - seen.append(j) - self.assertEqual(seen, 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) diff --git a/.venv/Lib/site-packages/greenlet/tests/test_generator_nested.py b/.venv/Lib/site-packages/greenlet/tests/test_generator_nested.py deleted file mode 100644 index 8d752a6..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_generator_nested.py +++ /dev/null @@ -1,168 +0,0 @@ - -from greenlet import greenlet -from . import TestCase -from .leakcheck import fails_leakcheck - -class genlet(greenlet): - parent = None - def __init__(self, *args, **kwds): - self.args = args - self.kwds = kwds - self.child = None - - def run(self): - # Note the function is packed in a tuple - # to avoid creating a bound method for it. - fn, = self.fn - fn(*self.args, **self.kwds) - - def __iter__(self): - return self - - def set_child(self, child): - self.child = child - - def __next__(self): - if self.child: - child = self.child - while child.child: - tmp = child - child = child.child - tmp.child = None - - result = child.switch() - else: - self.parent = greenlet.getcurrent() - result = self.switch() - - if self: - return result - - raise StopIteration - - next = __next__ - -def Yield(value, level=1): - g = greenlet.getcurrent() - - while level != 0: - if not isinstance(g, genlet): - raise RuntimeError('yield outside a genlet') - if level > 1: - g.parent.set_child(g) - g = g.parent - level -= 1 - - g.switch(value) - - -def Genlet(func): - class TheGenlet(genlet): - fn = (func,) - return TheGenlet - -# ____________________________________________________________ - - -def g1(n, seen): - for i in range(n): - seen.append(i + 1) - yield i - - -def g2(n, seen): - for i in range(n): - seen.append(i + 1) - Yield(i) - -g2 = Genlet(g2) - - -def nested(i): - Yield(i) - - -def g3(n, seen): - for i in range(n): - seen.append(i + 1) - nested(i) -g3 = Genlet(g3) - - -def a(n): - if n == 0: - return - for ii in ax(n - 1): - Yield(ii) - Yield(n) -ax = Genlet(a) - - -def perms(l): - if len(l) > 1: - for e in l: - # No syntactical sugar for generator expressions - x = [Yield([e] + p) for p in perms([x for x in l if x != e])] - assert x - else: - Yield(l) -perms = Genlet(perms) - - -def gr1(n): - for ii in range(1, n): - Yield(ii) - Yield(ii * ii, 2) - -gr1 = Genlet(gr1) - - -def gr2(n, seen): - for ii in gr1(n): - seen.append(ii) - -gr2 = Genlet(gr2) - - -class NestedGeneratorTests(TestCase): - def test_layered_genlets(self): - seen = [] - for ii in gr2(5, seen): - seen.append(ii) - self.assertEqual(seen, [1, 1, 2, 4, 3, 9, 4, 16]) - - @fails_leakcheck - def test_permutations(self): - gen_perms = perms(list(range(4))) - permutations = list(gen_perms) - self.assertEqual(len(permutations), 4 * 3 * 2 * 1) - self.assertIn([0, 1, 2, 3], permutations) - self.assertIn([3, 2, 1, 0], permutations) - res = [] - for ii in zip(perms(list(range(4))), perms(list(range(3)))): - res.append(ii) - self.assertEqual( - res, - [([0, 1, 2, 3], [0, 1, 2]), ([0, 1, 3, 2], [0, 2, 1]), - ([0, 2, 1, 3], [1, 0, 2]), ([0, 2, 3, 1], [1, 2, 0]), - ([0, 3, 1, 2], [2, 0, 1]), ([0, 3, 2, 1], [2, 1, 0])]) - # XXX Test to make sure we are working as a generator expression - - def test_genlet_simple(self): - for g in g1, g2, g3: - seen = [] - for _ in range(3): - for j in g(5, seen): - seen.append(j) - self.assertEqual(seen, 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4]) - - def test_genlet_bad(self): - try: - Yield(10) - except RuntimeError: - pass - - def test_nested_genlets(self): - seen = [] - for ii in ax(5): - seen.append(ii) diff --git a/.venv/Lib/site-packages/greenlet/tests/test_greenlet.py b/.venv/Lib/site-packages/greenlet/tests/test_greenlet.py deleted file mode 100644 index c4aabea..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_greenlet.py +++ /dev/null @@ -1,1324 +0,0 @@ -import gc -import sys -import time -import threading -import unittest - -from abc import ABCMeta -from abc import abstractmethod - -import greenlet -from greenlet import greenlet as RawGreenlet -from . import TestCase -from . import RUNNING_ON_MANYLINUX -from . import PY313 -from .leakcheck import fails_leakcheck - - -# We manually manage locks in many tests -# pylint:disable=consider-using-with -# pylint:disable=too-many-public-methods -# This module is quite large. -# TODO: Refactor into separate test files. For example, -# put all the regression tests that used to produce -# crashes in test_greenlet_no_crash; put tests that DO deliberately crash -# the interpreter into test_greenlet_crash. -# pylint:disable=too-many-lines - -class SomeError(Exception): - pass - - -def fmain(seen): - try: - greenlet.getcurrent().parent.switch() - except: - seen.append(sys.exc_info()[0]) - raise - raise SomeError - - -def send_exception(g, exc): - # note: send_exception(g, exc) can be now done with g.throw(exc). - # the purpose of this test is to explicitly check the propagation rules. - def crasher(exc): - raise exc - g1 = RawGreenlet(crasher, parent=g) - g1.switch(exc) - - -class TestGreenlet(TestCase): - - def _do_simple_test(self): - lst = [] - - def f(): - lst.append(1) - greenlet.getcurrent().parent.switch() - lst.append(3) - g = RawGreenlet(f) - lst.append(0) - g.switch() - lst.append(2) - g.switch() - lst.append(4) - self.assertEqual(lst, list(range(5))) - - def test_simple(self): - self._do_simple_test() - - def test_switch_no_run_raises_AttributeError(self): - g = RawGreenlet() - with self.assertRaises(AttributeError) as exc: - g.switch() - - self.assertIn("run", str(exc.exception)) - - def test_throw_no_run_raises_AttributeError(self): - g = RawGreenlet() - with self.assertRaises(AttributeError) as exc: - g.throw(SomeError) - - self.assertIn("run", str(exc.exception)) - - def test_parent_equals_None(self): - g = RawGreenlet(parent=None) - self.assertIsNotNone(g) - self.assertIs(g.parent, greenlet.getcurrent()) - - def test_run_equals_None(self): - g = RawGreenlet(run=None) - self.assertIsNotNone(g) - self.assertIsNone(g.run) - - def test_two_children(self): - lst = [] - - def f(): - lst.append(1) - greenlet.getcurrent().parent.switch() - lst.extend([1, 1]) - g = RawGreenlet(f) - h = RawGreenlet(f) - g.switch() - self.assertEqual(len(lst), 1) - h.switch() - self.assertEqual(len(lst), 2) - h.switch() - self.assertEqual(len(lst), 4) - self.assertEqual(h.dead, True) - g.switch() - self.assertEqual(len(lst), 6) - self.assertEqual(g.dead, True) - - def test_two_recursive_children(self): - lst = [] - - def f(): - lst.append('b') - greenlet.getcurrent().parent.switch() - - def g(): - lst.append('a') - g = RawGreenlet(f) - g.switch() - lst.append('c') - - g = RawGreenlet(g) - self.assertEqual(sys.getrefcount(g), 2) - g.switch() - self.assertEqual(lst, ['a', 'b', 'c']) - # Just the one in this frame, plus the one on the stack we pass to the function - self.assertEqual(sys.getrefcount(g), 2) - - def test_threads(self): - success = [] - - def f(): - self._do_simple_test() - success.append(True) - ths = [threading.Thread(target=f) for i in range(10)] - for th in ths: - th.start() - for th in ths: - th.join(10) - self.assertEqual(len(success), len(ths)) - - def test_exception(self): - seen = [] - g1 = RawGreenlet(fmain) - g2 = RawGreenlet(fmain) - g1.switch(seen) - g2.switch(seen) - g2.parent = g1 - - self.assertEqual(seen, []) - #with self.assertRaises(SomeError): - # p("***Switching back") - # g2.switch() - # Creating this as a bound method can reveal bugs that - # are hidden on newer versions of Python that avoid creating - # bound methods for direct expressions; IOW, don't use the `with` - # form! - self.assertRaises(SomeError, g2.switch) - self.assertEqual(seen, [SomeError]) - - value = g2.switch() - self.assertEqual(value, ()) - self.assertEqual(seen, [SomeError]) - - value = g2.switch(25) - self.assertEqual(value, 25) - self.assertEqual(seen, [SomeError]) - - - def test_send_exception(self): - seen = [] - g1 = RawGreenlet(fmain) - g1.switch(seen) - self.assertRaises(KeyError, send_exception, g1, KeyError) - self.assertEqual(seen, [KeyError]) - - def test_dealloc(self): - seen = [] - g1 = RawGreenlet(fmain) - g2 = RawGreenlet(fmain) - g1.switch(seen) - g2.switch(seen) - self.assertEqual(seen, []) - del g1 - gc.collect() - self.assertEqual(seen, [greenlet.GreenletExit]) - del g2 - gc.collect() - self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit]) - - def test_dealloc_catches_GreenletExit_throws_other(self): - def run(): - try: - greenlet.getcurrent().parent.switch() - except greenlet.GreenletExit: - raise SomeError from None - - g = RawGreenlet(run) - g.switch() - # Destroying the only reference to the greenlet causes it - # to get GreenletExit; when it in turn raises, even though we're the parent - # we don't get the exception, it just gets printed. - # When we run on 3.8 only, we can use sys.unraisablehook - oldstderr = sys.stderr - from io import StringIO - stderr = sys.stderr = StringIO() - try: - del g - finally: - sys.stderr = oldstderr - - v = stderr.getvalue() - self.assertIn("Exception", v) - self.assertIn('ignored', v) - self.assertIn("SomeError", v) - - - @unittest.skipIf( - PY313 and RUNNING_ON_MANYLINUX, - "Sometimes flaky (getting one GreenletExit in the second list)" - # Probably due to funky timing interactions? - # TODO: FIXME Make that work. - ) - - def test_dealloc_other_thread(self): - seen = [] - someref = [] - - bg_glet_created_running_and_no_longer_ref_in_bg = threading.Event() - fg_ref_released = threading.Event() - bg_should_be_clear = threading.Event() - ok_to_exit_bg_thread = threading.Event() - - def f(): - g1 = RawGreenlet(fmain) - g1.switch(seen) - someref.append(g1) - del g1 - gc.collect() - - bg_glet_created_running_and_no_longer_ref_in_bg.set() - fg_ref_released.wait(3) - - RawGreenlet() # trigger release - bg_should_be_clear.set() - ok_to_exit_bg_thread.wait(3) - RawGreenlet() # One more time - - t = threading.Thread(target=f) - t.start() - bg_glet_created_running_and_no_longer_ref_in_bg.wait(10) - - self.assertEqual(seen, []) - self.assertEqual(len(someref), 1) - del someref[:] - gc.collect() - # g1 is not released immediately because it's from another thread - self.assertEqual(seen, []) - fg_ref_released.set() - bg_should_be_clear.wait(3) - try: - self.assertEqual(seen, [greenlet.GreenletExit]) - finally: - ok_to_exit_bg_thread.set() - t.join(10) - del seen[:] - del someref[:] - - def test_frame(self): - def f1(): - f = sys._getframe(0) # pylint:disable=protected-access - self.assertEqual(f.f_back, None) - greenlet.getcurrent().parent.switch(f) - return "meaning of life" - g = RawGreenlet(f1) - frame = g.switch() - self.assertTrue(frame is g.gr_frame) - self.assertTrue(g) - - from_g = g.switch() - self.assertFalse(g) - self.assertEqual(from_g, 'meaning of life') - self.assertEqual(g.gr_frame, None) - - def test_thread_bug(self): - def runner(x): - g = RawGreenlet(lambda: time.sleep(x)) - g.switch() - t1 = threading.Thread(target=runner, args=(0.2,)) - t2 = threading.Thread(target=runner, args=(0.3,)) - t1.start() - t2.start() - t1.join(10) - t2.join(10) - - def test_switch_kwargs(self): - def run(a, b): - self.assertEqual(a, 4) - self.assertEqual(b, 2) - return 42 - x = RawGreenlet(run).switch(a=4, b=2) - self.assertEqual(x, 42) - - def test_switch_kwargs_to_parent(self): - def run(x): - greenlet.getcurrent().parent.switch(x=x) - greenlet.getcurrent().parent.switch(2, x=3) - return x, x ** 2 - g = RawGreenlet(run) - self.assertEqual({'x': 3}, g.switch(3)) - self.assertEqual(((2,), {'x': 3}), g.switch()) - self.assertEqual((3, 9), g.switch()) - - def test_switch_to_another_thread(self): - data = {} - created_event = threading.Event() - done_event = threading.Event() - - def run(): - data['g'] = RawGreenlet(lambda: None) - created_event.set() - done_event.wait(10) - thread = threading.Thread(target=run) - thread.start() - created_event.wait(10) - with self.assertRaises(greenlet.error): - data['g'].switch() - done_event.set() - thread.join(10) - # XXX: Should handle this automatically - data.clear() - - def test_exc_state(self): - def f(): - try: - raise ValueError('fun') - except: # pylint:disable=bare-except - exc_info = sys.exc_info() - RawGreenlet(h).switch() - self.assertEqual(exc_info, sys.exc_info()) - - def h(): - self.assertEqual(sys.exc_info(), (None, None, None)) - - RawGreenlet(f).switch() - - def test_instance_dict(self): - def f(): - greenlet.getcurrent().test = 42 - def deldict(g): - del g.__dict__ - def setdict(g, value): - g.__dict__ = value - g = RawGreenlet(f) - self.assertEqual(g.__dict__, {}) - g.switch() - self.assertEqual(g.test, 42) - self.assertEqual(g.__dict__, {'test': 42}) - g.__dict__ = g.__dict__ - self.assertEqual(g.__dict__, {'test': 42}) - self.assertRaises(TypeError, deldict, g) - self.assertRaises(TypeError, setdict, g, 42) - - def test_running_greenlet_has_no_run(self): - has_run = [] - def func(): - has_run.append( - hasattr(greenlet.getcurrent(), 'run') - ) - - g = RawGreenlet(func) - g.switch() - self.assertEqual(has_run, [False]) - - def test_deepcopy(self): - import copy - self.assertRaises(TypeError, copy.copy, RawGreenlet()) - self.assertRaises(TypeError, copy.deepcopy, RawGreenlet()) - - def test_parent_restored_on_kill(self): - hub = RawGreenlet(lambda: None) - main = greenlet.getcurrent() - result = [] - def worker(): - try: - # Wait to be killed by going back to the test. - main.switch() - except greenlet.GreenletExit: - # Resurrect and switch to parent - result.append(greenlet.getcurrent().parent) - result.append(greenlet.getcurrent()) - hub.switch() - g = RawGreenlet(worker, parent=hub) - g.switch() - # delete the only reference, thereby raising GreenletExit - del g - self.assertTrue(result) - self.assertIs(result[0], main) - self.assertIs(result[1].parent, hub) - # Delete them, thereby breaking the cycle between the greenlet - # and the frame, which otherwise would never be collectable - # XXX: We should be able to automatically fix this. - del result[:] - hub = None - main = None - - def test_parent_return_failure(self): - # No run causes AttributeError on switch - g1 = RawGreenlet() - # Greenlet that implicitly switches to parent - g2 = RawGreenlet(lambda: None, parent=g1) - # AttributeError should propagate to us, no fatal errors - with self.assertRaises(AttributeError): - g2.switch() - - def test_throw_exception_not_lost(self): - class mygreenlet(RawGreenlet): - def __getattribute__(self, name): - try: - raise Exception # pylint:disable=broad-exception-raised - except: # pylint:disable=bare-except - pass - return RawGreenlet.__getattribute__(self, name) - g = mygreenlet(lambda: None) - self.assertRaises(SomeError, g.throw, SomeError()) - - @fails_leakcheck - def _do_test_throw_to_dead_thread_doesnt_crash(self, wait_for_cleanup=False): - result = [] - def worker(): - greenlet.getcurrent().parent.switch() - - def creator(): - g = RawGreenlet(worker) - g.switch() - result.append(g) - if wait_for_cleanup: - # Let this greenlet eventually be cleaned up. - g.switch() - greenlet.getcurrent() - t = threading.Thread(target=creator) - t.start() - t.join(10) - del t - # But, depending on the operating system, the thread - # deallocator may not actually have run yet! So we can't be - # sure about the error message unless we wait. - if wait_for_cleanup: - self.wait_for_pending_cleanups() - with self.assertRaises(greenlet.error) as exc: - result[0].throw(SomeError) - - if not wait_for_cleanup: - s = str(exc.exception) - self.assertTrue( - s == "cannot switch to a different thread (which happens to have exited)" - or 'Cannot switch' in s - ) - else: - self.assertEqual( - str(exc.exception), - "cannot switch to a different thread (which happens to have exited)", - ) - - if hasattr(result[0].gr_frame, 'clear'): - # The frame is actually executing (it thinks), we can't clear it. - with self.assertRaises(RuntimeError): - result[0].gr_frame.clear() - # Unfortunately, this doesn't actually clear the references, they're in the - # fast local array. - if not wait_for_cleanup: - # f_locals has no clear method in Python 3.13 - if hasattr(result[0].gr_frame.f_locals, 'clear'): - result[0].gr_frame.f_locals.clear() - else: - self.assertIsNone(result[0].gr_frame) - - del creator - worker = None - del result[:] - # XXX: we ought to be able to automatically fix this. - # See issue 252 - self.expect_greenlet_leak = True # direct us not to wait for it to go away - - @fails_leakcheck - def test_throw_to_dead_thread_doesnt_crash(self): - self._do_test_throw_to_dead_thread_doesnt_crash() - - def test_throw_to_dead_thread_doesnt_crash_wait(self): - self._do_test_throw_to_dead_thread_doesnt_crash(True) - - @fails_leakcheck - def test_recursive_startup(self): - class convoluted(RawGreenlet): - def __init__(self): - RawGreenlet.__init__(self) - self.count = 0 - def __getattribute__(self, name): - if name == 'run' and self.count == 0: - self.count = 1 - self.switch(43) - return RawGreenlet.__getattribute__(self, name) - def run(self, value): - while True: - self.parent.switch(value) - g = convoluted() - self.assertEqual(g.switch(42), 43) - # Exits the running greenlet, otherwise it leaks - # XXX: We should be able to automatically fix this - #g.throw(greenlet.GreenletExit) - #del g - self.expect_greenlet_leak = True - - def test_threaded_updatecurrent(self): - # released when main thread should execute - lock1 = threading.Lock() - lock1.acquire() - # released when another thread should execute - lock2 = threading.Lock() - lock2.acquire() - class finalized(object): - def __del__(self): - # happens while in green_updatecurrent() in main greenlet - # should be very careful not to accidentally call it again - # at the same time we must make sure another thread executes - lock2.release() - lock1.acquire() - # now ts_current belongs to another thread - def deallocator(): - greenlet.getcurrent().parent.switch() - def fthread(): - lock2.acquire() - greenlet.getcurrent() - del g[0] - lock1.release() - lock2.acquire() - greenlet.getcurrent() - lock1.release() - main = greenlet.getcurrent() - g = [RawGreenlet(deallocator)] - g[0].bomb = finalized() - g[0].switch() - t = threading.Thread(target=fthread) - t.start() - # let another thread grab ts_current and deallocate g[0] - lock2.release() - lock1.acquire() - # this is the corner stone - # getcurrent() will notice that ts_current belongs to another thread - # and start the update process, which would notice that g[0] should - # be deallocated, and that will execute an object's finalizer. Now, - # that object will let another thread run so it can grab ts_current - # again, which would likely crash the interpreter if there's no - # check for this case at the end of green_updatecurrent(). This test - # passes if getcurrent() returns correct result, but it's likely - # to randomly crash if it's not anyway. - self.assertEqual(greenlet.getcurrent(), main) - # wait for another thread to complete, just in case - t.join(10) - - def test_dealloc_switch_args_not_lost(self): - seen = [] - def worker(): - # wait for the value - value = greenlet.getcurrent().parent.switch() - # delete all references to ourself - del worker[0] - initiator.parent = greenlet.getcurrent().parent - # switch to main with the value, but because - # ts_current is the last reference to us we - # return here immediately, where we resurrect ourself. - try: - greenlet.getcurrent().parent.switch(value) - finally: - seen.append(greenlet.getcurrent()) - def initiator(): - return 42 # implicitly falls thru to parent - - worker = [RawGreenlet(worker)] - - worker[0].switch() # prime worker - initiator = RawGreenlet(initiator, worker[0]) - value = initiator.switch() - self.assertTrue(seen) - self.assertEqual(value, 42) - - def test_tuple_subclass(self): - # The point of this test is to see what happens when a custom - # tuple subclass is used as an object passed directly to the C - # function ``green_switch``; part of ``green_switch`` checks - # the ``len()`` of the ``args`` tuple, and that can call back - # into Python. Here, when it calls back into Python, we - # recursively enter ``green_switch`` again. - - # This test is really only relevant on Python 2. The builtin - # `apply` function directly passes the given args tuple object - # to the underlying function, whereas the Python 3 version - # unpacks and repacks into an actual tuple. This could still - # happen using the C API on Python 3 though. We should write a - # builtin version of apply() ourself. - def _apply(func, a, k): - func(*a, **k) - - class mytuple(tuple): - def __len__(self): - greenlet.getcurrent().switch() - return tuple.__len__(self) - args = mytuple() - kwargs = dict(a=42) - def switchapply(): - _apply(greenlet.getcurrent().parent.switch, args, kwargs) - g = RawGreenlet(switchapply) - self.assertEqual(g.switch(), kwargs) - - def test_abstract_subclasses(self): - AbstractSubclass = ABCMeta( - 'AbstractSubclass', - (RawGreenlet,), - {'run': abstractmethod(lambda self: None)}) - - class BadSubclass(AbstractSubclass): - pass - - class GoodSubclass(AbstractSubclass): - def run(self): - pass - - GoodSubclass() # should not raise - self.assertRaises(TypeError, BadSubclass) - - def test_implicit_parent_with_threads(self): - if not gc.isenabled(): - return # cannot test with disabled gc - N = gc.get_threshold()[0] - if N < 50: - return # cannot test with such a small N - def attempt(): - lock1 = threading.Lock() - lock1.acquire() - lock2 = threading.Lock() - lock2.acquire() - recycled = [False] - def another_thread(): - lock1.acquire() # wait for gc - greenlet.getcurrent() # update ts_current - lock2.release() # release gc - t = threading.Thread(target=another_thread) - t.start() - class gc_callback(object): - def __del__(self): - lock1.release() - lock2.acquire() - recycled[0] = True - class garbage(object): - def __init__(self): - self.cycle = self - self.callback = gc_callback() - l = [] - x = range(N*2) - current = greenlet.getcurrent() - g = garbage() - for _ in x: - g = None # lose reference to garbage - if recycled[0]: - # gc callback called prematurely - t.join(10) - return False - last = RawGreenlet() - if recycled[0]: - break # yes! gc called in green_new - l.append(last) # increase allocation counter - else: - # gc callback not called when expected - gc.collect() - if recycled[0]: - t.join(10) - return False - self.assertEqual(last.parent, current) - for g in l: - self.assertEqual(g.parent, current) - return True - for _ in range(5): - if attempt(): - break - - def test_issue_245_reference_counting_subclass_no_threads(self): - # https://github.com/python-greenlet/greenlet/issues/245 - # Before the fix, this crashed pretty reliably on - # Python 3.10, at least on macOS; but much less reliably on other - # interpreters (memory layout must have changed). - # The threaded test crashed more reliably on more interpreters. - from greenlet import getcurrent - from greenlet import GreenletExit - - class Greenlet(RawGreenlet): - pass - - initial_refs = sys.getrefcount(Greenlet) - # This has to be an instance variable because - # Python 2 raises a SyntaxError if we delete a local - # variable referenced in an inner scope. - self.glets = [] # pylint:disable=attribute-defined-outside-init - - def greenlet_main(): - try: - getcurrent().parent.switch() - except GreenletExit: - self.glets.append(getcurrent()) - - # Before the - for _ in range(10): - Greenlet(greenlet_main).switch() - - del self.glets - self.assertEqual(sys.getrefcount(Greenlet), initial_refs) - - @unittest.skipIf( - PY313 and RUNNING_ON_MANYLINUX, - "The manylinux images appear to hang on this test on 3.13rc2" - # Or perhaps I just got tired of waiting for the 450s timeout. - # Still, it shouldn't take anywhere near that long. Does not reproduce in - # Ubuntu images, on macOS or Windows. - ) - def test_issue_245_reference_counting_subclass_threads(self): - # https://github.com/python-greenlet/greenlet/issues/245 - from threading import Thread - from threading import Event - - from greenlet import getcurrent - - class MyGreenlet(RawGreenlet): - pass - - glets = [] - ref_cleared = Event() - - def greenlet_main(): - getcurrent().parent.switch() - - def thread_main(greenlet_running_event): - mine = MyGreenlet(greenlet_main) - glets.append(mine) - # The greenlets being deleted must be active - mine.switch() - # Don't keep any reference to it in this thread - del mine - # Let main know we published our greenlet. - greenlet_running_event.set() - # Wait for main to let us know the references are - # gone and the greenlet objects no longer reachable - ref_cleared.wait(10) - # The creating thread must call getcurrent() (or a few other - # greenlet APIs) because that's when the thread-local list of dead - # greenlets gets cleared. - getcurrent() - - # We start with 3 references to the subclass: - # - This module - # - Its __mro__ - # - The __subclassess__ attribute of greenlet - # - (If we call gc.get_referents(), we find four entries, including - # some other tuple ``(greenlet)`` that I'm not sure about but must be part - # of the machinery.) - # - # On Python 3.10 it's often enough to just run 3 threads; on Python 2.7, - # more threads are needed, and the results are still - # non-deterministic. Presumably the memory layouts are different - initial_refs = sys.getrefcount(MyGreenlet) - thread_ready_events = [] - for _ in range( - initial_refs + 45 - ): - event = Event() - thread = Thread(target=thread_main, args=(event,)) - thread_ready_events.append(event) - thread.start() - - - for done_event in thread_ready_events: - done_event.wait(10) - - - del glets[:] - ref_cleared.set() - # Let any other thread run; it will crash the interpreter - # if not fixed (or silently corrupt memory and we possibly crash - # later). - self.wait_for_pending_cleanups() - self.assertEqual(sys.getrefcount(MyGreenlet), initial_refs) - - def test_falling_off_end_switches_to_unstarted_parent_raises_error(self): - def no_args(): - return 13 - - parent_never_started = RawGreenlet(no_args) - - def leaf(): - return 42 - - child = RawGreenlet(leaf, parent_never_started) - - # Because the run function takes to arguments - with self.assertRaises(TypeError): - child.switch() - - def test_falling_off_end_switches_to_unstarted_parent_works(self): - def one_arg(x): - return (x, 24) - - parent_never_started = RawGreenlet(one_arg) - - def leaf(): - return 42 - - child = RawGreenlet(leaf, parent_never_started) - - result = child.switch() - self.assertEqual(result, (42, 24)) - - def test_switch_to_dead_greenlet_with_unstarted_perverse_parent(self): - class Parent(RawGreenlet): - def __getattribute__(self, name): - if name == 'run': - raise SomeError - - - parent_never_started = Parent() - seen = [] - child = RawGreenlet(lambda: seen.append(42), parent_never_started) - # Because we automatically start the parent when the child is - # finished - with self.assertRaises(SomeError): - child.switch() - - self.assertEqual(seen, [42]) - - with self.assertRaises(SomeError): - child.switch() - self.assertEqual(seen, [42]) - - def test_switch_to_dead_greenlet_reparent(self): - seen = [] - parent_never_started = RawGreenlet(lambda: seen.append(24)) - child = RawGreenlet(lambda: seen.append(42)) - - child.switch() - self.assertEqual(seen, [42]) - - child.parent = parent_never_started - # This actually is the same as switching to the parent. - result = child.switch() - self.assertIsNone(result) - self.assertEqual(seen, [42, 24]) - - def test_can_access_f_back_of_suspended_greenlet(self): - # This tests our frame rewriting to work around Python 3.12+ having - # some interpreter frames on the C stack. It will crash in the absence - # of that logic. - main = greenlet.getcurrent() - - def outer(): - inner() - - def inner(): - main.switch(sys._getframe(0)) - - hub = RawGreenlet(outer) - # start it - hub.switch() - - # start another greenlet to make sure we aren't relying on - # anything in `hub` still being on the C stack - unrelated = RawGreenlet(lambda: None) - unrelated.switch() - - # now it is suspended - self.assertIsNotNone(hub.gr_frame) - self.assertEqual(hub.gr_frame.f_code.co_name, "inner") - self.assertIsNotNone(hub.gr_frame.f_back) - self.assertEqual(hub.gr_frame.f_back.f_code.co_name, "outer") - # The next line is what would crash - self.assertIsNone(hub.gr_frame.f_back.f_back) - - def test_get_stack_with_nested_c_calls(self): - from functools import partial - from . import _test_extension_cpp - - def recurse(v): - if v > 0: - return v * _test_extension_cpp.test_call(partial(recurse, v - 1)) - return greenlet.getcurrent().parent.switch() - - gr = RawGreenlet(recurse) - gr.switch(5) - frame = gr.gr_frame - for i in range(5): - self.assertEqual(frame.f_locals["v"], i) - frame = frame.f_back - self.assertEqual(frame.f_locals["v"], 5) - self.assertIsNone(frame.f_back) - self.assertEqual(gr.switch(10), 1200) # 1200 = 5! * 10 - - def test_frames_always_exposed(self): - # On Python 3.12 this will crash if we don't set the - # gr_frames_always_exposed attribute. More background: - # https://github.com/python-greenlet/greenlet/issues/388 - main = greenlet.getcurrent() - - def outer(): - inner(sys._getframe(0)) - - def inner(frame): - main.switch(frame) - - gr = RawGreenlet(outer) - frame = gr.switch() - - # Do something else to clobber the part of the C stack used by `gr`, - # so we can't skate by on "it just happened to still be there" - unrelated = RawGreenlet(lambda: None) - unrelated.switch() - - self.assertEqual(frame.f_code.co_name, "outer") - # The next line crashes on 3.12 if we haven't exposed the frames. - self.assertIsNone(frame.f_back) - - -class TestGreenletSetParentErrors(TestCase): - def test_threaded_reparent(self): - data = {} - created_event = threading.Event() - done_event = threading.Event() - - def run(): - data['g'] = RawGreenlet(lambda: None) - created_event.set() - done_event.wait(10) - - def blank(): - greenlet.getcurrent().parent.switch() - - thread = threading.Thread(target=run) - thread.start() - created_event.wait(10) - g = RawGreenlet(blank) - g.switch() - with self.assertRaises(ValueError) as exc: - g.parent = data['g'] - done_event.set() - thread.join(10) - - self.assertEqual(str(exc.exception), "parent cannot be on a different thread") - - def test_unexpected_reparenting(self): - another = [] - def worker(): - g = RawGreenlet(lambda: None) - another.append(g) - g.switch() - t = threading.Thread(target=worker) - t.start() - t.join(10) - # The first time we switch (running g_initialstub(), which is - # when we look up the run attribute) we attempt to change the - # parent to one from another thread (which also happens to be - # dead). ``g_initialstub()`` should detect this and raise a - # greenlet error. - # - # EXCEPT: With the fix for #252, this is actually detected - # sooner, when setting the parent itself. Prior to that fix, - # the main greenlet from the background thread kept a valid - # value for ``run_info``, and appeared to be a valid parent - # until we actually started the greenlet. But now that it's - # cleared, this test is catching whether ``green_setparent`` - # can detect the dead thread. - # - # Further refactoring once again changes this back to a greenlet.error - # - # We need to wait for the cleanup to happen, but we're - # deliberately leaking a main greenlet here. - self.wait_for_pending_cleanups(initial_main_greenlets=self.main_greenlets_before_test + 1) - - class convoluted(RawGreenlet): - def __getattribute__(self, name): - if name == 'run': - self.parent = another[0] # pylint:disable=attribute-defined-outside-init - return RawGreenlet.__getattribute__(self, name) - g = convoluted(lambda: None) - with self.assertRaises(greenlet.error) as exc: - g.switch() - self.assertEqual(str(exc.exception), - "cannot switch to a different thread (which happens to have exited)") - del another[:] - - def test_unexpected_reparenting_thread_running(self): - # Like ``test_unexpected_reparenting``, except the background thread is - # actually still alive. - another = [] - switched_to_greenlet = threading.Event() - keep_main_alive = threading.Event() - def worker(): - g = RawGreenlet(lambda: None) - another.append(g) - g.switch() - switched_to_greenlet.set() - keep_main_alive.wait(10) - class convoluted(RawGreenlet): - def __getattribute__(self, name): - if name == 'run': - self.parent = another[0] # pylint:disable=attribute-defined-outside-init - return RawGreenlet.__getattribute__(self, name) - - t = threading.Thread(target=worker) - t.start() - - switched_to_greenlet.wait(10) - try: - g = convoluted(lambda: None) - - with self.assertRaises(greenlet.error) as exc: - g.switch() - self.assertIn("Cannot switch to a different thread", str(exc.exception)) - self.assertIn("Expected", str(exc.exception)) - self.assertIn("Current", str(exc.exception)) - finally: - keep_main_alive.set() - t.join(10) - # XXX: Should handle this automatically. - del another[:] - - def test_cannot_delete_parent(self): - worker = RawGreenlet(lambda: None) - self.assertIs(worker.parent, greenlet.getcurrent()) - - with self.assertRaises(AttributeError) as exc: - del worker.parent - self.assertEqual(str(exc.exception), "can't delete attribute") - - def test_cannot_delete_parent_of_main(self): - with self.assertRaises(AttributeError) as exc: - del greenlet.getcurrent().parent - self.assertEqual(str(exc.exception), "can't delete attribute") - - - def test_main_greenlet_parent_is_none(self): - # assuming we're in a main greenlet here. - self.assertIsNone(greenlet.getcurrent().parent) - - def test_set_parent_wrong_types(self): - def bg(): - # Go back to main. - greenlet.getcurrent().parent.switch() - - def check(glet): - for p in None, 1, self, "42": - with self.assertRaises(TypeError) as exc: - glet.parent = p - - self.assertEqual( - str(exc.exception), - "GreenletChecker: Expected any type of greenlet, not " + type(p).__name__) - - # First, not running - g = RawGreenlet(bg) - self.assertFalse(g) - check(g) - - # Then when running. - g.switch() - self.assertTrue(g) - check(g) - - # Let it finish - g.switch() - - - def test_trivial_cycle(self): - glet = RawGreenlet(lambda: None) - with self.assertRaises(ValueError) as exc: - glet.parent = glet - self.assertEqual(str(exc.exception), "cyclic parent chain") - - def test_trivial_cycle_main(self): - # This used to produce a ValueError, but we catch it earlier than that now. - with self.assertRaises(AttributeError) as exc: - greenlet.getcurrent().parent = greenlet.getcurrent() - self.assertEqual(str(exc.exception), "cannot set the parent of a main greenlet") - - def test_deeper_cycle(self): - g1 = RawGreenlet(lambda: None) - g2 = RawGreenlet(lambda: None) - g3 = RawGreenlet(lambda: None) - - g1.parent = g2 - g2.parent = g3 - with self.assertRaises(ValueError) as exc: - g3.parent = g1 - self.assertEqual(str(exc.exception), "cyclic parent chain") - - -class TestRepr(TestCase): - - def assertEndsWith(self, got, suffix): - self.assertTrue(got.endswith(suffix), (got, suffix)) - - def test_main_while_running(self): - r = repr(greenlet.getcurrent()) - self.assertEndsWith(r, " current active started main>") - - def test_main_in_background(self): - main = greenlet.getcurrent() - def run(): - return repr(main) - - g = RawGreenlet(run) - r = g.switch() - self.assertEndsWith(r, ' suspended active started main>') - - def test_initial(self): - r = repr(RawGreenlet()) - self.assertEndsWith(r, ' pending>') - - def test_main_from_other_thread(self): - main = greenlet.getcurrent() - - class T(threading.Thread): - original_main = thread_main = None - main_glet = None - def run(self): - self.original_main = repr(main) - self.main_glet = greenlet.getcurrent() - self.thread_main = repr(self.main_glet) - - t = T() - t.start() - t.join(10) - - self.assertEndsWith(t.original_main, ' suspended active started main>') - self.assertEndsWith(t.thread_main, ' current active started main>') - # give the machinery time to notice the death of the thread, - # and clean it up. Note that we don't use - # ``expect_greenlet_leak`` or wait_for_pending_cleanups, - # because at this point we know we have an extra greenlet - # still reachable. - for _ in range(3): - time.sleep(0.001) - - # In the past, main greenlets, even from dead threads, never - # really appear dead. We have fixed that, and we also report - # that the thread is dead in the repr. (Do this multiple times - # to make sure that we don't self-modify and forget our state - # in the C++ code). - for _ in range(3): - self.assertTrue(t.main_glet.dead) - r = repr(t.main_glet) - self.assertEndsWith(r, ' (thread exited) dead>') - - def test_dead(self): - g = RawGreenlet(lambda: None) - g.switch() - self.assertEndsWith(repr(g), ' dead>') - self.assertNotIn('suspended', repr(g)) - self.assertNotIn('started', repr(g)) - self.assertNotIn('active', repr(g)) - - def test_formatting_produces_native_str(self): - # https://github.com/python-greenlet/greenlet/issues/218 - # %s formatting on Python 2 was producing unicode, not str. - - g_dead = RawGreenlet(lambda: None) - g_not_started = RawGreenlet(lambda: None) - g_cur = greenlet.getcurrent() - - for g in g_dead, g_not_started, g_cur: - - self.assertIsInstance( - '%s' % (g,), - str - ) - self.assertIsInstance( - '%r' % (g,), - str, - ) - - -class TestMainGreenlet(TestCase): - # Tests some implementation details, and relies on some - # implementation details. - - def _check_current_is_main(self): - # implementation detail - assert 'main' in repr(greenlet.getcurrent()) - - t = type(greenlet.getcurrent()) - assert 'main' not in repr(t) - return t - - def test_main_greenlet_type_can_be_subclassed(self): - main_type = self._check_current_is_main() - subclass = type('subclass', (main_type,), {}) - self.assertIsNotNone(subclass) - - def test_main_greenlet_is_greenlet(self): - self._check_current_is_main() - self.assertIsInstance(greenlet.getcurrent(), RawGreenlet) - - - -class TestBrokenGreenlets(TestCase): - # Tests for things that used to, or still do, terminate the interpreter. - # This often means doing unsavory things. - - def test_failed_to_initialstub(self): - def func(): - raise AssertionError("Never get here") - - - g = greenlet._greenlet.UnswitchableGreenlet(func) - g.force_switch_error = True - - with self.assertRaisesRegex(SystemError, - "Failed to switch stacks into a greenlet for the first time."): - g.switch() - - def test_failed_to_switch_into_running(self): - runs = [] - def func(): - runs.append(1) - greenlet.getcurrent().parent.switch() - runs.append(2) - greenlet.getcurrent().parent.switch() - runs.append(3) # pragma: no cover - - g = greenlet._greenlet.UnswitchableGreenlet(func) - g.switch() - self.assertEqual(runs, [1]) - g.switch() - self.assertEqual(runs, [1, 2]) - g.force_switch_error = True - - with self.assertRaisesRegex(SystemError, - "Failed to switch stacks into a running greenlet."): - g.switch() - - # If we stopped here, we would fail the leakcheck, because we've left - # the ``inner_bootstrap()`` C frame and its descendents hanging around, - # which have a bunch of Python references. They'll never get cleaned up - # if we don't let the greenlet finish. - g.force_switch_error = False - g.switch() - self.assertEqual(runs, [1, 2, 3]) - - def test_failed_to_slp_switch_into_running(self): - ex = self.assertScriptRaises('fail_slp_switch.py') - - self.assertIn('fail_slp_switch is running', ex.output) - self.assertIn(ex.returncode, self.get_expected_returncodes_for_aborted_process()) - - def test_reentrant_switch_two_greenlets(self): - # Before we started capturing the arguments in g_switch_finish, this could crash. - output = self.run_script('fail_switch_two_greenlets.py') - self.assertIn('In g1_run', output) - self.assertIn('TRACE', output) - self.assertIn('LEAVE TRACE', output) - self.assertIn('Falling off end of main', output) - self.assertIn('Falling off end of g1_run', output) - self.assertIn('Falling off end of g2', output) - - def test_reentrant_switch_three_greenlets(self): - # On debug builds of greenlet, this used to crash with an assertion error; - # on non-debug versions, it ran fine (which it should not do!). - # Now it always crashes correctly with a TypeError - ex = self.assertScriptRaises('fail_switch_three_greenlets.py', exitcodes=(1,)) - - self.assertIn('TypeError', ex.output) - self.assertIn('positional arguments', ex.output) - - def test_reentrant_switch_three_greenlets2(self): - # This actually passed on debug and non-debug builds. It - # should probably have been triggering some debug assertions - # but it didn't. - # - # I think the fixes for the above test also kicked in here. - output = self.run_script('fail_switch_three_greenlets2.py') - self.assertIn( - "RESULTS: [('trace', 'switch'), " - "('trace', 'switch'), ('g2 arg', 'g2 from tracefunc'), " - "('trace', 'switch'), ('main g1', 'from g2_run'), ('trace', 'switch'), " - "('g1 arg', 'g1 from main'), ('trace', 'switch'), ('main g2', 'from g1_run'), " - "('trace', 'switch'), ('g1 from parent', 'g1 from main 2'), ('trace', 'switch'), " - "('main g1.2', 'g1 done'), ('trace', 'switch'), ('g2 from parent', ()), " - "('trace', 'switch'), ('main g2.2', 'g2 done')]", - output - ) - - def test_reentrant_switch_GreenletAlreadyStartedInPython(self): - output = self.run_script('fail_initialstub_already_started.py') - - self.assertIn( - "RESULTS: ['Begin C', 'Switch to b from B.__getattribute__ in C', " - "('Begin B', ()), '_B_run switching to main', ('main from c', 'From B'), " - "'B.__getattribute__ back from main in C', ('Begin A', (None,)), " - "('A dead?', True, 'B dead?', True, 'C dead?', False), " - "'C done', ('main from c.2', None)]", - output - ) - - def test_reentrant_switch_run_callable_has_del(self): - output = self.run_script('fail_clearing_run_switches.py') - self.assertIn( - "RESULTS [" - "('G.__getattribute__', 'run'), ('RunCallable', '__del__'), " - "('main: g.switch()', 'from RunCallable'), ('run_func', 'enter')" - "]", - output - ) - -if __name__ == '__main__': - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_greenlet_trash.py b/.venv/Lib/site-packages/greenlet/tests/test_greenlet_trash.py deleted file mode 100644 index c1fc137..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_greenlet_trash.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Tests for greenlets interacting with the CPython trash can API. - -The CPython trash can API is not designed to be re-entered from a -single thread. But this can happen using greenlets, if something -during the object deallocation process switches greenlets, and this second -greenlet then causes the trash can to get entered again. Here, we do this -very explicitly, but in other cases (like gevent) it could be arbitrarily more -complicated: for example, a weakref callback might try to acquire a lock that's -already held by another greenlet; that would allow a greenlet switch to occur. - -See https://github.com/gevent/gevent/issues/1909 - -This test is fragile and relies on details of the CPython -implementation (like most of the rest of this package): - - - We enter the trashcan and deferred deallocation after - ``_PyTrash_UNWIND_LEVEL`` calls. This constant, defined in - CPython's object.c, is generally 50. That's basically how many objects are required to - get us into the deferred deallocation situation. - - - The test fails by hitting an ``assert()`` in object.c; if the - build didn't enable assert, then we don't catch this. - - - If the test fails in that way, the interpreter crashes. -""" -from __future__ import print_function, absolute_import, division - -import unittest - - -class TestTrashCanReEnter(unittest.TestCase): - - def test_it(self): - try: - # pylint:disable-next=no-name-in-module - from greenlet._greenlet import get_tstate_trash_delete_nesting # pylint:disable=unused-import - except ImportError: - import sys - # Python 3.13 has not "trash delete nesting" anymore (but "delete later") - assert sys.version_info[:2] >= (3, 13) - self.skipTest("get_tstate_trash_delete_nesting is not available.") - - # Try several times to trigger it, because it isn't 100% - # reliable. - for _ in range(10): - self.check_it() - - def check_it(self): # pylint:disable=too-many-statements - import greenlet - from greenlet._greenlet import get_tstate_trash_delete_nesting # pylint:disable=no-name-in-module - main = greenlet.getcurrent() - - assert get_tstate_trash_delete_nesting() == 0 - - # We expect to be in deferred deallocation after this many - # deallocations have occurred. TODO: I wish we had a better way to do - # this --- that was before get_tstate_trash_delete_nesting; perhaps - # we can use that API to do better? - TRASH_UNWIND_LEVEL = 50 - # How many objects to put in a container; it's the container that - # queues objects for deferred deallocation. - OBJECTS_PER_CONTAINER = 500 - - class Dealloc: # define the class here because we alter class variables each time we run. - """ - An object with a ``__del__`` method. When it starts getting deallocated - from a deferred trash can run, it switches greenlets, allocates more objects - which then also go in the trash can. If we don't save state appropriately, - nesting gets out of order and we can crash the interpreter. - """ - - #: Has our deallocation actually run and switched greenlets? - #: When it does, this will be set to the current greenlet. This should - #: be happening in the main greenlet, so we check that down below. - SPAWNED = False - - #: Has the background greenlet run? - BG_RAN = False - - BG_GLET = None - - #: How many of these things have ever been allocated. - CREATED = 0 - - #: How many of these things have ever been deallocated. - DESTROYED = 0 - - #: How many were destroyed not in the main greenlet. There should always - #: be some. - #: If the test is broken or things change in the trashcan implementation, - #: this may not be correct. - DESTROYED_BG = 0 - - def __init__(self, sequence_number): - """ - :param sequence_number: The ordinal of this object during - one particular creation run. This is used to detect (guess, really) - when we have entered the trash can's deferred deallocation. - """ - self.i = sequence_number - Dealloc.CREATED += 1 - - def __del__(self): - if self.i == TRASH_UNWIND_LEVEL and not self.SPAWNED: - Dealloc.SPAWNED = greenlet.getcurrent() - other = Dealloc.BG_GLET = greenlet.greenlet(background_greenlet) - x = other.switch() - assert x == 42 - # It's important that we don't switch back to the greenlet, - # we leave it hanging there in an incomplete state. But we don't let it - # get collected, either. If we complete it now, while we're still - # in the scope of the initial trash can, things work out and we - # don't see the problem. We need this greenlet to complete - # at some point in the future, after we've exited this trash can invocation. - del other - elif self.i == 40 and greenlet.getcurrent() is not main: - Dealloc.BG_RAN = True - try: - main.switch(42) - except greenlet.GreenletExit as ex: - # We expect this; all references to us go away - # while we're still running, and we need to finish deleting - # ourself. - Dealloc.BG_RAN = type(ex) - del ex - - # Record the fact that we're dead last of all. This ensures that - # we actually get returned too. - Dealloc.DESTROYED += 1 - if greenlet.getcurrent() is not main: - Dealloc.DESTROYED_BG += 1 - - - def background_greenlet(): - # We direct through a second function, instead of - # directly calling ``make_some()``, so that we have complete - # control over when these objects are destroyed: we need them - # to be destroyed in the context of the background greenlet - t = make_some() - del t # Triggere deletion. - - def make_some(): - t = () - i = OBJECTS_PER_CONTAINER - while i: - # Nest the tuples; it's the recursion that gets us - # into trash. - t = (Dealloc(i), t) - i -= 1 - return t - - - some = make_some() - self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER) - self.assertEqual(Dealloc.DESTROYED, 0) - - # If we're going to crash, it should be on the following line. - # We only crash if ``assert()`` is enabled, of course. - del some - - # For non-debug builds of CPython, we won't crash. The best we can do is check - # the nesting level explicitly. - self.assertEqual(0, get_tstate_trash_delete_nesting()) - - # Discard this, raising GreenletExit into where it is waiting. - Dealloc.BG_GLET = None - # The same nesting level maintains. - self.assertEqual(0, get_tstate_trash_delete_nesting()) - - # We definitely cleaned some up in the background - self.assertGreater(Dealloc.DESTROYED_BG, 0) - - # Make sure all the cleanups happened. - self.assertIs(Dealloc.SPAWNED, main) - self.assertTrue(Dealloc.BG_RAN) - self.assertEqual(Dealloc.BG_RAN, greenlet.GreenletExit) - self.assertEqual(Dealloc.CREATED, Dealloc.DESTROYED ) - self.assertEqual(Dealloc.CREATED, OBJECTS_PER_CONTAINER * 2) - - import gc - gc.collect() - - -if __name__ == '__main__': - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_leaks.py b/.venv/Lib/site-packages/greenlet/tests/test_leaks.py deleted file mode 100644 index ed1fa71..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_leaks.py +++ /dev/null @@ -1,443 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Testing scenarios that may have leaked. -""" -from __future__ import print_function, absolute_import, division - -import sys -import gc - -import time -import weakref -import threading - - -import greenlet -from . import TestCase -from .leakcheck import fails_leakcheck -from .leakcheck import ignores_leakcheck -from .leakcheck import RUNNING_ON_MANYLINUX - -# pylint:disable=protected-access - -assert greenlet.GREENLET_USE_GC # Option to disable this was removed in 1.0 - -class HasFinalizerTracksInstances(object): - EXTANT_INSTANCES = set() - def __init__(self, msg): - self.msg = sys.intern(msg) - self.EXTANT_INSTANCES.add(id(self)) - def __del__(self): - self.EXTANT_INSTANCES.remove(id(self)) - def __repr__(self): - return "" % ( - id(self), self.msg - ) - @classmethod - def reset(cls): - cls.EXTANT_INSTANCES.clear() - - -class TestLeaks(TestCase): - - def test_arg_refs(self): - args = ('a', 'b', 'c') - refcount_before = sys.getrefcount(args) - # pylint:disable=unnecessary-lambda - g = greenlet.greenlet( - lambda *args: greenlet.getcurrent().parent.switch(*args)) - for _ in range(100): - g.switch(*args) - self.assertEqual(sys.getrefcount(args), refcount_before) - - def test_kwarg_refs(self): - kwargs = {} - # pylint:disable=unnecessary-lambda - g = greenlet.greenlet( - lambda **kwargs: greenlet.getcurrent().parent.switch(**kwargs)) - for _ in range(100): - g.switch(**kwargs) - self.assertEqual(sys.getrefcount(kwargs), 2) - - - @staticmethod - def __recycle_threads(): - # By introducing a thread that does sleep we allow other threads, - # that have triggered their __block condition, but did not have a - # chance to deallocate their thread state yet, to finally do so. - # The way it works is by requiring a GIL switch (different thread), - # which does a GIL release (sleep), which might do a GIL switch - # to finished threads and allow them to clean up. - def worker(): - time.sleep(0.001) - t = threading.Thread(target=worker) - t.start() - time.sleep(0.001) - t.join(10) - - def test_threaded_leak(self): - gg = [] - def worker(): - # only main greenlet present - gg.append(weakref.ref(greenlet.getcurrent())) - for _ in range(2): - t = threading.Thread(target=worker) - t.start() - t.join(10) - del t - greenlet.getcurrent() # update ts_current - self.__recycle_threads() - greenlet.getcurrent() # update ts_current - gc.collect() - greenlet.getcurrent() # update ts_current - for g in gg: - self.assertIsNone(g()) - - def test_threaded_adv_leak(self): - gg = [] - def worker(): - # main and additional *finished* greenlets - ll = greenlet.getcurrent().ll = [] - def additional(): - ll.append(greenlet.getcurrent()) - for _ in range(2): - greenlet.greenlet(additional).switch() - gg.append(weakref.ref(greenlet.getcurrent())) - for _ in range(2): - t = threading.Thread(target=worker) - t.start() - t.join(10) - del t - greenlet.getcurrent() # update ts_current - self.__recycle_threads() - greenlet.getcurrent() # update ts_current - gc.collect() - greenlet.getcurrent() # update ts_current - for g in gg: - self.assertIsNone(g()) - - def assertClocksUsed(self): - used = greenlet._greenlet.get_clocks_used_doing_optional_cleanup() - self.assertGreaterEqual(used, 0) - # we don't lose the value - greenlet._greenlet.enable_optional_cleanup(True) - used2 = greenlet._greenlet.get_clocks_used_doing_optional_cleanup() - self.assertEqual(used, used2) - self.assertGreater(greenlet._greenlet.CLOCKS_PER_SEC, 1) - - def _check_issue251(self, - manually_collect_background=True, - explicit_reference_to_switch=False): - # See https://github.com/python-greenlet/greenlet/issues/251 - # Killing a greenlet (probably not the main one) - # in one thread from another thread would - # result in leaking a list (the ts_delkey list). - # We no longer use lists to hold that stuff, though. - - # For the test to be valid, even empty lists have to be tracked by the - # GC - - assert gc.is_tracked([]) - HasFinalizerTracksInstances.reset() - greenlet.getcurrent() - greenlets_before = self.count_objects(greenlet.greenlet, exact_kind=False) - - background_glet_running = threading.Event() - background_glet_killed = threading.Event() - background_greenlets = [] - - # XXX: Switching this to a greenlet subclass that overrides - # run results in all callers failing the leaktest; that - # greenlet instance is leaked. There's a bound method for - # run() living on the stack of the greenlet in g_initialstub, - # and since we don't manually switch back to the background - # greenlet to let it "fall off the end" and exit the - # g_initialstub function, it never gets cleaned up. Making the - # garbage collector aware of this bound method (making it an - # attribute of the greenlet structure and traversing into it) - # doesn't help, for some reason. - def background_greenlet(): - # Throw control back to the main greenlet. - jd = HasFinalizerTracksInstances("DELETING STACK OBJECT") - greenlet._greenlet.set_thread_local( - 'test_leaks_key', - HasFinalizerTracksInstances("DELETING THREAD STATE")) - # Explicitly keeping 'switch' in a local variable - # breaks this test in all versions - if explicit_reference_to_switch: - s = greenlet.getcurrent().parent.switch - s([jd]) - else: - greenlet.getcurrent().parent.switch([jd]) - - bg_main_wrefs = [] - - def background_thread(): - glet = greenlet.greenlet(background_greenlet) - bg_main_wrefs.append(weakref.ref(glet.parent)) - - background_greenlets.append(glet) - glet.switch() # Be sure it's active. - # Control is ours again. - del glet # Delete one reference from the thread it runs in. - background_glet_running.set() - background_glet_killed.wait(10) - - # To trigger the background collection of the dead - # greenlet, thus clearing out the contents of the list, we - # need to run some APIs. See issue 252. - if manually_collect_background: - greenlet.getcurrent() - - - t = threading.Thread(target=background_thread) - t.start() - background_glet_running.wait(10) - greenlet.getcurrent() - lists_before = self.count_objects(list, exact_kind=True) - - assert len(background_greenlets) == 1 - self.assertFalse(background_greenlets[0].dead) - # Delete the last reference to the background greenlet - # from a different thread. This puts it in the background thread's - # ts_delkey list. - del background_greenlets[:] - background_glet_killed.set() - - # Now wait for the background thread to die. - t.join(10) - del t - # As part of the fix for 252, we need to cycle the ceval.c - # interpreter loop to be sure it has had a chance to process - # the pending call. - self.wait_for_pending_cleanups() - - lists_after = self.count_objects(list, exact_kind=True) - greenlets_after = self.count_objects(greenlet.greenlet, exact_kind=False) - - # On 2.7, we observe that lists_after is smaller than - # lists_before. No idea what lists got cleaned up. All the - # Python 3 versions match exactly. - self.assertLessEqual(lists_after, lists_before) - # On versions after 3.6, we've successfully cleaned up the - # greenlet references thanks to the internal "vectorcall" - # protocol; prior to that, there is a reference path through - # the ``greenlet.switch`` method still on the stack that we - # can't reach to clean up. The C code goes through terrific - # lengths to clean that up. - if not explicit_reference_to_switch \ - and greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None: - # If cleanup was disabled, though, we may not find it. - self.assertEqual(greenlets_after, greenlets_before) - if manually_collect_background: - # TODO: Figure out how to make this work! - # The one on the stack is still leaking somehow - # in the non-manually-collect state. - self.assertEqual(HasFinalizerTracksInstances.EXTANT_INSTANCES, set()) - else: - # The explicit reference prevents us from collecting it - # and it isn't always found by the GC either for some - # reason. The entire frame is leaked somehow, on some - # platforms (e.g., MacPorts builds of Python (all - # versions!)), but not on other platforms (the linux and - # windows builds on GitHub actions and Appveyor). So we'd - # like to write a test that proves that the main greenlet - # sticks around, and we can on my machine (macOS 11.6, - # MacPorts builds of everything) but we can't write that - # same test on other platforms. However, hopefully iteration - # done by leakcheck will find it. - pass - - if greenlet._greenlet.get_clocks_used_doing_optional_cleanup() is not None: - self.assertClocksUsed() - - def test_issue251_killing_cross_thread_leaks_list(self): - self._check_issue251() - - def test_issue251_with_cleanup_disabled(self): - greenlet._greenlet.enable_optional_cleanup(False) - try: - self._check_issue251() - finally: - greenlet._greenlet.enable_optional_cleanup(True) - - @fails_leakcheck - def test_issue251_issue252_need_to_collect_in_background(self): - # Between greenlet 1.1.2 and the next version, this was still - # failing because the leak of the list still exists when we - # don't call a greenlet API before exiting the thread. The - # proximate cause is that neither of the two greenlets from - # the background thread are actually being destroyed, even - # though the GC is in fact visiting both objects. It's not - # clear where that leak is? For some reason the thread-local - # dict holding it isn't being cleaned up. - # - # The leak, I think, is in the CPYthon internal function that - # calls into green_switch(). The argument tuple is still on - # the C stack somewhere and can't be reached? That doesn't - # make sense, because the tuple should be collectable when - # this object goes away. - # - # Note that this test sometimes spuriously passes on Linux, - # for some reason, but I've never seen it pass on macOS. - self._check_issue251(manually_collect_background=False) - - @fails_leakcheck - def test_issue251_issue252_need_to_collect_in_background_cleanup_disabled(self): - self.expect_greenlet_leak = True - greenlet._greenlet.enable_optional_cleanup(False) - try: - self._check_issue251(manually_collect_background=False) - finally: - greenlet._greenlet.enable_optional_cleanup(True) - - @fails_leakcheck - def test_issue251_issue252_explicit_reference_not_collectable(self): - self._check_issue251( - manually_collect_background=False, - explicit_reference_to_switch=True) - - UNTRACK_ATTEMPTS = 100 - - def _only_test_some_versions(self): - # We're only looking for this problem specifically on 3.11, - # and this set of tests is relatively fragile, depending on - # OS and memory management details. So we want to run it on 3.11+ - # (obviously) but not every older 3.x version in order to reduce - # false negatives. At the moment, those false results seem to have - # resolved, so we are actually running this on 3.8+ - assert sys.version_info[0] >= 3 - if sys.version_info[:2] < (3, 8): - self.skipTest('Only observed on 3.11') - if RUNNING_ON_MANYLINUX: - self.skipTest("Slow and not worth repeating here") - - @ignores_leakcheck - # Because we're just trying to track raw memory, not objects, and running - # the leakcheck makes an already slow test slower. - def test_untracked_memory_doesnt_increase(self): - # See https://github.com/gevent/gevent/issues/1924 - # and https://github.com/python-greenlet/greenlet/issues/328 - self._only_test_some_versions() - def f(): - return 1 - - ITER = 10000 - def run_it(): - for _ in range(ITER): - greenlet.greenlet(f).switch() - - # Establish baseline - for _ in range(3): - run_it() - - # uss: (Linux, macOS, Windows): aka "Unique Set Size", this is - # the memory which is unique to a process and which would be - # freed if the process was terminated right now. - uss_before = self.get_process_uss() - - for count in range(self.UNTRACK_ATTEMPTS): - uss_before = max(uss_before, self.get_process_uss()) - run_it() - - uss_after = self.get_process_uss() - if uss_after <= uss_before and count > 1: - break - - self.assertLessEqual(uss_after, uss_before) - - def _check_untracked_memory_thread(self, deallocate_in_thread=True): - self._only_test_some_versions() - # Like the above test, but what if there are a bunch of - # unfinished greenlets in a thread that dies? - # Does it matter if we deallocate in the thread or not? - EXIT_COUNT = [0] - - def f(): - try: - greenlet.getcurrent().parent.switch() - except greenlet.GreenletExit: - EXIT_COUNT[0] += 1 - raise - return 1 - - ITER = 10000 - def run_it(): - glets = [] - for _ in range(ITER): - # Greenlet starts, switches back to us. - # We keep a strong reference to the greenlet though so it doesn't - # get a GreenletExit exception. - g = greenlet.greenlet(f) - glets.append(g) - g.switch() - - return glets - - test = self - - class ThreadFunc: - uss_before = uss_after = 0 - glets = () - ITER = 2 - def __call__(self): - self.uss_before = test.get_process_uss() - - for _ in range(self.ITER): - self.glets += tuple(run_it()) - - for g in self.glets: - test.assertIn('suspended active', str(g)) - # Drop them. - if deallocate_in_thread: - self.glets = () - self.uss_after = test.get_process_uss() - - # Establish baseline - uss_before = uss_after = None - for count in range(self.UNTRACK_ATTEMPTS): - EXIT_COUNT[0] = 0 - thread_func = ThreadFunc() - t = threading.Thread(target=thread_func) - t.start() - t.join(30) - self.assertFalse(t.is_alive()) - - if uss_before is None: - uss_before = thread_func.uss_before - - uss_before = max(uss_before, thread_func.uss_before) - if deallocate_in_thread: - self.assertEqual(thread_func.glets, ()) - self.assertEqual(EXIT_COUNT[0], ITER * thread_func.ITER) - - del thread_func # Deallocate the greenlets; but this won't raise into them - del t - if not deallocate_in_thread: - self.assertEqual(EXIT_COUNT[0], 0) - if deallocate_in_thread: - self.wait_for_pending_cleanups() - - uss_after = self.get_process_uss() - # See if we achieve a non-growth state at some point. Break when we do. - if uss_after <= uss_before and count > 1: - break - - self.wait_for_pending_cleanups() - uss_after = self.get_process_uss() - self.assertLessEqual(uss_after, uss_before, "after attempts %d" % (count,)) - - @ignores_leakcheck - # Because we're just trying to track raw memory, not objects, and running - # the leakcheck makes an already slow test slower. - def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_thread(self): - self._check_untracked_memory_thread(deallocate_in_thread=True) - - @ignores_leakcheck - # Because the main greenlets from the background threads do not exit in a timely fashion, - # we fail the object-based leakchecks. - def test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main(self): - self._check_untracked_memory_thread(deallocate_in_thread=False) - -if __name__ == '__main__': - __import__('unittest').main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_stack_saved.py b/.venv/Lib/site-packages/greenlet/tests/test_stack_saved.py deleted file mode 100644 index b362bf9..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_stack_saved.py +++ /dev/null @@ -1,19 +0,0 @@ -import greenlet -from . import TestCase - - -class Test(TestCase): - - def test_stack_saved(self): - main = greenlet.getcurrent() - self.assertEqual(main._stack_saved, 0) - - def func(): - main.switch(main._stack_saved) - - g = greenlet.greenlet(func) - x = g.switch() - self.assertGreater(x, 0) - self.assertGreater(g._stack_saved, 0) - g.switch() - self.assertEqual(g._stack_saved, 0) diff --git a/.venv/Lib/site-packages/greenlet/tests/test_throw.py b/.venv/Lib/site-packages/greenlet/tests/test_throw.py deleted file mode 100644 index f4f9a14..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_throw.py +++ /dev/null @@ -1,128 +0,0 @@ -import sys - - -from greenlet import greenlet -from . import TestCase - -def switch(*args): - return greenlet.getcurrent().parent.switch(*args) - - -class ThrowTests(TestCase): - def test_class(self): - def f(): - try: - switch("ok") - except RuntimeError: - switch("ok") - return - switch("fail") - g = greenlet(f) - res = g.switch() - self.assertEqual(res, "ok") - res = g.throw(RuntimeError) - self.assertEqual(res, "ok") - - def test_val(self): - def f(): - try: - switch("ok") - except RuntimeError: - val = sys.exc_info()[1] - if str(val) == "ciao": - switch("ok") - return - switch("fail") - - g = greenlet(f) - res = g.switch() - self.assertEqual(res, "ok") - res = g.throw(RuntimeError("ciao")) - self.assertEqual(res, "ok") - - g = greenlet(f) - res = g.switch() - self.assertEqual(res, "ok") - res = g.throw(RuntimeError, "ciao") - self.assertEqual(res, "ok") - - def test_kill(self): - def f(): - switch("ok") - switch("fail") - g = greenlet(f) - res = g.switch() - self.assertEqual(res, "ok") - res = g.throw() - self.assertTrue(isinstance(res, greenlet.GreenletExit)) - self.assertTrue(g.dead) - res = g.throw() # immediately eaten by the already-dead greenlet - self.assertTrue(isinstance(res, greenlet.GreenletExit)) - - def test_throw_goes_to_original_parent(self): - main = greenlet.getcurrent() - - def f1(): - try: - main.switch("f1 ready to catch") - except IndexError: - return "caught" - return "normal exit" - - def f2(): - main.switch("from f2") - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - with self.assertRaises(IndexError): - g2.throw(IndexError) - self.assertTrue(g2.dead) - self.assertTrue(g1.dead) - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - res = g1.switch() - self.assertEqual(res, "f1 ready to catch") - res = g2.throw(IndexError) - self.assertEqual(res, "caught") - self.assertTrue(g2.dead) - self.assertTrue(g1.dead) - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - res = g1.switch() - self.assertEqual(res, "f1 ready to catch") - res = g2.switch() - self.assertEqual(res, "from f2") - res = g2.throw(IndexError) - self.assertEqual(res, "caught") - self.assertTrue(g2.dead) - self.assertTrue(g1.dead) - - def test_non_traceback_param(self): - with self.assertRaises(TypeError) as exc: - greenlet.getcurrent().throw( - Exception, - Exception(), - self - ) - self.assertEqual(str(exc.exception), - "throw() third argument must be a traceback object") - - def test_instance_of_wrong_type(self): - with self.assertRaises(TypeError) as exc: - greenlet.getcurrent().throw( - Exception(), - BaseException() - ) - - self.assertEqual(str(exc.exception), - "instance exception may not have a separate value") - - def test_not_throwable(self): - with self.assertRaises(TypeError) as exc: - greenlet.getcurrent().throw( - "abc" - ) - self.assertEqual(str(exc.exception), - "exceptions must be classes, or instances, not str") diff --git a/.venv/Lib/site-packages/greenlet/tests/test_tracing.py b/.venv/Lib/site-packages/greenlet/tests/test_tracing.py deleted file mode 100644 index c044d4b..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_tracing.py +++ /dev/null @@ -1,291 +0,0 @@ -from __future__ import print_function -import sys -import greenlet -import unittest - -from . import TestCase -from . import PY312 - -# https://discuss.python.org/t/cpython-3-12-greenlet-and-tracing-profiling-how-to-not-crash-and-get-correct-results/33144/2 -DEBUG_BUILD_PY312 = ( - PY312 and hasattr(sys, 'gettotalrefcount'), - "Broken on debug builds of Python 3.12" -) - -class SomeError(Exception): - pass - -class GreenletTracer(object): - oldtrace = None - - def __init__(self, error_on_trace=False): - self.actions = [] - self.error_on_trace = error_on_trace - - def __call__(self, *args): - self.actions.append(args) - if self.error_on_trace: - raise SomeError - - def __enter__(self): - self.oldtrace = greenlet.settrace(self) - return self.actions - - def __exit__(self, *args): - greenlet.settrace(self.oldtrace) - - -class TestGreenletTracing(TestCase): - """ - Tests of ``greenlet.settrace()`` - """ - - def test_a_greenlet_tracing(self): - main = greenlet.getcurrent() - def dummy(): - pass - def dummyexc(): - raise SomeError() - - with GreenletTracer() as actions: - g1 = greenlet.greenlet(dummy) - g1.switch() - g2 = greenlet.greenlet(dummyexc) - self.assertRaises(SomeError, g2.switch) - - self.assertEqual(actions, [ - ('switch', (main, g1)), - ('switch', (g1, main)), - ('switch', (main, g2)), - ('throw', (g2, main)), - ]) - - def test_b_exception_disables_tracing(self): - main = greenlet.getcurrent() - def dummy(): - main.switch() - g = greenlet.greenlet(dummy) - g.switch() - with GreenletTracer(error_on_trace=True) as actions: - self.assertRaises(SomeError, g.switch) - self.assertEqual(greenlet.gettrace(), None) - - self.assertEqual(actions, [ - ('switch', (main, g)), - ]) - - def test_set_same_tracer_twice(self): - # https://github.com/python-greenlet/greenlet/issues/332 - # Our logic in asserting that the tracefunction should - # gain a reference was incorrect if the same tracefunction was set - # twice. - tracer = GreenletTracer() - with tracer: - greenlet.settrace(tracer) - - -class PythonTracer(object): - oldtrace = None - - def __init__(self): - self.actions = [] - - def __call__(self, frame, event, arg): - # Record the co_name so we have an idea what function we're in. - self.actions.append((event, frame.f_code.co_name)) - - def __enter__(self): - self.oldtrace = sys.setprofile(self) - return self.actions - - def __exit__(self, *args): - sys.setprofile(self.oldtrace) - -def tpt_callback(): - return 42 - -class TestPythonTracing(TestCase): - """ - Tests of the interaction of ``sys.settrace()`` - with greenlet facilities. - - NOTE: Most of this is probably CPython specific. - """ - - maxDiff = None - - def test_trace_events_trivial(self): - with PythonTracer() as actions: - tpt_callback() - # If we use the sys.settrace instead of setprofile, we get - # this: - - # self.assertEqual(actions, [ - # ('call', 'tpt_callback'), - # ('call', '__exit__'), - # ]) - - self.assertEqual(actions, [ - ('return', '__enter__'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('call', '__exit__'), - ('c_call', '__exit__'), - ]) - - def _trace_switch(self, glet): - with PythonTracer() as actions: - glet.switch() - return actions - - def _check_trace_events_func_already_set(self, glet): - actions = self._trace_switch(glet) - self.assertEqual(actions, [ - ('return', '__enter__'), - ('c_call', '_trace_switch'), - ('call', 'run'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('return', 'run'), - ('c_return', '_trace_switch'), - ('call', '__exit__'), - ('c_call', '__exit__'), - ]) - - def test_trace_events_into_greenlet_func_already_set(self): - def run(): - return tpt_callback() - - self._check_trace_events_func_already_set(greenlet.greenlet(run)) - - def test_trace_events_into_greenlet_subclass_already_set(self): - class X(greenlet.greenlet): - def run(self): - return tpt_callback() - self._check_trace_events_func_already_set(X()) - - def _check_trace_events_from_greenlet_sets_profiler(self, g, tracer): - g.switch() - tpt_callback() - tracer.__exit__() - self.assertEqual(tracer.actions, [ - ('return', '__enter__'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('return', 'run'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('call', '__exit__'), - ('c_call', '__exit__'), - ]) - - - def test_trace_events_from_greenlet_func_sets_profiler(self): - tracer = PythonTracer() - def run(): - tracer.__enter__() - return tpt_callback() - - self._check_trace_events_from_greenlet_sets_profiler(greenlet.greenlet(run), - tracer) - - def test_trace_events_from_greenlet_subclass_sets_profiler(self): - tracer = PythonTracer() - class X(greenlet.greenlet): - def run(self): - tracer.__enter__() - return tpt_callback() - - self._check_trace_events_from_greenlet_sets_profiler(X(), tracer) - - @unittest.skipIf(*DEBUG_BUILD_PY312) - def test_trace_events_multiple_greenlets_switching(self): - tracer = PythonTracer() - - g1 = None - g2 = None - - def g1_run(): - tracer.__enter__() - tpt_callback() - g2.switch() - tpt_callback() - return 42 - - def g2_run(): - tpt_callback() - tracer.__exit__() - tpt_callback() - g1.switch() - - g1 = greenlet.greenlet(g1_run) - g2 = greenlet.greenlet(g2_run) - - x = g1.switch() - self.assertEqual(x, 42) - tpt_callback() # ensure not in the trace - self.assertEqual(tracer.actions, [ - ('return', '__enter__'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('c_call', 'g1_run'), - ('call', 'g2_run'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('call', '__exit__'), - ('c_call', '__exit__'), - ]) - - @unittest.skipIf(*DEBUG_BUILD_PY312) - def test_trace_events_multiple_greenlets_switching_siblings(self): - # Like the first version, but get both greenlets running first - # as "siblings" and then establish the tracing. - tracer = PythonTracer() - - g1 = None - g2 = None - - def g1_run(): - greenlet.getcurrent().parent.switch() - tracer.__enter__() - tpt_callback() - g2.switch() - tpt_callback() - return 42 - - def g2_run(): - greenlet.getcurrent().parent.switch() - - tpt_callback() - tracer.__exit__() - tpt_callback() - g1.switch() - - g1 = greenlet.greenlet(g1_run) - g2 = greenlet.greenlet(g2_run) - - # Start g1 - g1.switch() - # And it immediately returns control to us. - # Start g2 - g2.switch() - # Which also returns. Now kick of the real part of the - # test. - x = g1.switch() - self.assertEqual(x, 42) - - tpt_callback() # ensure not in the trace - self.assertEqual(tracer.actions, [ - ('return', '__enter__'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('c_call', 'g1_run'), - ('call', 'tpt_callback'), - ('return', 'tpt_callback'), - ('call', '__exit__'), - ('c_call', '__exit__'), - ]) - - -if __name__ == '__main__': - unittest.main() diff --git a/.venv/Lib/site-packages/greenlet/tests/test_version.py b/.venv/Lib/site-packages/greenlet/tests/test_version.py deleted file mode 100644 index 96c17cf..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_version.py +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env python -from __future__ import absolute_import -from __future__ import print_function - -import sys -import os -from unittest import TestCase as NonLeakingTestCase - -import greenlet - -# No reason to run this multiple times under leakchecks, -# it doesn't do anything. -class VersionTests(NonLeakingTestCase): - def test_version(self): - def find_dominating_file(name): - if os.path.exists(name): - return name - - tried = [] - here = os.path.abspath(os.path.dirname(__file__)) - for i in range(10): - up = ['..'] * i - path = [here] + up + [name] - fname = os.path.join(*path) - fname = os.path.abspath(fname) - tried.append(fname) - if os.path.exists(fname): - return fname - raise AssertionError("Could not find file " + name + "; checked " + str(tried)) - - try: - setup_py = find_dominating_file('setup.py') - except AssertionError as e: - self.skipTest("Unable to find setup.py; must be out of tree. " + str(e)) - - - invoke_setup = "%s %s --version" % (sys.executable, setup_py) - with os.popen(invoke_setup) as f: - sversion = f.read().strip() - - self.assertEqual(sversion, greenlet.__version__) diff --git a/.venv/Lib/site-packages/greenlet/tests/test_weakref.py b/.venv/Lib/site-packages/greenlet/tests/test_weakref.py deleted file mode 100644 index 05a38a7..0000000 --- a/.venv/Lib/site-packages/greenlet/tests/test_weakref.py +++ /dev/null @@ -1,35 +0,0 @@ -import gc -import weakref - - -import greenlet -from . import TestCase - -class WeakRefTests(TestCase): - def test_dead_weakref(self): - def _dead_greenlet(): - g = greenlet.greenlet(lambda: None) - g.switch() - return g - o = weakref.ref(_dead_greenlet()) - gc.collect() - self.assertEqual(o(), None) - - def test_inactive_weakref(self): - o = weakref.ref(greenlet.greenlet()) - gc.collect() - self.assertEqual(o(), None) - - def test_dealloc_weakref(self): - seen = [] - def worker(): - try: - greenlet.getcurrent().parent.switch() - finally: - seen.append(g()) - g = greenlet.greenlet(worker) - g.switch() - g2 = greenlet.greenlet(lambda: None, g) - g = weakref.ref(g2) - g2 = None - self.assertEqual(seen, [None]) diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/INSTALLER b/.venv/Lib/site-packages/h11-0.14.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/LICENSE.txt b/.venv/Lib/site-packages/h11-0.14.0.dist-info/LICENSE.txt deleted file mode 100644 index 8f080ea..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Nathaniel J. Smith and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/METADATA b/.venv/Lib/site-packages/h11-0.14.0.dist-info/METADATA deleted file mode 100644 index cf12a82..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/METADATA +++ /dev/null @@ -1,193 +0,0 @@ -Metadata-Version: 2.1 -Name: h11 -Version: 0.14.0 -Summary: A pure-Python, bring-your-own-I/O implementation of HTTP/1.1 -Home-page: https://github.com/python-hyper/h11 -Author: Nathaniel J. Smith -Author-email: njs@pobox.com -License: MIT -Classifier: Development Status :: 3 - Alpha -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -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: Topic :: Internet :: WWW/HTTP -Classifier: Topic :: System :: Networking -Requires-Python: >=3.7 -License-File: LICENSE.txt -Requires-Dist: typing-extensions ; python_version < "3.8" - -h11 -=== - -.. image:: https://travis-ci.org/python-hyper/h11.svg?branch=master - :target: https://travis-ci.org/python-hyper/h11 - :alt: Automated test status - -.. image:: https://codecov.io/gh/python-hyper/h11/branch/master/graph/badge.svg - :target: https://codecov.io/gh/python-hyper/h11 - :alt: Test coverage - -.. image:: https://readthedocs.org/projects/h11/badge/?version=latest - :target: http://h11.readthedocs.io/en/latest/?badge=latest - :alt: Documentation Status - -This is a little HTTP/1.1 library written from scratch in Python, -heavily inspired by `hyper-h2 `_. - -It's a "bring-your-own-I/O" library; h11 contains no IO code -whatsoever. This means you can hook h11 up to your favorite network -API, and that could be anything you want: synchronous, threaded, -asynchronous, or your own implementation of `RFC 6214 -`_ -- h11 won't judge you. -(Compare this to the current state of the art, where every time a `new -network API `_ comes along then someone -gets to start over reimplementing the entire HTTP protocol from -scratch.) Cory Benfield made an `excellent blog post describing the -benefits of this approach -`_, or if you like video -then here's his `PyCon 2016 talk on the same theme -`_. - -This also means that h11 is not immediately useful out of the box: -it's a toolkit for building programs that speak HTTP, not something -that could directly replace ``requests`` or ``twisted.web`` or -whatever. But h11 makes it much easier to implement something like -``requests`` or ``twisted.web``. - -At a high level, working with h11 goes like this: - -1) First, create an ``h11.Connection`` object to track the state of a - single HTTP/1.1 connection. - -2) When you read data off the network, pass it to - ``conn.receive_data(...)``; you'll get back a list of objects - representing high-level HTTP "events". - -3) When you want to send a high-level HTTP event, create the - corresponding "event" object and pass it to ``conn.send(...)``; - this will give you back some bytes that you can then push out - through the network. - -For example, a client might instantiate and then send a -``h11.Request`` object, then zero or more ``h11.Data`` objects for the -request body (e.g., if this is a POST), and then a -``h11.EndOfMessage`` to indicate the end of the message. Then the -server would then send back a ``h11.Response``, some ``h11.Data``, and -its own ``h11.EndOfMessage``. If either side violates the protocol, -you'll get a ``h11.ProtocolError`` exception. - -h11 is suitable for implementing both servers and clients, and has a -pleasantly symmetric API: the events you send as a client are exactly -the ones that you receive as a server and vice-versa. - -`Here's an example of a tiny HTTP client -`_ - -It also has `a fine manual `_. - -FAQ ---- - -*Whyyyyy?* - -I wanted to play with HTTP in `Curio -`__ and `Trio -`__, which at the time didn't have any -HTTP libraries. So I thought, no big deal, Python has, like, a dozen -different implementations of HTTP, surely I can find one that's -reusable. I didn't find one, but I did find Cory's call-to-arms -blog-post. So I figured, well, fine, if I have to implement HTTP from -scratch, at least I can make sure no-one *else* has to ever again. - -*Should I use it?* - -Maybe. You should be aware that it's a very young project. But, it's -feature complete and has an exhaustive test-suite and complete docs, -so the next step is for people to try using it and see how it goes -:-). If you do then please let us know -- if nothing else we'll want -to talk to you before making any incompatible changes! - -*What are the features/limitations?* - -Roughly speaking, it's trying to be a robust, complete, and non-hacky -implementation of the first "chapter" of the HTTP/1.1 spec: `RFC 7230: -HTTP/1.1 Message Syntax and Routing -`_. That is, it mostly focuses on -implementing HTTP at the level of taking bytes on and off the wire, -and the headers related to that, and tries to be anal about spec -conformance. It doesn't know about higher-level concerns like URL -routing, conditional GETs, cross-origin cookie policies, or content -negotiation. But it does know how to take care of framing, -cross-version differences in keep-alive handling, and the "obsolete -line folding" rule, so you can focus your energies on the hard / -interesting parts for your application, and it tries to support the -full specification in the sense that any useful HTTP/1.1 conformant -application should be able to use h11. - -It's pure Python, and has no dependencies outside of the standard -library. - -It has a test suite with 100.0% coverage for both statements and -branches. - -Currently it supports Python 3 (testing on 3.7-3.10) and PyPy 3. -The last Python 2-compatible version was h11 0.11.x. -(Originally it had a Cython wrapper for `http-parser -`_ and a beautiful nested state -machine implemented with ``yield from`` to postprocess the output. But -I had to take these out -- the new *parser* needs fewer lines-of-code -than the old *parser wrapper*, is written in pure Python, uses no -exotic language syntax, and has more features. It's sad, really; that -old state machine was really slick. I just need a few sentences here -to mourn that.) - -I don't know how fast it is. I haven't benchmarked or profiled it yet, -so it's probably got a few pointless hot spots, and I've been trying -to err on the side of simplicity and robustness instead of -micro-optimization. But at the architectural level I tried hard to -avoid fundamentally bad decisions, e.g., I believe that all the -parsing algorithms remain linear-time even in the face of pathological -input like slowloris, and there are no byte-by-byte loops. (I also -believe that it maintains bounded memory usage in the face of -arbitrary/pathological input.) - -The whole library is ~800 lines-of-code. You can read and understand -the whole thing in less than an hour. Most of the energy invested in -this so far has been spent on trying to keep things simple by -minimizing special-cases and ad hoc state manipulation; even though it -is now quite small and simple, I'm still annoyed that I haven't -figured out how to make it even smaller and simpler. (Unfortunately, -HTTP does not lend itself to simplicity.) - -The API is ~feature complete and I don't expect the general outlines -to change much, but you can't judge an API's ergonomics until you -actually document and use it, so I'd expect some changes in the -details. - -*How do I try it?* - -.. code-block:: sh - - $ pip install h11 - $ git clone git@github.com:python-hyper/h11 - $ cd h11/examples - $ python basic-client.py - -and go from there. - -*License?* - -MIT - -*Code of conduct?* - -Contributors are requested to follow our `code of conduct -`_ in -all project spaces. diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/RECORD b/.venv/Lib/site-packages/h11-0.14.0.dist-info/RECORD deleted file mode 100644 index 57c1a6e..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/RECORD +++ /dev/null @@ -1,52 +0,0 @@ -h11-0.14.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -h11-0.14.0.dist-info/LICENSE.txt,sha256=N9tbuFkm2yikJ6JYZ_ELEjIAOuob5pzLhRE4rbjm82E,1124 -h11-0.14.0.dist-info/METADATA,sha256=B7pZ0m7WBXNs17vl6hUH9bJTL9s37DaGvY31w7jNxSg,8175 -h11-0.14.0.dist-info/RECORD,, -h11-0.14.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 -h11-0.14.0.dist-info/top_level.txt,sha256=F7dC4jl3zeh8TGHEPaWJrMbeuoWbS379Gwdi-Yvdcis,4 -h11/__init__.py,sha256=iO1KzkSO42yZ6ffg-VMgbx_ZVTWGUY00nRYEWn-s3kY,1507 -h11/__pycache__/__init__.cpython-311.pyc,, -h11/__pycache__/_abnf.cpython-311.pyc,, -h11/__pycache__/_connection.cpython-311.pyc,, -h11/__pycache__/_events.cpython-311.pyc,, -h11/__pycache__/_headers.cpython-311.pyc,, -h11/__pycache__/_readers.cpython-311.pyc,, -h11/__pycache__/_receivebuffer.cpython-311.pyc,, -h11/__pycache__/_state.cpython-311.pyc,, -h11/__pycache__/_util.cpython-311.pyc,, -h11/__pycache__/_version.cpython-311.pyc,, -h11/__pycache__/_writers.cpython-311.pyc,, -h11/_abnf.py,sha256=ybixr0xsupnkA6GFAyMubuXF6Tc1lb_hF890NgCsfNc,4815 -h11/_connection.py,sha256=eS2sorMD0zKLCFiB9lW9W9F_Nzny2tjHa4e6s1ujr1c,26539 -h11/_events.py,sha256=LEfuvg1AbhHaVRwxCd0I-pFn9-ezUOaoL8o2Kvy1PBA,11816 -h11/_headers.py,sha256=RqB8cd8CN0blYPzcLe5qeCh-phv6D1U_CHj4hs67lgQ,10230 -h11/_readers.py,sha256=EbSed0jzwVUiD1nOPAeUcVE4Flf3wXkxfb8c06-OTBM,8383 -h11/_receivebuffer.py,sha256=xrspsdsNgWFxRfQcTXxR8RrdjRXXTK0Io5cQYWpJ1Ws,5252 -h11/_state.py,sha256=k1VL6SDbaPkSrZ-49ewCXDpuiUS69_46YhbWjuV1qEY,13300 -h11/_util.py,sha256=LWkkjXyJaFlAy6Lt39w73UStklFT5ovcvo0TkY7RYuk,4888 -h11/_version.py,sha256=LVyTdiZRzIIEv79UyOgbM5iUrJUllEzlCWaJEYBY1zc,686 -h11/_writers.py,sha256=oFKm6PtjeHfbj4RLX7VB7KDc1gIY53gXG3_HR9ltmTA,5081 -h11/py.typed,sha256=sow9soTwP9T_gEAQSVh7Gb8855h04Nwmhs2We-JRgZM,7 -h11/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -h11/tests/__pycache__/__init__.cpython-311.pyc,, -h11/tests/__pycache__/helpers.cpython-311.pyc,, -h11/tests/__pycache__/test_against_stdlib_http.cpython-311.pyc,, -h11/tests/__pycache__/test_connection.cpython-311.pyc,, -h11/tests/__pycache__/test_events.cpython-311.pyc,, -h11/tests/__pycache__/test_headers.cpython-311.pyc,, -h11/tests/__pycache__/test_helpers.cpython-311.pyc,, -h11/tests/__pycache__/test_io.cpython-311.pyc,, -h11/tests/__pycache__/test_receivebuffer.cpython-311.pyc,, -h11/tests/__pycache__/test_state.cpython-311.pyc,, -h11/tests/__pycache__/test_util.cpython-311.pyc,, -h11/tests/data/test-file,sha256=ZJ03Rqs98oJw29OHzJg7LlMzyGQaRAY0r3AqBeM2wVU,65 -h11/tests/helpers.py,sha256=a1EVG_p7xU4wRsa3tMPTRxuaKCmretok9sxXWvqfmQA,3355 -h11/tests/test_against_stdlib_http.py,sha256=cojCHgHXFQ8gWhNlEEwl3trmOpN-5uDukRoHnElqo3A,3995 -h11/tests/test_connection.py,sha256=ZbPLDPclKvjgjAhgk-WlCPBaf17c4XUIV2tpaW08jOI,38720 -h11/tests/test_events.py,sha256=LPVLbcV-NvPNK9fW3rraR6Bdpz1hAlsWubMtNaJ5gHg,4657 -h11/tests/test_headers.py,sha256=qd8T1Zenuz5GbD6wklSJ5G8VS7trrYgMV0jT-SMvqg8,5612 -h11/tests/test_helpers.py,sha256=kAo0CEM4LGqmyyP2ZFmhsyq3UFJqoFfAbzu3hbWreRM,794 -h11/tests/test_io.py,sha256=uCZVnjarkRBkudfC1ij-KSCQ71XWJhnkgkgWWkKgYPQ,16386 -h11/tests/test_receivebuffer.py,sha256=3jGbeJM36Akqg_pAhPb7XzIn2NS6RhPg-Ryg8Eu6ytk,3454 -h11/tests/test_state.py,sha256=rqll9WqFsJPE0zSrtCn9LH659mPKsDeXZ-DwXwleuBQ,8928 -h11/tests/test_util.py,sha256=VO5L4nSFe4pgtSwKuv6u_6l0H7UeizF5WKuHTWreg70,2970 diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/WHEEL b/.venv/Lib/site-packages/h11-0.14.0.dist-info/WHEEL deleted file mode 100644 index 5bad85f..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/Lib/site-packages/h11-0.14.0.dist-info/top_level.txt b/.venv/Lib/site-packages/h11-0.14.0.dist-info/top_level.txt deleted file mode 100644 index 0d24def..0000000 --- a/.venv/Lib/site-packages/h11-0.14.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -h11 diff --git a/.venv/Lib/site-packages/h11/__init__.py b/.venv/Lib/site-packages/h11/__init__.py deleted file mode 100644 index 989e92c..0000000 --- a/.venv/Lib/site-packages/h11/__init__.py +++ /dev/null @@ -1,62 +0,0 @@ -# A highish-level implementation of the HTTP/1.1 wire protocol (RFC 7230), -# containing no networking code at all, loosely modelled on hyper-h2's generic -# implementation of HTTP/2 (and in particular the h2.connection.H2Connection -# class). There's still a bunch of subtle details you need to get right if you -# want to make this actually useful, because it doesn't implement all the -# semantics to check that what you're asking to write to the wire is sensible, -# but at least it gets you out of dealing with the wire itself. - -from h11._connection import Connection, NEED_DATA, PAUSED -from h11._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from h11._state import ( - CLIENT, - CLOSED, - DONE, - ERROR, - IDLE, - MIGHT_SWITCH_PROTOCOL, - MUST_CLOSE, - SEND_BODY, - SEND_RESPONSE, - SERVER, - SWITCHED_PROTOCOL, -) -from h11._util import LocalProtocolError, ProtocolError, RemoteProtocolError -from h11._version import __version__ - -PRODUCT_ID = "python-h11/" + __version__ - - -__all__ = ( - "Connection", - "NEED_DATA", - "PAUSED", - "ConnectionClosed", - "Data", - "EndOfMessage", - "Event", - "InformationalResponse", - "Request", - "Response", - "CLIENT", - "CLOSED", - "DONE", - "ERROR", - "IDLE", - "MUST_CLOSE", - "SEND_BODY", - "SEND_RESPONSE", - "SERVER", - "SWITCHED_PROTOCOL", - "ProtocolError", - "LocalProtocolError", - "RemoteProtocolError", -) diff --git a/.venv/Lib/site-packages/h11/_abnf.py b/.venv/Lib/site-packages/h11/_abnf.py deleted file mode 100644 index 933587f..0000000 --- a/.venv/Lib/site-packages/h11/_abnf.py +++ /dev/null @@ -1,132 +0,0 @@ -# We use native strings for all the re patterns, to take advantage of string -# formatting, and then convert to bytestrings when compiling the final re -# objects. - -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#whitespace -# OWS = *( SP / HTAB ) -# ; optional whitespace -OWS = r"[ \t]*" - -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.token.separators -# token = 1*tchar -# -# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" -# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" -# / DIGIT / ALPHA -# ; any VCHAR, except delimiters -token = r"[-!#$%&'*+.^_`|~0-9a-zA-Z]+" - -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#header.fields -# field-name = token -field_name = token - -# The standard says: -# -# field-value = *( field-content / obs-fold ) -# field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] -# field-vchar = VCHAR / obs-text -# obs-fold = CRLF 1*( SP / HTAB ) -# ; obsolete line folding -# ; see Section 3.2.4 -# -# https://tools.ietf.org/html/rfc5234#appendix-B.1 -# -# VCHAR = %x21-7E -# ; visible (printing) characters -# -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#rule.quoted-string -# obs-text = %x80-FF -# -# However, the standard definition of field-content is WRONG! It disallows -# fields containing a single visible character surrounded by whitespace, -# e.g. "foo a bar". -# -# See: https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 -# -# So our definition of field_content attempts to fix it up... -# -# Also, we allow lots of control characters, because apparently people assume -# that they're legal in practice (e.g., google analytics makes cookies with -# \x01 in them!): -# https://github.com/python-hyper/h11/issues/57 -# We still don't allow NUL or whitespace, because those are often treated as -# meta-characters and letting them through can lead to nasty issues like SSRF. -vchar = r"[\x21-\x7e]" -vchar_or_obs_text = r"[^\x00\s]" -field_vchar = vchar_or_obs_text -field_content = r"{field_vchar}+(?:[ \t]+{field_vchar}+)*".format(**globals()) - -# We handle obs-fold at a different level, and our fixed-up field_content -# already grows to swallow the whole value, so ? instead of * -field_value = r"({field_content})?".format(**globals()) - -# header-field = field-name ":" OWS field-value OWS -header_field = ( - r"(?P{field_name})" - r":" - r"{OWS}" - r"(?P{field_value})" - r"{OWS}".format(**globals()) -) - -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#request.line -# -# request-line = method SP request-target SP HTTP-version CRLF -# method = token -# HTTP-version = HTTP-name "/" DIGIT "." DIGIT -# HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive -# -# request-target is complicated (see RFC 7230 sec 5.3) -- could be path, full -# URL, host+port (for connect), or even "*", but in any case we are guaranteed -# that it contists of the visible printing characters. -method = token -request_target = r"{vchar}+".format(**globals()) -http_version = r"HTTP/(?P[0-9]\.[0-9])" -request_line = ( - r"(?P{method})" - r" " - r"(?P{request_target})" - r" " - r"{http_version}".format(**globals()) -) - -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#status.line -# -# status-line = HTTP-version SP status-code SP reason-phrase CRLF -# status-code = 3DIGIT -# reason-phrase = *( HTAB / SP / VCHAR / obs-text ) -status_code = r"[0-9]{3}" -reason_phrase = r"([ \t]|{vchar_or_obs_text})*".format(**globals()) -status_line = ( - r"{http_version}" - r" " - r"(?P{status_code})" - # However, there are apparently a few too many servers out there that just - # leave out the reason phrase: - # https://github.com/scrapy/scrapy/issues/345#issuecomment-281756036 - # https://github.com/seanmonstar/httparse/issues/29 - # so make it optional. ?: is a non-capturing group. - r"(?: (?P{reason_phrase}))?".format(**globals()) -) - -HEXDIG = r"[0-9A-Fa-f]" -# Actually -# -# chunk-size = 1*HEXDIG -# -# but we impose an upper-limit to avoid ridiculosity. len(str(2**64)) == 20 -chunk_size = r"({HEXDIG}){{1,20}}".format(**globals()) -# Actually -# -# chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) -# -# but we aren't parsing the things so we don't really care. -chunk_ext = r";.*" -chunk_header = ( - r"(?P{chunk_size})" - r"(?P{chunk_ext})?" - r"{OWS}\r\n".format( - **globals() - ) # Even though the specification does not allow for extra whitespaces, - # we are lenient with trailing whitespaces because some servers on the wild use it. -) diff --git a/.venv/Lib/site-packages/h11/_connection.py b/.venv/Lib/site-packages/h11/_connection.py deleted file mode 100644 index d175270..0000000 --- a/.venv/Lib/site-packages/h11/_connection.py +++ /dev/null @@ -1,633 +0,0 @@ -# This contains the main Connection class. Everything in h11 revolves around -# this. -from typing import Any, Callable, cast, Dict, List, Optional, Tuple, Type, Union - -from ._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from ._headers import get_comma_header, has_expect_100_continue, set_comma_header -from ._readers import READERS, ReadersType -from ._receivebuffer import ReceiveBuffer -from ._state import ( - _SWITCH_CONNECT, - _SWITCH_UPGRADE, - CLIENT, - ConnectionState, - DONE, - ERROR, - MIGHT_SWITCH_PROTOCOL, - SEND_BODY, - SERVER, - SWITCHED_PROTOCOL, -) -from ._util import ( # Import the internal things we need - LocalProtocolError, - RemoteProtocolError, - Sentinel, -) -from ._writers import WRITERS, WritersType - -# Everything in __all__ gets re-exported as part of the h11 public API. -__all__ = ["Connection", "NEED_DATA", "PAUSED"] - - -class NEED_DATA(Sentinel, metaclass=Sentinel): - pass - - -class PAUSED(Sentinel, metaclass=Sentinel): - pass - - -# If we ever have this much buffered without it making a complete parseable -# event, we error out. The only time we really buffer is when reading the -# request/response line + headers together, so this is effectively the limit on -# the size of that. -# -# Some precedents for defaults: -# - node.js: 80 * 1024 -# - tomcat: 8 * 1024 -# - IIS: 16 * 1024 -# - Apache: <8 KiB per line> -DEFAULT_MAX_INCOMPLETE_EVENT_SIZE = 16 * 1024 - -# RFC 7230's rules for connection lifecycles: -# - If either side says they want to close the connection, then the connection -# must close. -# - HTTP/1.1 defaults to keep-alive unless someone says Connection: close -# - HTTP/1.0 defaults to close unless both sides say Connection: keep-alive -# (and even this is a mess -- e.g. if you're implementing a proxy then -# sending Connection: keep-alive is forbidden). -# -# We simplify life by simply not supporting keep-alive with HTTP/1.0 peers. So -# our rule is: -# - If someone says Connection: close, we will close -# - If someone uses HTTP/1.0, we will close. -def _keep_alive(event: Union[Request, Response]) -> bool: - connection = get_comma_header(event.headers, b"connection") - if b"close" in connection: - return False - if getattr(event, "http_version", b"1.1") < b"1.1": - return False - return True - - -def _body_framing( - request_method: bytes, event: Union[Request, Response] -) -> Tuple[str, Union[Tuple[()], Tuple[int]]]: - # Called when we enter SEND_BODY to figure out framing information for - # this body. - # - # These are the only two events that can trigger a SEND_BODY state: - assert type(event) in (Request, Response) - # Returns one of: - # - # ("content-length", count) - # ("chunked", ()) - # ("http/1.0", ()) - # - # which are (lookup key, *args) for constructing body reader/writer - # objects. - # - # Reference: https://tools.ietf.org/html/rfc7230#section-3.3.3 - # - # Step 1: some responses always have an empty body, regardless of what the - # headers say. - if type(event) is Response: - if ( - event.status_code in (204, 304) - or request_method == b"HEAD" - or (request_method == b"CONNECT" and 200 <= event.status_code < 300) - ): - return ("content-length", (0,)) - # Section 3.3.3 also lists another case -- responses with status_code - # < 200. For us these are InformationalResponses, not Responses, so - # they can't get into this function in the first place. - assert event.status_code >= 200 - - # Step 2: check for Transfer-Encoding (T-E beats C-L): - transfer_encodings = get_comma_header(event.headers, b"transfer-encoding") - if transfer_encodings: - assert transfer_encodings == [b"chunked"] - return ("chunked", ()) - - # Step 3: check for Content-Length - content_lengths = get_comma_header(event.headers, b"content-length") - if content_lengths: - return ("content-length", (int(content_lengths[0]),)) - - # Step 4: no applicable headers; fallback/default depends on type - if type(event) is Request: - return ("content-length", (0,)) - else: - return ("http/1.0", ()) - - -################################################################ -# -# The main Connection class -# -################################################################ - - -class Connection: - """An object encapsulating the state of an HTTP connection. - - Args: - our_role: If you're implementing a client, pass :data:`h11.CLIENT`. If - you're implementing a server, pass :data:`h11.SERVER`. - - max_incomplete_event_size (int): - The maximum number of bytes we're willing to buffer of an - incomplete event. In practice this mostly sets a limit on the - maximum size of the request/response line + headers. If this is - exceeded, then :meth:`next_event` will raise - :exc:`RemoteProtocolError`. - - """ - - def __init__( - self, - our_role: Type[Sentinel], - max_incomplete_event_size: int = DEFAULT_MAX_INCOMPLETE_EVENT_SIZE, - ) -> None: - self._max_incomplete_event_size = max_incomplete_event_size - # State and role tracking - if our_role not in (CLIENT, SERVER): - raise ValueError("expected CLIENT or SERVER, not {!r}".format(our_role)) - self.our_role = our_role - self.their_role: Type[Sentinel] - if our_role is CLIENT: - self.their_role = SERVER - else: - self.their_role = CLIENT - self._cstate = ConnectionState() - - # Callables for converting data->events or vice-versa given the - # current state - self._writer = self._get_io_object(self.our_role, None, WRITERS) - self._reader = self._get_io_object(self.their_role, None, READERS) - - # Holds any unprocessed received data - self._receive_buffer = ReceiveBuffer() - # If this is true, then it indicates that the incoming connection was - # closed *after* the end of whatever's in self._receive_buffer: - self._receive_buffer_closed = False - - # Extra bits of state that don't fit into the state machine. - # - # These two are only used to interpret framing headers for figuring - # out how to read/write response bodies. their_http_version is also - # made available as a convenient public API. - self.their_http_version: Optional[bytes] = None - self._request_method: Optional[bytes] = None - # This is pure flow-control and doesn't at all affect the set of legal - # transitions, so no need to bother ConnectionState with it: - self.client_is_waiting_for_100_continue = False - - @property - def states(self) -> Dict[Type[Sentinel], Type[Sentinel]]: - """A dictionary like:: - - {CLIENT: , SERVER: } - - See :ref:`state-machine` for details. - - """ - return dict(self._cstate.states) - - @property - def our_state(self) -> Type[Sentinel]: - """The current state of whichever role we are playing. See - :ref:`state-machine` for details. - """ - return self._cstate.states[self.our_role] - - @property - def their_state(self) -> Type[Sentinel]: - """The current state of whichever role we are NOT playing. See - :ref:`state-machine` for details. - """ - return self._cstate.states[self.their_role] - - @property - def they_are_waiting_for_100_continue(self) -> bool: - return self.their_role is CLIENT and self.client_is_waiting_for_100_continue - - def start_next_cycle(self) -> None: - """Attempt to reset our connection state for a new request/response - cycle. - - If both client and server are in :data:`DONE` state, then resets them - both to :data:`IDLE` state in preparation for a new request/response - cycle on this same connection. Otherwise, raises a - :exc:`LocalProtocolError`. - - See :ref:`keepalive-and-pipelining`. - - """ - old_states = dict(self._cstate.states) - self._cstate.start_next_cycle() - self._request_method = None - # self.their_http_version gets left alone, since it presumably lasts - # beyond a single request/response cycle - assert not self.client_is_waiting_for_100_continue - self._respond_to_state_changes(old_states) - - def _process_error(self, role: Type[Sentinel]) -> None: - old_states = dict(self._cstate.states) - self._cstate.process_error(role) - self._respond_to_state_changes(old_states) - - def _server_switch_event(self, event: Event) -> Optional[Type[Sentinel]]: - if type(event) is InformationalResponse and event.status_code == 101: - return _SWITCH_UPGRADE - if type(event) is Response: - if ( - _SWITCH_CONNECT in self._cstate.pending_switch_proposals - and 200 <= event.status_code < 300 - ): - return _SWITCH_CONNECT - return None - - # All events go through here - def _process_event(self, role: Type[Sentinel], event: Event) -> None: - # First, pass the event through the state machine to make sure it - # succeeds. - old_states = dict(self._cstate.states) - if role is CLIENT and type(event) is Request: - if event.method == b"CONNECT": - self._cstate.process_client_switch_proposal(_SWITCH_CONNECT) - if get_comma_header(event.headers, b"upgrade"): - self._cstate.process_client_switch_proposal(_SWITCH_UPGRADE) - server_switch_event = None - if role is SERVER: - server_switch_event = self._server_switch_event(event) - self._cstate.process_event(role, type(event), server_switch_event) - - # Then perform the updates triggered by it. - - if type(event) is Request: - self._request_method = event.method - - if role is self.their_role and type(event) in ( - Request, - Response, - InformationalResponse, - ): - event = cast(Union[Request, Response, InformationalResponse], event) - self.their_http_version = event.http_version - - # Keep alive handling - # - # RFC 7230 doesn't really say what one should do if Connection: close - # shows up on a 1xx InformationalResponse. I think the idea is that - # this is not supposed to happen. In any case, if it does happen, we - # ignore it. - if type(event) in (Request, Response) and not _keep_alive( - cast(Union[Request, Response], event) - ): - self._cstate.process_keep_alive_disabled() - - # 100-continue - if type(event) is Request and has_expect_100_continue(event): - self.client_is_waiting_for_100_continue = True - if type(event) in (InformationalResponse, Response): - self.client_is_waiting_for_100_continue = False - if role is CLIENT and type(event) in (Data, EndOfMessage): - self.client_is_waiting_for_100_continue = False - - self._respond_to_state_changes(old_states, event) - - def _get_io_object( - self, - role: Type[Sentinel], - event: Optional[Event], - io_dict: Union[ReadersType, WritersType], - ) -> Optional[Callable[..., Any]]: - # event may be None; it's only used when entering SEND_BODY - state = self._cstate.states[role] - if state is SEND_BODY: - # Special case: the io_dict has a dict of reader/writer factories - # that depend on the request/response framing. - framing_type, args = _body_framing( - cast(bytes, self._request_method), cast(Union[Request, Response], event) - ) - return io_dict[SEND_BODY][framing_type](*args) # type: ignore[index] - else: - # General case: the io_dict just has the appropriate reader/writer - # for this state - return io_dict.get((role, state)) # type: ignore[return-value] - - # This must be called after any action that might have caused - # self._cstate.states to change. - def _respond_to_state_changes( - self, - old_states: Dict[Type[Sentinel], Type[Sentinel]], - event: Optional[Event] = None, - ) -> None: - # Update reader/writer - if self.our_state != old_states[self.our_role]: - self._writer = self._get_io_object(self.our_role, event, WRITERS) - if self.their_state != old_states[self.their_role]: - self._reader = self._get_io_object(self.their_role, event, READERS) - - @property - def trailing_data(self) -> Tuple[bytes, bool]: - """Data that has been received, but not yet processed, represented as - a tuple with two elements, where the first is a byte-string containing - the unprocessed data itself, and the second is a bool that is True if - the receive connection was closed. - - See :ref:`switching-protocols` for discussion of why you'd want this. - """ - return (bytes(self._receive_buffer), self._receive_buffer_closed) - - def receive_data(self, data: bytes) -> None: - """Add data to our internal receive buffer. - - This does not actually do any processing on the data, just stores - it. To trigger processing, you have to call :meth:`next_event`. - - Args: - data (:term:`bytes-like object`): - The new data that was just received. - - Special case: If *data* is an empty byte-string like ``b""``, - then this indicates that the remote side has closed the - connection (end of file). Normally this is convenient, because - standard Python APIs like :meth:`file.read` or - :meth:`socket.recv` use ``b""`` to indicate end-of-file, while - other failures to read are indicated using other mechanisms - like raising :exc:`TimeoutError`. When using such an API you - can just blindly pass through whatever you get from ``read`` - to :meth:`receive_data`, and everything will work. - - But, if you have an API where reading an empty string is a - valid non-EOF condition, then you need to be aware of this and - make sure to check for such strings and avoid passing them to - :meth:`receive_data`. - - Returns: - Nothing, but after calling this you should call :meth:`next_event` - to parse the newly received data. - - Raises: - RuntimeError: - Raised if you pass an empty *data*, indicating EOF, and then - pass a non-empty *data*, indicating more data that somehow - arrived after the EOF. - - (Calling ``receive_data(b"")`` multiple times is fine, - and equivalent to calling it once.) - - """ - if data: - if self._receive_buffer_closed: - raise RuntimeError("received close, then received more data?") - self._receive_buffer += data - else: - self._receive_buffer_closed = True - - def _extract_next_receive_event( - self, - ) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: - state = self.their_state - # We don't pause immediately when they enter DONE, because even in - # DONE state we can still process a ConnectionClosed() event. But - # if we have data in our buffer, then we definitely aren't getting - # a ConnectionClosed() immediately and we need to pause. - if state is DONE and self._receive_buffer: - return PAUSED - if state is MIGHT_SWITCH_PROTOCOL or state is SWITCHED_PROTOCOL: - return PAUSED - assert self._reader is not None - event = self._reader(self._receive_buffer) - if event is None: - if not self._receive_buffer and self._receive_buffer_closed: - # In some unusual cases (basically just HTTP/1.0 bodies), EOF - # triggers an actual protocol event; in that case, we want to - # return that event, and then the state will change and we'll - # get called again to generate the actual ConnectionClosed(). - if hasattr(self._reader, "read_eof"): - event = self._reader.read_eof() # type: ignore[attr-defined] - else: - event = ConnectionClosed() - if event is None: - event = NEED_DATA - return event # type: ignore[no-any-return] - - def next_event(self) -> Union[Event, Type[NEED_DATA], Type[PAUSED]]: - """Parse the next event out of our receive buffer, update our internal - state, and return it. - - This is a mutating operation -- think of it like calling :func:`next` - on an iterator. - - Returns: - : One of three things: - - 1) An event object -- see :ref:`events`. - - 2) The special constant :data:`NEED_DATA`, which indicates that - you need to read more data from your socket and pass it to - :meth:`receive_data` before this method will be able to return - any more events. - - 3) The special constant :data:`PAUSED`, which indicates that we - are not in a state where we can process incoming data (usually - because the peer has finished their part of the current - request/response cycle, and you have not yet called - :meth:`start_next_cycle`). See :ref:`flow-control` for details. - - Raises: - RemoteProtocolError: - The peer has misbehaved. You should close the connection - (possibly after sending some kind of 4xx response). - - Once this method returns :class:`ConnectionClosed` once, then all - subsequent calls will also return :class:`ConnectionClosed`. - - If this method raises any exception besides :exc:`RemoteProtocolError` - then that's a bug -- if it happens please file a bug report! - - If this method raises any exception then it also sets - :attr:`Connection.their_state` to :data:`ERROR` -- see - :ref:`error-handling` for discussion. - - """ - - if self.their_state is ERROR: - raise RemoteProtocolError("Can't receive data when peer state is ERROR") - try: - event = self._extract_next_receive_event() - if event not in [NEED_DATA, PAUSED]: - self._process_event(self.their_role, cast(Event, event)) - if event is NEED_DATA: - if len(self._receive_buffer) > self._max_incomplete_event_size: - # 431 is "Request header fields too large" which is pretty - # much the only situation where we can get here - raise RemoteProtocolError( - "Receive buffer too long", error_status_hint=431 - ) - if self._receive_buffer_closed: - # We're still trying to complete some event, but that's - # never going to happen because no more data is coming - raise RemoteProtocolError("peer unexpectedly closed connection") - return event - except BaseException as exc: - self._process_error(self.their_role) - if isinstance(exc, LocalProtocolError): - exc._reraise_as_remote_protocol_error() - else: - raise - - def send(self, event: Event) -> Optional[bytes]: - """Convert a high-level event into bytes that can be sent to the peer, - while updating our internal state machine. - - Args: - event: The :ref:`event ` to send. - - Returns: - If ``type(event) is ConnectionClosed``, then returns - ``None``. Otherwise, returns a :term:`bytes-like object`. - - Raises: - LocalProtocolError: - Sending this event at this time would violate our - understanding of the HTTP/1.1 protocol. - - If this method raises any exception then it also sets - :attr:`Connection.our_state` to :data:`ERROR` -- see - :ref:`error-handling` for discussion. - - """ - data_list = self.send_with_data_passthrough(event) - if data_list is None: - return None - else: - return b"".join(data_list) - - def send_with_data_passthrough(self, event: Event) -> Optional[List[bytes]]: - """Identical to :meth:`send`, except that in situations where - :meth:`send` returns a single :term:`bytes-like object`, this instead - returns a list of them -- and when sending a :class:`Data` event, this - list is guaranteed to contain the exact object you passed in as - :attr:`Data.data`. See :ref:`sendfile` for discussion. - - """ - if self.our_state is ERROR: - raise LocalProtocolError("Can't send data when our state is ERROR") - try: - if type(event) is Response: - event = self._clean_up_response_headers_for_sending(event) - # We want to call _process_event before calling the writer, - # because if someone tries to do something invalid then this will - # give a sensible error message, while our writers all just assume - # they will only receive valid events. But, _process_event might - # change self._writer. So we have to do a little dance: - writer = self._writer - self._process_event(self.our_role, event) - if type(event) is ConnectionClosed: - return None - else: - # In any situation where writer is None, process_event should - # have raised ProtocolError - assert writer is not None - data_list: List[bytes] = [] - writer(event, data_list.append) - return data_list - except: - self._process_error(self.our_role) - raise - - def send_failed(self) -> None: - """Notify the state machine that we failed to send the data it gave - us. - - This causes :attr:`Connection.our_state` to immediately become - :data:`ERROR` -- see :ref:`error-handling` for discussion. - - """ - self._process_error(self.our_role) - - # When sending a Response, we take responsibility for a few things: - # - # - Sometimes you MUST set Connection: close. We take care of those - # times. (You can also set it yourself if you want, and if you do then - # we'll respect that and close the connection at the right time. But you - # don't have to worry about that unless you want to.) - # - # - The user has to set Content-Length if they want it. Otherwise, for - # responses that have bodies (e.g. not HEAD), then we will automatically - # select the right mechanism for streaming a body of unknown length, - # which depends on depending on the peer's HTTP version. - # - # This function's *only* responsibility is making sure headers are set up - # right -- everything downstream just looks at the headers. There are no - # side channels. - def _clean_up_response_headers_for_sending(self, response: Response) -> Response: - assert type(response) is Response - - headers = response.headers - need_close = False - - # HEAD requests need some special handling: they always act like they - # have Content-Length: 0, and that's how _body_framing treats - # them. But their headers are supposed to match what we would send if - # the request was a GET. (Technically there is one deviation allowed: - # we're allowed to leave out the framing headers -- see - # https://tools.ietf.org/html/rfc7231#section-4.3.2 . But it's just as - # easy to get them right.) - method_for_choosing_headers = cast(bytes, self._request_method) - if method_for_choosing_headers == b"HEAD": - method_for_choosing_headers = b"GET" - framing_type, _ = _body_framing(method_for_choosing_headers, response) - if framing_type in ("chunked", "http/1.0"): - # This response has a body of unknown length. - # If our peer is HTTP/1.1, we use Transfer-Encoding: chunked - # If our peer is HTTP/1.0, we use no framing headers, and close the - # connection afterwards. - # - # Make sure to clear Content-Length (in principle user could have - # set both and then we ignored Content-Length b/c - # Transfer-Encoding overwrote it -- this would be naughty of them, - # but the HTTP spec says that if our peer does this then we have - # to fix it instead of erroring out, so we'll accord the user the - # same respect). - headers = set_comma_header(headers, b"content-length", []) - if self.their_http_version is None or self.their_http_version < b"1.1": - # Either we never got a valid request and are sending back an - # error (their_http_version is None), so we assume the worst; - # or else we did get a valid HTTP/1.0 request, so we know that - # they don't understand chunked encoding. - headers = set_comma_header(headers, b"transfer-encoding", []) - # This is actually redundant ATM, since currently we - # unconditionally disable keep-alive when talking to HTTP/1.0 - # peers. But let's be defensive just in case we add - # Connection: keep-alive support later: - if self._request_method != b"HEAD": - need_close = True - else: - headers = set_comma_header(headers, b"transfer-encoding", [b"chunked"]) - - if not self._cstate.keep_alive or need_close: - # Make sure Connection: close is set - connection = set(get_comma_header(headers, b"connection")) - connection.discard(b"keep-alive") - connection.add(b"close") - headers = set_comma_header(headers, b"connection", sorted(connection)) - - return Response( - headers=headers, - status_code=response.status_code, - http_version=response.http_version, - reason=response.reason, - ) diff --git a/.venv/Lib/site-packages/h11/_events.py b/.venv/Lib/site-packages/h11/_events.py deleted file mode 100644 index 075bf8a..0000000 --- a/.venv/Lib/site-packages/h11/_events.py +++ /dev/null @@ -1,369 +0,0 @@ -# High level events that make up HTTP/1.1 conversations. Loosely inspired by -# the corresponding events in hyper-h2: -# -# http://python-hyper.org/h2/en/stable/api.html#events -# -# Don't subclass these. Stuff will break. - -import re -from abc import ABC -from dataclasses import dataclass, field -from typing import Any, cast, Dict, List, Tuple, Union - -from ._abnf import method, request_target -from ._headers import Headers, normalize_and_validate -from ._util import bytesify, LocalProtocolError, validate - -# Everything in __all__ gets re-exported as part of the h11 public API. -__all__ = [ - "Event", - "Request", - "InformationalResponse", - "Response", - "Data", - "EndOfMessage", - "ConnectionClosed", -] - -method_re = re.compile(method.encode("ascii")) -request_target_re = re.compile(request_target.encode("ascii")) - - -class Event(ABC): - """ - Base class for h11 events. - """ - - __slots__ = () - - -@dataclass(init=False, frozen=True) -class Request(Event): - """The beginning of an HTTP request. - - Fields: - - .. attribute:: method - - An HTTP method, e.g. ``b"GET"`` or ``b"POST"``. Always a byte - string. :term:`Bytes-like objects ` and native - strings containing only ascii characters will be automatically - converted to byte strings. - - .. attribute:: target - - The target of an HTTP request, e.g. ``b"/index.html"``, or one of the - more exotic formats described in `RFC 7320, section 5.3 - `_. Always a byte - string. :term:`Bytes-like objects ` and native - strings containing only ascii characters will be automatically - converted to byte strings. - - .. attribute:: headers - - Request headers, represented as a list of (name, value) pairs. See - :ref:`the header normalization rules ` for details. - - .. attribute:: http_version - - The HTTP protocol version, represented as a byte string like - ``b"1.1"``. See :ref:`the HTTP version normalization rules - ` for details. - - """ - - __slots__ = ("method", "headers", "target", "http_version") - - method: bytes - headers: Headers - target: bytes - http_version: bytes - - def __init__( - self, - *, - method: Union[bytes, str], - headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], - target: Union[bytes, str], - http_version: Union[bytes, str] = b"1.1", - _parsed: bool = False, - ) -> None: - super().__init__() - if isinstance(headers, Headers): - object.__setattr__(self, "headers", headers) - else: - object.__setattr__( - self, "headers", normalize_and_validate(headers, _parsed=_parsed) - ) - if not _parsed: - object.__setattr__(self, "method", bytesify(method)) - object.__setattr__(self, "target", bytesify(target)) - object.__setattr__(self, "http_version", bytesify(http_version)) - else: - object.__setattr__(self, "method", method) - object.__setattr__(self, "target", target) - object.__setattr__(self, "http_version", http_version) - - # "A server MUST respond with a 400 (Bad Request) status code to any - # HTTP/1.1 request message that lacks a Host header field and to any - # request message that contains more than one Host header field or a - # Host header field with an invalid field-value." - # -- https://tools.ietf.org/html/rfc7230#section-5.4 - host_count = 0 - for name, value in self.headers: - if name == b"host": - host_count += 1 - if self.http_version == b"1.1" and host_count == 0: - raise LocalProtocolError("Missing mandatory Host: header") - if host_count > 1: - raise LocalProtocolError("Found multiple Host: headers") - - validate(method_re, self.method, "Illegal method characters") - validate(request_target_re, self.target, "Illegal target characters") - - # This is an unhashable type. - __hash__ = None # type: ignore - - -@dataclass(init=False, frozen=True) -class _ResponseBase(Event): - __slots__ = ("headers", "http_version", "reason", "status_code") - - headers: Headers - http_version: bytes - reason: bytes - status_code: int - - def __init__( - self, - *, - headers: Union[Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]]], - status_code: int, - http_version: Union[bytes, str] = b"1.1", - reason: Union[bytes, str] = b"", - _parsed: bool = False, - ) -> None: - super().__init__() - if isinstance(headers, Headers): - object.__setattr__(self, "headers", headers) - else: - object.__setattr__( - self, "headers", normalize_and_validate(headers, _parsed=_parsed) - ) - if not _parsed: - object.__setattr__(self, "reason", bytesify(reason)) - object.__setattr__(self, "http_version", bytesify(http_version)) - if not isinstance(status_code, int): - raise LocalProtocolError("status code must be integer") - # Because IntEnum objects are instances of int, but aren't - # duck-compatible (sigh), see gh-72. - object.__setattr__(self, "status_code", int(status_code)) - else: - object.__setattr__(self, "reason", reason) - object.__setattr__(self, "http_version", http_version) - object.__setattr__(self, "status_code", status_code) - - self.__post_init__() - - def __post_init__(self) -> None: - pass - - # This is an unhashable type. - __hash__ = None # type: ignore - - -@dataclass(init=False, frozen=True) -class InformationalResponse(_ResponseBase): - """An HTTP informational response. - - Fields: - - .. attribute:: status_code - - The status code of this response, as an integer. For an - :class:`InformationalResponse`, this is always in the range [100, - 200). - - .. attribute:: headers - - Request headers, represented as a list of (name, value) pairs. See - :ref:`the header normalization rules ` for - details. - - .. attribute:: http_version - - The HTTP protocol version, represented as a byte string like - ``b"1.1"``. See :ref:`the HTTP version normalization rules - ` for details. - - .. attribute:: reason - - The reason phrase of this response, as a byte string. For example: - ``b"OK"``, or ``b"Not Found"``. - - """ - - def __post_init__(self) -> None: - if not (100 <= self.status_code < 200): - raise LocalProtocolError( - "InformationalResponse status_code should be in range " - "[100, 200), not {}".format(self.status_code) - ) - - # This is an unhashable type. - __hash__ = None # type: ignore - - -@dataclass(init=False, frozen=True) -class Response(_ResponseBase): - """The beginning of an HTTP response. - - Fields: - - .. attribute:: status_code - - The status code of this response, as an integer. For an - :class:`Response`, this is always in the range [200, - 1000). - - .. attribute:: headers - - Request headers, represented as a list of (name, value) pairs. See - :ref:`the header normalization rules ` for details. - - .. attribute:: http_version - - The HTTP protocol version, represented as a byte string like - ``b"1.1"``. See :ref:`the HTTP version normalization rules - ` for details. - - .. attribute:: reason - - The reason phrase of this response, as a byte string. For example: - ``b"OK"``, or ``b"Not Found"``. - - """ - - def __post_init__(self) -> None: - if not (200 <= self.status_code < 1000): - raise LocalProtocolError( - "Response status_code should be in range [200, 1000), not {}".format( - self.status_code - ) - ) - - # This is an unhashable type. - __hash__ = None # type: ignore - - -@dataclass(init=False, frozen=True) -class Data(Event): - """Part of an HTTP message body. - - Fields: - - .. attribute:: data - - A :term:`bytes-like object` containing part of a message body. Or, if - using the ``combine=False`` argument to :meth:`Connection.send`, then - any object that your socket writing code knows what to do with, and for - which calling :func:`len` returns the number of bytes that will be - written -- see :ref:`sendfile` for details. - - .. attribute:: chunk_start - - A marker that indicates whether this data object is from the start of a - chunked transfer encoding chunk. This field is ignored when when a Data - event is provided to :meth:`Connection.send`: it is only valid on - events emitted from :meth:`Connection.next_event`. You probably - shouldn't use this attribute at all; see - :ref:`chunk-delimiters-are-bad` for details. - - .. attribute:: chunk_end - - A marker that indicates whether this data object is the last for a - given chunked transfer encoding chunk. This field is ignored when when - a Data event is provided to :meth:`Connection.send`: it is only valid - on events emitted from :meth:`Connection.next_event`. You probably - shouldn't use this attribute at all; see - :ref:`chunk-delimiters-are-bad` for details. - - """ - - __slots__ = ("data", "chunk_start", "chunk_end") - - data: bytes - chunk_start: bool - chunk_end: bool - - def __init__( - self, data: bytes, chunk_start: bool = False, chunk_end: bool = False - ) -> None: - object.__setattr__(self, "data", data) - object.__setattr__(self, "chunk_start", chunk_start) - object.__setattr__(self, "chunk_end", chunk_end) - - # This is an unhashable type. - __hash__ = None # type: ignore - - -# XX FIXME: "A recipient MUST ignore (or consider as an error) any fields that -# are forbidden to be sent in a trailer, since processing them as if they were -# present in the header section might bypass external security filters." -# https://svn.tools.ietf.org/svn/wg/httpbis/specs/rfc7230.html#chunked.trailer.part -# Unfortunately, the list of forbidden fields is long and vague :-/ -@dataclass(init=False, frozen=True) -class EndOfMessage(Event): - """The end of an HTTP message. - - Fields: - - .. attribute:: headers - - Default value: ``[]`` - - Any trailing headers attached to this message, represented as a list of - (name, value) pairs. See :ref:`the header normalization rules - ` for details. - - Must be empty unless ``Transfer-Encoding: chunked`` is in use. - - """ - - __slots__ = ("headers",) - - headers: Headers - - def __init__( - self, - *, - headers: Union[ - Headers, List[Tuple[bytes, bytes]], List[Tuple[str, str]], None - ] = None, - _parsed: bool = False, - ) -> None: - super().__init__() - if headers is None: - headers = Headers([]) - elif not isinstance(headers, Headers): - headers = normalize_and_validate(headers, _parsed=_parsed) - - object.__setattr__(self, "headers", headers) - - # This is an unhashable type. - __hash__ = None # type: ignore - - -@dataclass(frozen=True) -class ConnectionClosed(Event): - """This event indicates that the sender has closed their outgoing - connection. - - Note that this does not necessarily mean that they can't *receive* further - data, because TCP connections are composed to two one-way channels which - can be closed independently. See :ref:`closing` for details. - - No fields. - """ - - pass diff --git a/.venv/Lib/site-packages/h11/_headers.py b/.venv/Lib/site-packages/h11/_headers.py deleted file mode 100644 index b97d020..0000000 --- a/.venv/Lib/site-packages/h11/_headers.py +++ /dev/null @@ -1,278 +0,0 @@ -import re -from typing import AnyStr, cast, List, overload, Sequence, Tuple, TYPE_CHECKING, Union - -from ._abnf import field_name, field_value -from ._util import bytesify, LocalProtocolError, validate - -if TYPE_CHECKING: - from ._events import Request - -try: - from typing import Literal -except ImportError: - from typing_extensions import Literal # type: ignore - - -# Facts -# ----- -# -# Headers are: -# keys: case-insensitive ascii -# values: mixture of ascii and raw bytes -# -# "Historically, HTTP has allowed field content with text in the ISO-8859-1 -# charset [ISO-8859-1], supporting other charsets only through use of -# [RFC2047] encoding. In practice, most HTTP header field values use only a -# subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD -# limit their field values to US-ASCII octets. A recipient SHOULD treat other -# octets in field content (obs-text) as opaque data." -# And it deprecates all non-ascii values -# -# Leading/trailing whitespace in header names is forbidden -# -# Values get leading/trailing whitespace stripped -# -# Content-Disposition actually needs to contain unicode semantically; to -# accomplish this it has a terrifically weird way of encoding the filename -# itself as ascii (and even this still has lots of cross-browser -# incompatibilities) -# -# Order is important: -# "a proxy MUST NOT change the order of these field values when forwarding a -# message" -# (and there are several headers where the order indicates a preference) -# -# Multiple occurences of the same header: -# "A sender MUST NOT generate multiple header fields with the same field name -# in a message unless either the entire field value for that header field is -# defined as a comma-separated list [or the header is Set-Cookie which gets a -# special exception]" - RFC 7230. (cookies are in RFC 6265) -# -# So every header aside from Set-Cookie can be merged by b", ".join if it -# occurs repeatedly. But, of course, they can't necessarily be split by -# .split(b","), because quoting. -# -# Given all this mess (case insensitive, duplicates allowed, order is -# important, ...), there doesn't appear to be any standard way to handle -# headers in Python -- they're almost like dicts, but... actually just -# aren't. For now we punt and just use a super simple representation: headers -# are a list of pairs -# -# [(name1, value1), (name2, value2), ...] -# -# where all entries are bytestrings, names are lowercase and have no -# leading/trailing whitespace, and values are bytestrings with no -# leading/trailing whitespace. Searching and updating are done via naive O(n) -# methods. -# -# Maybe a dict-of-lists would be better? - -_content_length_re = re.compile(rb"[0-9]+") -_field_name_re = re.compile(field_name.encode("ascii")) -_field_value_re = re.compile(field_value.encode("ascii")) - - -class Headers(Sequence[Tuple[bytes, bytes]]): - """ - A list-like interface that allows iterating over headers as byte-pairs - of (lowercased-name, value). - - Internally we actually store the representation as three-tuples, - including both the raw original casing, in order to preserve casing - over-the-wire, and the lowercased name, for case-insensitive comparisions. - - r = Request( - method="GET", - target="/", - headers=[("Host", "example.org"), ("Connection", "keep-alive")], - http_version="1.1", - ) - assert r.headers == [ - (b"host", b"example.org"), - (b"connection", b"keep-alive") - ] - assert r.headers.raw_items() == [ - (b"Host", b"example.org"), - (b"Connection", b"keep-alive") - ] - """ - - __slots__ = "_full_items" - - def __init__(self, full_items: List[Tuple[bytes, bytes, bytes]]) -> None: - self._full_items = full_items - - def __bool__(self) -> bool: - return bool(self._full_items) - - def __eq__(self, other: object) -> bool: - return list(self) == list(other) # type: ignore - - def __len__(self) -> int: - return len(self._full_items) - - def __repr__(self) -> str: - return "" % repr(list(self)) - - def __getitem__(self, idx: int) -> Tuple[bytes, bytes]: # type: ignore[override] - _, name, value = self._full_items[idx] - return (name, value) - - def raw_items(self) -> List[Tuple[bytes, bytes]]: - return [(raw_name, value) for raw_name, _, value in self._full_items] - - -HeaderTypes = Union[ - List[Tuple[bytes, bytes]], - List[Tuple[bytes, str]], - List[Tuple[str, bytes]], - List[Tuple[str, str]], -] - - -@overload -def normalize_and_validate(headers: Headers, _parsed: Literal[True]) -> Headers: - ... - - -@overload -def normalize_and_validate(headers: HeaderTypes, _parsed: Literal[False]) -> Headers: - ... - - -@overload -def normalize_and_validate( - headers: Union[Headers, HeaderTypes], _parsed: bool = False -) -> Headers: - ... - - -def normalize_and_validate( - headers: Union[Headers, HeaderTypes], _parsed: bool = False -) -> Headers: - new_headers = [] - seen_content_length = None - saw_transfer_encoding = False - for name, value in headers: - # For headers coming out of the parser, we can safely skip some steps, - # because it always returns bytes and has already run these regexes - # over the data: - if not _parsed: - name = bytesify(name) - value = bytesify(value) - validate(_field_name_re, name, "Illegal header name {!r}", name) - validate(_field_value_re, value, "Illegal header value {!r}", value) - assert isinstance(name, bytes) - assert isinstance(value, bytes) - - raw_name = name - name = name.lower() - if name == b"content-length": - lengths = {length.strip() for length in value.split(b",")} - if len(lengths) != 1: - raise LocalProtocolError("conflicting Content-Length headers") - value = lengths.pop() - validate(_content_length_re, value, "bad Content-Length") - if seen_content_length is None: - seen_content_length = value - new_headers.append((raw_name, name, value)) - elif seen_content_length != value: - raise LocalProtocolError("conflicting Content-Length headers") - elif name == b"transfer-encoding": - # "A server that receives a request message with a transfer coding - # it does not understand SHOULD respond with 501 (Not - # Implemented)." - # https://tools.ietf.org/html/rfc7230#section-3.3.1 - if saw_transfer_encoding: - raise LocalProtocolError( - "multiple Transfer-Encoding headers", error_status_hint=501 - ) - # "All transfer-coding names are case-insensitive" - # -- https://tools.ietf.org/html/rfc7230#section-4 - value = value.lower() - if value != b"chunked": - raise LocalProtocolError( - "Only Transfer-Encoding: chunked is supported", - error_status_hint=501, - ) - saw_transfer_encoding = True - new_headers.append((raw_name, name, value)) - else: - new_headers.append((raw_name, name, value)) - return Headers(new_headers) - - -def get_comma_header(headers: Headers, name: bytes) -> List[bytes]: - # Should only be used for headers whose value is a list of - # comma-separated, case-insensitive values. - # - # The header name `name` is expected to be lower-case bytes. - # - # Connection: meets these criteria (including cast insensitivity). - # - # Content-Length: technically is just a single value (1*DIGIT), but the - # standard makes reference to implementations that do multiple values, and - # using this doesn't hurt. Ditto, case insensitivity doesn't things either - # way. - # - # Transfer-Encoding: is more complex (allows for quoted strings), so - # splitting on , is actually wrong. For example, this is legal: - # - # Transfer-Encoding: foo; options="1,2", chunked - # - # and should be parsed as - # - # foo; options="1,2" - # chunked - # - # but this naive function will parse it as - # - # foo; options="1 - # 2" - # chunked - # - # However, this is okay because the only thing we are going to do with - # any Transfer-Encoding is reject ones that aren't just "chunked", so - # both of these will be treated the same anyway. - # - # Expect: the only legal value is the literal string - # "100-continue". Splitting on commas is harmless. Case insensitive. - # - out: List[bytes] = [] - for _, found_name, found_raw_value in headers._full_items: - if found_name == name: - found_raw_value = found_raw_value.lower() - for found_split_value in found_raw_value.split(b","): - found_split_value = found_split_value.strip() - if found_split_value: - out.append(found_split_value) - return out - - -def set_comma_header(headers: Headers, name: bytes, new_values: List[bytes]) -> Headers: - # The header name `name` is expected to be lower-case bytes. - # - # Note that when we store the header we use title casing for the header - # names, in order to match the conventional HTTP header style. - # - # Simply calling `.title()` is a blunt approach, but it's correct - # here given the cases where we're using `set_comma_header`... - # - # Connection, Content-Length, Transfer-Encoding. - new_headers: List[Tuple[bytes, bytes]] = [] - for found_raw_name, found_name, found_raw_value in headers._full_items: - if found_name != name: - new_headers.append((found_raw_name, found_raw_value)) - for new_value in new_values: - new_headers.append((name.title(), new_value)) - return normalize_and_validate(new_headers) - - -def has_expect_100_continue(request: "Request") -> bool: - # https://tools.ietf.org/html/rfc7231#section-5.1.1 - # "A server that receives a 100-continue expectation in an HTTP/1.0 request - # MUST ignore that expectation." - if request.http_version < b"1.1": - return False - expect = get_comma_header(request.headers, b"expect") - return b"100-continue" in expect diff --git a/.venv/Lib/site-packages/h11/_readers.py b/.venv/Lib/site-packages/h11/_readers.py deleted file mode 100644 index 08a9574..0000000 --- a/.venv/Lib/site-packages/h11/_readers.py +++ /dev/null @@ -1,247 +0,0 @@ -# Code to read HTTP data -# -# Strategy: each reader is a callable which takes a ReceiveBuffer object, and -# either: -# 1) consumes some of it and returns an Event -# 2) raises a LocalProtocolError (for consistency -- e.g. we call validate() -# and it might raise a LocalProtocolError, so simpler just to always use -# this) -# 3) returns None, meaning "I need more data" -# -# If they have a .read_eof attribute, then this will be called if an EOF is -# received -- but this is optional. Either way, the actual ConnectionClosed -# event will be generated afterwards. -# -# READERS is a dict describing how to pick a reader. It maps states to either: -# - a reader -# - or, for body readers, a dict of per-framing reader factories - -import re -from typing import Any, Callable, Dict, Iterable, NoReturn, Optional, Tuple, Type, Union - -from ._abnf import chunk_header, header_field, request_line, status_line -from ._events import Data, EndOfMessage, InformationalResponse, Request, Response -from ._receivebuffer import ReceiveBuffer -from ._state import ( - CLIENT, - CLOSED, - DONE, - IDLE, - MUST_CLOSE, - SEND_BODY, - SEND_RESPONSE, - SERVER, -) -from ._util import LocalProtocolError, RemoteProtocolError, Sentinel, validate - -__all__ = ["READERS"] - -header_field_re = re.compile(header_field.encode("ascii")) -obs_fold_re = re.compile(rb"[ \t]+") - - -def _obsolete_line_fold(lines: Iterable[bytes]) -> Iterable[bytes]: - it = iter(lines) - last: Optional[bytes] = None - for line in it: - match = obs_fold_re.match(line) - if match: - if last is None: - raise LocalProtocolError("continuation line at start of headers") - if not isinstance(last, bytearray): - # Cast to a mutable type, avoiding copy on append to ensure O(n) time - last = bytearray(last) - last += b" " - last += line[match.end() :] - else: - if last is not None: - yield last - last = line - if last is not None: - yield last - - -def _decode_header_lines( - lines: Iterable[bytes], -) -> Iterable[Tuple[bytes, bytes]]: - for line in _obsolete_line_fold(lines): - matches = validate(header_field_re, line, "illegal header line: {!r}", line) - yield (matches["field_name"], matches["field_value"]) - - -request_line_re = re.compile(request_line.encode("ascii")) - - -def maybe_read_from_IDLE_client(buf: ReceiveBuffer) -> Optional[Request]: - lines = buf.maybe_extract_lines() - if lines is None: - if buf.is_next_line_obviously_invalid_request_line(): - raise LocalProtocolError("illegal request line") - return None - if not lines: - raise LocalProtocolError("no request line received") - matches = validate( - request_line_re, lines[0], "illegal request line: {!r}", lines[0] - ) - return Request( - headers=list(_decode_header_lines(lines[1:])), _parsed=True, **matches - ) - - -status_line_re = re.compile(status_line.encode("ascii")) - - -def maybe_read_from_SEND_RESPONSE_server( - buf: ReceiveBuffer, -) -> Union[InformationalResponse, Response, None]: - lines = buf.maybe_extract_lines() - if lines is None: - if buf.is_next_line_obviously_invalid_request_line(): - raise LocalProtocolError("illegal request line") - return None - if not lines: - raise LocalProtocolError("no response line received") - matches = validate(status_line_re, lines[0], "illegal status line: {!r}", lines[0]) - http_version = ( - b"1.1" if matches["http_version"] is None else matches["http_version"] - ) - reason = b"" if matches["reason"] is None else matches["reason"] - status_code = int(matches["status_code"]) - class_: Union[Type[InformationalResponse], Type[Response]] = ( - InformationalResponse if status_code < 200 else Response - ) - return class_( - headers=list(_decode_header_lines(lines[1:])), - _parsed=True, - status_code=status_code, - reason=reason, - http_version=http_version, - ) - - -class ContentLengthReader: - def __init__(self, length: int) -> None: - self._length = length - self._remaining = length - - def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: - if self._remaining == 0: - return EndOfMessage() - data = buf.maybe_extract_at_most(self._remaining) - if data is None: - return None - self._remaining -= len(data) - return Data(data=data) - - def read_eof(self) -> NoReturn: - raise RemoteProtocolError( - "peer closed connection without sending complete message body " - "(received {} bytes, expected {})".format( - self._length - self._remaining, self._length - ) - ) - - -chunk_header_re = re.compile(chunk_header.encode("ascii")) - - -class ChunkedReader: - def __init__(self) -> None: - self._bytes_in_chunk = 0 - # After reading a chunk, we have to throw away the trailing \r\n; if - # this is >0 then we discard that many bytes before resuming regular - # de-chunkification. - self._bytes_to_discard = 0 - self._reading_trailer = False - - def __call__(self, buf: ReceiveBuffer) -> Union[Data, EndOfMessage, None]: - if self._reading_trailer: - lines = buf.maybe_extract_lines() - if lines is None: - return None - return EndOfMessage(headers=list(_decode_header_lines(lines))) - if self._bytes_to_discard > 0: - data = buf.maybe_extract_at_most(self._bytes_to_discard) - if data is None: - return None - self._bytes_to_discard -= len(data) - if self._bytes_to_discard > 0: - return None - # else, fall through and read some more - assert self._bytes_to_discard == 0 - if self._bytes_in_chunk == 0: - # We need to refill our chunk count - chunk_header = buf.maybe_extract_next_line() - if chunk_header is None: - return None - matches = validate( - chunk_header_re, - chunk_header, - "illegal chunk header: {!r}", - chunk_header, - ) - # XX FIXME: we discard chunk extensions. Does anyone care? - self._bytes_in_chunk = int(matches["chunk_size"], base=16) - if self._bytes_in_chunk == 0: - self._reading_trailer = True - return self(buf) - chunk_start = True - else: - chunk_start = False - assert self._bytes_in_chunk > 0 - data = buf.maybe_extract_at_most(self._bytes_in_chunk) - if data is None: - return None - self._bytes_in_chunk -= len(data) - if self._bytes_in_chunk == 0: - self._bytes_to_discard = 2 - chunk_end = True - else: - chunk_end = False - return Data(data=data, chunk_start=chunk_start, chunk_end=chunk_end) - - def read_eof(self) -> NoReturn: - raise RemoteProtocolError( - "peer closed connection without sending complete message body " - "(incomplete chunked read)" - ) - - -class Http10Reader: - def __call__(self, buf: ReceiveBuffer) -> Optional[Data]: - data = buf.maybe_extract_at_most(999999999) - if data is None: - return None - return Data(data=data) - - def read_eof(self) -> EndOfMessage: - return EndOfMessage() - - -def expect_nothing(buf: ReceiveBuffer) -> None: - if buf: - raise LocalProtocolError("Got data when expecting EOF") - return None - - -ReadersType = Dict[ - Union[Type[Sentinel], Tuple[Type[Sentinel], Type[Sentinel]]], - Union[Callable[..., Any], Dict[str, Callable[..., Any]]], -] - -READERS: ReadersType = { - (CLIENT, IDLE): maybe_read_from_IDLE_client, - (SERVER, IDLE): maybe_read_from_SEND_RESPONSE_server, - (SERVER, SEND_RESPONSE): maybe_read_from_SEND_RESPONSE_server, - (CLIENT, DONE): expect_nothing, - (CLIENT, MUST_CLOSE): expect_nothing, - (CLIENT, CLOSED): expect_nothing, - (SERVER, DONE): expect_nothing, - (SERVER, MUST_CLOSE): expect_nothing, - (SERVER, CLOSED): expect_nothing, - SEND_BODY: { - "chunked": ChunkedReader, - "content-length": ContentLengthReader, - "http/1.0": Http10Reader, - }, -} diff --git a/.venv/Lib/site-packages/h11/_receivebuffer.py b/.venv/Lib/site-packages/h11/_receivebuffer.py deleted file mode 100644 index e5c4e08..0000000 --- a/.venv/Lib/site-packages/h11/_receivebuffer.py +++ /dev/null @@ -1,153 +0,0 @@ -import re -import sys -from typing import List, Optional, Union - -__all__ = ["ReceiveBuffer"] - - -# Operations we want to support: -# - find next \r\n or \r\n\r\n (\n or \n\n are also acceptable), -# or wait until there is one -# - read at-most-N bytes -# Goals: -# - on average, do this fast -# - worst case, do this in O(n) where n is the number of bytes processed -# Plan: -# - store bytearray, offset, how far we've searched for a separator token -# - use the how-far-we've-searched data to avoid rescanning -# - while doing a stream of uninterrupted processing, advance offset instead -# of constantly copying -# WARNING: -# - I haven't benchmarked or profiled any of this yet. -# -# Note that starting in Python 3.4, deleting the initial n bytes from a -# bytearray is amortized O(n), thanks to some excellent work by Antoine -# Martin: -# -# https://bugs.python.org/issue19087 -# -# This means that if we only supported 3.4+, we could get rid of the code here -# involving self._start and self.compress, because it's doing exactly the same -# thing that bytearray now does internally. -# -# BUT unfortunately, we still support 2.7, and reading short segments out of a -# long buffer MUST be O(bytes read) to avoid DoS issues, so we can't actually -# delete this code. Yet: -# -# https://pythonclock.org/ -# -# (Two things to double-check first though: make sure PyPy also has the -# optimization, and benchmark to make sure it's a win, since we do have a -# slightly clever thing where we delay calling compress() until we've -# processed a whole event, which could in theory be slightly more efficient -# than the internal bytearray support.) -blank_line_regex = re.compile(b"\n\r?\n", re.MULTILINE) - - -class ReceiveBuffer: - def __init__(self) -> None: - self._data = bytearray() - self._next_line_search = 0 - self._multiple_lines_search = 0 - - def __iadd__(self, byteslike: Union[bytes, bytearray]) -> "ReceiveBuffer": - self._data += byteslike - return self - - def __bool__(self) -> bool: - return bool(len(self)) - - def __len__(self) -> int: - return len(self._data) - - # for @property unprocessed_data - def __bytes__(self) -> bytes: - return bytes(self._data) - - def _extract(self, count: int) -> bytearray: - # extracting an initial slice of the data buffer and return it - out = self._data[:count] - del self._data[:count] - - self._next_line_search = 0 - self._multiple_lines_search = 0 - - return out - - def maybe_extract_at_most(self, count: int) -> Optional[bytearray]: - """ - Extract a fixed number of bytes from the buffer. - """ - out = self._data[:count] - if not out: - return None - - return self._extract(count) - - def maybe_extract_next_line(self) -> Optional[bytearray]: - """ - Extract the first line, if it is completed in the buffer. - """ - # Only search in buffer space that we've not already looked at. - search_start_index = max(0, self._next_line_search - 1) - partial_idx = self._data.find(b"\r\n", search_start_index) - - if partial_idx == -1: - self._next_line_search = len(self._data) - return None - - # + 2 is to compensate len(b"\r\n") - idx = partial_idx + 2 - - return self._extract(idx) - - def maybe_extract_lines(self) -> Optional[List[bytearray]]: - """ - Extract everything up to the first blank line, and return a list of lines. - """ - # Handle the case where we have an immediate empty line. - if self._data[:1] == b"\n": - self._extract(1) - return [] - - if self._data[:2] == b"\r\n": - self._extract(2) - return [] - - # Only search in buffer space that we've not already looked at. - match = blank_line_regex.search(self._data, self._multiple_lines_search) - if match is None: - self._multiple_lines_search = max(0, len(self._data) - 2) - return None - - # Truncate the buffer and return it. - idx = match.span(0)[-1] - out = self._extract(idx) - lines = out.split(b"\n") - - for line in lines: - if line.endswith(b"\r"): - del line[-1] - - assert lines[-2] == lines[-1] == b"" - - del lines[-2:] - - return lines - - # In theory we should wait until `\r\n` before starting to validate - # incoming data. However it's interesting to detect (very) invalid data - # early given they might not even contain `\r\n` at all (hence only - # timeout will get rid of them). - # This is not a 100% effective detection but more of a cheap sanity check - # allowing for early abort in some useful cases. - # This is especially interesting when peer is messing up with HTTPS and - # sent us a TLS stream where we were expecting plain HTTP given all - # versions of TLS so far start handshake with a 0x16 message type code. - def is_next_line_obviously_invalid_request_line(self) -> bool: - try: - # HTTP header line must not contain non-printable characters - # and should not start with a space - return self._data[0] < 0x21 - except IndexError: - return False diff --git a/.venv/Lib/site-packages/h11/_state.py b/.venv/Lib/site-packages/h11/_state.py deleted file mode 100644 index 3593430..0000000 --- a/.venv/Lib/site-packages/h11/_state.py +++ /dev/null @@ -1,367 +0,0 @@ -################################################################ -# The core state machine -################################################################ -# -# Rule 1: everything that affects the state machine and state transitions must -# live here in this file. As much as possible goes into the table-based -# representation, but for the bits that don't quite fit, the actual code and -# state must nonetheless live here. -# -# Rule 2: this file does not know about what role we're playing; it only knows -# about HTTP request/response cycles in the abstract. This ensures that we -# don't cheat and apply different rules to local and remote parties. -# -# -# Theory of operation -# =================== -# -# Possibly the simplest way to think about this is that we actually have 5 -# different state machines here. Yes, 5. These are: -# -# 1) The client state, with its complicated automaton (see the docs) -# 2) The server state, with its complicated automaton (see the docs) -# 3) The keep-alive state, with possible states {True, False} -# 4) The SWITCH_CONNECT state, with possible states {False, True} -# 5) The SWITCH_UPGRADE state, with possible states {False, True} -# -# For (3)-(5), the first state listed is the initial state. -# -# (1)-(3) are stored explicitly in member variables. The last -# two are stored implicitly in the pending_switch_proposals set as: -# (state of 4) == (_SWITCH_CONNECT in pending_switch_proposals) -# (state of 5) == (_SWITCH_UPGRADE in pending_switch_proposals) -# -# And each of these machines has two different kinds of transitions: -# -# a) Event-triggered -# b) State-triggered -# -# Event triggered is the obvious thing that you'd think it is: some event -# happens, and if it's the right event at the right time then a transition -# happens. But there are somewhat complicated rules for which machines can -# "see" which events. (As a rule of thumb, if a machine "sees" an event, this -# means two things: the event can affect the machine, and if the machine is -# not in a state where it expects that event then it's an error.) These rules -# are: -# -# 1) The client machine sees all h11.events objects emitted by the client. -# -# 2) The server machine sees all h11.events objects emitted by the server. -# -# It also sees the client's Request event. -# -# And sometimes, server events are annotated with a _SWITCH_* event. For -# example, we can have a (Response, _SWITCH_CONNECT) event, which is -# different from a regular Response event. -# -# 3) The keep-alive machine sees the process_keep_alive_disabled() event -# (which is derived from Request/Response events), and this event -# transitions it from True -> False, or from False -> False. There's no way -# to transition back. -# -# 4&5) The _SWITCH_* machines transition from False->True when we get a -# Request that proposes the relevant type of switch (via -# process_client_switch_proposals), and they go from True->False when we -# get a Response that has no _SWITCH_* annotation. -# -# So that's event-triggered transitions. -# -# State-triggered transitions are less standard. What they do here is couple -# the machines together. The way this works is, when certain *joint* -# configurations of states are achieved, then we automatically transition to a -# new *joint* state. So, for example, if we're ever in a joint state with -# -# client: DONE -# keep-alive: False -# -# then the client state immediately transitions to: -# -# client: MUST_CLOSE -# -# This is fundamentally different from an event-based transition, because it -# doesn't matter how we arrived at the {client: DONE, keep-alive: False} state -# -- maybe the client transitioned SEND_BODY -> DONE, or keep-alive -# transitioned True -> False. Either way, once this precondition is satisfied, -# this transition is immediately triggered. -# -# What if two conflicting state-based transitions get enabled at the same -# time? In practice there's only one case where this arises (client DONE -> -# MIGHT_SWITCH_PROTOCOL versus DONE -> MUST_CLOSE), and we resolve it by -# explicitly prioritizing the DONE -> MIGHT_SWITCH_PROTOCOL transition. -# -# Implementation -# -------------- -# -# The event-triggered transitions for the server and client machines are all -# stored explicitly in a table. Ditto for the state-triggered transitions that -# involve just the server and client state. -# -# The transitions for the other machines, and the state-triggered transitions -# that involve the other machines, are written out as explicit Python code. -# -# It'd be nice if there were some cleaner way to do all this. This isn't -# *too* terrible, but I feel like it could probably be better. -# -# WARNING -# ------- -# -# The script that generates the state machine diagrams for the docs knows how -# to read out the EVENT_TRIGGERED_TRANSITIONS and STATE_TRIGGERED_TRANSITIONS -# tables. But it can't automatically read the transitions that are written -# directly in Python code. So if you touch those, you need to also update the -# script to keep it in sync! -from typing import cast, Dict, Optional, Set, Tuple, Type, Union - -from ._events import * -from ._util import LocalProtocolError, Sentinel - -# Everything in __all__ gets re-exported as part of the h11 public API. -__all__ = [ - "CLIENT", - "SERVER", - "IDLE", - "SEND_RESPONSE", - "SEND_BODY", - "DONE", - "MUST_CLOSE", - "CLOSED", - "MIGHT_SWITCH_PROTOCOL", - "SWITCHED_PROTOCOL", - "ERROR", -] - - -class CLIENT(Sentinel, metaclass=Sentinel): - pass - - -class SERVER(Sentinel, metaclass=Sentinel): - pass - - -# States -class IDLE(Sentinel, metaclass=Sentinel): - pass - - -class SEND_RESPONSE(Sentinel, metaclass=Sentinel): - pass - - -class SEND_BODY(Sentinel, metaclass=Sentinel): - pass - - -class DONE(Sentinel, metaclass=Sentinel): - pass - - -class MUST_CLOSE(Sentinel, metaclass=Sentinel): - pass - - -class CLOSED(Sentinel, metaclass=Sentinel): - pass - - -class ERROR(Sentinel, metaclass=Sentinel): - pass - - -# Switch types -class MIGHT_SWITCH_PROTOCOL(Sentinel, metaclass=Sentinel): - pass - - -class SWITCHED_PROTOCOL(Sentinel, metaclass=Sentinel): - pass - - -class _SWITCH_UPGRADE(Sentinel, metaclass=Sentinel): - pass - - -class _SWITCH_CONNECT(Sentinel, metaclass=Sentinel): - pass - - -EventTransitionType = Dict[ - Type[Sentinel], - Dict[ - Type[Sentinel], - Dict[Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], Type[Sentinel]], - ], -] - -EVENT_TRIGGERED_TRANSITIONS: EventTransitionType = { - CLIENT: { - IDLE: {Request: SEND_BODY, ConnectionClosed: CLOSED}, - SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, - DONE: {ConnectionClosed: CLOSED}, - MUST_CLOSE: {ConnectionClosed: CLOSED}, - CLOSED: {ConnectionClosed: CLOSED}, - MIGHT_SWITCH_PROTOCOL: {}, - SWITCHED_PROTOCOL: {}, - ERROR: {}, - }, - SERVER: { - IDLE: { - ConnectionClosed: CLOSED, - Response: SEND_BODY, - # Special case: server sees client Request events, in this form - (Request, CLIENT): SEND_RESPONSE, - }, - SEND_RESPONSE: { - InformationalResponse: SEND_RESPONSE, - Response: SEND_BODY, - (InformationalResponse, _SWITCH_UPGRADE): SWITCHED_PROTOCOL, - (Response, _SWITCH_CONNECT): SWITCHED_PROTOCOL, - }, - SEND_BODY: {Data: SEND_BODY, EndOfMessage: DONE}, - DONE: {ConnectionClosed: CLOSED}, - MUST_CLOSE: {ConnectionClosed: CLOSED}, - CLOSED: {ConnectionClosed: CLOSED}, - SWITCHED_PROTOCOL: {}, - ERROR: {}, - }, -} - -StateTransitionType = Dict[ - Tuple[Type[Sentinel], Type[Sentinel]], Dict[Type[Sentinel], Type[Sentinel]] -] - -# NB: there are also some special-case state-triggered transitions hard-coded -# into _fire_state_triggered_transitions below. -STATE_TRIGGERED_TRANSITIONS: StateTransitionType = { - # (Client state, Server state) -> new states - # Protocol negotiation - (MIGHT_SWITCH_PROTOCOL, SWITCHED_PROTOCOL): {CLIENT: SWITCHED_PROTOCOL}, - # Socket shutdown - (CLOSED, DONE): {SERVER: MUST_CLOSE}, - (CLOSED, IDLE): {SERVER: MUST_CLOSE}, - (ERROR, DONE): {SERVER: MUST_CLOSE}, - (DONE, CLOSED): {CLIENT: MUST_CLOSE}, - (IDLE, CLOSED): {CLIENT: MUST_CLOSE}, - (DONE, ERROR): {CLIENT: MUST_CLOSE}, -} - - -class ConnectionState: - def __init__(self) -> None: - # Extra bits of state that don't quite fit into the state model. - - # If this is False then it enables the automatic DONE -> MUST_CLOSE - # transition. Don't set this directly; call .keep_alive_disabled() - self.keep_alive = True - - # This is a subset of {UPGRADE, CONNECT}, containing the proposals - # made by the client for switching protocols. - self.pending_switch_proposals: Set[Type[Sentinel]] = set() - - self.states: Dict[Type[Sentinel], Type[Sentinel]] = {CLIENT: IDLE, SERVER: IDLE} - - def process_error(self, role: Type[Sentinel]) -> None: - self.states[role] = ERROR - self._fire_state_triggered_transitions() - - def process_keep_alive_disabled(self) -> None: - self.keep_alive = False - self._fire_state_triggered_transitions() - - def process_client_switch_proposal(self, switch_event: Type[Sentinel]) -> None: - self.pending_switch_proposals.add(switch_event) - self._fire_state_triggered_transitions() - - def process_event( - self, - role: Type[Sentinel], - event_type: Type[Event], - server_switch_event: Optional[Type[Sentinel]] = None, - ) -> None: - _event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]] = event_type - if server_switch_event is not None: - assert role is SERVER - if server_switch_event not in self.pending_switch_proposals: - raise LocalProtocolError( - "Received server {} event without a pending proposal".format( - server_switch_event - ) - ) - _event_type = (event_type, server_switch_event) - if server_switch_event is None and _event_type is Response: - self.pending_switch_proposals = set() - self._fire_event_triggered_transitions(role, _event_type) - # Special case: the server state does get to see Request - # events. - if _event_type is Request: - assert role is CLIENT - self._fire_event_triggered_transitions(SERVER, (Request, CLIENT)) - self._fire_state_triggered_transitions() - - def _fire_event_triggered_transitions( - self, - role: Type[Sentinel], - event_type: Union[Type[Event], Tuple[Type[Event], Type[Sentinel]]], - ) -> None: - state = self.states[role] - try: - new_state = EVENT_TRIGGERED_TRANSITIONS[role][state][event_type] - except KeyError: - event_type = cast(Type[Event], event_type) - raise LocalProtocolError( - "can't handle event type {} when role={} and state={}".format( - event_type.__name__, role, self.states[role] - ) - ) from None - self.states[role] = new_state - - def _fire_state_triggered_transitions(self) -> None: - # We apply these rules repeatedly until converging on a fixed point - while True: - start_states = dict(self.states) - - # It could happen that both these special-case transitions are - # enabled at the same time: - # - # DONE -> MIGHT_SWITCH_PROTOCOL - # DONE -> MUST_CLOSE - # - # For example, this will always be true of a HTTP/1.0 client - # requesting CONNECT. If this happens, the protocol switch takes - # priority. From there the client will either go to - # SWITCHED_PROTOCOL, in which case it's none of our business when - # they close the connection, or else the server will deny the - # request, in which case the client will go back to DONE and then - # from there to MUST_CLOSE. - if self.pending_switch_proposals: - if self.states[CLIENT] is DONE: - self.states[CLIENT] = MIGHT_SWITCH_PROTOCOL - - if not self.pending_switch_proposals: - if self.states[CLIENT] is MIGHT_SWITCH_PROTOCOL: - self.states[CLIENT] = DONE - - if not self.keep_alive: - for role in (CLIENT, SERVER): - if self.states[role] is DONE: - self.states[role] = MUST_CLOSE - - # Tabular state-triggered transitions - joint_state = (self.states[CLIENT], self.states[SERVER]) - changes = STATE_TRIGGERED_TRANSITIONS.get(joint_state, {}) - self.states.update(changes) - - if self.states == start_states: - # Fixed point reached - return - - def start_next_cycle(self) -> None: - if self.states != {CLIENT: DONE, SERVER: DONE}: - raise LocalProtocolError( - "not in a reusable state. self.states={}".format(self.states) - ) - # Can't reach DONE/DONE with any of these active, but still, let's be - # sure. - assert self.keep_alive - assert not self.pending_switch_proposals - self.states = {CLIENT: IDLE, SERVER: IDLE} diff --git a/.venv/Lib/site-packages/h11/_util.py b/.venv/Lib/site-packages/h11/_util.py deleted file mode 100644 index 6718445..0000000 --- a/.venv/Lib/site-packages/h11/_util.py +++ /dev/null @@ -1,135 +0,0 @@ -from typing import Any, Dict, NoReturn, Pattern, Tuple, Type, TypeVar, Union - -__all__ = [ - "ProtocolError", - "LocalProtocolError", - "RemoteProtocolError", - "validate", - "bytesify", -] - - -class ProtocolError(Exception): - """Exception indicating a violation of the HTTP/1.1 protocol. - - This as an abstract base class, with two concrete base classes: - :exc:`LocalProtocolError`, which indicates that you tried to do something - that HTTP/1.1 says is illegal, and :exc:`RemoteProtocolError`, which - indicates that the remote peer tried to do something that HTTP/1.1 says is - illegal. See :ref:`error-handling` for details. - - In addition to the normal :exc:`Exception` features, it has one attribute: - - .. attribute:: error_status_hint - - This gives a suggestion as to what status code a server might use if - this error occurred as part of a request. - - For a :exc:`RemoteProtocolError`, this is useful as a suggestion for - how you might want to respond to a misbehaving peer, if you're - implementing a server. - - For a :exc:`LocalProtocolError`, this can be taken as a suggestion for - how your peer might have responded to *you* if h11 had allowed you to - continue. - - The default is 400 Bad Request, a generic catch-all for protocol - violations. - - """ - - def __init__(self, msg: str, error_status_hint: int = 400) -> None: - if type(self) is ProtocolError: - raise TypeError("tried to directly instantiate ProtocolError") - Exception.__init__(self, msg) - self.error_status_hint = error_status_hint - - -# Strategy: there are a number of public APIs where a LocalProtocolError can -# be raised (send(), all the different event constructors, ...), and only one -# public API where RemoteProtocolError can be raised -# (receive_data()). Therefore we always raise LocalProtocolError internally, -# and then receive_data will translate this into a RemoteProtocolError. -# -# Internally: -# LocalProtocolError is the generic "ProtocolError". -# Externally: -# LocalProtocolError is for local errors and RemoteProtocolError is for -# remote errors. -class LocalProtocolError(ProtocolError): - def _reraise_as_remote_protocol_error(self) -> NoReturn: - # After catching a LocalProtocolError, use this method to re-raise it - # as a RemoteProtocolError. This method must be called from inside an - # except: block. - # - # An easy way to get an equivalent RemoteProtocolError is just to - # modify 'self' in place. - self.__class__ = RemoteProtocolError # type: ignore - # But the re-raising is somewhat non-trivial -- you might think that - # now that we've modified the in-flight exception object, that just - # doing 'raise' to re-raise it would be enough. But it turns out that - # this doesn't work, because Python tracks the exception type - # (exc_info[0]) separately from the exception object (exc_info[1]), - # and we only modified the latter. So we really do need to re-raise - # the new type explicitly. - # On py3, the traceback is part of the exception object, so our - # in-place modification preserved it and we can just re-raise: - raise self - - -class RemoteProtocolError(ProtocolError): - pass - - -def validate( - regex: Pattern[bytes], data: bytes, msg: str = "malformed data", *format_args: Any -) -> Dict[str, bytes]: - match = regex.fullmatch(data) - if not match: - if format_args: - msg = msg.format(*format_args) - raise LocalProtocolError(msg) - return match.groupdict() - - -# Sentinel values -# -# - Inherit identity-based comparison and hashing from object -# - Have a nice repr -# - Have a *bonus property*: type(sentinel) is sentinel -# -# The bonus property is useful if you want to take the return value from -# next_event() and do some sort of dispatch based on type(event). - -_T_Sentinel = TypeVar("_T_Sentinel", bound="Sentinel") - - -class Sentinel(type): - def __new__( - cls: Type[_T_Sentinel], - name: str, - bases: Tuple[type, ...], - namespace: Dict[str, Any], - **kwds: Any - ) -> _T_Sentinel: - assert bases == (Sentinel,) - v = super().__new__(cls, name, bases, namespace, **kwds) - v.__class__ = v # type: ignore - return v - - def __repr__(self) -> str: - return self.__name__ - - -# Used for methods, request targets, HTTP versions, header names, and header -# values. Accepts ascii-strings, or bytes/bytearray/memoryview/..., and always -# returns bytes. -def bytesify(s: Union[bytes, bytearray, memoryview, int, str]) -> bytes: - # Fast-path: - if type(s) is bytes: - return s - if isinstance(s, str): - s = s.encode("ascii") - if isinstance(s, int): - raise TypeError("expected bytes-like object, not int") - return bytes(s) diff --git a/.venv/Lib/site-packages/h11/_version.py b/.venv/Lib/site-packages/h11/_version.py deleted file mode 100644 index 4c89113..0000000 --- a/.venv/Lib/site-packages/h11/_version.py +++ /dev/null @@ -1,16 +0,0 @@ -# This file must be kept very simple, because it is consumed from several -# places -- it is imported by h11/__init__.py, execfile'd by setup.py, etc. - -# We use a simple scheme: -# 1.0.0 -> 1.0.0+dev -> 1.1.0 -> 1.1.0+dev -# where the +dev versions are never released into the wild, they're just what -# we stick into the VCS in between releases. -# -# This is compatible with PEP 440: -# http://legacy.python.org/dev/peps/pep-0440/ -# via the use of the "local suffix" "+dev", which is disallowed on index -# servers and causes 1.0.0+dev to sort after plain 1.0.0, which is what we -# want. (Contrast with the special suffix 1.0.0.dev, which sorts *before* -# 1.0.0.) - -__version__ = "0.14.0" diff --git a/.venv/Lib/site-packages/h11/_writers.py b/.venv/Lib/site-packages/h11/_writers.py deleted file mode 100644 index 939cdb9..0000000 --- a/.venv/Lib/site-packages/h11/_writers.py +++ /dev/null @@ -1,145 +0,0 @@ -# Code to read HTTP data -# -# Strategy: each writer takes an event + a write-some-bytes function, which is -# calls. -# -# WRITERS is a dict describing how to pick a reader. It maps states to either: -# - a writer -# - or, for body writers, a dict of framin-dependent writer factories - -from typing import Any, Callable, Dict, List, Tuple, Type, Union - -from ._events import Data, EndOfMessage, Event, InformationalResponse, Request, Response -from ._headers import Headers -from ._state import CLIENT, IDLE, SEND_BODY, SEND_RESPONSE, SERVER -from ._util import LocalProtocolError, Sentinel - -__all__ = ["WRITERS"] - -Writer = Callable[[bytes], Any] - - -def write_headers(headers: Headers, write: Writer) -> None: - # "Since the Host field-value is critical information for handling a - # request, a user agent SHOULD generate Host as the first header field - # following the request-line." - RFC 7230 - raw_items = headers._full_items - for raw_name, name, value in raw_items: - if name == b"host": - write(b"%s: %s\r\n" % (raw_name, value)) - for raw_name, name, value in raw_items: - if name != b"host": - write(b"%s: %s\r\n" % (raw_name, value)) - write(b"\r\n") - - -def write_request(request: Request, write: Writer) -> None: - if request.http_version != b"1.1": - raise LocalProtocolError("I only send HTTP/1.1") - write(b"%s %s HTTP/1.1\r\n" % (request.method, request.target)) - write_headers(request.headers, write) - - -# Shared between InformationalResponse and Response -def write_any_response( - response: Union[InformationalResponse, Response], write: Writer -) -> None: - if response.http_version != b"1.1": - raise LocalProtocolError("I only send HTTP/1.1") - status_bytes = str(response.status_code).encode("ascii") - # We don't bother sending ascii status messages like "OK"; they're - # optional and ignored by the protocol. (But the space after the numeric - # status code is mandatory.) - # - # XX FIXME: could at least make an effort to pull out the status message - # from stdlib's http.HTTPStatus table. Or maybe just steal their enums - # (either by import or copy/paste). We already accept them as status codes - # since they're of type IntEnum < int. - write(b"HTTP/1.1 %s %s\r\n" % (status_bytes, response.reason)) - write_headers(response.headers, write) - - -class BodyWriter: - def __call__(self, event: Event, write: Writer) -> None: - if type(event) is Data: - self.send_data(event.data, write) - elif type(event) is EndOfMessage: - self.send_eom(event.headers, write) - else: # pragma: no cover - assert False - - def send_data(self, data: bytes, write: Writer) -> None: - pass - - def send_eom(self, headers: Headers, write: Writer) -> None: - pass - - -# -# These are all careful not to do anything to 'data' except call len(data) and -# write(data). This allows us to transparently pass-through funny objects, -# like placeholder objects referring to files on disk that will be sent via -# sendfile(2). -# -class ContentLengthWriter(BodyWriter): - def __init__(self, length: int) -> None: - self._length = length - - def send_data(self, data: bytes, write: Writer) -> None: - self._length -= len(data) - if self._length < 0: - raise LocalProtocolError("Too much data for declared Content-Length") - write(data) - - def send_eom(self, headers: Headers, write: Writer) -> None: - if self._length != 0: - raise LocalProtocolError("Too little data for declared Content-Length") - if headers: - raise LocalProtocolError("Content-Length and trailers don't mix") - - -class ChunkedWriter(BodyWriter): - def send_data(self, data: bytes, write: Writer) -> None: - # if we encoded 0-length data in the naive way, it would look like an - # end-of-message. - if not data: - return - write(b"%x\r\n" % len(data)) - write(data) - write(b"\r\n") - - def send_eom(self, headers: Headers, write: Writer) -> None: - write(b"0\r\n") - write_headers(headers, write) - - -class Http10Writer(BodyWriter): - def send_data(self, data: bytes, write: Writer) -> None: - write(data) - - def send_eom(self, headers: Headers, write: Writer) -> None: - if headers: - raise LocalProtocolError("can't send trailers to HTTP/1.0 client") - # no need to close the socket ourselves, that will be taken care of by - # Connection: close machinery - - -WritersType = Dict[ - Union[Tuple[Type[Sentinel], Type[Sentinel]], Type[Sentinel]], - Union[ - Dict[str, Type[BodyWriter]], - Callable[[Union[InformationalResponse, Response], Writer], None], - Callable[[Request, Writer], None], - ], -] - -WRITERS: WritersType = { - (CLIENT, IDLE): write_request, - (SERVER, IDLE): write_any_response, - (SERVER, SEND_RESPONSE): write_any_response, - SEND_BODY: { - "chunked": ChunkedWriter, - "content-length": ContentLengthWriter, - "http/1.0": Http10Writer, - }, -} diff --git a/.venv/Lib/site-packages/h11/py.typed b/.venv/Lib/site-packages/h11/py.typed deleted file mode 100644 index f5642f7..0000000 --- a/.venv/Lib/site-packages/h11/py.typed +++ /dev/null @@ -1 +0,0 @@ -Marker diff --git a/.venv/Lib/site-packages/h11/tests/__init__.py b/.venv/Lib/site-packages/h11/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/h11/tests/data/test-file b/.venv/Lib/site-packages/h11/tests/data/test-file deleted file mode 100644 index d0be0a6..0000000 --- a/.venv/Lib/site-packages/h11/tests/data/test-file +++ /dev/null @@ -1 +0,0 @@ -92b12bc045050b55b848d37167a1a63947c364579889ce1d39788e45e9fac9e5 diff --git a/.venv/Lib/site-packages/h11/tests/helpers.py b/.venv/Lib/site-packages/h11/tests/helpers.py deleted file mode 100644 index 571be44..0000000 --- a/.venv/Lib/site-packages/h11/tests/helpers.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import cast, List, Type, Union, ValuesView - -from .._connection import Connection, NEED_DATA, PAUSED -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .._state import CLIENT, CLOSED, DONE, MUST_CLOSE, SERVER -from .._util import Sentinel - -try: - from typing import Literal -except ImportError: - from typing_extensions import Literal # type: ignore - - -def get_all_events(conn: Connection) -> List[Event]: - got_events = [] - while True: - event = conn.next_event() - if event in (NEED_DATA, PAUSED): - break - event = cast(Event, event) - got_events.append(event) - if type(event) is ConnectionClosed: - break - return got_events - - -def receive_and_get(conn: Connection, data: bytes) -> List[Event]: - conn.receive_data(data) - return get_all_events(conn) - - -# Merges adjacent Data events, converts payloads to bytestrings, and removes -# chunk boundaries. -def normalize_data_events(in_events: List[Event]) -> List[Event]: - out_events: List[Event] = [] - for event in in_events: - if type(event) is Data: - event = Data(data=bytes(event.data), chunk_start=False, chunk_end=False) - if out_events and type(out_events[-1]) is type(event) is Data: - out_events[-1] = Data( - data=out_events[-1].data + event.data, - chunk_start=out_events[-1].chunk_start, - chunk_end=out_events[-1].chunk_end, - ) - else: - out_events.append(event) - return out_events - - -# Given that we want to write tests that push some events through a Connection -# and check that its state updates appropriately... we might as make a habit -# of pushing them through two Connections with a fake network link in -# between. -class ConnectionPair: - def __init__(self) -> None: - self.conn = {CLIENT: Connection(CLIENT), SERVER: Connection(SERVER)} - self.other = {CLIENT: SERVER, SERVER: CLIENT} - - @property - def conns(self) -> ValuesView[Connection]: - return self.conn.values() - - # expect="match" if expect=send_events; expect=[...] to say what expected - def send( - self, - role: Type[Sentinel], - send_events: Union[List[Event], Event], - expect: Union[List[Event], Event, Literal["match"]] = "match", - ) -> bytes: - if not isinstance(send_events, list): - send_events = [send_events] - data = b"" - closed = False - for send_event in send_events: - new_data = self.conn[role].send(send_event) - if new_data is None: - closed = True - else: - data += new_data - # send uses b"" to mean b"", and None to mean closed - # receive uses b"" to mean closed, and None to mean "try again" - # so we have to translate between the two conventions - if data: - self.conn[self.other[role]].receive_data(data) - if closed: - self.conn[self.other[role]].receive_data(b"") - got_events = get_all_events(self.conn[self.other[role]]) - if expect == "match": - expect = send_events - if not isinstance(expect, list): - expect = [expect] - assert got_events == expect - return data diff --git a/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py b/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py deleted file mode 100644 index d2ee131..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_against_stdlib_http.py +++ /dev/null @@ -1,115 +0,0 @@ -import json -import os.path -import socket -import socketserver -import threading -from contextlib import closing, contextmanager -from http.server import SimpleHTTPRequestHandler -from typing import Callable, Generator -from urllib.request import urlopen - -import h11 - - -@contextmanager -def socket_server( - handler: Callable[..., socketserver.BaseRequestHandler] -) -> Generator[socketserver.TCPServer, None, None]: - httpd = socketserver.TCPServer(("127.0.0.1", 0), handler) - thread = threading.Thread( - target=httpd.serve_forever, kwargs={"poll_interval": 0.01} - ) - thread.daemon = True - try: - thread.start() - yield httpd - finally: - httpd.shutdown() - - -test_file_path = os.path.join(os.path.dirname(__file__), "data/test-file") -with open(test_file_path, "rb") as f: - test_file_data = f.read() - - -class SingleMindedRequestHandler(SimpleHTTPRequestHandler): - def translate_path(self, path: str) -> str: - return test_file_path - - -def test_h11_as_client() -> None: - with socket_server(SingleMindedRequestHandler) as httpd: - with closing(socket.create_connection(httpd.server_address)) as s: - c = h11.Connection(h11.CLIENT) - - s.sendall( - c.send( # type: ignore[arg-type] - h11.Request( - method="GET", target="/foo", headers=[("Host", "localhost")] - ) - ) - ) - s.sendall(c.send(h11.EndOfMessage())) # type: ignore[arg-type] - - data = bytearray() - while True: - event = c.next_event() - print(event) - if event is h11.NEED_DATA: - # Use a small read buffer to make things more challenging - # and exercise more paths :-) - c.receive_data(s.recv(10)) - continue - if type(event) is h11.Response: - assert event.status_code == 200 - if type(event) is h11.Data: - data += event.data - if type(event) is h11.EndOfMessage: - break - assert bytes(data) == test_file_data - - -class H11RequestHandler(socketserver.BaseRequestHandler): - def handle(self) -> None: - with closing(self.request) as s: - c = h11.Connection(h11.SERVER) - request = None - while True: - event = c.next_event() - if event is h11.NEED_DATA: - # Use a small read buffer to make things more challenging - # and exercise more paths :-) - c.receive_data(s.recv(10)) - continue - if type(event) is h11.Request: - request = event - if type(event) is h11.EndOfMessage: - break - assert request is not None - info = json.dumps( - { - "method": request.method.decode("ascii"), - "target": request.target.decode("ascii"), - "headers": { - name.decode("ascii"): value.decode("ascii") - for (name, value) in request.headers - }, - } - ) - s.sendall(c.send(h11.Response(status_code=200, headers=[]))) # type: ignore[arg-type] - s.sendall(c.send(h11.Data(data=info.encode("ascii")))) - s.sendall(c.send(h11.EndOfMessage())) - - -def test_h11_as_server() -> None: - with socket_server(H11RequestHandler) as httpd: - host, port = httpd.server_address - url = "http://{}:{}/some-path".format(host, port) - with closing(urlopen(url)) as f: - assert f.getcode() == 200 - data = f.read() - info = json.loads(data.decode("ascii")) - print(info) - assert info["method"] == "GET" - assert info["target"] == "/some-path" - assert "urllib" in info["headers"]["user-agent"] diff --git a/.venv/Lib/site-packages/h11/tests/test_connection.py b/.venv/Lib/site-packages/h11/tests/test_connection.py deleted file mode 100644 index 73a27b9..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_connection.py +++ /dev/null @@ -1,1122 +0,0 @@ -from typing import Any, cast, Dict, List, Optional, Tuple, Type - -import pytest - -from .._connection import _body_framing, _keep_alive, Connection, NEED_DATA, PAUSED -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .._state import ( - CLIENT, - CLOSED, - DONE, - ERROR, - IDLE, - MIGHT_SWITCH_PROTOCOL, - MUST_CLOSE, - SEND_BODY, - SEND_RESPONSE, - SERVER, - SWITCHED_PROTOCOL, -) -from .._util import LocalProtocolError, RemoteProtocolError, Sentinel -from .helpers import ConnectionPair, get_all_events, receive_and_get - - -def test__keep_alive() -> None: - assert _keep_alive( - Request(method="GET", target="/", headers=[("Host", "Example.com")]) - ) - assert not _keep_alive( - Request( - method="GET", - target="/", - headers=[("Host", "Example.com"), ("Connection", "close")], - ) - ) - assert not _keep_alive( - Request( - method="GET", - target="/", - headers=[("Host", "Example.com"), ("Connection", "a, b, cLOse, foo")], - ) - ) - assert not _keep_alive( - Request(method="GET", target="/", headers=[], http_version="1.0") # type: ignore[arg-type] - ) - - assert _keep_alive(Response(status_code=200, headers=[])) # type: ignore[arg-type] - assert not _keep_alive(Response(status_code=200, headers=[("Connection", "close")])) - assert not _keep_alive( - Response(status_code=200, headers=[("Connection", "a, b, cLOse, foo")]) - ) - assert not _keep_alive(Response(status_code=200, headers=[], http_version="1.0")) # type: ignore[arg-type] - - -def test__body_framing() -> None: - def headers(cl: Optional[int], te: bool) -> List[Tuple[str, str]]: - headers = [] - if cl is not None: - headers.append(("Content-Length", str(cl))) - if te: - headers.append(("Transfer-Encoding", "chunked")) - return headers - - def resp( - status_code: int = 200, cl: Optional[int] = None, te: bool = False - ) -> Response: - return Response(status_code=status_code, headers=headers(cl, te)) - - def req(cl: Optional[int] = None, te: bool = False) -> Request: - h = headers(cl, te) - h += [("Host", "example.com")] - return Request(method="GET", target="/", headers=h) - - # Special cases where the headers are ignored: - for kwargs in [{}, {"cl": 100}, {"te": True}, {"cl": 100, "te": True}]: - kwargs = cast(Dict[str, Any], kwargs) - for meth, r in [ - (b"HEAD", resp(**kwargs)), - (b"GET", resp(status_code=204, **kwargs)), - (b"GET", resp(status_code=304, **kwargs)), - ]: - assert _body_framing(meth, r) == ("content-length", (0,)) - - # Transfer-encoding - for kwargs in [{"te": True}, {"cl": 100, "te": True}]: - kwargs = cast(Dict[str, Any], kwargs) - for meth, r in [(None, req(**kwargs)), (b"GET", resp(**kwargs))]: # type: ignore - assert _body_framing(meth, r) == ("chunked", ()) - - # Content-Length - for meth, r in [(None, req(cl=100)), (b"GET", resp(cl=100))]: # type: ignore - assert _body_framing(meth, r) == ("content-length", (100,)) - - # No headers - assert _body_framing(None, req()) == ("content-length", (0,)) # type: ignore - assert _body_framing(b"GET", resp()) == ("http/1.0", ()) - - -def test_Connection_basics_and_content_length() -> None: - with pytest.raises(ValueError): - Connection("CLIENT") # type: ignore - - p = ConnectionPair() - assert p.conn[CLIENT].our_role is CLIENT - assert p.conn[CLIENT].their_role is SERVER - assert p.conn[SERVER].our_role is SERVER - assert p.conn[SERVER].their_role is CLIENT - - data = p.send( - CLIENT, - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Content-Length", "10")], - ), - ) - assert data == ( - b"GET / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 10\r\n\r\n" - ) - - for conn in p.conns: - assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - assert p.conn[CLIENT].our_state is SEND_BODY - assert p.conn[CLIENT].their_state is SEND_RESPONSE - assert p.conn[SERVER].our_state is SEND_RESPONSE - assert p.conn[SERVER].their_state is SEND_BODY - - assert p.conn[CLIENT].their_http_version is None - assert p.conn[SERVER].their_http_version == b"1.1" - - data = p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type] - assert data == b"HTTP/1.1 100 \r\n\r\n" - - data = p.send(SERVER, Response(status_code=200, headers=[("Content-Length", "11")])) - assert data == b"HTTP/1.1 200 \r\nContent-Length: 11\r\n\r\n" - - for conn in p.conns: - assert conn.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY} - - assert p.conn[CLIENT].their_http_version == b"1.1" - assert p.conn[SERVER].their_http_version == b"1.1" - - data = p.send(CLIENT, Data(data=b"12345")) - assert data == b"12345" - data = p.send( - CLIENT, Data(data=b"67890"), expect=[Data(data=b"67890"), EndOfMessage()] - ) - assert data == b"67890" - data = p.send(CLIENT, EndOfMessage(), expect=[]) - assert data == b"" - - for conn in p.conns: - assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY} - - data = p.send(SERVER, Data(data=b"1234567890")) - assert data == b"1234567890" - data = p.send(SERVER, Data(data=b"1"), expect=[Data(data=b"1"), EndOfMessage()]) - assert data == b"1" - data = p.send(SERVER, EndOfMessage(), expect=[]) - assert data == b"" - - for conn in p.conns: - assert conn.states == {CLIENT: DONE, SERVER: DONE} - - -def test_chunked() -> None: - p = ConnectionPair() - - p.send( - CLIENT, - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")], - ), - ) - data = p.send(CLIENT, Data(data=b"1234567890", chunk_start=True, chunk_end=True)) - assert data == b"a\r\n1234567890\r\n" - data = p.send(CLIENT, Data(data=b"abcde", chunk_start=True, chunk_end=True)) - assert data == b"5\r\nabcde\r\n" - data = p.send(CLIENT, Data(data=b""), expect=[]) - assert data == b"" - data = p.send(CLIENT, EndOfMessage(headers=[("hello", "there")])) - assert data == b"0\r\nhello: there\r\n\r\n" - - p.send( - SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")]) - ) - p.send(SERVER, Data(data=b"54321", chunk_start=True, chunk_end=True)) - p.send(SERVER, Data(data=b"12345", chunk_start=True, chunk_end=True)) - p.send(SERVER, EndOfMessage()) - - for conn in p.conns: - assert conn.states == {CLIENT: DONE, SERVER: DONE} - - -def test_chunk_boundaries() -> None: - conn = Connection(our_role=SERVER) - - request = ( - b"POST / HTTP/1.1\r\n" - b"Host: example.com\r\n" - b"Transfer-Encoding: chunked\r\n" - b"\r\n" - ) - conn.receive_data(request) - assert conn.next_event() == Request( - method="POST", - target="/", - headers=[("Host", "example.com"), ("Transfer-Encoding", "chunked")], - ) - assert conn.next_event() is NEED_DATA - - conn.receive_data(b"5\r\nhello\r\n") - assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True) - - conn.receive_data(b"5\r\nhel") - assert conn.next_event() == Data(data=b"hel", chunk_start=True, chunk_end=False) - - conn.receive_data(b"l") - assert conn.next_event() == Data(data=b"l", chunk_start=False, chunk_end=False) - - conn.receive_data(b"o\r\n") - assert conn.next_event() == Data(data=b"o", chunk_start=False, chunk_end=True) - - conn.receive_data(b"5\r\nhello") - assert conn.next_event() == Data(data=b"hello", chunk_start=True, chunk_end=True) - - conn.receive_data(b"\r\n") - assert conn.next_event() == NEED_DATA - - conn.receive_data(b"0\r\n\r\n") - assert conn.next_event() == EndOfMessage() - - -def test_client_talking_to_http10_server() -> None: - c = Connection(CLIENT) - c.send(Request(method="GET", target="/", headers=[("Host", "example.com")])) - c.send(EndOfMessage()) - assert c.our_state is DONE - # No content-length, so Http10 framing for body - assert receive_and_get(c, b"HTTP/1.0 200 OK\r\n\r\n") == [ - Response(status_code=200, headers=[], http_version="1.0", reason=b"OK") # type: ignore[arg-type] - ] - assert c.our_state is MUST_CLOSE - assert receive_and_get(c, b"12345") == [Data(data=b"12345")] - assert receive_and_get(c, b"67890") == [Data(data=b"67890")] - assert receive_and_get(c, b"") == [EndOfMessage(), ConnectionClosed()] - assert c.their_state is CLOSED - - -def test_server_talking_to_http10_client() -> None: - c = Connection(SERVER) - # No content-length, so no body - # NB: no host header - assert receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") == [ - Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] - EndOfMessage(), - ] - assert c.their_state is MUST_CLOSE - - # We automatically Connection: close back at them - assert ( - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" - ) - - assert c.send(Data(data=b"12345")) == b"12345" - assert c.send(EndOfMessage()) == b"" - assert c.our_state is MUST_CLOSE - - # Check that it works if they do send Content-Length - c = Connection(SERVER) - # NB: no host header - assert receive_and_get(c, b"POST / HTTP/1.0\r\nContent-Length: 10\r\n\r\n1") == [ - Request( - method="POST", - target="/", - headers=[("Content-Length", "10")], - http_version="1.0", - ), - Data(data=b"1"), - ] - assert receive_and_get(c, b"234567890") == [Data(data=b"234567890"), EndOfMessage()] - assert c.their_state is MUST_CLOSE - assert receive_and_get(c, b"") == [ConnectionClosed()] - - -def test_automatic_transfer_encoding_in_response() -> None: - # Check that in responses, the user can specify either Transfer-Encoding: - # chunked or no framing at all, and in both cases we automatically select - # the right option depending on whether the peer speaks HTTP/1.0 or - # HTTP/1.1 - for user_headers in [ - [("Transfer-Encoding", "chunked")], - [], - # In fact, this even works if Content-Length is set, - # because if both are set then Transfer-Encoding wins - [("Transfer-Encoding", "chunked"), ("Content-Length", "100")], - ]: - user_headers = cast(List[Tuple[str, str]], user_headers) - p = ConnectionPair() - p.send( - CLIENT, - [ - Request(method="GET", target="/", headers=[("Host", "example.com")]), - EndOfMessage(), - ], - ) - # When speaking to HTTP/1.1 client, all of the above cases get - # normalized to Transfer-Encoding: chunked - p.send( - SERVER, - Response(status_code=200, headers=user_headers), - expect=Response( - status_code=200, headers=[("Transfer-Encoding", "chunked")] - ), - ) - - # When speaking to HTTP/1.0 client, all of the above cases get - # normalized to no-framing-headers - c = Connection(SERVER) - receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") - assert ( - c.send(Response(status_code=200, headers=user_headers)) - == b"HTTP/1.1 200 \r\nConnection: close\r\n\r\n" - ) - assert c.send(Data(data=b"12345")) == b"12345" - - -def test_automagic_connection_close_handling() -> None: - p = ConnectionPair() - # If the user explicitly sets Connection: close, then we notice and - # respect it - p.send( - CLIENT, - [ - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Connection", "close")], - ), - EndOfMessage(), - ], - ) - for conn in p.conns: - assert conn.states[CLIENT] is MUST_CLOSE - # And if the client sets it, the server automatically echoes it back - p.send( - SERVER, - # no header here... - [Response(status_code=204, headers=[]), EndOfMessage()], # type: ignore[arg-type] - # ...but oh look, it arrived anyway - expect=[ - Response(status_code=204, headers=[("connection", "close")]), - EndOfMessage(), - ], - ) - for conn in p.conns: - assert conn.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} - - -def test_100_continue() -> None: - def setup() -> ConnectionPair: - p = ConnectionPair() - p.send( - CLIENT, - Request( - method="GET", - target="/", - headers=[ - ("Host", "example.com"), - ("Content-Length", "100"), - ("Expect", "100-continue"), - ], - ), - ) - for conn in p.conns: - assert conn.client_is_waiting_for_100_continue - assert not p.conn[CLIENT].they_are_waiting_for_100_continue - assert p.conn[SERVER].they_are_waiting_for_100_continue - return p - - # Disabled by 100 Continue - p = setup() - p.send(SERVER, InformationalResponse(status_code=100, headers=[])) # type: ignore[arg-type] - for conn in p.conns: - assert not conn.client_is_waiting_for_100_continue - assert not conn.they_are_waiting_for_100_continue - - # Disabled by a real response - p = setup() - p.send( - SERVER, Response(status_code=200, headers=[("Transfer-Encoding", "chunked")]) - ) - for conn in p.conns: - assert not conn.client_is_waiting_for_100_continue - assert not conn.they_are_waiting_for_100_continue - - # Disabled by the client going ahead and sending stuff anyway - p = setup() - p.send(CLIENT, Data(data=b"12345")) - for conn in p.conns: - assert not conn.client_is_waiting_for_100_continue - assert not conn.they_are_waiting_for_100_continue - - -def test_max_incomplete_event_size_countermeasure() -> None: - # Infinitely long headers are definitely not okay - c = Connection(SERVER) - c.receive_data(b"GET / HTTP/1.0\r\nEndless: ") - assert c.next_event() is NEED_DATA - with pytest.raises(RemoteProtocolError): - while True: - c.receive_data(b"a" * 1024) - c.next_event() - - # Checking that the same header is accepted / rejected depending on the - # max_incomplete_event_size setting: - c = Connection(SERVER, max_incomplete_event_size=5000) - c.receive_data(b"GET / HTTP/1.0\r\nBig: ") - c.receive_data(b"a" * 4000) - c.receive_data(b"\r\n\r\n") - assert get_all_events(c) == [ - Request( - method="GET", target="/", http_version="1.0", headers=[("big", "a" * 4000)] - ), - EndOfMessage(), - ] - - c = Connection(SERVER, max_incomplete_event_size=4000) - c.receive_data(b"GET / HTTP/1.0\r\nBig: ") - c.receive_data(b"a" * 4000) - with pytest.raises(RemoteProtocolError): - c.next_event() - - # Temporarily exceeding the size limit is fine, as long as its done with - # complete events: - c = Connection(SERVER, max_incomplete_event_size=5000) - c.receive_data(b"GET / HTTP/1.0\r\nContent-Length: 10000") - c.receive_data(b"\r\n\r\n" + b"a" * 10000) - assert get_all_events(c) == [ - Request( - method="GET", - target="/", - http_version="1.0", - headers=[("Content-Length", "10000")], - ), - Data(data=b"a" * 10000), - EndOfMessage(), - ] - - c = Connection(SERVER, max_incomplete_event_size=100) - # Two pipelined requests to create a way-too-big receive buffer... but - # it's fine because we're not checking - c.receive_data( - b"GET /1 HTTP/1.1\r\nHost: a\r\n\r\n" - b"GET /2 HTTP/1.1\r\nHost: b\r\n\r\n" + b"X" * 1000 - ) - assert get_all_events(c) == [ - Request(method="GET", target="/1", headers=[("host", "a")]), - EndOfMessage(), - ] - # Even more data comes in, still no problem - c.receive_data(b"X" * 1000) - # We can respond and reuse to get the second pipelined request - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - c.start_next_cycle() - assert get_all_events(c) == [ - Request(method="GET", target="/2", headers=[("host", "b")]), - EndOfMessage(), - ] - # But once we unpause and try to read the next message, and find that it's - # incomplete and the buffer is *still* way too large, then *that's* a - # problem: - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - c.start_next_cycle() - with pytest.raises(RemoteProtocolError): - c.next_event() - - -def test_reuse_simple() -> None: - p = ConnectionPair() - p.send( - CLIENT, - [Request(method="GET", target="/", headers=[("Host", "a")]), EndOfMessage()], - ) - p.send( - SERVER, - [ - Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), - EndOfMessage(), - ], - ) - for conn in p.conns: - assert conn.states == {CLIENT: DONE, SERVER: DONE} - conn.start_next_cycle() - - p.send( - CLIENT, - [ - Request(method="DELETE", target="/foo", headers=[("Host", "a")]), - EndOfMessage(), - ], - ) - p.send( - SERVER, - [ - Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), - EndOfMessage(), - ], - ) - - -def test_pipelining() -> None: - # Client doesn't support pipelining, so we have to do this by hand - c = Connection(SERVER) - assert c.next_event() is NEED_DATA - # 3 requests all bunched up - c.receive_data( - b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" - b"12345" - b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" - b"67890" - b"GET /3 HTTP/1.1\r\nHost: a.com\r\n\r\n" - ) - assert get_all_events(c) == [ - Request( - method="GET", - target="/1", - headers=[("Host", "a.com"), ("Content-Length", "5")], - ), - Data(data=b"12345"), - EndOfMessage(), - ] - assert c.their_state is DONE - assert c.our_state is SEND_RESPONSE - - assert c.next_event() is PAUSED - - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - assert c.their_state is DONE - assert c.our_state is DONE - - c.start_next_cycle() - - assert get_all_events(c) == [ - Request( - method="GET", - target="/2", - headers=[("Host", "a.com"), ("Content-Length", "5")], - ), - Data(data=b"67890"), - EndOfMessage(), - ] - assert c.next_event() is PAUSED - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - c.start_next_cycle() - - assert get_all_events(c) == [ - Request(method="GET", target="/3", headers=[("Host", "a.com")]), - EndOfMessage(), - ] - # Doesn't pause this time, no trailing data - assert c.next_event() is NEED_DATA - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - - # Arrival of more data triggers pause - assert c.next_event() is NEED_DATA - c.receive_data(b"SADF") - assert c.next_event() is PAUSED - assert c.trailing_data == (b"SADF", False) - # If EOF arrives while paused, we don't see that either: - c.receive_data(b"") - assert c.trailing_data == (b"SADF", True) - assert c.next_event() is PAUSED - c.receive_data(b"") - assert c.next_event() is PAUSED - # Can't call receive_data with non-empty buf after closing it - with pytest.raises(RuntimeError): - c.receive_data(b"FDSA") - - -def test_protocol_switch() -> None: - for (req, deny, accept) in [ - ( - Request( - method="CONNECT", - target="example.com:443", - headers=[("Host", "foo"), ("Content-Length", "1")], - ), - Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), - Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), - ), - ( - Request( - method="GET", - target="/", - headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], - ), - Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), - InformationalResponse(status_code=101, headers=[("Upgrade", "a")]), - ), - ( - Request( - method="CONNECT", - target="example.com:443", - headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], - ), - Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), - # Accept CONNECT, not upgrade - Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), - ), - ( - Request( - method="CONNECT", - target="example.com:443", - headers=[("Host", "foo"), ("Content-Length", "1"), ("Upgrade", "a, b")], - ), - Response(status_code=404, headers=[(b"transfer-encoding", b"chunked")]), - # Accept Upgrade, not CONNECT - InformationalResponse(status_code=101, headers=[("Upgrade", "b")]), - ), - ]: - - def setup() -> ConnectionPair: - p = ConnectionPair() - p.send(CLIENT, req) - # No switch-related state change stuff yet; the client has to - # finish the request before that kicks in - for conn in p.conns: - assert conn.states[CLIENT] is SEND_BODY - p.send(CLIENT, [Data(data=b"1"), EndOfMessage()]) - for conn in p.conns: - assert conn.states[CLIENT] is MIGHT_SWITCH_PROTOCOL - assert p.conn[SERVER].next_event() is PAUSED - return p - - # Test deny case - p = setup() - p.send(SERVER, deny) - for conn in p.conns: - assert conn.states == {CLIENT: DONE, SERVER: SEND_BODY} - p.send(SERVER, EndOfMessage()) - # Check that re-use is still allowed after a denial - for conn in p.conns: - conn.start_next_cycle() - - # Test accept case - p = setup() - p.send(SERVER, accept) - for conn in p.conns: - assert conn.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} - conn.receive_data(b"123") - assert conn.next_event() is PAUSED - conn.receive_data(b"456") - assert conn.next_event() is PAUSED - assert conn.trailing_data == (b"123456", False) - - # Pausing in might-switch, then recovery - # (weird artificial case where the trailing data actually is valid - # HTTP for some reason, because this makes it easier to test the state - # logic) - p = setup() - sc = p.conn[SERVER] - sc.receive_data(b"GET / HTTP/1.0\r\n\r\n") - assert sc.next_event() is PAUSED - assert sc.trailing_data == (b"GET / HTTP/1.0\r\n\r\n", False) - sc.send(deny) - assert sc.next_event() is PAUSED - sc.send(EndOfMessage()) - sc.start_next_cycle() - assert get_all_events(sc) == [ - Request(method="GET", target="/", headers=[], http_version="1.0"), # type: ignore[arg-type] - EndOfMessage(), - ] - - # When we're DONE, have no trailing data, and the connection gets - # closed, we report ConnectionClosed(). When we're in might-switch or - # switched, we don't. - p = setup() - sc = p.conn[SERVER] - sc.receive_data(b"") - assert sc.next_event() is PAUSED - assert sc.trailing_data == (b"", True) - p.send(SERVER, accept) - assert sc.next_event() is PAUSED - - p = setup() - sc = p.conn[SERVER] - sc.receive_data(b"") - assert sc.next_event() is PAUSED - sc.send(deny) - assert sc.next_event() == ConnectionClosed() - - # You can't send after switching protocols, or while waiting for a - # protocol switch - p = setup() - with pytest.raises(LocalProtocolError): - p.conn[CLIENT].send( - Request(method="GET", target="/", headers=[("Host", "a")]) - ) - p = setup() - p.send(SERVER, accept) - with pytest.raises(LocalProtocolError): - p.conn[SERVER].send(Data(data=b"123")) - - -def test_close_simple() -> None: - # Just immediately closing a new connection without anything having - # happened yet. - for (who_shot_first, who_shot_second) in [(CLIENT, SERVER), (SERVER, CLIENT)]: - - def setup() -> ConnectionPair: - p = ConnectionPair() - p.send(who_shot_first, ConnectionClosed()) - for conn in p.conns: - assert conn.states == { - who_shot_first: CLOSED, - who_shot_second: MUST_CLOSE, - } - return p - - # You can keep putting b"" into a closed connection, and you keep - # getting ConnectionClosed() out: - p = setup() - assert p.conn[who_shot_second].next_event() == ConnectionClosed() - assert p.conn[who_shot_second].next_event() == ConnectionClosed() - p.conn[who_shot_second].receive_data(b"") - assert p.conn[who_shot_second].next_event() == ConnectionClosed() - # Second party can close... - p = setup() - p.send(who_shot_second, ConnectionClosed()) - for conn in p.conns: - assert conn.our_state is CLOSED - assert conn.their_state is CLOSED - # But trying to receive new data on a closed connection is a - # RuntimeError (not ProtocolError, because the problem here isn't - # violation of HTTP, it's violation of physics) - p = setup() - with pytest.raises(RuntimeError): - p.conn[who_shot_second].receive_data(b"123") - # And receiving new data on a MUST_CLOSE connection is a ProtocolError - p = setup() - p.conn[who_shot_first].receive_data(b"GET") - with pytest.raises(RemoteProtocolError): - p.conn[who_shot_first].next_event() - - -def test_close_different_states() -> None: - req = [ - Request(method="GET", target="/foo", headers=[("Host", "a")]), - EndOfMessage(), - ] - resp = [ - Response(status_code=200, headers=[(b"transfer-encoding", b"chunked")]), - EndOfMessage(), - ] - - # Client before request - p = ConnectionPair() - p.send(CLIENT, ConnectionClosed()) - for conn in p.conns: - assert conn.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} - - # Client after request - p = ConnectionPair() - p.send(CLIENT, req) - p.send(CLIENT, ConnectionClosed()) - for conn in p.conns: - assert conn.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} - - # Server after request -> not allowed - p = ConnectionPair() - p.send(CLIENT, req) - with pytest.raises(LocalProtocolError): - p.conn[SERVER].send(ConnectionClosed()) - p.conn[CLIENT].receive_data(b"") - with pytest.raises(RemoteProtocolError): - p.conn[CLIENT].next_event() - - # Server after response - p = ConnectionPair() - p.send(CLIENT, req) - p.send(SERVER, resp) - p.send(SERVER, ConnectionClosed()) - for conn in p.conns: - assert conn.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} - - # Both after closing (ConnectionClosed() is idempotent) - p = ConnectionPair() - p.send(CLIENT, req) - p.send(SERVER, resp) - p.send(CLIENT, ConnectionClosed()) - p.send(SERVER, ConnectionClosed()) - p.send(CLIENT, ConnectionClosed()) - p.send(SERVER, ConnectionClosed()) - - # In the middle of sending -> not allowed - p = ConnectionPair() - p.send( - CLIENT, - Request( - method="GET", target="/", headers=[("Host", "a"), ("Content-Length", "10")] - ), - ) - with pytest.raises(LocalProtocolError): - p.conn[CLIENT].send(ConnectionClosed()) - p.conn[SERVER].receive_data(b"") - with pytest.raises(RemoteProtocolError): - p.conn[SERVER].next_event() - - -# Receive several requests and then client shuts down their side of the -# connection; we can respond to each -def test_pipelined_close() -> None: - c = Connection(SERVER) - # 2 requests then a close - c.receive_data( - b"GET /1 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" - b"12345" - b"GET /2 HTTP/1.1\r\nHost: a.com\r\nContent-Length: 5\r\n\r\n" - b"67890" - ) - c.receive_data(b"") - assert get_all_events(c) == [ - Request( - method="GET", - target="/1", - headers=[("host", "a.com"), ("content-length", "5")], - ), - Data(data=b"12345"), - EndOfMessage(), - ] - assert c.states[CLIENT] is DONE - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - assert c.states[SERVER] is DONE - c.start_next_cycle() - assert get_all_events(c) == [ - Request( - method="GET", - target="/2", - headers=[("host", "a.com"), ("content-length", "5")], - ), - Data(data=b"67890"), - EndOfMessage(), - ConnectionClosed(), - ] - assert c.states == {CLIENT: CLOSED, SERVER: SEND_RESPONSE} - c.send(Response(status_code=200, headers=[])) # type: ignore[arg-type] - c.send(EndOfMessage()) - assert c.states == {CLIENT: CLOSED, SERVER: MUST_CLOSE} - c.send(ConnectionClosed()) - assert c.states == {CLIENT: CLOSED, SERVER: CLOSED} - - -def test_sendfile() -> None: - class SendfilePlaceholder: - def __len__(self) -> int: - return 10 - - placeholder = SendfilePlaceholder() - - def setup( - header: Tuple[str, str], http_version: str - ) -> Tuple[Connection, Optional[List[bytes]]]: - c = Connection(SERVER) - receive_and_get( - c, "GET / HTTP/{}\r\nHost: a\r\n\r\n".format(http_version).encode("ascii") - ) - headers = [] - if header: - headers.append(header) - c.send(Response(status_code=200, headers=headers)) - return c, c.send_with_data_passthrough(Data(data=placeholder)) # type: ignore - - c, data = setup(("Content-Length", "10"), "1.1") - assert data == [placeholder] # type: ignore - # Raises an error if the connection object doesn't think we've sent - # exactly 10 bytes - c.send(EndOfMessage()) - - _, data = setup(("Transfer-Encoding", "chunked"), "1.1") - assert placeholder in data # type: ignore - data[data.index(placeholder)] = b"x" * 10 # type: ignore - assert b"".join(data) == b"a\r\nxxxxxxxxxx\r\n" # type: ignore - - c, data = setup(None, "1.0") # type: ignore - assert data == [placeholder] # type: ignore - assert c.our_state is SEND_BODY - - -def test_errors() -> None: - # After a receive error, you can't receive - for role in [CLIENT, SERVER]: - c = Connection(our_role=role) - c.receive_data(b"gibberish\r\n\r\n") - with pytest.raises(RemoteProtocolError): - c.next_event() - # Now any attempt to receive continues to raise - assert c.their_state is ERROR - assert c.our_state is not ERROR - print(c._cstate.states) - with pytest.raises(RemoteProtocolError): - c.next_event() - # But we can still yell at the client for sending us gibberish - if role is SERVER: - assert ( - c.send(Response(status_code=400, headers=[])) # type: ignore[arg-type] - == b"HTTP/1.1 400 \r\nConnection: close\r\n\r\n" - ) - - # After an error sending, you can no longer send - # (This is especially important for things like content-length errors, - # where there's complex internal state being modified) - def conn(role: Type[Sentinel]) -> Connection: - c = Connection(our_role=role) - if role is SERVER: - # Put it into the state where it *could* send a response... - receive_and_get(c, b"GET / HTTP/1.0\r\n\r\n") - assert c.our_state is SEND_RESPONSE - return c - - for role in [CLIENT, SERVER]: - if role is CLIENT: - # This HTTP/1.0 request won't be detected as bad until after we go - # through the state machine and hit the writing code - good = Request(method="GET", target="/", headers=[("Host", "example.com")]) - bad = Request( - method="GET", - target="/", - headers=[("Host", "example.com")], - http_version="1.0", - ) - elif role is SERVER: - good = Response(status_code=200, headers=[]) # type: ignore[arg-type,assignment] - bad = Response(status_code=200, headers=[], http_version="1.0") # type: ignore[arg-type,assignment] - # Make sure 'good' actually is good - c = conn(role) - c.send(good) - assert c.our_state is not ERROR - # Do that again, but this time sending 'bad' first - c = conn(role) - with pytest.raises(LocalProtocolError): - c.send(bad) - assert c.our_state is ERROR - assert c.their_state is not ERROR - # Now 'good' is not so good - with pytest.raises(LocalProtocolError): - c.send(good) - - # And check send_failed() too - c = conn(role) - c.send_failed() - assert c.our_state is ERROR - assert c.their_state is not ERROR - # This is idempotent - c.send_failed() - assert c.our_state is ERROR - assert c.their_state is not ERROR - - -def test_idle_receive_nothing() -> None: - # At one point this incorrectly raised an error - for role in [CLIENT, SERVER]: - c = Connection(role) - assert c.next_event() is NEED_DATA - - -def test_connection_drop() -> None: - c = Connection(SERVER) - c.receive_data(b"GET /") - assert c.next_event() is NEED_DATA - c.receive_data(b"") - with pytest.raises(RemoteProtocolError): - c.next_event() - - -def test_408_request_timeout() -> None: - # Should be able to send this spontaneously as a server without seeing - # anything from client - p = ConnectionPair() - p.send(SERVER, Response(status_code=408, headers=[(b"connection", b"close")])) - - -# This used to raise IndexError -def test_empty_request() -> None: - c = Connection(SERVER) - c.receive_data(b"\r\n") - with pytest.raises(RemoteProtocolError): - c.next_event() - - -# This used to raise IndexError -def test_empty_response() -> None: - c = Connection(CLIENT) - c.send(Request(method="GET", target="/", headers=[("Host", "a")])) - c.receive_data(b"\r\n") - with pytest.raises(RemoteProtocolError): - c.next_event() - - -@pytest.mark.parametrize( - "data", - [ - b"\x00", - b"\x20", - b"\x16\x03\x01\x00\xa5", # Typical start of a TLS Client Hello - ], -) -def test_early_detection_of_invalid_request(data: bytes) -> None: - c = Connection(SERVER) - # Early detection should occur before even receiving a `\r\n` - c.receive_data(data) - with pytest.raises(RemoteProtocolError): - c.next_event() - - -@pytest.mark.parametrize( - "data", - [ - b"\x00", - b"\x20", - b"\x16\x03\x03\x00\x31", # Typical start of a TLS Server Hello - ], -) -def test_early_detection_of_invalid_response(data: bytes) -> None: - c = Connection(CLIENT) - # Early detection should occur before even receiving a `\r\n` - c.receive_data(data) - with pytest.raises(RemoteProtocolError): - c.next_event() - - -# This used to give different headers for HEAD and GET. -# The correct way to handle HEAD is to put whatever headers we *would* have -# put if it were a GET -- even though we know that for HEAD, those headers -# will be ignored. -def test_HEAD_framing_headers() -> None: - def setup(method: bytes, http_version: bytes) -> Connection: - c = Connection(SERVER) - c.receive_data( - method + b" / HTTP/" + http_version + b"\r\n" + b"Host: example.com\r\n\r\n" - ) - assert type(c.next_event()) is Request - assert type(c.next_event()) is EndOfMessage - return c - - for method in [b"GET", b"HEAD"]: - # No Content-Length, HTTP/1.1 peer, should use chunked - c = setup(method, b"1.1") - assert ( - c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] - b"Transfer-Encoding: chunked\r\n\r\n" - ) - - # No Content-Length, HTTP/1.0 peer, frame with connection: close - c = setup(method, b"1.0") - assert ( - c.send(Response(status_code=200, headers=[])) == b"HTTP/1.1 200 \r\n" # type: ignore[arg-type] - b"Connection: close\r\n\r\n" - ) - - # Content-Length + Transfer-Encoding, TE wins - c = setup(method, b"1.1") - assert ( - c.send( - Response( - status_code=200, - headers=[ - ("Content-Length", "100"), - ("Transfer-Encoding", "chunked"), - ], - ) - ) - == b"HTTP/1.1 200 \r\n" - b"Transfer-Encoding: chunked\r\n\r\n" - ) - - -def test_special_exceptions_for_lost_connection_in_message_body() -> None: - c = Connection(SERVER) - c.receive_data( - b"POST / HTTP/1.1\r\n" b"Host: example.com\r\n" b"Content-Length: 100\r\n\r\n" - ) - assert type(c.next_event()) is Request - assert c.next_event() is NEED_DATA - c.receive_data(b"12345") - assert c.next_event() == Data(data=b"12345") - c.receive_data(b"") - with pytest.raises(RemoteProtocolError) as excinfo: - c.next_event() - assert "received 5 bytes" in str(excinfo.value) - assert "expected 100" in str(excinfo.value) - - c = Connection(SERVER) - c.receive_data( - b"POST / HTTP/1.1\r\n" - b"Host: example.com\r\n" - b"Transfer-Encoding: chunked\r\n\r\n" - ) - assert type(c.next_event()) is Request - assert c.next_event() is NEED_DATA - c.receive_data(b"8\r\n012345") - assert c.next_event().data == b"012345" # type: ignore - c.receive_data(b"") - with pytest.raises(RemoteProtocolError) as excinfo: - c.next_event() - assert "incomplete chunked read" in str(excinfo.value) diff --git a/.venv/Lib/site-packages/h11/tests/test_events.py b/.venv/Lib/site-packages/h11/tests/test_events.py deleted file mode 100644 index bc6c313..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_events.py +++ /dev/null @@ -1,150 +0,0 @@ -from http import HTTPStatus - -import pytest - -from .. import _events -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .._util import LocalProtocolError - - -def test_events() -> None: - with pytest.raises(LocalProtocolError): - # Missing Host: - req = Request( - method="GET", target="/", headers=[("a", "b")], http_version="1.1" - ) - # But this is okay (HTTP/1.0) - req = Request(method="GET", target="/", headers=[("a", "b")], http_version="1.0") - # fields are normalized - assert req.method == b"GET" - assert req.target == b"/" - assert req.headers == [(b"a", b"b")] - assert req.http_version == b"1.0" - - # This is also okay -- has a Host (with weird capitalization, which is ok) - req = Request( - method="GET", - target="/", - headers=[("a", "b"), ("hOSt", "example.com")], - http_version="1.1", - ) - # we normalize header capitalization - assert req.headers == [(b"a", b"b"), (b"host", b"example.com")] - - # Multiple host is bad too - with pytest.raises(LocalProtocolError): - req = Request( - method="GET", - target="/", - headers=[("Host", "a"), ("Host", "a")], - http_version="1.1", - ) - # Even for HTTP/1.0 - with pytest.raises(LocalProtocolError): - req = Request( - method="GET", - target="/", - headers=[("Host", "a"), ("Host", "a")], - http_version="1.0", - ) - - # Header values are validated - for bad_char in "\x00\r\n\f\v": - with pytest.raises(LocalProtocolError): - req = Request( - method="GET", - target="/", - headers=[("Host", "a"), ("Foo", "asd" + bad_char)], - http_version="1.0", - ) - - # But for compatibility we allow non-whitespace control characters, even - # though they're forbidden by the spec. - Request( - method="GET", - target="/", - headers=[("Host", "a"), ("Foo", "asd\x01\x02\x7f")], - http_version="1.0", - ) - - # Request target is validated - for bad_byte in b"\x00\x20\x7f\xee": - target = bytearray(b"/") - target.append(bad_byte) - with pytest.raises(LocalProtocolError): - Request( - method="GET", target=target, headers=[("Host", "a")], http_version="1.1" - ) - - # Request method is validated - with pytest.raises(LocalProtocolError): - Request( - method="GET / HTTP/1.1", - target=target, - headers=[("Host", "a")], - http_version="1.1", - ) - - ir = InformationalResponse(status_code=100, headers=[("Host", "a")]) - assert ir.status_code == 100 - assert ir.headers == [(b"host", b"a")] - assert ir.http_version == b"1.1" - - with pytest.raises(LocalProtocolError): - InformationalResponse(status_code=200, headers=[("Host", "a")]) - - resp = Response(status_code=204, headers=[], http_version="1.0") # type: ignore[arg-type] - assert resp.status_code == 204 - assert resp.headers == [] - assert resp.http_version == b"1.0" - - with pytest.raises(LocalProtocolError): - resp = Response(status_code=100, headers=[], http_version="1.0") # type: ignore[arg-type] - - with pytest.raises(LocalProtocolError): - Response(status_code="100", headers=[], http_version="1.0") # type: ignore[arg-type] - - with pytest.raises(LocalProtocolError): - InformationalResponse(status_code=b"100", headers=[], http_version="1.0") # type: ignore[arg-type] - - d = Data(data=b"asdf") - assert d.data == b"asdf" - - eom = EndOfMessage() - assert eom.headers == [] - - cc = ConnectionClosed() - assert repr(cc) == "ConnectionClosed()" - - -def test_intenum_status_code() -> None: - # https://github.com/python-hyper/h11/issues/72 - - r = Response(status_code=HTTPStatus.OK, headers=[], http_version="1.0") # type: ignore[arg-type] - assert r.status_code == HTTPStatus.OK - assert type(r.status_code) is not type(HTTPStatus.OK) - assert type(r.status_code) is int - - -def test_header_casing() -> None: - r = Request( - method="GET", - target="/", - headers=[("Host", "example.org"), ("Connection", "keep-alive")], - http_version="1.1", - ) - assert len(r.headers) == 2 - assert r.headers[0] == (b"host", b"example.org") - assert r.headers == [(b"host", b"example.org"), (b"connection", b"keep-alive")] - assert r.headers.raw_items() == [ - (b"Host", b"example.org"), - (b"Connection", b"keep-alive"), - ] diff --git a/.venv/Lib/site-packages/h11/tests/test_headers.py b/.venv/Lib/site-packages/h11/tests/test_headers.py deleted file mode 100644 index ba53d08..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_headers.py +++ /dev/null @@ -1,157 +0,0 @@ -import pytest - -from .._events import Request -from .._headers import ( - get_comma_header, - has_expect_100_continue, - Headers, - normalize_and_validate, - set_comma_header, -) -from .._util import LocalProtocolError - - -def test_normalize_and_validate() -> None: - assert normalize_and_validate([("foo", "bar")]) == [(b"foo", b"bar")] - assert normalize_and_validate([(b"foo", b"bar")]) == [(b"foo", b"bar")] - - # no leading/trailing whitespace in names - with pytest.raises(LocalProtocolError): - normalize_and_validate([(b"foo ", "bar")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([(b" foo", "bar")]) - - # no weird characters in names - with pytest.raises(LocalProtocolError) as excinfo: - normalize_and_validate([(b"foo bar", b"baz")]) - assert "foo bar" in str(excinfo.value) - with pytest.raises(LocalProtocolError): - normalize_and_validate([(b"foo\x00bar", b"baz")]) - # Not even 8-bit characters: - with pytest.raises(LocalProtocolError): - normalize_and_validate([(b"foo\xffbar", b"baz")]) - # And not even the control characters we allow in values: - with pytest.raises(LocalProtocolError): - normalize_and_validate([(b"foo\x01bar", b"baz")]) - - # no return or NUL characters in values - with pytest.raises(LocalProtocolError) as excinfo: - normalize_and_validate([("foo", "bar\rbaz")]) - assert "bar\\rbaz" in str(excinfo.value) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", "bar\nbaz")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", "bar\x00baz")]) - # no leading/trailing whitespace - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", "barbaz ")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", " barbaz")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", "barbaz\t")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("foo", "\tbarbaz")]) - - # content-length - assert normalize_and_validate([("Content-Length", "1")]) == [ - (b"content-length", b"1") - ] - with pytest.raises(LocalProtocolError): - normalize_and_validate([("Content-Length", "asdf")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("Content-Length", "1x")]) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("Content-Length", "1"), ("Content-Length", "2")]) - assert normalize_and_validate( - [("Content-Length", "0"), ("Content-Length", "0")] - ) == [(b"content-length", b"0")] - assert normalize_and_validate([("Content-Length", "0 , 0")]) == [ - (b"content-length", b"0") - ] - with pytest.raises(LocalProtocolError): - normalize_and_validate( - [("Content-Length", "1"), ("Content-Length", "1"), ("Content-Length", "2")] - ) - with pytest.raises(LocalProtocolError): - normalize_and_validate([("Content-Length", "1 , 1,2")]) - - # transfer-encoding - assert normalize_and_validate([("Transfer-Encoding", "chunked")]) == [ - (b"transfer-encoding", b"chunked") - ] - assert normalize_and_validate([("Transfer-Encoding", "cHuNkEd")]) == [ - (b"transfer-encoding", b"chunked") - ] - with pytest.raises(LocalProtocolError) as excinfo: - normalize_and_validate([("Transfer-Encoding", "gzip")]) - assert excinfo.value.error_status_hint == 501 # Not Implemented - with pytest.raises(LocalProtocolError) as excinfo: - normalize_and_validate( - [("Transfer-Encoding", "chunked"), ("Transfer-Encoding", "gzip")] - ) - assert excinfo.value.error_status_hint == 501 # Not Implemented - - -def test_get_set_comma_header() -> None: - headers = normalize_and_validate( - [ - ("Connection", "close"), - ("whatever", "something"), - ("connectiON", "fOo,, , BAR"), - ] - ) - - assert get_comma_header(headers, b"connection") == [b"close", b"foo", b"bar"] - - headers = set_comma_header(headers, b"newthing", ["a", "b"]) # type: ignore - - with pytest.raises(LocalProtocolError): - set_comma_header(headers, b"newthing", [" a", "b"]) # type: ignore - - assert headers == [ - (b"connection", b"close"), - (b"whatever", b"something"), - (b"connection", b"fOo,, , BAR"), - (b"newthing", b"a"), - (b"newthing", b"b"), - ] - - headers = set_comma_header(headers, b"whatever", ["different thing"]) # type: ignore - - assert headers == [ - (b"connection", b"close"), - (b"connection", b"fOo,, , BAR"), - (b"newthing", b"a"), - (b"newthing", b"b"), - (b"whatever", b"different thing"), - ] - - -def test_has_100_continue() -> None: - assert has_expect_100_continue( - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Expect", "100-continue")], - ) - ) - assert not has_expect_100_continue( - Request(method="GET", target="/", headers=[("Host", "example.com")]) - ) - # Case insensitive - assert has_expect_100_continue( - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Expect", "100-Continue")], - ) - ) - # Doesn't work in HTTP/1.0 - assert not has_expect_100_continue( - Request( - method="GET", - target="/", - headers=[("Host", "example.com"), ("Expect", "100-continue")], - http_version="1.0", - ) - ) diff --git a/.venv/Lib/site-packages/h11/tests/test_helpers.py b/.venv/Lib/site-packages/h11/tests/test_helpers.py deleted file mode 100644 index c329c76..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_helpers.py +++ /dev/null @@ -1,32 +0,0 @@ -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .helpers import normalize_data_events - - -def test_normalize_data_events() -> None: - assert normalize_data_events( - [ - Data(data=bytearray(b"1")), - Data(data=b"2"), - Response(status_code=200, headers=[]), # type: ignore[arg-type] - Data(data=b"3"), - Data(data=b"4"), - EndOfMessage(), - Data(data=b"5"), - Data(data=b"6"), - Data(data=b"7"), - ] - ) == [ - Data(data=b"12"), - Response(status_code=200, headers=[]), # type: ignore[arg-type] - Data(data=b"34"), - EndOfMessage(), - Data(data=b"567"), - ] diff --git a/.venv/Lib/site-packages/h11/tests/test_io.py b/.venv/Lib/site-packages/h11/tests/test_io.py deleted file mode 100644 index 2b47c0e..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_io.py +++ /dev/null @@ -1,572 +0,0 @@ -from typing import Any, Callable, Generator, List - -import pytest - -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .._headers import Headers, normalize_and_validate -from .._readers import ( - _obsolete_line_fold, - ChunkedReader, - ContentLengthReader, - Http10Reader, - READERS, -) -from .._receivebuffer import ReceiveBuffer -from .._state import ( - CLIENT, - CLOSED, - DONE, - IDLE, - MIGHT_SWITCH_PROTOCOL, - MUST_CLOSE, - SEND_BODY, - SEND_RESPONSE, - SERVER, - SWITCHED_PROTOCOL, -) -from .._util import LocalProtocolError -from .._writers import ( - ChunkedWriter, - ContentLengthWriter, - Http10Writer, - write_any_response, - write_headers, - write_request, - WRITERS, -) -from .helpers import normalize_data_events - -SIMPLE_CASES = [ - ( - (CLIENT, IDLE), - Request( - method="GET", - target="/a", - headers=[("Host", "foo"), ("Connection", "close")], - ), - b"GET /a HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n", - ), - ( - (SERVER, SEND_RESPONSE), - Response(status_code=200, headers=[("Connection", "close")], reason=b"OK"), - b"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", - ), - ( - (SERVER, SEND_RESPONSE), - Response(status_code=200, headers=[], reason=b"OK"), # type: ignore[arg-type] - b"HTTP/1.1 200 OK\r\n\r\n", - ), - ( - (SERVER, SEND_RESPONSE), - InformationalResponse( - status_code=101, headers=[("Upgrade", "websocket")], reason=b"Upgrade" - ), - b"HTTP/1.1 101 Upgrade\r\nUpgrade: websocket\r\n\r\n", - ), - ( - (SERVER, SEND_RESPONSE), - InformationalResponse(status_code=101, headers=[], reason=b"Upgrade"), # type: ignore[arg-type] - b"HTTP/1.1 101 Upgrade\r\n\r\n", - ), -] - - -def dowrite(writer: Callable[..., None], obj: Any) -> bytes: - got_list: List[bytes] = [] - writer(obj, got_list.append) - return b"".join(got_list) - - -def tw(writer: Any, obj: Any, expected: Any) -> None: - got = dowrite(writer, obj) - assert got == expected - - -def makebuf(data: bytes) -> ReceiveBuffer: - buf = ReceiveBuffer() - buf += data - return buf - - -def tr(reader: Any, data: bytes, expected: Any) -> None: - def check(got: Any) -> None: - assert got == expected - # Headers should always be returned as bytes, not e.g. bytearray - # https://github.com/python-hyper/wsproto/pull/54#issuecomment-377709478 - for name, value in getattr(got, "headers", []): - assert type(name) is bytes - assert type(value) is bytes - - # Simple: consume whole thing - buf = makebuf(data) - check(reader(buf)) - assert not buf - - # Incrementally growing buffer - buf = ReceiveBuffer() - for i in range(len(data)): - assert reader(buf) is None - buf += data[i : i + 1] - check(reader(buf)) - - # Trailing data - buf = makebuf(data) - buf += b"trailing" - check(reader(buf)) - assert bytes(buf) == b"trailing" - - -def test_writers_simple() -> None: - for ((role, state), event, binary) in SIMPLE_CASES: - tw(WRITERS[role, state], event, binary) - - -def test_readers_simple() -> None: - for ((role, state), event, binary) in SIMPLE_CASES: - tr(READERS[role, state], binary, event) - - -def test_writers_unusual() -> None: - # Simple test of the write_headers utility routine - tw( - write_headers, - normalize_and_validate([("foo", "bar"), ("baz", "quux")]), - b"foo: bar\r\nbaz: quux\r\n\r\n", - ) - tw(write_headers, Headers([]), b"\r\n") - - # We understand HTTP/1.0, but we don't speak it - with pytest.raises(LocalProtocolError): - tw( - write_request, - Request( - method="GET", - target="/", - headers=[("Host", "foo"), ("Connection", "close")], - http_version="1.0", - ), - None, - ) - with pytest.raises(LocalProtocolError): - tw( - write_any_response, - Response( - status_code=200, headers=[("Connection", "close")], http_version="1.0" - ), - None, - ) - - -def test_readers_unusual() -> None: - # Reading HTTP/1.0 - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.0\r\nSome: header\r\n\r\n", - Request( - method="HEAD", - target="/foo", - headers=[("Some", "header")], - http_version="1.0", - ), - ) - - # check no-headers, since it's only legal with HTTP/1.0 - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.0\r\n\r\n", - Request(method="HEAD", target="/foo", headers=[], http_version="1.0"), # type: ignore[arg-type] - ) - - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.0 200 OK\r\nSome: header\r\n\r\n", - Response( - status_code=200, - headers=[("Some", "header")], - http_version="1.0", - reason=b"OK", - ), - ) - - # single-character header values (actually disallowed by the ABNF in RFC - # 7230 -- this is a bug in the standard that we originally copied...) - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.0 200 OK\r\n" b"Foo: a a a a a \r\n\r\n", - Response( - status_code=200, - headers=[("Foo", "a a a a a")], - http_version="1.0", - reason=b"OK", - ), - ) - - # Empty headers -- also legal - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.0 200 OK\r\n" b"Foo:\r\n\r\n", - Response( - status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK" - ), - ) - - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.0 200 OK\r\n" b"Foo: \t \t \r\n\r\n", - Response( - status_code=200, headers=[("Foo", "")], http_version="1.0", reason=b"OK" - ), - ) - - # Tolerate broken servers that leave off the response code - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.0 200\r\n" b"Foo: bar\r\n\r\n", - Response( - status_code=200, headers=[("Foo", "bar")], http_version="1.0", reason=b"" - ), - ) - - # Tolerate headers line endings (\r\n and \n) - # \n\r\b between headers and body - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.1 200 OK\r\nSomeHeader: val\n\r\n", - Response( - status_code=200, - headers=[("SomeHeader", "val")], - http_version="1.1", - reason="OK", - ), - ) - - # delimited only with \n - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.1 200 OK\nSomeHeader1: val1\nSomeHeader2: val2\n\n", - Response( - status_code=200, - headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")], - http_version="1.1", - reason="OK", - ), - ) - - # mixed \r\n and \n - tr( - READERS[SERVER, SEND_RESPONSE], - b"HTTP/1.1 200 OK\r\nSomeHeader1: val1\nSomeHeader2: val2\n\r\n", - Response( - status_code=200, - headers=[("SomeHeader1", "val1"), ("SomeHeader2", "val2")], - http_version="1.1", - reason="OK", - ), - ) - - # obsolete line folding - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" - b"Host: example.com\r\n" - b"Some: multi-line\r\n" - b" header\r\n" - b"\tnonsense\r\n" - b" \t \t\tI guess\r\n" - b"Connection: close\r\n" - b"More-nonsense: in the\r\n" - b" last header \r\n\r\n", - Request( - method="HEAD", - target="/foo", - headers=[ - ("Host", "example.com"), - ("Some", "multi-line header nonsense I guess"), - ("Connection", "close"), - ("More-nonsense", "in the last header"), - ], - ), - ) - - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" b" folded: line\r\n\r\n", - None, - ) - - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" b"foo : line\r\n\r\n", - None, - ) - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n", - None, - ) - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" b"foo\t: line\r\n\r\n", - None, - ) - with pytest.raises(LocalProtocolError): - tr(READERS[CLIENT, IDLE], b"HEAD /foo HTTP/1.1\r\n" b": line\r\n\r\n", None) - - -def test__obsolete_line_fold_bytes() -> None: - # _obsolete_line_fold has a defensive cast to bytearray, which is - # necessary to protect against O(n^2) behavior in case anyone ever passes - # in regular bytestrings... but right now we never pass in regular - # bytestrings. so this test just exists to get some coverage on that - # defensive cast. - assert list(_obsolete_line_fold([b"aaa", b"bbb", b" ccc", b"ddd"])) == [ - b"aaa", - bytearray(b"bbb ccc"), - b"ddd", - ] - - -def _run_reader_iter( - reader: Any, buf: bytes, do_eof: bool -) -> Generator[Any, None, None]: - while True: - event = reader(buf) - if event is None: - break - yield event - # body readers have undefined behavior after returning EndOfMessage, - # because this changes the state so they don't get called again - if type(event) is EndOfMessage: - break - if do_eof: - assert not buf - yield reader.read_eof() - - -def _run_reader(*args: Any) -> List[Event]: - events = list(_run_reader_iter(*args)) - return normalize_data_events(events) - - -def t_body_reader(thunk: Any, data: bytes, expected: Any, do_eof: bool = False) -> None: - # Simple: consume whole thing - print("Test 1") - buf = makebuf(data) - assert _run_reader(thunk(), buf, do_eof) == expected - - # Incrementally growing buffer - print("Test 2") - reader = thunk() - buf = ReceiveBuffer() - events = [] - for i in range(len(data)): - events += _run_reader(reader, buf, False) - buf += data[i : i + 1] - events += _run_reader(reader, buf, do_eof) - assert normalize_data_events(events) == expected - - is_complete = any(type(event) is EndOfMessage for event in expected) - if is_complete and not do_eof: - buf = makebuf(data + b"trailing") - assert _run_reader(thunk(), buf, False) == expected - - -def test_ContentLengthReader() -> None: - t_body_reader(lambda: ContentLengthReader(0), b"", [EndOfMessage()]) - - t_body_reader( - lambda: ContentLengthReader(10), - b"0123456789", - [Data(data=b"0123456789"), EndOfMessage()], - ) - - -def test_Http10Reader() -> None: - t_body_reader(Http10Reader, b"", [EndOfMessage()], do_eof=True) - t_body_reader(Http10Reader, b"asdf", [Data(data=b"asdf")], do_eof=False) - t_body_reader( - Http10Reader, b"asdf", [Data(data=b"asdf"), EndOfMessage()], do_eof=True - ) - - -def test_ChunkedReader() -> None: - t_body_reader(ChunkedReader, b"0\r\n\r\n", [EndOfMessage()]) - - t_body_reader( - ChunkedReader, - b"0\r\nSome: header\r\n\r\n", - [EndOfMessage(headers=[("Some", "header")])], - ) - - t_body_reader( - ChunkedReader, - b"5\r\n01234\r\n" - + b"10\r\n0123456789abcdef\r\n" - + b"0\r\n" - + b"Some: header\r\n\r\n", - [ - Data(data=b"012340123456789abcdef"), - EndOfMessage(headers=[("Some", "header")]), - ], - ) - - t_body_reader( - ChunkedReader, - b"5\r\n01234\r\n" + b"10\r\n0123456789abcdef\r\n" + b"0\r\n\r\n", - [Data(data=b"012340123456789abcdef"), EndOfMessage()], - ) - - # handles upper and lowercase hex - t_body_reader( - ChunkedReader, - b"aA\r\n" + b"x" * 0xAA + b"\r\n" + b"0\r\n\r\n", - [Data(data=b"x" * 0xAA), EndOfMessage()], - ) - - # refuses arbitrarily long chunk integers - with pytest.raises(LocalProtocolError): - # Technically this is legal HTTP/1.1, but we refuse to process chunk - # sizes that don't fit into 20 characters of hex - t_body_reader(ChunkedReader, b"9" * 100 + b"\r\nxxx", [Data(data=b"xxx")]) - - # refuses garbage in the chunk count - with pytest.raises(LocalProtocolError): - t_body_reader(ChunkedReader, b"10\x00\r\nxxx", None) - - # handles (and discards) "chunk extensions" omg wtf - t_body_reader( - ChunkedReader, - b"5; hello=there\r\n" - + b"xxxxx" - + b"\r\n" - + b'0; random="junk"; some=more; canbe=lonnnnngg\r\n\r\n', - [Data(data=b"xxxxx"), EndOfMessage()], - ) - - t_body_reader( - ChunkedReader, - b"5 \r\n01234\r\n" + b"0\r\n\r\n", - [Data(data=b"01234"), EndOfMessage()], - ) - - -def test_ContentLengthWriter() -> None: - w = ContentLengthWriter(5) - assert dowrite(w, Data(data=b"123")) == b"123" - assert dowrite(w, Data(data=b"45")) == b"45" - assert dowrite(w, EndOfMessage()) == b"" - - w = ContentLengthWriter(5) - with pytest.raises(LocalProtocolError): - dowrite(w, Data(data=b"123456")) - - w = ContentLengthWriter(5) - dowrite(w, Data(data=b"123")) - with pytest.raises(LocalProtocolError): - dowrite(w, Data(data=b"456")) - - w = ContentLengthWriter(5) - dowrite(w, Data(data=b"123")) - with pytest.raises(LocalProtocolError): - dowrite(w, EndOfMessage()) - - w = ContentLengthWriter(5) - dowrite(w, Data(data=b"123")) == b"123" - dowrite(w, Data(data=b"45")) == b"45" - with pytest.raises(LocalProtocolError): - dowrite(w, EndOfMessage(headers=[("Etag", "asdf")])) - - -def test_ChunkedWriter() -> None: - w = ChunkedWriter() - assert dowrite(w, Data(data=b"aaa")) == b"3\r\naaa\r\n" - assert dowrite(w, Data(data=b"a" * 20)) == b"14\r\n" + b"a" * 20 + b"\r\n" - - assert dowrite(w, Data(data=b"")) == b"" - - assert dowrite(w, EndOfMessage()) == b"0\r\n\r\n" - - assert ( - dowrite(w, EndOfMessage(headers=[("Etag", "asdf"), ("a", "b")])) - == b"0\r\nEtag: asdf\r\na: b\r\n\r\n" - ) - - -def test_Http10Writer() -> None: - w = Http10Writer() - assert dowrite(w, Data(data=b"1234")) == b"1234" - assert dowrite(w, EndOfMessage()) == b"" - - with pytest.raises(LocalProtocolError): - dowrite(w, EndOfMessage(headers=[("Etag", "asdf")])) - - -def test_reject_garbage_after_request_line() -> None: - with pytest.raises(LocalProtocolError): - tr(READERS[SERVER, SEND_RESPONSE], b"HTTP/1.0 200 OK\x00xxxx\r\n\r\n", None) - - -def test_reject_garbage_after_response_line() -> None: - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1 xxxxxx\r\n" b"Host: a\r\n\r\n", - None, - ) - - -def test_reject_garbage_in_header_line() -> None: - with pytest.raises(LocalProtocolError): - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" b"Host: foo\x00bar\r\n\r\n", - None, - ) - - -def test_reject_non_vchar_in_path() -> None: - for bad_char in b"\x00\x20\x7f\xee": - message = bytearray(b"HEAD /") - message.append(bad_char) - message.extend(b" HTTP/1.1\r\nHost: foobar\r\n\r\n") - with pytest.raises(LocalProtocolError): - tr(READERS[CLIENT, IDLE], message, None) - - -# https://github.com/python-hyper/h11/issues/57 -def test_allow_some_garbage_in_cookies() -> None: - tr( - READERS[CLIENT, IDLE], - b"HEAD /foo HTTP/1.1\r\n" - b"Host: foo\r\n" - b"Set-Cookie: ___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900\r\n" - b"\r\n", - Request( - method="HEAD", - target="/foo", - headers=[ - ("Host", "foo"), - ("Set-Cookie", "___utmvafIumyLc=kUd\x01UpAt; path=/; Max-Age=900"), - ], - ), - ) - - -def test_host_comes_first() -> None: - tw( - write_headers, - normalize_and_validate([("foo", "bar"), ("Host", "example.com")]), - b"Host: example.com\r\nfoo: bar\r\n\r\n", - ) diff --git a/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py b/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py deleted file mode 100644 index 21a3870..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_receivebuffer.py +++ /dev/null @@ -1,135 +0,0 @@ -import re -from typing import Tuple - -import pytest - -from .._receivebuffer import ReceiveBuffer - - -def test_receivebuffer() -> None: - b = ReceiveBuffer() - assert not b - assert len(b) == 0 - assert bytes(b) == b"" - - b += b"123" - assert b - assert len(b) == 3 - assert bytes(b) == b"123" - - assert bytes(b) == b"123" - - assert b.maybe_extract_at_most(2) == b"12" - assert b - assert len(b) == 1 - assert bytes(b) == b"3" - - assert bytes(b) == b"3" - - assert b.maybe_extract_at_most(10) == b"3" - assert bytes(b) == b"" - - assert b.maybe_extract_at_most(10) is None - assert not b - - ################################################################ - # maybe_extract_until_next - ################################################################ - - b += b"123\n456\r\n789\r\n" - - assert b.maybe_extract_next_line() == b"123\n456\r\n" - assert bytes(b) == b"789\r\n" - - assert b.maybe_extract_next_line() == b"789\r\n" - assert bytes(b) == b"" - - b += b"12\r" - assert b.maybe_extract_next_line() is None - assert bytes(b) == b"12\r" - - b += b"345\n\r" - assert b.maybe_extract_next_line() is None - assert bytes(b) == b"12\r345\n\r" - - # here we stopped at the middle of b"\r\n" delimiter - - b += b"\n6789aaa123\r\n" - assert b.maybe_extract_next_line() == b"12\r345\n\r\n" - assert b.maybe_extract_next_line() == b"6789aaa123\r\n" - assert b.maybe_extract_next_line() is None - assert bytes(b) == b"" - - ################################################################ - # maybe_extract_lines - ################################################################ - - b += b"123\r\na: b\r\nfoo:bar\r\n\r\ntrailing" - lines = b.maybe_extract_lines() - assert lines == [b"123", b"a: b", b"foo:bar"] - assert bytes(b) == b"trailing" - - assert b.maybe_extract_lines() is None - - b += b"\r\n\r" - assert b.maybe_extract_lines() is None - - assert b.maybe_extract_at_most(100) == b"trailing\r\n\r" - assert not b - - # Empty body case (as happens at the end of chunked encoding if there are - # no trailing headers, e.g.) - b += b"\r\ntrailing" - assert b.maybe_extract_lines() == [] - assert bytes(b) == b"trailing" - - -@pytest.mark.parametrize( - "data", - [ - pytest.param( - ( - b"HTTP/1.1 200 OK\r\n", - b"Content-type: text/plain\r\n", - b"Connection: close\r\n", - b"\r\n", - b"Some body", - ), - id="with_crlf_delimiter", - ), - pytest.param( - ( - b"HTTP/1.1 200 OK\n", - b"Content-type: text/plain\n", - b"Connection: close\n", - b"\n", - b"Some body", - ), - id="with_lf_only_delimiter", - ), - pytest.param( - ( - b"HTTP/1.1 200 OK\n", - b"Content-type: text/plain\r\n", - b"Connection: close\n", - b"\n", - b"Some body", - ), - id="with_mixed_crlf_and_lf", - ), - ], -) -def test_receivebuffer_for_invalid_delimiter(data: Tuple[bytes]) -> None: - b = ReceiveBuffer() - - for line in data: - b += line - - lines = b.maybe_extract_lines() - - assert lines == [ - b"HTTP/1.1 200 OK", - b"Content-type: text/plain", - b"Connection: close", - ] - assert bytes(b) == b"Some body" diff --git a/.venv/Lib/site-packages/h11/tests/test_state.py b/.venv/Lib/site-packages/h11/tests/test_state.py deleted file mode 100644 index bc974e6..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_state.py +++ /dev/null @@ -1,271 +0,0 @@ -import pytest - -from .._events import ( - ConnectionClosed, - Data, - EndOfMessage, - Event, - InformationalResponse, - Request, - Response, -) -from .._state import ( - _SWITCH_CONNECT, - _SWITCH_UPGRADE, - CLIENT, - CLOSED, - ConnectionState, - DONE, - IDLE, - MIGHT_SWITCH_PROTOCOL, - MUST_CLOSE, - SEND_BODY, - SEND_RESPONSE, - SERVER, - SWITCHED_PROTOCOL, -) -from .._util import LocalProtocolError - - -def test_ConnectionState() -> None: - cs = ConnectionState() - - # Basic event-triggered transitions - - assert cs.states == {CLIENT: IDLE, SERVER: IDLE} - - cs.process_event(CLIENT, Request) - # The SERVER-Request special case: - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - # Illegal transitions raise an error and nothing happens - with pytest.raises(LocalProtocolError): - cs.process_event(CLIENT, Request) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - cs.process_event(SERVER, InformationalResponse) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - cs.process_event(SERVER, Response) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_BODY} - - cs.process_event(CLIENT, EndOfMessage) - cs.process_event(SERVER, EndOfMessage) - assert cs.states == {CLIENT: DONE, SERVER: DONE} - - # State-triggered transition - - cs.process_event(SERVER, ConnectionClosed) - assert cs.states == {CLIENT: MUST_CLOSE, SERVER: CLOSED} - - -def test_ConnectionState_keep_alive() -> None: - # keep_alive = False - cs = ConnectionState() - cs.process_event(CLIENT, Request) - cs.process_keep_alive_disabled() - cs.process_event(CLIENT, EndOfMessage) - assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_RESPONSE} - - cs.process_event(SERVER, Response) - cs.process_event(SERVER, EndOfMessage) - assert cs.states == {CLIENT: MUST_CLOSE, SERVER: MUST_CLOSE} - - -def test_ConnectionState_keep_alive_in_DONE() -> None: - # Check that if keep_alive is disabled when the CLIENT is already in DONE, - # then this is sufficient to immediately trigger the DONE -> MUST_CLOSE - # transition - cs = ConnectionState() - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - assert cs.states[CLIENT] is DONE - cs.process_keep_alive_disabled() - assert cs.states[CLIENT] is MUST_CLOSE - - -def test_ConnectionState_switch_denied() -> None: - for switch_type in (_SWITCH_CONNECT, _SWITCH_UPGRADE): - for deny_early in (True, False): - cs = ConnectionState() - cs.process_client_switch_proposal(switch_type) - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, Data) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - assert switch_type in cs.pending_switch_proposals - - if deny_early: - # before client reaches DONE - cs.process_event(SERVER, Response) - assert not cs.pending_switch_proposals - - cs.process_event(CLIENT, EndOfMessage) - - if deny_early: - assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} - else: - assert cs.states == { - CLIENT: MIGHT_SWITCH_PROTOCOL, - SERVER: SEND_RESPONSE, - } - - cs.process_event(SERVER, InformationalResponse) - assert cs.states == { - CLIENT: MIGHT_SWITCH_PROTOCOL, - SERVER: SEND_RESPONSE, - } - - cs.process_event(SERVER, Response) - assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} - assert not cs.pending_switch_proposals - - -_response_type_for_switch = { - _SWITCH_UPGRADE: InformationalResponse, - _SWITCH_CONNECT: Response, - None: Response, -} - - -def test_ConnectionState_protocol_switch_accepted() -> None: - for switch_event in [_SWITCH_UPGRADE, _SWITCH_CONNECT]: - cs = ConnectionState() - cs.process_client_switch_proposal(switch_event) - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, Data) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - cs.process_event(CLIENT, EndOfMessage) - assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} - - cs.process_event(SERVER, InformationalResponse) - assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} - - cs.process_event(SERVER, _response_type_for_switch[switch_event], switch_event) - assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} - - -def test_ConnectionState_double_protocol_switch() -> None: - # CONNECT + Upgrade is legal! Very silly, but legal. So we support - # it. Because sometimes doing the silly thing is easier than not. - for server_switch in [None, _SWITCH_UPGRADE, _SWITCH_CONNECT]: - cs = ConnectionState() - cs.process_client_switch_proposal(_SWITCH_UPGRADE) - cs.process_client_switch_proposal(_SWITCH_CONNECT) - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} - cs.process_event( - SERVER, _response_type_for_switch[server_switch], server_switch - ) - if server_switch is None: - assert cs.states == {CLIENT: DONE, SERVER: SEND_BODY} - else: - assert cs.states == {CLIENT: SWITCHED_PROTOCOL, SERVER: SWITCHED_PROTOCOL} - - -def test_ConnectionState_inconsistent_protocol_switch() -> None: - for client_switches, server_switch in [ - ([], _SWITCH_CONNECT), - ([], _SWITCH_UPGRADE), - ([_SWITCH_UPGRADE], _SWITCH_CONNECT), - ([_SWITCH_CONNECT], _SWITCH_UPGRADE), - ]: - cs = ConnectionState() - for client_switch in client_switches: # type: ignore[attr-defined] - cs.process_client_switch_proposal(client_switch) - cs.process_event(CLIENT, Request) - with pytest.raises(LocalProtocolError): - cs.process_event(SERVER, Response, server_switch) - - -def test_ConnectionState_keepalive_protocol_switch_interaction() -> None: - # keep_alive=False + pending_switch_proposals - cs = ConnectionState() - cs.process_client_switch_proposal(_SWITCH_UPGRADE) - cs.process_event(CLIENT, Request) - cs.process_keep_alive_disabled() - cs.process_event(CLIENT, Data) - assert cs.states == {CLIENT: SEND_BODY, SERVER: SEND_RESPONSE} - - # the protocol switch "wins" - cs.process_event(CLIENT, EndOfMessage) - assert cs.states == {CLIENT: MIGHT_SWITCH_PROTOCOL, SERVER: SEND_RESPONSE} - - # but when the server denies the request, keep_alive comes back into play - cs.process_event(SERVER, Response) - assert cs.states == {CLIENT: MUST_CLOSE, SERVER: SEND_BODY} - - -def test_ConnectionState_reuse() -> None: - cs = ConnectionState() - - with pytest.raises(LocalProtocolError): - cs.start_next_cycle() - - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - - with pytest.raises(LocalProtocolError): - cs.start_next_cycle() - - cs.process_event(SERVER, Response) - cs.process_event(SERVER, EndOfMessage) - - cs.start_next_cycle() - assert cs.states == {CLIENT: IDLE, SERVER: IDLE} - - # No keepalive - - cs.process_event(CLIENT, Request) - cs.process_keep_alive_disabled() - cs.process_event(CLIENT, EndOfMessage) - cs.process_event(SERVER, Response) - cs.process_event(SERVER, EndOfMessage) - - with pytest.raises(LocalProtocolError): - cs.start_next_cycle() - - # One side closed - - cs = ConnectionState() - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - cs.process_event(CLIENT, ConnectionClosed) - cs.process_event(SERVER, Response) - cs.process_event(SERVER, EndOfMessage) - - with pytest.raises(LocalProtocolError): - cs.start_next_cycle() - - # Succesful protocol switch - - cs = ConnectionState() - cs.process_client_switch_proposal(_SWITCH_UPGRADE) - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - cs.process_event(SERVER, InformationalResponse, _SWITCH_UPGRADE) - - with pytest.raises(LocalProtocolError): - cs.start_next_cycle() - - # Failed protocol switch - - cs = ConnectionState() - cs.process_client_switch_proposal(_SWITCH_UPGRADE) - cs.process_event(CLIENT, Request) - cs.process_event(CLIENT, EndOfMessage) - cs.process_event(SERVER, Response) - cs.process_event(SERVER, EndOfMessage) - - cs.start_next_cycle() - assert cs.states == {CLIENT: IDLE, SERVER: IDLE} - - -def test_server_request_is_illegal() -> None: - # There used to be a bug in how we handled the Request special case that - # made this allowed... - cs = ConnectionState() - with pytest.raises(LocalProtocolError): - cs.process_event(SERVER, Request) diff --git a/.venv/Lib/site-packages/h11/tests/test_util.py b/.venv/Lib/site-packages/h11/tests/test_util.py deleted file mode 100644 index 79bc095..0000000 --- a/.venv/Lib/site-packages/h11/tests/test_util.py +++ /dev/null @@ -1,112 +0,0 @@ -import re -import sys -import traceback -from typing import NoReturn - -import pytest - -from .._util import ( - bytesify, - LocalProtocolError, - ProtocolError, - RemoteProtocolError, - Sentinel, - validate, -) - - -def test_ProtocolError() -> None: - with pytest.raises(TypeError): - ProtocolError("abstract base class") - - -def test_LocalProtocolError() -> None: - try: - raise LocalProtocolError("foo") - except LocalProtocolError as e: - assert str(e) == "foo" - assert e.error_status_hint == 400 - - try: - raise LocalProtocolError("foo", error_status_hint=418) - except LocalProtocolError as e: - assert str(e) == "foo" - assert e.error_status_hint == 418 - - def thunk() -> NoReturn: - raise LocalProtocolError("a", error_status_hint=420) - - try: - try: - thunk() - except LocalProtocolError as exc1: - orig_traceback = "".join(traceback.format_tb(sys.exc_info()[2])) - exc1._reraise_as_remote_protocol_error() - except RemoteProtocolError as exc2: - assert type(exc2) is RemoteProtocolError - assert exc2.args == ("a",) - assert exc2.error_status_hint == 420 - new_traceback = "".join(traceback.format_tb(sys.exc_info()[2])) - assert new_traceback.endswith(orig_traceback) - - -def test_validate() -> None: - my_re = re.compile(rb"(?P[0-9]+)\.(?P[0-9]+)") - with pytest.raises(LocalProtocolError): - validate(my_re, b"0.") - - groups = validate(my_re, b"0.1") - assert groups == {"group1": b"0", "group2": b"1"} - - # successful partial matches are an error - must match whole string - with pytest.raises(LocalProtocolError): - validate(my_re, b"0.1xx") - with pytest.raises(LocalProtocolError): - validate(my_re, b"0.1\n") - - -def test_validate_formatting() -> None: - my_re = re.compile(rb"foo") - - with pytest.raises(LocalProtocolError) as excinfo: - validate(my_re, b"", "oops") - assert "oops" in str(excinfo.value) - - with pytest.raises(LocalProtocolError) as excinfo: - validate(my_re, b"", "oops {}") - assert "oops {}" in str(excinfo.value) - - with pytest.raises(LocalProtocolError) as excinfo: - validate(my_re, b"", "oops {} xx", 10) - assert "oops 10 xx" in str(excinfo.value) - - -def test_make_sentinel() -> None: - class S(Sentinel, metaclass=Sentinel): - pass - - assert repr(S) == "S" - assert S == S - assert type(S).__name__ == "S" - assert S in {S} - assert type(S) is S - - class S2(Sentinel, metaclass=Sentinel): - pass - - assert repr(S2) == "S2" - assert S != S2 - assert S not in {S2} - assert type(S) is not type(S2) - - -def test_bytesify() -> None: - assert bytesify(b"123") == b"123" - assert bytesify(bytearray(b"123")) == b"123" - assert bytesify("123") == b"123" - - with pytest.raises(UnicodeEncodeError): - bytesify("\u1234") - - with pytest.raises(TypeError): - bytesify(10) diff --git a/.venv/Lib/site-packages/idna-3.10.dist-info/INSTALLER b/.venv/Lib/site-packages/idna-3.10.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/idna-3.10.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/idna-3.10.dist-info/LICENSE.md b/.venv/Lib/site-packages/idna-3.10.dist-info/LICENSE.md deleted file mode 100644 index 19b6b45..0000000 --- a/.venv/Lib/site-packages/idna-3.10.dist-info/LICENSE.md +++ /dev/null @@ -1,31 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2013-2024, Kim Davies and contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/.venv/Lib/site-packages/idna-3.10.dist-info/METADATA b/.venv/Lib/site-packages/idna-3.10.dist-info/METADATA deleted file mode 100644 index c42623e..0000000 --- a/.venv/Lib/site-packages/idna-3.10.dist-info/METADATA +++ /dev/null @@ -1,250 +0,0 @@ -Metadata-Version: 2.1 -Name: idna -Version: 3.10 -Summary: Internationalized Domain Names in Applications (IDNA) -Author-email: Kim Davies -Requires-Python: >=3.6 -Description-Content-Type: text/x-rst -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Internet :: Name Service (DNS) -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Utilities -Requires-Dist: ruff >= 0.6.2 ; extra == "all" -Requires-Dist: mypy >= 1.11.2 ; extra == "all" -Requires-Dist: pytest >= 8.3.2 ; extra == "all" -Requires-Dist: flake8 >= 7.1.1 ; extra == "all" -Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst -Project-URL: Issue tracker, https://github.com/kjd/idna/issues -Project-URL: Source, https://github.com/kjd/idna -Provides-Extra: all - -Internationalized Domain Names in Applications (IDNA) -===================================================== - -Support for the Internationalized Domain Names in -Applications (IDNA) protocol as specified in `RFC 5891 -`_. This is the latest version of -the protocol and is sometimes referred to as “IDNA 2008”. - -This library also provides support for Unicode Technical -Standard 46, `Unicode IDNA Compatibility Processing -`_. - -This acts as a suitable replacement for the “encodings.idna” -module that comes with the Python standard library, but which -only supports the older superseded IDNA specification (`RFC 3490 -`_). - -Basic functions are simply executed: - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - - -Installation ------------- - -This package is available for installation from PyPI: - -.. code-block:: bash - - $ python3 -m pip install idna - - -Usage ------ - -For typical usage, the ``encode`` and ``decode`` functions will take a -domain name argument and perform a conversion to A-labels or U-labels -respectively. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - -You may use the codec encoding and decoding methods using the -``idna.codec`` module: - -.. code-block:: pycon - - >>> import idna.codec - >>> print('домен.испытание'.encode('idna2008')) - b'xn--d1acufc.xn--80akhbyknj4f' - >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) - домен.испытание - -Conversions can be applied at a per-label basis using the ``ulabel`` or -``alabel`` functions if necessary: - -.. code-block:: pycon - - >>> idna.alabel('测试') - b'xn--0zwm56d' - -Compatibility Mapping (UTS #46) -+++++++++++++++++++++++++++++++ - -As described in `RFC 5895 `_, the -IDNA specification does not normalize input from different potential -ways a user may input a domain name. This functionality, known as -a “mapping”, is considered by the specification to be a local -user-interface issue distinct from IDNA conversion functionality. - -This library provides one such mapping that was developed by the -Unicode Consortium. Known as `Unicode IDNA Compatibility Processing -`_, it provides for both a regular -mapping for typical applications, as well as a transitional mapping to -help migrate from older IDNA 2003 applications. Strings are -preprocessed according to Section 4.4 “Preprocessing for IDNA2008” -prior to the IDNA operations. - -For example, “Königsgäßchen” is not a permissible label as *LATIN -CAPITAL LETTER K* is not allowed (nor are capital letters in general). -UTS 46 will convert this into lower case prior to applying the IDNA -conversion. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('Königsgäßchen') - ... - idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed - >>> idna.encode('Königsgäßchen', uts46=True) - b'xn--knigsgchen-b4a3dun' - >>> print(idna.decode('xn--knigsgchen-b4a3dun')) - königsgäßchen - -Transitional processing provides conversions to help transition from -the older 2003 standard to the current standard. For example, in the -original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was -converted into two *LATIN SMALL LETTER S* (ss), whereas in the current -IDNA specification this conversion is not performed. - -.. code-block:: pycon - - >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) - 'xn--knigsgsschen-lcb0w' - -Implementers should use transitional processing with caution, only in -rare cases where conversion from legacy labels to current labels must be -performed (i.e. IDNA implementations that pre-date 2008). For typical -applications that just need to convert labels, transitional processing -is unlikely to be beneficial and could produce unexpected incompatible -results. - -``encodings.idna`` Compatibility -++++++++++++++++++++++++++++++++ - -Function calls from the Python built-in ``encodings.idna`` module are -mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. -Simply substitute the ``import`` clause in your code to refer to the new -module name. - -Exceptions ----------- - -All errors raised during the conversion following the specification -should raise an exception derived from the ``idna.IDNAError`` base -class. - -More specific exceptions that may be generated as ``idna.IDNABidiError`` -when the error reflects an illegal combination of left-to-right and -right-to-left characters in a label; ``idna.InvalidCodepoint`` when -a specific codepoint is an illegal character in an IDN label (i.e. -INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is -illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ -but the contextual requirements are not satisfied.) - -Building and Diagnostics ------------------------- - -The IDNA and UTS 46 functionality relies upon pre-calculated lookup -tables for performance. These tables are derived from computing against -eligibility criteria in the respective standards. These tables are -computed using the command-line script ``tools/idna-data``. - -This tool will fetch relevant codepoint data from the Unicode repository -and perform the required calculations to identify eligibility. There are -three main modes: - -* ``idna-data make-libdata``. Generates ``idnadata.py`` and - ``uts46data.py``, the pre-calculated lookup tables used for IDNA and - UTS 46 conversions. Implementers who wish to track this library against - a different Unicode version may use this tool to manually generate a - different version of the ``idnadata.py`` and ``uts46data.py`` files. - -* ``idna-data make-table``. Generate a table of the IDNA disposition - (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix - B.1 of RFC 5892 and the pre-computed tables published by `IANA - `_. - -* ``idna-data U+0061``. Prints debugging output on the various - properties associated with an individual Unicode codepoint (in this - case, U+0061), that are used to assess the IDNA and UTS 46 status of a - codepoint. This is helpful in debugging or analysis. - -The tool accepts a number of arguments, described using ``idna-data --h``. Most notably, the ``--version`` argument allows the specification -of the version of Unicode to be used in computing the table data. For -example, ``idna-data --version 9.0.0 make-libdata`` will generate -library data against Unicode 9.0.0. - - -Additional Notes ----------------- - -* **Packages**. The latest tagged release version is published in the - `Python Package Index `_. - -* **Version support**. This library supports Python 3.6 and higher. - As this library serves as a low-level toolkit for a variety of - applications, many of which strive for broad compatibility with older - Python versions, there is no rush to remove older interpreter support. - Removing support for older versions should be well justified in that the - maintenance burden has become too high. - -* **Python 2**. Python 2 is supported by version 2.x of this library. - Use "idna<3" in your requirements file if you need this library for - a Python 2 application. Be advised that these versions are no longer - actively developed. - -* **Testing**. The library has a test suite based on each rule of the - IDNA specification, as well as tests that are provided as part of the - Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing - `_. - -* **Emoji**. It is an occasional request to support emoji domains in - this library. Encoding of symbols like emoji is expressly prohibited by - the technical standard IDNA 2008 and emoji domains are broadly phased - out across the domain industry due to associated security risks. For - now, applications that need to support these non-compliant labels - may wish to consider trying the encode/decode operation in this library - first, and then falling back to using `encodings.idna`. See `the Github - project `_ for more discussion. - diff --git a/.venv/Lib/site-packages/idna-3.10.dist-info/RECORD b/.venv/Lib/site-packages/idna-3.10.dist-info/RECORD deleted file mode 100644 index 9ef9fa8..0000000 --- a/.venv/Lib/site-packages/idna-3.10.dist-info/RECORD +++ /dev/null @@ -1,22 +0,0 @@ -idna-3.10.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -idna-3.10.dist-info/LICENSE.md,sha256=pZ8LDvNjWHQQmkRhykT_enDVBpboFHZ7-vch1Mmw2w8,1541 -idna-3.10.dist-info/METADATA,sha256=URR5ZyDfQ1PCEGhkYoojqfi2Ra0tau2--lhwG4XSfjI,10158 -idna-3.10.dist-info/RECORD,, -idna-3.10.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 -idna/__pycache__/__init__.cpython-311.pyc,, -idna/__pycache__/codec.cpython-311.pyc,, -idna/__pycache__/compat.cpython-311.pyc,, -idna/__pycache__/core.cpython-311.pyc,, -idna/__pycache__/idnadata.cpython-311.pyc,, -idna/__pycache__/intranges.cpython-311.pyc,, -idna/__pycache__/package_data.cpython-311.pyc,, -idna/__pycache__/uts46data.cpython-311.pyc,, -idna/codec.py,sha256=PEew3ItwzjW4hymbasnty2N2OXvNcgHB-JjrBuxHPYY,3422 -idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 -idna/core.py,sha256=YJYyAMnwiQEPjVC4-Fqu_p4CJ6yKKuDGmppBNQNQpFs,13239 -idna/idnadata.py,sha256=W30GcIGvtOWYwAjZj4ZjuouUutC6ffgNuyjJy7fZ-lo,78306 -idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 -idna/package_data.py,sha256=q59S3OXsc5VI8j6vSD0sGBMyk6zZ4vWFREE88yCJYKs,21 -idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -idna/uts46data.py,sha256=rt90K9J40gUSwppDPCrhjgi5AA6pWM65dEGRSf6rIhM,239289 diff --git a/.venv/Lib/site-packages/idna-3.10.dist-info/WHEEL b/.venv/Lib/site-packages/idna-3.10.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/.venv/Lib/site-packages/idna-3.10.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/Lib/site-packages/idna/__init__.py b/.venv/Lib/site-packages/idna/__init__.py deleted file mode 100644 index cfdc030..0000000 --- a/.venv/Lib/site-packages/idna/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -from .core import ( - IDNABidiError, - IDNAError, - InvalidCodepoint, - InvalidCodepointContext, - alabel, - check_bidi, - check_hyphen_ok, - check_initial_combiner, - check_label, - check_nfc, - decode, - encode, - ulabel, - uts46_remap, - valid_contextj, - valid_contexto, - valid_label_length, - valid_string_length, -) -from .intranges import intranges_contain -from .package_data import __version__ - -__all__ = [ - "__version__", - "IDNABidiError", - "IDNAError", - "InvalidCodepoint", - "InvalidCodepointContext", - "alabel", - "check_bidi", - "check_hyphen_ok", - "check_initial_combiner", - "check_label", - "check_nfc", - "decode", - "encode", - "intranges_contain", - "ulabel", - "uts46_remap", - "valid_contextj", - "valid_contexto", - "valid_label_length", - "valid_string_length", -] diff --git a/.venv/Lib/site-packages/idna/codec.py b/.venv/Lib/site-packages/idna/codec.py deleted file mode 100644 index 913abfd..0000000 --- a/.venv/Lib/site-packages/idna/codec.py +++ /dev/null @@ -1,122 +0,0 @@ -import codecs -import re -from typing import Any, Optional, Tuple - -from .core import IDNAError, alabel, decode, encode, ulabel - -_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") - - -class Codec(codecs.Codec): - def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return b"", 0 - - return encode(data), len(data) - - def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return "", 0 - - return decode(data), len(data) - - -class IncrementalEncoder(codecs.BufferedIncrementalEncoder): - def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return b"", 0 - - labels = _unicode_dots_re.split(data) - trailing_dot = b"" - if labels: - if not labels[-1]: - trailing_dot = b"." - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = b"." - - result = [] - size = 0 - for label in labels: - result.append(alabel(label)) - if size: - size += 1 - size += len(label) - - # Join with U+002E - result_bytes = b".".join(result) + trailing_dot - size += len(trailing_dot) - return result_bytes, size - - -class IncrementalDecoder(codecs.BufferedIncrementalDecoder): - def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: - if errors != "strict": - raise IDNAError('Unsupported error handling "{}"'.format(errors)) - - if not data: - return ("", 0) - - if not isinstance(data, str): - data = str(data, "ascii") - - labels = _unicode_dots_re.split(data) - trailing_dot = "" - if labels: - if not labels[-1]: - trailing_dot = "." - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = "." - - result = [] - size = 0 - for label in labels: - result.append(ulabel(label)) - if size: - size += 1 - size += len(label) - - result_str = ".".join(result) + trailing_dot - size += len(trailing_dot) - return (result_str, size) - - -class StreamWriter(Codec, codecs.StreamWriter): - pass - - -class StreamReader(Codec, codecs.StreamReader): - pass - - -def search_function(name: str) -> Optional[codecs.CodecInfo]: - if name != "idna2008": - return None - return codecs.CodecInfo( - name=name, - encode=Codec().encode, - decode=Codec().decode, - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamwriter=StreamWriter, - streamreader=StreamReader, - ) - - -codecs.register(search_function) diff --git a/.venv/Lib/site-packages/idna/compat.py b/.venv/Lib/site-packages/idna/compat.py deleted file mode 100644 index 1df9f2a..0000000 --- a/.venv/Lib/site-packages/idna/compat.py +++ /dev/null @@ -1,15 +0,0 @@ -from typing import Any, Union - -from .core import decode, encode - - -def ToASCII(label: str) -> bytes: - return encode(label) - - -def ToUnicode(label: Union[bytes, bytearray]) -> str: - return decode(label) - - -def nameprep(s: Any) -> None: - raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") diff --git a/.venv/Lib/site-packages/idna/core.py b/.venv/Lib/site-packages/idna/core.py deleted file mode 100644 index 9115f12..0000000 --- a/.venv/Lib/site-packages/idna/core.py +++ /dev/null @@ -1,437 +0,0 @@ -import bisect -import re -import unicodedata -from typing import Optional, Union - -from . import idnadata -from .intranges import intranges_contain - -_virama_combining_class = 9 -_alabel_prefix = b"xn--" -_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") - - -class IDNAError(UnicodeError): - """Base exception for all IDNA-encoding related problems""" - - pass - - -class IDNABidiError(IDNAError): - """Exception when bidirectional requirements are not satisfied""" - - pass - - -class InvalidCodepoint(IDNAError): - """Exception when a disallowed or unallocated codepoint is used""" - - pass - - -class InvalidCodepointContext(IDNAError): - """Exception when the codepoint is not valid in the context it is used""" - - pass - - -def _combining_class(cp: int) -> int: - v = unicodedata.combining(chr(cp)) - if v == 0: - if not unicodedata.name(chr(cp)): - raise ValueError("Unknown character in unicodedata") - return v - - -def _is_script(cp: str, script: str) -> bool: - return intranges_contain(ord(cp), idnadata.scripts[script]) - - -def _punycode(s: str) -> bytes: - return s.encode("punycode") - - -def _unot(s: int) -> str: - return "U+{:04X}".format(s) - - -def valid_label_length(label: Union[bytes, str]) -> bool: - if len(label) > 63: - return False - return True - - -def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: - if len(label) > (254 if trailing_dot else 253): - return False - return True - - -def check_bidi(label: str, check_ltr: bool = False) -> bool: - # Bidi rules should only be applied if string contains RTL characters - bidi_label = False - for idx, cp in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - if direction == "": - # String likely comes from a newer version of Unicode - raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx)) - if direction in ["R", "AL", "AN"]: - bidi_label = True - if not bidi_label and not check_ltr: - return True - - # Bidi rule 1 - direction = unicodedata.bidirectional(label[0]) - if direction in ["R", "AL"]: - rtl = True - elif direction == "L": - rtl = False - else: - raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label))) - - valid_ending = False - number_type: Optional[str] = None - for idx, cp in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - - if rtl: - # Bidi rule 2 - if direction not in [ - "R", - "AL", - "AN", - "EN", - "ES", - "CS", - "ET", - "ON", - "BN", - "NSM", - ]: - raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx)) - # Bidi rule 3 - if direction in ["R", "AL", "EN", "AN"]: - valid_ending = True - elif direction != "NSM": - valid_ending = False - # Bidi rule 4 - if direction in ["AN", "EN"]: - if not number_type: - number_type = direction - else: - if number_type != direction: - raise IDNABidiError("Can not mix numeral types in a right-to-left label") - else: - # Bidi rule 5 - if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]: - raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx)) - # Bidi rule 6 - if direction in ["L", "EN"]: - valid_ending = True - elif direction != "NSM": - valid_ending = False - - if not valid_ending: - raise IDNABidiError("Label ends with illegal codepoint directionality") - - return True - - -def check_initial_combiner(label: str) -> bool: - if unicodedata.category(label[0])[0] == "M": - raise IDNAError("Label begins with an illegal combining character") - return True - - -def check_hyphen_ok(label: str) -> bool: - if label[2:4] == "--": - raise IDNAError("Label has disallowed hyphens in 3rd and 4th position") - if label[0] == "-" or label[-1] == "-": - raise IDNAError("Label must not start or end with a hyphen") - return True - - -def check_nfc(label: str) -> None: - if unicodedata.normalize("NFC", label) != label: - raise IDNAError("Label must be in Normalization Form C") - - -def valid_contextj(label: str, pos: int) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x200C: - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - - ok = False - for i in range(pos - 1, -1, -1): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord("T"): - continue - elif joining_type in [ord("L"), ord("D")]: - ok = True - break - else: - break - - if not ok: - return False - - ok = False - for i in range(pos + 1, len(label)): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord("T"): - continue - elif joining_type in [ord("R"), ord("D")]: - ok = True - break - else: - break - return ok - - if cp_value == 0x200D: - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - return False - - else: - return False - - -def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x00B7: - if 0 < pos < len(label) - 1: - if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C: - return True - return False - - elif cp_value == 0x0375: - if pos < len(label) - 1 and len(label) > 1: - return _is_script(label[pos + 1], "Greek") - return False - - elif cp_value == 0x05F3 or cp_value == 0x05F4: - if pos > 0: - return _is_script(label[pos - 1], "Hebrew") - return False - - elif cp_value == 0x30FB: - for cp in label: - if cp == "\u30fb": - continue - if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"): - return True - return False - - elif 0x660 <= cp_value <= 0x669: - for cp in label: - if 0x6F0 <= ord(cp) <= 0x06F9: - return False - return True - - elif 0x6F0 <= cp_value <= 0x6F9: - for cp in label: - if 0x660 <= ord(cp) <= 0x0669: - return False - return True - - return False - - -def check_label(label: Union[str, bytes, bytearray]) -> None: - if isinstance(label, (bytes, bytearray)): - label = label.decode("utf-8") - if len(label) == 0: - raise IDNAError("Empty Label") - - check_nfc(label) - check_hyphen_ok(label) - check_initial_combiner(label) - - for pos, cp in enumerate(label): - cp_value = ord(cp) - if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]): - continue - elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]): - try: - if not valid_contextj(label, pos): - raise InvalidCodepointContext( - "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) - ) - except ValueError: - raise IDNAError( - "Unknown codepoint adjacent to joiner {} at position {} in {}".format( - _unot(cp_value), pos + 1, repr(label) - ) - ) - elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]): - if not valid_contexto(label, pos): - raise InvalidCodepointContext( - "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) - ) - else: - raise InvalidCodepoint( - "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label)) - ) - - check_bidi(label) - - -def alabel(label: str) -> bytes: - try: - label_bytes = label.encode("ascii") - ulabel(label_bytes) - if not valid_label_length(label_bytes): - raise IDNAError("Label too long") - return label_bytes - except UnicodeEncodeError: - pass - - check_label(label) - label_bytes = _alabel_prefix + _punycode(label) - - if not valid_label_length(label_bytes): - raise IDNAError("Label too long") - - return label_bytes - - -def ulabel(label: Union[str, bytes, bytearray]) -> str: - if not isinstance(label, (bytes, bytearray)): - try: - label_bytes = label.encode("ascii") - except UnicodeEncodeError: - check_label(label) - return label - else: - label_bytes = label - - label_bytes = label_bytes.lower() - if label_bytes.startswith(_alabel_prefix): - label_bytes = label_bytes[len(_alabel_prefix) :] - if not label_bytes: - raise IDNAError("Malformed A-label, no Punycode eligible content found") - if label_bytes.decode("ascii")[-1] == "-": - raise IDNAError("A-label must not end with a hyphen") - else: - check_label(label_bytes) - return label_bytes.decode("ascii") - - try: - label = label_bytes.decode("punycode") - except UnicodeError: - raise IDNAError("Invalid A-label") - check_label(label) - return label - - -def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: - """Re-map the characters in the string according to UTS46 processing.""" - from .uts46data import uts46data - - output = "" - - for pos, char in enumerate(domain): - code_point = ord(char) - try: - uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1] - status = uts46row[1] - replacement: Optional[str] = None - if len(uts46row) == 3: - replacement = uts46row[2] - if ( - status == "V" - or (status == "D" and not transitional) - or (status == "3" and not std3_rules and replacement is None) - ): - output += char - elif replacement is not None and ( - status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional) - ): - output += replacement - elif status != "I": - raise IndexError() - except IndexError: - raise InvalidCodepoint( - "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain)) - ) - - return unicodedata.normalize("NFC", output) - - -def encode( - s: Union[str, bytes, bytearray], - strict: bool = False, - uts46: bool = False, - std3_rules: bool = False, - transitional: bool = False, -) -> bytes: - if not isinstance(s, str): - try: - s = str(s, "ascii") - except UnicodeDecodeError: - raise IDNAError("should pass a unicode string to the function rather than a byte string.") - if uts46: - s = uts46_remap(s, std3_rules, transitional) - trailing_dot = False - result = [] - if strict: - labels = s.split(".") - else: - labels = _unicode_dots_re.split(s) - if not labels or labels == [""]: - raise IDNAError("Empty domain") - if labels[-1] == "": - del labels[-1] - trailing_dot = True - for label in labels: - s = alabel(label) - if s: - result.append(s) - else: - raise IDNAError("Empty label") - if trailing_dot: - result.append(b"") - s = b".".join(result) - if not valid_string_length(s, trailing_dot): - raise IDNAError("Domain too long") - return s - - -def decode( - s: Union[str, bytes, bytearray], - strict: bool = False, - uts46: bool = False, - std3_rules: bool = False, -) -> str: - try: - if not isinstance(s, str): - s = str(s, "ascii") - except UnicodeDecodeError: - raise IDNAError("Invalid ASCII in A-label") - if uts46: - s = uts46_remap(s, std3_rules, False) - trailing_dot = False - result = [] - if not strict: - labels = _unicode_dots_re.split(s) - else: - labels = s.split(".") - if not labels or labels == [""]: - raise IDNAError("Empty domain") - if not labels[-1]: - del labels[-1] - trailing_dot = True - for label in labels: - s = ulabel(label) - if s: - result.append(s) - else: - raise IDNAError("Empty label") - if trailing_dot: - result.append("") - return ".".join(result) diff --git a/.venv/Lib/site-packages/idna/idnadata.py b/.venv/Lib/site-packages/idna/idnadata.py deleted file mode 100644 index 4be6004..0000000 --- a/.venv/Lib/site-packages/idna/idnadata.py +++ /dev/null @@ -1,4243 +0,0 @@ -# This file is automatically generated by tools/idna-data - -__version__ = "15.1.0" -scripts = { - "Greek": ( - 0x37000000374, - 0x37500000378, - 0x37A0000037E, - 0x37F00000380, - 0x38400000385, - 0x38600000387, - 0x3880000038B, - 0x38C0000038D, - 0x38E000003A2, - 0x3A3000003E2, - 0x3F000000400, - 0x1D2600001D2B, - 0x1D5D00001D62, - 0x1D6600001D6B, - 0x1DBF00001DC0, - 0x1F0000001F16, - 0x1F1800001F1E, - 0x1F2000001F46, - 0x1F4800001F4E, - 0x1F5000001F58, - 0x1F5900001F5A, - 0x1F5B00001F5C, - 0x1F5D00001F5E, - 0x1F5F00001F7E, - 0x1F8000001FB5, - 0x1FB600001FC5, - 0x1FC600001FD4, - 0x1FD600001FDC, - 0x1FDD00001FF0, - 0x1FF200001FF5, - 0x1FF600001FFF, - 0x212600002127, - 0xAB650000AB66, - 0x101400001018F, - 0x101A0000101A1, - 0x1D2000001D246, - ), - "Han": ( - 0x2E8000002E9A, - 0x2E9B00002EF4, - 0x2F0000002FD6, - 0x300500003006, - 0x300700003008, - 0x30210000302A, - 0x30380000303C, - 0x340000004DC0, - 0x4E000000A000, - 0xF9000000FA6E, - 0xFA700000FADA, - 0x16FE200016FE4, - 0x16FF000016FF2, - 0x200000002A6E0, - 0x2A7000002B73A, - 0x2B7400002B81E, - 0x2B8200002CEA2, - 0x2CEB00002EBE1, - 0x2EBF00002EE5E, - 0x2F8000002FA1E, - 0x300000003134B, - 0x31350000323B0, - ), - "Hebrew": ( - 0x591000005C8, - 0x5D0000005EB, - 0x5EF000005F5, - 0xFB1D0000FB37, - 0xFB380000FB3D, - 0xFB3E0000FB3F, - 0xFB400000FB42, - 0xFB430000FB45, - 0xFB460000FB50, - ), - "Hiragana": ( - 0x304100003097, - 0x309D000030A0, - 0x1B0010001B120, - 0x1B1320001B133, - 0x1B1500001B153, - 0x1F2000001F201, - ), - "Katakana": ( - 0x30A1000030FB, - 0x30FD00003100, - 0x31F000003200, - 0x32D0000032FF, - 0x330000003358, - 0xFF660000FF70, - 0xFF710000FF9E, - 0x1AFF00001AFF4, - 0x1AFF50001AFFC, - 0x1AFFD0001AFFF, - 0x1B0000001B001, - 0x1B1200001B123, - 0x1B1550001B156, - 0x1B1640001B168, - ), -} -joining_types = { - 0xAD: 84, - 0x300: 84, - 0x301: 84, - 0x302: 84, - 0x303: 84, - 0x304: 84, - 0x305: 84, - 0x306: 84, - 0x307: 84, - 0x308: 84, - 0x309: 84, - 0x30A: 84, - 0x30B: 84, - 0x30C: 84, - 0x30D: 84, - 0x30E: 84, - 0x30F: 84, - 0x310: 84, - 0x311: 84, - 0x312: 84, - 0x313: 84, - 0x314: 84, - 0x315: 84, - 0x316: 84, - 0x317: 84, - 0x318: 84, - 0x319: 84, - 0x31A: 84, - 0x31B: 84, - 0x31C: 84, - 0x31D: 84, - 0x31E: 84, - 0x31F: 84, - 0x320: 84, - 0x321: 84, - 0x322: 84, - 0x323: 84, - 0x324: 84, - 0x325: 84, - 0x326: 84, - 0x327: 84, - 0x328: 84, - 0x329: 84, - 0x32A: 84, - 0x32B: 84, - 0x32C: 84, - 0x32D: 84, - 0x32E: 84, - 0x32F: 84, - 0x330: 84, - 0x331: 84, - 0x332: 84, - 0x333: 84, - 0x334: 84, - 0x335: 84, - 0x336: 84, - 0x337: 84, - 0x338: 84, - 0x339: 84, - 0x33A: 84, - 0x33B: 84, - 0x33C: 84, - 0x33D: 84, - 0x33E: 84, - 0x33F: 84, - 0x340: 84, - 0x341: 84, - 0x342: 84, - 0x343: 84, - 0x344: 84, - 0x345: 84, - 0x346: 84, - 0x347: 84, - 0x348: 84, - 0x349: 84, - 0x34A: 84, - 0x34B: 84, - 0x34C: 84, - 0x34D: 84, - 0x34E: 84, - 0x34F: 84, - 0x350: 84, - 0x351: 84, - 0x352: 84, - 0x353: 84, - 0x354: 84, - 0x355: 84, - 0x356: 84, - 0x357: 84, - 0x358: 84, - 0x359: 84, - 0x35A: 84, - 0x35B: 84, - 0x35C: 84, - 0x35D: 84, - 0x35E: 84, - 0x35F: 84, - 0x360: 84, - 0x361: 84, - 0x362: 84, - 0x363: 84, - 0x364: 84, - 0x365: 84, - 0x366: 84, - 0x367: 84, - 0x368: 84, - 0x369: 84, - 0x36A: 84, - 0x36B: 84, - 0x36C: 84, - 0x36D: 84, - 0x36E: 84, - 0x36F: 84, - 0x483: 84, - 0x484: 84, - 0x485: 84, - 0x486: 84, - 0x487: 84, - 0x488: 84, - 0x489: 84, - 0x591: 84, - 0x592: 84, - 0x593: 84, - 0x594: 84, - 0x595: 84, - 0x596: 84, - 0x597: 84, - 0x598: 84, - 0x599: 84, - 0x59A: 84, - 0x59B: 84, - 0x59C: 84, - 0x59D: 84, - 0x59E: 84, - 0x59F: 84, - 0x5A0: 84, - 0x5A1: 84, - 0x5A2: 84, - 0x5A3: 84, - 0x5A4: 84, - 0x5A5: 84, - 0x5A6: 84, - 0x5A7: 84, - 0x5A8: 84, - 0x5A9: 84, - 0x5AA: 84, - 0x5AB: 84, - 0x5AC: 84, - 0x5AD: 84, - 0x5AE: 84, - 0x5AF: 84, - 0x5B0: 84, - 0x5B1: 84, - 0x5B2: 84, - 0x5B3: 84, - 0x5B4: 84, - 0x5B5: 84, - 0x5B6: 84, - 0x5B7: 84, - 0x5B8: 84, - 0x5B9: 84, - 0x5BA: 84, - 0x5BB: 84, - 0x5BC: 84, - 0x5BD: 84, - 0x5BF: 84, - 0x5C1: 84, - 0x5C2: 84, - 0x5C4: 84, - 0x5C5: 84, - 0x5C7: 84, - 0x610: 84, - 0x611: 84, - 0x612: 84, - 0x613: 84, - 0x614: 84, - 0x615: 84, - 0x616: 84, - 0x617: 84, - 0x618: 84, - 0x619: 84, - 0x61A: 84, - 0x61C: 84, - 0x620: 68, - 0x622: 82, - 0x623: 82, - 0x624: 82, - 0x625: 82, - 0x626: 68, - 0x627: 82, - 0x628: 68, - 0x629: 82, - 0x62A: 68, - 0x62B: 68, - 0x62C: 68, - 0x62D: 68, - 0x62E: 68, - 0x62F: 82, - 0x630: 82, - 0x631: 82, - 0x632: 82, - 0x633: 68, - 0x634: 68, - 0x635: 68, - 0x636: 68, - 0x637: 68, - 0x638: 68, - 0x639: 68, - 0x63A: 68, - 0x63B: 68, - 0x63C: 68, - 0x63D: 68, - 0x63E: 68, - 0x63F: 68, - 0x640: 67, - 0x641: 68, - 0x642: 68, - 0x643: 68, - 0x644: 68, - 0x645: 68, - 0x646: 68, - 0x647: 68, - 0x648: 82, - 0x649: 68, - 0x64A: 68, - 0x64B: 84, - 0x64C: 84, - 0x64D: 84, - 0x64E: 84, - 0x64F: 84, - 0x650: 84, - 0x651: 84, - 0x652: 84, - 0x653: 84, - 0x654: 84, - 0x655: 84, - 0x656: 84, - 0x657: 84, - 0x658: 84, - 0x659: 84, - 0x65A: 84, - 0x65B: 84, - 0x65C: 84, - 0x65D: 84, - 0x65E: 84, - 0x65F: 84, - 0x66E: 68, - 0x66F: 68, - 0x670: 84, - 0x671: 82, - 0x672: 82, - 0x673: 82, - 0x675: 82, - 0x676: 82, - 0x677: 82, - 0x678: 68, - 0x679: 68, - 0x67A: 68, - 0x67B: 68, - 0x67C: 68, - 0x67D: 68, - 0x67E: 68, - 0x67F: 68, - 0x680: 68, - 0x681: 68, - 0x682: 68, - 0x683: 68, - 0x684: 68, - 0x685: 68, - 0x686: 68, - 0x687: 68, - 0x688: 82, - 0x689: 82, - 0x68A: 82, - 0x68B: 82, - 0x68C: 82, - 0x68D: 82, - 0x68E: 82, - 0x68F: 82, - 0x690: 82, - 0x691: 82, - 0x692: 82, - 0x693: 82, - 0x694: 82, - 0x695: 82, - 0x696: 82, - 0x697: 82, - 0x698: 82, - 0x699: 82, - 0x69A: 68, - 0x69B: 68, - 0x69C: 68, - 0x69D: 68, - 0x69E: 68, - 0x69F: 68, - 0x6A0: 68, - 0x6A1: 68, - 0x6A2: 68, - 0x6A3: 68, - 0x6A4: 68, - 0x6A5: 68, - 0x6A6: 68, - 0x6A7: 68, - 0x6A8: 68, - 0x6A9: 68, - 0x6AA: 68, - 0x6AB: 68, - 0x6AC: 68, - 0x6AD: 68, - 0x6AE: 68, - 0x6AF: 68, - 0x6B0: 68, - 0x6B1: 68, - 0x6B2: 68, - 0x6B3: 68, - 0x6B4: 68, - 0x6B5: 68, - 0x6B6: 68, - 0x6B7: 68, - 0x6B8: 68, - 0x6B9: 68, - 0x6BA: 68, - 0x6BB: 68, - 0x6BC: 68, - 0x6BD: 68, - 0x6BE: 68, - 0x6BF: 68, - 0x6C0: 82, - 0x6C1: 68, - 0x6C2: 68, - 0x6C3: 82, - 0x6C4: 82, - 0x6C5: 82, - 0x6C6: 82, - 0x6C7: 82, - 0x6C8: 82, - 0x6C9: 82, - 0x6CA: 82, - 0x6CB: 82, - 0x6CC: 68, - 0x6CD: 82, - 0x6CE: 68, - 0x6CF: 82, - 0x6D0: 68, - 0x6D1: 68, - 0x6D2: 82, - 0x6D3: 82, - 0x6D5: 82, - 0x6D6: 84, - 0x6D7: 84, - 0x6D8: 84, - 0x6D9: 84, - 0x6DA: 84, - 0x6DB: 84, - 0x6DC: 84, - 0x6DF: 84, - 0x6E0: 84, - 0x6E1: 84, - 0x6E2: 84, - 0x6E3: 84, - 0x6E4: 84, - 0x6E7: 84, - 0x6E8: 84, - 0x6EA: 84, - 0x6EB: 84, - 0x6EC: 84, - 0x6ED: 84, - 0x6EE: 82, - 0x6EF: 82, - 0x6FA: 68, - 0x6FB: 68, - 0x6FC: 68, - 0x6FF: 68, - 0x70F: 84, - 0x710: 82, - 0x711: 84, - 0x712: 68, - 0x713: 68, - 0x714: 68, - 0x715: 82, - 0x716: 82, - 0x717: 82, - 0x718: 82, - 0x719: 82, - 0x71A: 68, - 0x71B: 68, - 0x71C: 68, - 0x71D: 68, - 0x71E: 82, - 0x71F: 68, - 0x720: 68, - 0x721: 68, - 0x722: 68, - 0x723: 68, - 0x724: 68, - 0x725: 68, - 0x726: 68, - 0x727: 68, - 0x728: 82, - 0x729: 68, - 0x72A: 82, - 0x72B: 68, - 0x72C: 82, - 0x72D: 68, - 0x72E: 68, - 0x72F: 82, - 0x730: 84, - 0x731: 84, - 0x732: 84, - 0x733: 84, - 0x734: 84, - 0x735: 84, - 0x736: 84, - 0x737: 84, - 0x738: 84, - 0x739: 84, - 0x73A: 84, - 0x73B: 84, - 0x73C: 84, - 0x73D: 84, - 0x73E: 84, - 0x73F: 84, - 0x740: 84, - 0x741: 84, - 0x742: 84, - 0x743: 84, - 0x744: 84, - 0x745: 84, - 0x746: 84, - 0x747: 84, - 0x748: 84, - 0x749: 84, - 0x74A: 84, - 0x74D: 82, - 0x74E: 68, - 0x74F: 68, - 0x750: 68, - 0x751: 68, - 0x752: 68, - 0x753: 68, - 0x754: 68, - 0x755: 68, - 0x756: 68, - 0x757: 68, - 0x758: 68, - 0x759: 82, - 0x75A: 82, - 0x75B: 82, - 0x75C: 68, - 0x75D: 68, - 0x75E: 68, - 0x75F: 68, - 0x760: 68, - 0x761: 68, - 0x762: 68, - 0x763: 68, - 0x764: 68, - 0x765: 68, - 0x766: 68, - 0x767: 68, - 0x768: 68, - 0x769: 68, - 0x76A: 68, - 0x76B: 82, - 0x76C: 82, - 0x76D: 68, - 0x76E: 68, - 0x76F: 68, - 0x770: 68, - 0x771: 82, - 0x772: 68, - 0x773: 82, - 0x774: 82, - 0x775: 68, - 0x776: 68, - 0x777: 68, - 0x778: 82, - 0x779: 82, - 0x77A: 68, - 0x77B: 68, - 0x77C: 68, - 0x77D: 68, - 0x77E: 68, - 0x77F: 68, - 0x7A6: 84, - 0x7A7: 84, - 0x7A8: 84, - 0x7A9: 84, - 0x7AA: 84, - 0x7AB: 84, - 0x7AC: 84, - 0x7AD: 84, - 0x7AE: 84, - 0x7AF: 84, - 0x7B0: 84, - 0x7CA: 68, - 0x7CB: 68, - 0x7CC: 68, - 0x7CD: 68, - 0x7CE: 68, - 0x7CF: 68, - 0x7D0: 68, - 0x7D1: 68, - 0x7D2: 68, - 0x7D3: 68, - 0x7D4: 68, - 0x7D5: 68, - 0x7D6: 68, - 0x7D7: 68, - 0x7D8: 68, - 0x7D9: 68, - 0x7DA: 68, - 0x7DB: 68, - 0x7DC: 68, - 0x7DD: 68, - 0x7DE: 68, - 0x7DF: 68, - 0x7E0: 68, - 0x7E1: 68, - 0x7E2: 68, - 0x7E3: 68, - 0x7E4: 68, - 0x7E5: 68, - 0x7E6: 68, - 0x7E7: 68, - 0x7E8: 68, - 0x7E9: 68, - 0x7EA: 68, - 0x7EB: 84, - 0x7EC: 84, - 0x7ED: 84, - 0x7EE: 84, - 0x7EF: 84, - 0x7F0: 84, - 0x7F1: 84, - 0x7F2: 84, - 0x7F3: 84, - 0x7FA: 67, - 0x7FD: 84, - 0x816: 84, - 0x817: 84, - 0x818: 84, - 0x819: 84, - 0x81B: 84, - 0x81C: 84, - 0x81D: 84, - 0x81E: 84, - 0x81F: 84, - 0x820: 84, - 0x821: 84, - 0x822: 84, - 0x823: 84, - 0x825: 84, - 0x826: 84, - 0x827: 84, - 0x829: 84, - 0x82A: 84, - 0x82B: 84, - 0x82C: 84, - 0x82D: 84, - 0x840: 82, - 0x841: 68, - 0x842: 68, - 0x843: 68, - 0x844: 68, - 0x845: 68, - 0x846: 82, - 0x847: 82, - 0x848: 68, - 0x849: 82, - 0x84A: 68, - 0x84B: 68, - 0x84C: 68, - 0x84D: 68, - 0x84E: 68, - 0x84F: 68, - 0x850: 68, - 0x851: 68, - 0x852: 68, - 0x853: 68, - 0x854: 82, - 0x855: 68, - 0x856: 82, - 0x857: 82, - 0x858: 82, - 0x859: 84, - 0x85A: 84, - 0x85B: 84, - 0x860: 68, - 0x862: 68, - 0x863: 68, - 0x864: 68, - 0x865: 68, - 0x867: 82, - 0x868: 68, - 0x869: 82, - 0x86A: 82, - 0x870: 82, - 0x871: 82, - 0x872: 82, - 0x873: 82, - 0x874: 82, - 0x875: 82, - 0x876: 82, - 0x877: 82, - 0x878: 82, - 0x879: 82, - 0x87A: 82, - 0x87B: 82, - 0x87C: 82, - 0x87D: 82, - 0x87E: 82, - 0x87F: 82, - 0x880: 82, - 0x881: 82, - 0x882: 82, - 0x883: 67, - 0x884: 67, - 0x885: 67, - 0x886: 68, - 0x889: 68, - 0x88A: 68, - 0x88B: 68, - 0x88C: 68, - 0x88D: 68, - 0x88E: 82, - 0x898: 84, - 0x899: 84, - 0x89A: 84, - 0x89B: 84, - 0x89C: 84, - 0x89D: 84, - 0x89E: 84, - 0x89F: 84, - 0x8A0: 68, - 0x8A1: 68, - 0x8A2: 68, - 0x8A3: 68, - 0x8A4: 68, - 0x8A5: 68, - 0x8A6: 68, - 0x8A7: 68, - 0x8A8: 68, - 0x8A9: 68, - 0x8AA: 82, - 0x8AB: 82, - 0x8AC: 82, - 0x8AE: 82, - 0x8AF: 68, - 0x8B0: 68, - 0x8B1: 82, - 0x8B2: 82, - 0x8B3: 68, - 0x8B4: 68, - 0x8B5: 68, - 0x8B6: 68, - 0x8B7: 68, - 0x8B8: 68, - 0x8B9: 82, - 0x8BA: 68, - 0x8BB: 68, - 0x8BC: 68, - 0x8BD: 68, - 0x8BE: 68, - 0x8BF: 68, - 0x8C0: 68, - 0x8C1: 68, - 0x8C2: 68, - 0x8C3: 68, - 0x8C4: 68, - 0x8C5: 68, - 0x8C6: 68, - 0x8C7: 68, - 0x8C8: 68, - 0x8CA: 84, - 0x8CB: 84, - 0x8CC: 84, - 0x8CD: 84, - 0x8CE: 84, - 0x8CF: 84, - 0x8D0: 84, - 0x8D1: 84, - 0x8D2: 84, - 0x8D3: 84, - 0x8D4: 84, - 0x8D5: 84, - 0x8D6: 84, - 0x8D7: 84, - 0x8D8: 84, - 0x8D9: 84, - 0x8DA: 84, - 0x8DB: 84, - 0x8DC: 84, - 0x8DD: 84, - 0x8DE: 84, - 0x8DF: 84, - 0x8E0: 84, - 0x8E1: 84, - 0x8E3: 84, - 0x8E4: 84, - 0x8E5: 84, - 0x8E6: 84, - 0x8E7: 84, - 0x8E8: 84, - 0x8E9: 84, - 0x8EA: 84, - 0x8EB: 84, - 0x8EC: 84, - 0x8ED: 84, - 0x8EE: 84, - 0x8EF: 84, - 0x8F0: 84, - 0x8F1: 84, - 0x8F2: 84, - 0x8F3: 84, - 0x8F4: 84, - 0x8F5: 84, - 0x8F6: 84, - 0x8F7: 84, - 0x8F8: 84, - 0x8F9: 84, - 0x8FA: 84, - 0x8FB: 84, - 0x8FC: 84, - 0x8FD: 84, - 0x8FE: 84, - 0x8FF: 84, - 0x900: 84, - 0x901: 84, - 0x902: 84, - 0x93A: 84, - 0x93C: 84, - 0x941: 84, - 0x942: 84, - 0x943: 84, - 0x944: 84, - 0x945: 84, - 0x946: 84, - 0x947: 84, - 0x948: 84, - 0x94D: 84, - 0x951: 84, - 0x952: 84, - 0x953: 84, - 0x954: 84, - 0x955: 84, - 0x956: 84, - 0x957: 84, - 0x962: 84, - 0x963: 84, - 0x981: 84, - 0x9BC: 84, - 0x9C1: 84, - 0x9C2: 84, - 0x9C3: 84, - 0x9C4: 84, - 0x9CD: 84, - 0x9E2: 84, - 0x9E3: 84, - 0x9FE: 84, - 0xA01: 84, - 0xA02: 84, - 0xA3C: 84, - 0xA41: 84, - 0xA42: 84, - 0xA47: 84, - 0xA48: 84, - 0xA4B: 84, - 0xA4C: 84, - 0xA4D: 84, - 0xA51: 84, - 0xA70: 84, - 0xA71: 84, - 0xA75: 84, - 0xA81: 84, - 0xA82: 84, - 0xABC: 84, - 0xAC1: 84, - 0xAC2: 84, - 0xAC3: 84, - 0xAC4: 84, - 0xAC5: 84, - 0xAC7: 84, - 0xAC8: 84, - 0xACD: 84, - 0xAE2: 84, - 0xAE3: 84, - 0xAFA: 84, - 0xAFB: 84, - 0xAFC: 84, - 0xAFD: 84, - 0xAFE: 84, - 0xAFF: 84, - 0xB01: 84, - 0xB3C: 84, - 0xB3F: 84, - 0xB41: 84, - 0xB42: 84, - 0xB43: 84, - 0xB44: 84, - 0xB4D: 84, - 0xB55: 84, - 0xB56: 84, - 0xB62: 84, - 0xB63: 84, - 0xB82: 84, - 0xBC0: 84, - 0xBCD: 84, - 0xC00: 84, - 0xC04: 84, - 0xC3C: 84, - 0xC3E: 84, - 0xC3F: 84, - 0xC40: 84, - 0xC46: 84, - 0xC47: 84, - 0xC48: 84, - 0xC4A: 84, - 0xC4B: 84, - 0xC4C: 84, - 0xC4D: 84, - 0xC55: 84, - 0xC56: 84, - 0xC62: 84, - 0xC63: 84, - 0xC81: 84, - 0xCBC: 84, - 0xCBF: 84, - 0xCC6: 84, - 0xCCC: 84, - 0xCCD: 84, - 0xCE2: 84, - 0xCE3: 84, - 0xD00: 84, - 0xD01: 84, - 0xD3B: 84, - 0xD3C: 84, - 0xD41: 84, - 0xD42: 84, - 0xD43: 84, - 0xD44: 84, - 0xD4D: 84, - 0xD62: 84, - 0xD63: 84, - 0xD81: 84, - 0xDCA: 84, - 0xDD2: 84, - 0xDD3: 84, - 0xDD4: 84, - 0xDD6: 84, - 0xE31: 84, - 0xE34: 84, - 0xE35: 84, - 0xE36: 84, - 0xE37: 84, - 0xE38: 84, - 0xE39: 84, - 0xE3A: 84, - 0xE47: 84, - 0xE48: 84, - 0xE49: 84, - 0xE4A: 84, - 0xE4B: 84, - 0xE4C: 84, - 0xE4D: 84, - 0xE4E: 84, - 0xEB1: 84, - 0xEB4: 84, - 0xEB5: 84, - 0xEB6: 84, - 0xEB7: 84, - 0xEB8: 84, - 0xEB9: 84, - 0xEBA: 84, - 0xEBB: 84, - 0xEBC: 84, - 0xEC8: 84, - 0xEC9: 84, - 0xECA: 84, - 0xECB: 84, - 0xECC: 84, - 0xECD: 84, - 0xECE: 84, - 0xF18: 84, - 0xF19: 84, - 0xF35: 84, - 0xF37: 84, - 0xF39: 84, - 0xF71: 84, - 0xF72: 84, - 0xF73: 84, - 0xF74: 84, - 0xF75: 84, - 0xF76: 84, - 0xF77: 84, - 0xF78: 84, - 0xF79: 84, - 0xF7A: 84, - 0xF7B: 84, - 0xF7C: 84, - 0xF7D: 84, - 0xF7E: 84, - 0xF80: 84, - 0xF81: 84, - 0xF82: 84, - 0xF83: 84, - 0xF84: 84, - 0xF86: 84, - 0xF87: 84, - 0xF8D: 84, - 0xF8E: 84, - 0xF8F: 84, - 0xF90: 84, - 0xF91: 84, - 0xF92: 84, - 0xF93: 84, - 0xF94: 84, - 0xF95: 84, - 0xF96: 84, - 0xF97: 84, - 0xF99: 84, - 0xF9A: 84, - 0xF9B: 84, - 0xF9C: 84, - 0xF9D: 84, - 0xF9E: 84, - 0xF9F: 84, - 0xFA0: 84, - 0xFA1: 84, - 0xFA2: 84, - 0xFA3: 84, - 0xFA4: 84, - 0xFA5: 84, - 0xFA6: 84, - 0xFA7: 84, - 0xFA8: 84, - 0xFA9: 84, - 0xFAA: 84, - 0xFAB: 84, - 0xFAC: 84, - 0xFAD: 84, - 0xFAE: 84, - 0xFAF: 84, - 0xFB0: 84, - 0xFB1: 84, - 0xFB2: 84, - 0xFB3: 84, - 0xFB4: 84, - 0xFB5: 84, - 0xFB6: 84, - 0xFB7: 84, - 0xFB8: 84, - 0xFB9: 84, - 0xFBA: 84, - 0xFBB: 84, - 0xFBC: 84, - 0xFC6: 84, - 0x102D: 84, - 0x102E: 84, - 0x102F: 84, - 0x1030: 84, - 0x1032: 84, - 0x1033: 84, - 0x1034: 84, - 0x1035: 84, - 0x1036: 84, - 0x1037: 84, - 0x1039: 84, - 0x103A: 84, - 0x103D: 84, - 0x103E: 84, - 0x1058: 84, - 0x1059: 84, - 0x105E: 84, - 0x105F: 84, - 0x1060: 84, - 0x1071: 84, - 0x1072: 84, - 0x1073: 84, - 0x1074: 84, - 0x1082: 84, - 0x1085: 84, - 0x1086: 84, - 0x108D: 84, - 0x109D: 84, - 0x135D: 84, - 0x135E: 84, - 0x135F: 84, - 0x1712: 84, - 0x1713: 84, - 0x1714: 84, - 0x1732: 84, - 0x1733: 84, - 0x1752: 84, - 0x1753: 84, - 0x1772: 84, - 0x1773: 84, - 0x17B4: 84, - 0x17B5: 84, - 0x17B7: 84, - 0x17B8: 84, - 0x17B9: 84, - 0x17BA: 84, - 0x17BB: 84, - 0x17BC: 84, - 0x17BD: 84, - 0x17C6: 84, - 0x17C9: 84, - 0x17CA: 84, - 0x17CB: 84, - 0x17CC: 84, - 0x17CD: 84, - 0x17CE: 84, - 0x17CF: 84, - 0x17D0: 84, - 0x17D1: 84, - 0x17D2: 84, - 0x17D3: 84, - 0x17DD: 84, - 0x1807: 68, - 0x180A: 67, - 0x180B: 84, - 0x180C: 84, - 0x180D: 84, - 0x180F: 84, - 0x1820: 68, - 0x1821: 68, - 0x1822: 68, - 0x1823: 68, - 0x1824: 68, - 0x1825: 68, - 0x1826: 68, - 0x1827: 68, - 0x1828: 68, - 0x1829: 68, - 0x182A: 68, - 0x182B: 68, - 0x182C: 68, - 0x182D: 68, - 0x182E: 68, - 0x182F: 68, - 0x1830: 68, - 0x1831: 68, - 0x1832: 68, - 0x1833: 68, - 0x1834: 68, - 0x1835: 68, - 0x1836: 68, - 0x1837: 68, - 0x1838: 68, - 0x1839: 68, - 0x183A: 68, - 0x183B: 68, - 0x183C: 68, - 0x183D: 68, - 0x183E: 68, - 0x183F: 68, - 0x1840: 68, - 0x1841: 68, - 0x1842: 68, - 0x1843: 68, - 0x1844: 68, - 0x1845: 68, - 0x1846: 68, - 0x1847: 68, - 0x1848: 68, - 0x1849: 68, - 0x184A: 68, - 0x184B: 68, - 0x184C: 68, - 0x184D: 68, - 0x184E: 68, - 0x184F: 68, - 0x1850: 68, - 0x1851: 68, - 0x1852: 68, - 0x1853: 68, - 0x1854: 68, - 0x1855: 68, - 0x1856: 68, - 0x1857: 68, - 0x1858: 68, - 0x1859: 68, - 0x185A: 68, - 0x185B: 68, - 0x185C: 68, - 0x185D: 68, - 0x185E: 68, - 0x185F: 68, - 0x1860: 68, - 0x1861: 68, - 0x1862: 68, - 0x1863: 68, - 0x1864: 68, - 0x1865: 68, - 0x1866: 68, - 0x1867: 68, - 0x1868: 68, - 0x1869: 68, - 0x186A: 68, - 0x186B: 68, - 0x186C: 68, - 0x186D: 68, - 0x186E: 68, - 0x186F: 68, - 0x1870: 68, - 0x1871: 68, - 0x1872: 68, - 0x1873: 68, - 0x1874: 68, - 0x1875: 68, - 0x1876: 68, - 0x1877: 68, - 0x1878: 68, - 0x1885: 84, - 0x1886: 84, - 0x1887: 68, - 0x1888: 68, - 0x1889: 68, - 0x188A: 68, - 0x188B: 68, - 0x188C: 68, - 0x188D: 68, - 0x188E: 68, - 0x188F: 68, - 0x1890: 68, - 0x1891: 68, - 0x1892: 68, - 0x1893: 68, - 0x1894: 68, - 0x1895: 68, - 0x1896: 68, - 0x1897: 68, - 0x1898: 68, - 0x1899: 68, - 0x189A: 68, - 0x189B: 68, - 0x189C: 68, - 0x189D: 68, - 0x189E: 68, - 0x189F: 68, - 0x18A0: 68, - 0x18A1: 68, - 0x18A2: 68, - 0x18A3: 68, - 0x18A4: 68, - 0x18A5: 68, - 0x18A6: 68, - 0x18A7: 68, - 0x18A8: 68, - 0x18A9: 84, - 0x18AA: 68, - 0x1920: 84, - 0x1921: 84, - 0x1922: 84, - 0x1927: 84, - 0x1928: 84, - 0x1932: 84, - 0x1939: 84, - 0x193A: 84, - 0x193B: 84, - 0x1A17: 84, - 0x1A18: 84, - 0x1A1B: 84, - 0x1A56: 84, - 0x1A58: 84, - 0x1A59: 84, - 0x1A5A: 84, - 0x1A5B: 84, - 0x1A5C: 84, - 0x1A5D: 84, - 0x1A5E: 84, - 0x1A60: 84, - 0x1A62: 84, - 0x1A65: 84, - 0x1A66: 84, - 0x1A67: 84, - 0x1A68: 84, - 0x1A69: 84, - 0x1A6A: 84, - 0x1A6B: 84, - 0x1A6C: 84, - 0x1A73: 84, - 0x1A74: 84, - 0x1A75: 84, - 0x1A76: 84, - 0x1A77: 84, - 0x1A78: 84, - 0x1A79: 84, - 0x1A7A: 84, - 0x1A7B: 84, - 0x1A7C: 84, - 0x1A7F: 84, - 0x1AB0: 84, - 0x1AB1: 84, - 0x1AB2: 84, - 0x1AB3: 84, - 0x1AB4: 84, - 0x1AB5: 84, - 0x1AB6: 84, - 0x1AB7: 84, - 0x1AB8: 84, - 0x1AB9: 84, - 0x1ABA: 84, - 0x1ABB: 84, - 0x1ABC: 84, - 0x1ABD: 84, - 0x1ABE: 84, - 0x1ABF: 84, - 0x1AC0: 84, - 0x1AC1: 84, - 0x1AC2: 84, - 0x1AC3: 84, - 0x1AC4: 84, - 0x1AC5: 84, - 0x1AC6: 84, - 0x1AC7: 84, - 0x1AC8: 84, - 0x1AC9: 84, - 0x1ACA: 84, - 0x1ACB: 84, - 0x1ACC: 84, - 0x1ACD: 84, - 0x1ACE: 84, - 0x1B00: 84, - 0x1B01: 84, - 0x1B02: 84, - 0x1B03: 84, - 0x1B34: 84, - 0x1B36: 84, - 0x1B37: 84, - 0x1B38: 84, - 0x1B39: 84, - 0x1B3A: 84, - 0x1B3C: 84, - 0x1B42: 84, - 0x1B6B: 84, - 0x1B6C: 84, - 0x1B6D: 84, - 0x1B6E: 84, - 0x1B6F: 84, - 0x1B70: 84, - 0x1B71: 84, - 0x1B72: 84, - 0x1B73: 84, - 0x1B80: 84, - 0x1B81: 84, - 0x1BA2: 84, - 0x1BA3: 84, - 0x1BA4: 84, - 0x1BA5: 84, - 0x1BA8: 84, - 0x1BA9: 84, - 0x1BAB: 84, - 0x1BAC: 84, - 0x1BAD: 84, - 0x1BE6: 84, - 0x1BE8: 84, - 0x1BE9: 84, - 0x1BED: 84, - 0x1BEF: 84, - 0x1BF0: 84, - 0x1BF1: 84, - 0x1C2C: 84, - 0x1C2D: 84, - 0x1C2E: 84, - 0x1C2F: 84, - 0x1C30: 84, - 0x1C31: 84, - 0x1C32: 84, - 0x1C33: 84, - 0x1C36: 84, - 0x1C37: 84, - 0x1CD0: 84, - 0x1CD1: 84, - 0x1CD2: 84, - 0x1CD4: 84, - 0x1CD5: 84, - 0x1CD6: 84, - 0x1CD7: 84, - 0x1CD8: 84, - 0x1CD9: 84, - 0x1CDA: 84, - 0x1CDB: 84, - 0x1CDC: 84, - 0x1CDD: 84, - 0x1CDE: 84, - 0x1CDF: 84, - 0x1CE0: 84, - 0x1CE2: 84, - 0x1CE3: 84, - 0x1CE4: 84, - 0x1CE5: 84, - 0x1CE6: 84, - 0x1CE7: 84, - 0x1CE8: 84, - 0x1CED: 84, - 0x1CF4: 84, - 0x1CF8: 84, - 0x1CF9: 84, - 0x1DC0: 84, - 0x1DC1: 84, - 0x1DC2: 84, - 0x1DC3: 84, - 0x1DC4: 84, - 0x1DC5: 84, - 0x1DC6: 84, - 0x1DC7: 84, - 0x1DC8: 84, - 0x1DC9: 84, - 0x1DCA: 84, - 0x1DCB: 84, - 0x1DCC: 84, - 0x1DCD: 84, - 0x1DCE: 84, - 0x1DCF: 84, - 0x1DD0: 84, - 0x1DD1: 84, - 0x1DD2: 84, - 0x1DD3: 84, - 0x1DD4: 84, - 0x1DD5: 84, - 0x1DD6: 84, - 0x1DD7: 84, - 0x1DD8: 84, - 0x1DD9: 84, - 0x1DDA: 84, - 0x1DDB: 84, - 0x1DDC: 84, - 0x1DDD: 84, - 0x1DDE: 84, - 0x1DDF: 84, - 0x1DE0: 84, - 0x1DE1: 84, - 0x1DE2: 84, - 0x1DE3: 84, - 0x1DE4: 84, - 0x1DE5: 84, - 0x1DE6: 84, - 0x1DE7: 84, - 0x1DE8: 84, - 0x1DE9: 84, - 0x1DEA: 84, - 0x1DEB: 84, - 0x1DEC: 84, - 0x1DED: 84, - 0x1DEE: 84, - 0x1DEF: 84, - 0x1DF0: 84, - 0x1DF1: 84, - 0x1DF2: 84, - 0x1DF3: 84, - 0x1DF4: 84, - 0x1DF5: 84, - 0x1DF6: 84, - 0x1DF7: 84, - 0x1DF8: 84, - 0x1DF9: 84, - 0x1DFA: 84, - 0x1DFB: 84, - 0x1DFC: 84, - 0x1DFD: 84, - 0x1DFE: 84, - 0x1DFF: 84, - 0x200B: 84, - 0x200D: 67, - 0x200E: 84, - 0x200F: 84, - 0x202A: 84, - 0x202B: 84, - 0x202C: 84, - 0x202D: 84, - 0x202E: 84, - 0x2060: 84, - 0x2061: 84, - 0x2062: 84, - 0x2063: 84, - 0x2064: 84, - 0x206A: 84, - 0x206B: 84, - 0x206C: 84, - 0x206D: 84, - 0x206E: 84, - 0x206F: 84, - 0x20D0: 84, - 0x20D1: 84, - 0x20D2: 84, - 0x20D3: 84, - 0x20D4: 84, - 0x20D5: 84, - 0x20D6: 84, - 0x20D7: 84, - 0x20D8: 84, - 0x20D9: 84, - 0x20DA: 84, - 0x20DB: 84, - 0x20DC: 84, - 0x20DD: 84, - 0x20DE: 84, - 0x20DF: 84, - 0x20E0: 84, - 0x20E1: 84, - 0x20E2: 84, - 0x20E3: 84, - 0x20E4: 84, - 0x20E5: 84, - 0x20E6: 84, - 0x20E7: 84, - 0x20E8: 84, - 0x20E9: 84, - 0x20EA: 84, - 0x20EB: 84, - 0x20EC: 84, - 0x20ED: 84, - 0x20EE: 84, - 0x20EF: 84, - 0x20F0: 84, - 0x2CEF: 84, - 0x2CF0: 84, - 0x2CF1: 84, - 0x2D7F: 84, - 0x2DE0: 84, - 0x2DE1: 84, - 0x2DE2: 84, - 0x2DE3: 84, - 0x2DE4: 84, - 0x2DE5: 84, - 0x2DE6: 84, - 0x2DE7: 84, - 0x2DE8: 84, - 0x2DE9: 84, - 0x2DEA: 84, - 0x2DEB: 84, - 0x2DEC: 84, - 0x2DED: 84, - 0x2DEE: 84, - 0x2DEF: 84, - 0x2DF0: 84, - 0x2DF1: 84, - 0x2DF2: 84, - 0x2DF3: 84, - 0x2DF4: 84, - 0x2DF5: 84, - 0x2DF6: 84, - 0x2DF7: 84, - 0x2DF8: 84, - 0x2DF9: 84, - 0x2DFA: 84, - 0x2DFB: 84, - 0x2DFC: 84, - 0x2DFD: 84, - 0x2DFE: 84, - 0x2DFF: 84, - 0x302A: 84, - 0x302B: 84, - 0x302C: 84, - 0x302D: 84, - 0x3099: 84, - 0x309A: 84, - 0xA66F: 84, - 0xA670: 84, - 0xA671: 84, - 0xA672: 84, - 0xA674: 84, - 0xA675: 84, - 0xA676: 84, - 0xA677: 84, - 0xA678: 84, - 0xA679: 84, - 0xA67A: 84, - 0xA67B: 84, - 0xA67C: 84, - 0xA67D: 84, - 0xA69E: 84, - 0xA69F: 84, - 0xA6F0: 84, - 0xA6F1: 84, - 0xA802: 84, - 0xA806: 84, - 0xA80B: 84, - 0xA825: 84, - 0xA826: 84, - 0xA82C: 84, - 0xA840: 68, - 0xA841: 68, - 0xA842: 68, - 0xA843: 68, - 0xA844: 68, - 0xA845: 68, - 0xA846: 68, - 0xA847: 68, - 0xA848: 68, - 0xA849: 68, - 0xA84A: 68, - 0xA84B: 68, - 0xA84C: 68, - 0xA84D: 68, - 0xA84E: 68, - 0xA84F: 68, - 0xA850: 68, - 0xA851: 68, - 0xA852: 68, - 0xA853: 68, - 0xA854: 68, - 0xA855: 68, - 0xA856: 68, - 0xA857: 68, - 0xA858: 68, - 0xA859: 68, - 0xA85A: 68, - 0xA85B: 68, - 0xA85C: 68, - 0xA85D: 68, - 0xA85E: 68, - 0xA85F: 68, - 0xA860: 68, - 0xA861: 68, - 0xA862: 68, - 0xA863: 68, - 0xA864: 68, - 0xA865: 68, - 0xA866: 68, - 0xA867: 68, - 0xA868: 68, - 0xA869: 68, - 0xA86A: 68, - 0xA86B: 68, - 0xA86C: 68, - 0xA86D: 68, - 0xA86E: 68, - 0xA86F: 68, - 0xA870: 68, - 0xA871: 68, - 0xA872: 76, - 0xA8C4: 84, - 0xA8C5: 84, - 0xA8E0: 84, - 0xA8E1: 84, - 0xA8E2: 84, - 0xA8E3: 84, - 0xA8E4: 84, - 0xA8E5: 84, - 0xA8E6: 84, - 0xA8E7: 84, - 0xA8E8: 84, - 0xA8E9: 84, - 0xA8EA: 84, - 0xA8EB: 84, - 0xA8EC: 84, - 0xA8ED: 84, - 0xA8EE: 84, - 0xA8EF: 84, - 0xA8F0: 84, - 0xA8F1: 84, - 0xA8FF: 84, - 0xA926: 84, - 0xA927: 84, - 0xA928: 84, - 0xA929: 84, - 0xA92A: 84, - 0xA92B: 84, - 0xA92C: 84, - 0xA92D: 84, - 0xA947: 84, - 0xA948: 84, - 0xA949: 84, - 0xA94A: 84, - 0xA94B: 84, - 0xA94C: 84, - 0xA94D: 84, - 0xA94E: 84, - 0xA94F: 84, - 0xA950: 84, - 0xA951: 84, - 0xA980: 84, - 0xA981: 84, - 0xA982: 84, - 0xA9B3: 84, - 0xA9B6: 84, - 0xA9B7: 84, - 0xA9B8: 84, - 0xA9B9: 84, - 0xA9BC: 84, - 0xA9BD: 84, - 0xA9E5: 84, - 0xAA29: 84, - 0xAA2A: 84, - 0xAA2B: 84, - 0xAA2C: 84, - 0xAA2D: 84, - 0xAA2E: 84, - 0xAA31: 84, - 0xAA32: 84, - 0xAA35: 84, - 0xAA36: 84, - 0xAA43: 84, - 0xAA4C: 84, - 0xAA7C: 84, - 0xAAB0: 84, - 0xAAB2: 84, - 0xAAB3: 84, - 0xAAB4: 84, - 0xAAB7: 84, - 0xAAB8: 84, - 0xAABE: 84, - 0xAABF: 84, - 0xAAC1: 84, - 0xAAEC: 84, - 0xAAED: 84, - 0xAAF6: 84, - 0xABE5: 84, - 0xABE8: 84, - 0xABED: 84, - 0xFB1E: 84, - 0xFE00: 84, - 0xFE01: 84, - 0xFE02: 84, - 0xFE03: 84, - 0xFE04: 84, - 0xFE05: 84, - 0xFE06: 84, - 0xFE07: 84, - 0xFE08: 84, - 0xFE09: 84, - 0xFE0A: 84, - 0xFE0B: 84, - 0xFE0C: 84, - 0xFE0D: 84, - 0xFE0E: 84, - 0xFE0F: 84, - 0xFE20: 84, - 0xFE21: 84, - 0xFE22: 84, - 0xFE23: 84, - 0xFE24: 84, - 0xFE25: 84, - 0xFE26: 84, - 0xFE27: 84, - 0xFE28: 84, - 0xFE29: 84, - 0xFE2A: 84, - 0xFE2B: 84, - 0xFE2C: 84, - 0xFE2D: 84, - 0xFE2E: 84, - 0xFE2F: 84, - 0xFEFF: 84, - 0xFFF9: 84, - 0xFFFA: 84, - 0xFFFB: 84, - 0x101FD: 84, - 0x102E0: 84, - 0x10376: 84, - 0x10377: 84, - 0x10378: 84, - 0x10379: 84, - 0x1037A: 84, - 0x10A01: 84, - 0x10A02: 84, - 0x10A03: 84, - 0x10A05: 84, - 0x10A06: 84, - 0x10A0C: 84, - 0x10A0D: 84, - 0x10A0E: 84, - 0x10A0F: 84, - 0x10A38: 84, - 0x10A39: 84, - 0x10A3A: 84, - 0x10A3F: 84, - 0x10AC0: 68, - 0x10AC1: 68, - 0x10AC2: 68, - 0x10AC3: 68, - 0x10AC4: 68, - 0x10AC5: 82, - 0x10AC7: 82, - 0x10AC9: 82, - 0x10ACA: 82, - 0x10ACD: 76, - 0x10ACE: 82, - 0x10ACF: 82, - 0x10AD0: 82, - 0x10AD1: 82, - 0x10AD2: 82, - 0x10AD3: 68, - 0x10AD4: 68, - 0x10AD5: 68, - 0x10AD6: 68, - 0x10AD7: 76, - 0x10AD8: 68, - 0x10AD9: 68, - 0x10ADA: 68, - 0x10ADB: 68, - 0x10ADC: 68, - 0x10ADD: 82, - 0x10ADE: 68, - 0x10ADF: 68, - 0x10AE0: 68, - 0x10AE1: 82, - 0x10AE4: 82, - 0x10AE5: 84, - 0x10AE6: 84, - 0x10AEB: 68, - 0x10AEC: 68, - 0x10AED: 68, - 0x10AEE: 68, - 0x10AEF: 82, - 0x10B80: 68, - 0x10B81: 82, - 0x10B82: 68, - 0x10B83: 82, - 0x10B84: 82, - 0x10B85: 82, - 0x10B86: 68, - 0x10B87: 68, - 0x10B88: 68, - 0x10B89: 82, - 0x10B8A: 68, - 0x10B8B: 68, - 0x10B8C: 82, - 0x10B8D: 68, - 0x10B8E: 82, - 0x10B8F: 82, - 0x10B90: 68, - 0x10B91: 82, - 0x10BA9: 82, - 0x10BAA: 82, - 0x10BAB: 82, - 0x10BAC: 82, - 0x10BAD: 68, - 0x10BAE: 68, - 0x10D00: 76, - 0x10D01: 68, - 0x10D02: 68, - 0x10D03: 68, - 0x10D04: 68, - 0x10D05: 68, - 0x10D06: 68, - 0x10D07: 68, - 0x10D08: 68, - 0x10D09: 68, - 0x10D0A: 68, - 0x10D0B: 68, - 0x10D0C: 68, - 0x10D0D: 68, - 0x10D0E: 68, - 0x10D0F: 68, - 0x10D10: 68, - 0x10D11: 68, - 0x10D12: 68, - 0x10D13: 68, - 0x10D14: 68, - 0x10D15: 68, - 0x10D16: 68, - 0x10D17: 68, - 0x10D18: 68, - 0x10D19: 68, - 0x10D1A: 68, - 0x10D1B: 68, - 0x10D1C: 68, - 0x10D1D: 68, - 0x10D1E: 68, - 0x10D1F: 68, - 0x10D20: 68, - 0x10D21: 68, - 0x10D22: 82, - 0x10D23: 68, - 0x10D24: 84, - 0x10D25: 84, - 0x10D26: 84, - 0x10D27: 84, - 0x10EAB: 84, - 0x10EAC: 84, - 0x10EFD: 84, - 0x10EFE: 84, - 0x10EFF: 84, - 0x10F30: 68, - 0x10F31: 68, - 0x10F32: 68, - 0x10F33: 82, - 0x10F34: 68, - 0x10F35: 68, - 0x10F36: 68, - 0x10F37: 68, - 0x10F38: 68, - 0x10F39: 68, - 0x10F3A: 68, - 0x10F3B: 68, - 0x10F3C: 68, - 0x10F3D: 68, - 0x10F3E: 68, - 0x10F3F: 68, - 0x10F40: 68, - 0x10F41: 68, - 0x10F42: 68, - 0x10F43: 68, - 0x10F44: 68, - 0x10F46: 84, - 0x10F47: 84, - 0x10F48: 84, - 0x10F49: 84, - 0x10F4A: 84, - 0x10F4B: 84, - 0x10F4C: 84, - 0x10F4D: 84, - 0x10F4E: 84, - 0x10F4F: 84, - 0x10F50: 84, - 0x10F51: 68, - 0x10F52: 68, - 0x10F53: 68, - 0x10F54: 82, - 0x10F70: 68, - 0x10F71: 68, - 0x10F72: 68, - 0x10F73: 68, - 0x10F74: 82, - 0x10F75: 82, - 0x10F76: 68, - 0x10F77: 68, - 0x10F78: 68, - 0x10F79: 68, - 0x10F7A: 68, - 0x10F7B: 68, - 0x10F7C: 68, - 0x10F7D: 68, - 0x10F7E: 68, - 0x10F7F: 68, - 0x10F80: 68, - 0x10F81: 68, - 0x10F82: 84, - 0x10F83: 84, - 0x10F84: 84, - 0x10F85: 84, - 0x10FB0: 68, - 0x10FB2: 68, - 0x10FB3: 68, - 0x10FB4: 82, - 0x10FB5: 82, - 0x10FB6: 82, - 0x10FB8: 68, - 0x10FB9: 82, - 0x10FBA: 82, - 0x10FBB: 68, - 0x10FBC: 68, - 0x10FBD: 82, - 0x10FBE: 68, - 0x10FBF: 68, - 0x10FC1: 68, - 0x10FC2: 82, - 0x10FC3: 82, - 0x10FC4: 68, - 0x10FC9: 82, - 0x10FCA: 68, - 0x10FCB: 76, - 0x11001: 84, - 0x11038: 84, - 0x11039: 84, - 0x1103A: 84, - 0x1103B: 84, - 0x1103C: 84, - 0x1103D: 84, - 0x1103E: 84, - 0x1103F: 84, - 0x11040: 84, - 0x11041: 84, - 0x11042: 84, - 0x11043: 84, - 0x11044: 84, - 0x11045: 84, - 0x11046: 84, - 0x11070: 84, - 0x11073: 84, - 0x11074: 84, - 0x1107F: 84, - 0x11080: 84, - 0x11081: 84, - 0x110B3: 84, - 0x110B4: 84, - 0x110B5: 84, - 0x110B6: 84, - 0x110B9: 84, - 0x110BA: 84, - 0x110C2: 84, - 0x11100: 84, - 0x11101: 84, - 0x11102: 84, - 0x11127: 84, - 0x11128: 84, - 0x11129: 84, - 0x1112A: 84, - 0x1112B: 84, - 0x1112D: 84, - 0x1112E: 84, - 0x1112F: 84, - 0x11130: 84, - 0x11131: 84, - 0x11132: 84, - 0x11133: 84, - 0x11134: 84, - 0x11173: 84, - 0x11180: 84, - 0x11181: 84, - 0x111B6: 84, - 0x111B7: 84, - 0x111B8: 84, - 0x111B9: 84, - 0x111BA: 84, - 0x111BB: 84, - 0x111BC: 84, - 0x111BD: 84, - 0x111BE: 84, - 0x111C9: 84, - 0x111CA: 84, - 0x111CB: 84, - 0x111CC: 84, - 0x111CF: 84, - 0x1122F: 84, - 0x11230: 84, - 0x11231: 84, - 0x11234: 84, - 0x11236: 84, - 0x11237: 84, - 0x1123E: 84, - 0x11241: 84, - 0x112DF: 84, - 0x112E3: 84, - 0x112E4: 84, - 0x112E5: 84, - 0x112E6: 84, - 0x112E7: 84, - 0x112E8: 84, - 0x112E9: 84, - 0x112EA: 84, - 0x11300: 84, - 0x11301: 84, - 0x1133B: 84, - 0x1133C: 84, - 0x11340: 84, - 0x11366: 84, - 0x11367: 84, - 0x11368: 84, - 0x11369: 84, - 0x1136A: 84, - 0x1136B: 84, - 0x1136C: 84, - 0x11370: 84, - 0x11371: 84, - 0x11372: 84, - 0x11373: 84, - 0x11374: 84, - 0x11438: 84, - 0x11439: 84, - 0x1143A: 84, - 0x1143B: 84, - 0x1143C: 84, - 0x1143D: 84, - 0x1143E: 84, - 0x1143F: 84, - 0x11442: 84, - 0x11443: 84, - 0x11444: 84, - 0x11446: 84, - 0x1145E: 84, - 0x114B3: 84, - 0x114B4: 84, - 0x114B5: 84, - 0x114B6: 84, - 0x114B7: 84, - 0x114B8: 84, - 0x114BA: 84, - 0x114BF: 84, - 0x114C0: 84, - 0x114C2: 84, - 0x114C3: 84, - 0x115B2: 84, - 0x115B3: 84, - 0x115B4: 84, - 0x115B5: 84, - 0x115BC: 84, - 0x115BD: 84, - 0x115BF: 84, - 0x115C0: 84, - 0x115DC: 84, - 0x115DD: 84, - 0x11633: 84, - 0x11634: 84, - 0x11635: 84, - 0x11636: 84, - 0x11637: 84, - 0x11638: 84, - 0x11639: 84, - 0x1163A: 84, - 0x1163D: 84, - 0x1163F: 84, - 0x11640: 84, - 0x116AB: 84, - 0x116AD: 84, - 0x116B0: 84, - 0x116B1: 84, - 0x116B2: 84, - 0x116B3: 84, - 0x116B4: 84, - 0x116B5: 84, - 0x116B7: 84, - 0x1171D: 84, - 0x1171E: 84, - 0x1171F: 84, - 0x11722: 84, - 0x11723: 84, - 0x11724: 84, - 0x11725: 84, - 0x11727: 84, - 0x11728: 84, - 0x11729: 84, - 0x1172A: 84, - 0x1172B: 84, - 0x1182F: 84, - 0x11830: 84, - 0x11831: 84, - 0x11832: 84, - 0x11833: 84, - 0x11834: 84, - 0x11835: 84, - 0x11836: 84, - 0x11837: 84, - 0x11839: 84, - 0x1183A: 84, - 0x1193B: 84, - 0x1193C: 84, - 0x1193E: 84, - 0x11943: 84, - 0x119D4: 84, - 0x119D5: 84, - 0x119D6: 84, - 0x119D7: 84, - 0x119DA: 84, - 0x119DB: 84, - 0x119E0: 84, - 0x11A01: 84, - 0x11A02: 84, - 0x11A03: 84, - 0x11A04: 84, - 0x11A05: 84, - 0x11A06: 84, - 0x11A07: 84, - 0x11A08: 84, - 0x11A09: 84, - 0x11A0A: 84, - 0x11A33: 84, - 0x11A34: 84, - 0x11A35: 84, - 0x11A36: 84, - 0x11A37: 84, - 0x11A38: 84, - 0x11A3B: 84, - 0x11A3C: 84, - 0x11A3D: 84, - 0x11A3E: 84, - 0x11A47: 84, - 0x11A51: 84, - 0x11A52: 84, - 0x11A53: 84, - 0x11A54: 84, - 0x11A55: 84, - 0x11A56: 84, - 0x11A59: 84, - 0x11A5A: 84, - 0x11A5B: 84, - 0x11A8A: 84, - 0x11A8B: 84, - 0x11A8C: 84, - 0x11A8D: 84, - 0x11A8E: 84, - 0x11A8F: 84, - 0x11A90: 84, - 0x11A91: 84, - 0x11A92: 84, - 0x11A93: 84, - 0x11A94: 84, - 0x11A95: 84, - 0x11A96: 84, - 0x11A98: 84, - 0x11A99: 84, - 0x11C30: 84, - 0x11C31: 84, - 0x11C32: 84, - 0x11C33: 84, - 0x11C34: 84, - 0x11C35: 84, - 0x11C36: 84, - 0x11C38: 84, - 0x11C39: 84, - 0x11C3A: 84, - 0x11C3B: 84, - 0x11C3C: 84, - 0x11C3D: 84, - 0x11C3F: 84, - 0x11C92: 84, - 0x11C93: 84, - 0x11C94: 84, - 0x11C95: 84, - 0x11C96: 84, - 0x11C97: 84, - 0x11C98: 84, - 0x11C99: 84, - 0x11C9A: 84, - 0x11C9B: 84, - 0x11C9C: 84, - 0x11C9D: 84, - 0x11C9E: 84, - 0x11C9F: 84, - 0x11CA0: 84, - 0x11CA1: 84, - 0x11CA2: 84, - 0x11CA3: 84, - 0x11CA4: 84, - 0x11CA5: 84, - 0x11CA6: 84, - 0x11CA7: 84, - 0x11CAA: 84, - 0x11CAB: 84, - 0x11CAC: 84, - 0x11CAD: 84, - 0x11CAE: 84, - 0x11CAF: 84, - 0x11CB0: 84, - 0x11CB2: 84, - 0x11CB3: 84, - 0x11CB5: 84, - 0x11CB6: 84, - 0x11D31: 84, - 0x11D32: 84, - 0x11D33: 84, - 0x11D34: 84, - 0x11D35: 84, - 0x11D36: 84, - 0x11D3A: 84, - 0x11D3C: 84, - 0x11D3D: 84, - 0x11D3F: 84, - 0x11D40: 84, - 0x11D41: 84, - 0x11D42: 84, - 0x11D43: 84, - 0x11D44: 84, - 0x11D45: 84, - 0x11D47: 84, - 0x11D90: 84, - 0x11D91: 84, - 0x11D95: 84, - 0x11D97: 84, - 0x11EF3: 84, - 0x11EF4: 84, - 0x11F00: 84, - 0x11F01: 84, - 0x11F36: 84, - 0x11F37: 84, - 0x11F38: 84, - 0x11F39: 84, - 0x11F3A: 84, - 0x11F40: 84, - 0x11F42: 84, - 0x13430: 84, - 0x13431: 84, - 0x13432: 84, - 0x13433: 84, - 0x13434: 84, - 0x13435: 84, - 0x13436: 84, - 0x13437: 84, - 0x13438: 84, - 0x13439: 84, - 0x1343A: 84, - 0x1343B: 84, - 0x1343C: 84, - 0x1343D: 84, - 0x1343E: 84, - 0x1343F: 84, - 0x13440: 84, - 0x13447: 84, - 0x13448: 84, - 0x13449: 84, - 0x1344A: 84, - 0x1344B: 84, - 0x1344C: 84, - 0x1344D: 84, - 0x1344E: 84, - 0x1344F: 84, - 0x13450: 84, - 0x13451: 84, - 0x13452: 84, - 0x13453: 84, - 0x13454: 84, - 0x13455: 84, - 0x16AF0: 84, - 0x16AF1: 84, - 0x16AF2: 84, - 0x16AF3: 84, - 0x16AF4: 84, - 0x16B30: 84, - 0x16B31: 84, - 0x16B32: 84, - 0x16B33: 84, - 0x16B34: 84, - 0x16B35: 84, - 0x16B36: 84, - 0x16F4F: 84, - 0x16F8F: 84, - 0x16F90: 84, - 0x16F91: 84, - 0x16F92: 84, - 0x16FE4: 84, - 0x1BC9D: 84, - 0x1BC9E: 84, - 0x1BCA0: 84, - 0x1BCA1: 84, - 0x1BCA2: 84, - 0x1BCA3: 84, - 0x1CF00: 84, - 0x1CF01: 84, - 0x1CF02: 84, - 0x1CF03: 84, - 0x1CF04: 84, - 0x1CF05: 84, - 0x1CF06: 84, - 0x1CF07: 84, - 0x1CF08: 84, - 0x1CF09: 84, - 0x1CF0A: 84, - 0x1CF0B: 84, - 0x1CF0C: 84, - 0x1CF0D: 84, - 0x1CF0E: 84, - 0x1CF0F: 84, - 0x1CF10: 84, - 0x1CF11: 84, - 0x1CF12: 84, - 0x1CF13: 84, - 0x1CF14: 84, - 0x1CF15: 84, - 0x1CF16: 84, - 0x1CF17: 84, - 0x1CF18: 84, - 0x1CF19: 84, - 0x1CF1A: 84, - 0x1CF1B: 84, - 0x1CF1C: 84, - 0x1CF1D: 84, - 0x1CF1E: 84, - 0x1CF1F: 84, - 0x1CF20: 84, - 0x1CF21: 84, - 0x1CF22: 84, - 0x1CF23: 84, - 0x1CF24: 84, - 0x1CF25: 84, - 0x1CF26: 84, - 0x1CF27: 84, - 0x1CF28: 84, - 0x1CF29: 84, - 0x1CF2A: 84, - 0x1CF2B: 84, - 0x1CF2C: 84, - 0x1CF2D: 84, - 0x1CF30: 84, - 0x1CF31: 84, - 0x1CF32: 84, - 0x1CF33: 84, - 0x1CF34: 84, - 0x1CF35: 84, - 0x1CF36: 84, - 0x1CF37: 84, - 0x1CF38: 84, - 0x1CF39: 84, - 0x1CF3A: 84, - 0x1CF3B: 84, - 0x1CF3C: 84, - 0x1CF3D: 84, - 0x1CF3E: 84, - 0x1CF3F: 84, - 0x1CF40: 84, - 0x1CF41: 84, - 0x1CF42: 84, - 0x1CF43: 84, - 0x1CF44: 84, - 0x1CF45: 84, - 0x1CF46: 84, - 0x1D167: 84, - 0x1D168: 84, - 0x1D169: 84, - 0x1D173: 84, - 0x1D174: 84, - 0x1D175: 84, - 0x1D176: 84, - 0x1D177: 84, - 0x1D178: 84, - 0x1D179: 84, - 0x1D17A: 84, - 0x1D17B: 84, - 0x1D17C: 84, - 0x1D17D: 84, - 0x1D17E: 84, - 0x1D17F: 84, - 0x1D180: 84, - 0x1D181: 84, - 0x1D182: 84, - 0x1D185: 84, - 0x1D186: 84, - 0x1D187: 84, - 0x1D188: 84, - 0x1D189: 84, - 0x1D18A: 84, - 0x1D18B: 84, - 0x1D1AA: 84, - 0x1D1AB: 84, - 0x1D1AC: 84, - 0x1D1AD: 84, - 0x1D242: 84, - 0x1D243: 84, - 0x1D244: 84, - 0x1DA00: 84, - 0x1DA01: 84, - 0x1DA02: 84, - 0x1DA03: 84, - 0x1DA04: 84, - 0x1DA05: 84, - 0x1DA06: 84, - 0x1DA07: 84, - 0x1DA08: 84, - 0x1DA09: 84, - 0x1DA0A: 84, - 0x1DA0B: 84, - 0x1DA0C: 84, - 0x1DA0D: 84, - 0x1DA0E: 84, - 0x1DA0F: 84, - 0x1DA10: 84, - 0x1DA11: 84, - 0x1DA12: 84, - 0x1DA13: 84, - 0x1DA14: 84, - 0x1DA15: 84, - 0x1DA16: 84, - 0x1DA17: 84, - 0x1DA18: 84, - 0x1DA19: 84, - 0x1DA1A: 84, - 0x1DA1B: 84, - 0x1DA1C: 84, - 0x1DA1D: 84, - 0x1DA1E: 84, - 0x1DA1F: 84, - 0x1DA20: 84, - 0x1DA21: 84, - 0x1DA22: 84, - 0x1DA23: 84, - 0x1DA24: 84, - 0x1DA25: 84, - 0x1DA26: 84, - 0x1DA27: 84, - 0x1DA28: 84, - 0x1DA29: 84, - 0x1DA2A: 84, - 0x1DA2B: 84, - 0x1DA2C: 84, - 0x1DA2D: 84, - 0x1DA2E: 84, - 0x1DA2F: 84, - 0x1DA30: 84, - 0x1DA31: 84, - 0x1DA32: 84, - 0x1DA33: 84, - 0x1DA34: 84, - 0x1DA35: 84, - 0x1DA36: 84, - 0x1DA3B: 84, - 0x1DA3C: 84, - 0x1DA3D: 84, - 0x1DA3E: 84, - 0x1DA3F: 84, - 0x1DA40: 84, - 0x1DA41: 84, - 0x1DA42: 84, - 0x1DA43: 84, - 0x1DA44: 84, - 0x1DA45: 84, - 0x1DA46: 84, - 0x1DA47: 84, - 0x1DA48: 84, - 0x1DA49: 84, - 0x1DA4A: 84, - 0x1DA4B: 84, - 0x1DA4C: 84, - 0x1DA4D: 84, - 0x1DA4E: 84, - 0x1DA4F: 84, - 0x1DA50: 84, - 0x1DA51: 84, - 0x1DA52: 84, - 0x1DA53: 84, - 0x1DA54: 84, - 0x1DA55: 84, - 0x1DA56: 84, - 0x1DA57: 84, - 0x1DA58: 84, - 0x1DA59: 84, - 0x1DA5A: 84, - 0x1DA5B: 84, - 0x1DA5C: 84, - 0x1DA5D: 84, - 0x1DA5E: 84, - 0x1DA5F: 84, - 0x1DA60: 84, - 0x1DA61: 84, - 0x1DA62: 84, - 0x1DA63: 84, - 0x1DA64: 84, - 0x1DA65: 84, - 0x1DA66: 84, - 0x1DA67: 84, - 0x1DA68: 84, - 0x1DA69: 84, - 0x1DA6A: 84, - 0x1DA6B: 84, - 0x1DA6C: 84, - 0x1DA75: 84, - 0x1DA84: 84, - 0x1DA9B: 84, - 0x1DA9C: 84, - 0x1DA9D: 84, - 0x1DA9E: 84, - 0x1DA9F: 84, - 0x1DAA1: 84, - 0x1DAA2: 84, - 0x1DAA3: 84, - 0x1DAA4: 84, - 0x1DAA5: 84, - 0x1DAA6: 84, - 0x1DAA7: 84, - 0x1DAA8: 84, - 0x1DAA9: 84, - 0x1DAAA: 84, - 0x1DAAB: 84, - 0x1DAAC: 84, - 0x1DAAD: 84, - 0x1DAAE: 84, - 0x1DAAF: 84, - 0x1E000: 84, - 0x1E001: 84, - 0x1E002: 84, - 0x1E003: 84, - 0x1E004: 84, - 0x1E005: 84, - 0x1E006: 84, - 0x1E008: 84, - 0x1E009: 84, - 0x1E00A: 84, - 0x1E00B: 84, - 0x1E00C: 84, - 0x1E00D: 84, - 0x1E00E: 84, - 0x1E00F: 84, - 0x1E010: 84, - 0x1E011: 84, - 0x1E012: 84, - 0x1E013: 84, - 0x1E014: 84, - 0x1E015: 84, - 0x1E016: 84, - 0x1E017: 84, - 0x1E018: 84, - 0x1E01B: 84, - 0x1E01C: 84, - 0x1E01D: 84, - 0x1E01E: 84, - 0x1E01F: 84, - 0x1E020: 84, - 0x1E021: 84, - 0x1E023: 84, - 0x1E024: 84, - 0x1E026: 84, - 0x1E027: 84, - 0x1E028: 84, - 0x1E029: 84, - 0x1E02A: 84, - 0x1E08F: 84, - 0x1E130: 84, - 0x1E131: 84, - 0x1E132: 84, - 0x1E133: 84, - 0x1E134: 84, - 0x1E135: 84, - 0x1E136: 84, - 0x1E2AE: 84, - 0x1E2EC: 84, - 0x1E2ED: 84, - 0x1E2EE: 84, - 0x1E2EF: 84, - 0x1E4EC: 84, - 0x1E4ED: 84, - 0x1E4EE: 84, - 0x1E4EF: 84, - 0x1E8D0: 84, - 0x1E8D1: 84, - 0x1E8D2: 84, - 0x1E8D3: 84, - 0x1E8D4: 84, - 0x1E8D5: 84, - 0x1E8D6: 84, - 0x1E900: 68, - 0x1E901: 68, - 0x1E902: 68, - 0x1E903: 68, - 0x1E904: 68, - 0x1E905: 68, - 0x1E906: 68, - 0x1E907: 68, - 0x1E908: 68, - 0x1E909: 68, - 0x1E90A: 68, - 0x1E90B: 68, - 0x1E90C: 68, - 0x1E90D: 68, - 0x1E90E: 68, - 0x1E90F: 68, - 0x1E910: 68, - 0x1E911: 68, - 0x1E912: 68, - 0x1E913: 68, - 0x1E914: 68, - 0x1E915: 68, - 0x1E916: 68, - 0x1E917: 68, - 0x1E918: 68, - 0x1E919: 68, - 0x1E91A: 68, - 0x1E91B: 68, - 0x1E91C: 68, - 0x1E91D: 68, - 0x1E91E: 68, - 0x1E91F: 68, - 0x1E920: 68, - 0x1E921: 68, - 0x1E922: 68, - 0x1E923: 68, - 0x1E924: 68, - 0x1E925: 68, - 0x1E926: 68, - 0x1E927: 68, - 0x1E928: 68, - 0x1E929: 68, - 0x1E92A: 68, - 0x1E92B: 68, - 0x1E92C: 68, - 0x1E92D: 68, - 0x1E92E: 68, - 0x1E92F: 68, - 0x1E930: 68, - 0x1E931: 68, - 0x1E932: 68, - 0x1E933: 68, - 0x1E934: 68, - 0x1E935: 68, - 0x1E936: 68, - 0x1E937: 68, - 0x1E938: 68, - 0x1E939: 68, - 0x1E93A: 68, - 0x1E93B: 68, - 0x1E93C: 68, - 0x1E93D: 68, - 0x1E93E: 68, - 0x1E93F: 68, - 0x1E940: 68, - 0x1E941: 68, - 0x1E942: 68, - 0x1E943: 68, - 0x1E944: 84, - 0x1E945: 84, - 0x1E946: 84, - 0x1E947: 84, - 0x1E948: 84, - 0x1E949: 84, - 0x1E94A: 84, - 0x1E94B: 84, - 0xE0001: 84, - 0xE0020: 84, - 0xE0021: 84, - 0xE0022: 84, - 0xE0023: 84, - 0xE0024: 84, - 0xE0025: 84, - 0xE0026: 84, - 0xE0027: 84, - 0xE0028: 84, - 0xE0029: 84, - 0xE002A: 84, - 0xE002B: 84, - 0xE002C: 84, - 0xE002D: 84, - 0xE002E: 84, - 0xE002F: 84, - 0xE0030: 84, - 0xE0031: 84, - 0xE0032: 84, - 0xE0033: 84, - 0xE0034: 84, - 0xE0035: 84, - 0xE0036: 84, - 0xE0037: 84, - 0xE0038: 84, - 0xE0039: 84, - 0xE003A: 84, - 0xE003B: 84, - 0xE003C: 84, - 0xE003D: 84, - 0xE003E: 84, - 0xE003F: 84, - 0xE0040: 84, - 0xE0041: 84, - 0xE0042: 84, - 0xE0043: 84, - 0xE0044: 84, - 0xE0045: 84, - 0xE0046: 84, - 0xE0047: 84, - 0xE0048: 84, - 0xE0049: 84, - 0xE004A: 84, - 0xE004B: 84, - 0xE004C: 84, - 0xE004D: 84, - 0xE004E: 84, - 0xE004F: 84, - 0xE0050: 84, - 0xE0051: 84, - 0xE0052: 84, - 0xE0053: 84, - 0xE0054: 84, - 0xE0055: 84, - 0xE0056: 84, - 0xE0057: 84, - 0xE0058: 84, - 0xE0059: 84, - 0xE005A: 84, - 0xE005B: 84, - 0xE005C: 84, - 0xE005D: 84, - 0xE005E: 84, - 0xE005F: 84, - 0xE0060: 84, - 0xE0061: 84, - 0xE0062: 84, - 0xE0063: 84, - 0xE0064: 84, - 0xE0065: 84, - 0xE0066: 84, - 0xE0067: 84, - 0xE0068: 84, - 0xE0069: 84, - 0xE006A: 84, - 0xE006B: 84, - 0xE006C: 84, - 0xE006D: 84, - 0xE006E: 84, - 0xE006F: 84, - 0xE0070: 84, - 0xE0071: 84, - 0xE0072: 84, - 0xE0073: 84, - 0xE0074: 84, - 0xE0075: 84, - 0xE0076: 84, - 0xE0077: 84, - 0xE0078: 84, - 0xE0079: 84, - 0xE007A: 84, - 0xE007B: 84, - 0xE007C: 84, - 0xE007D: 84, - 0xE007E: 84, - 0xE007F: 84, - 0xE0100: 84, - 0xE0101: 84, - 0xE0102: 84, - 0xE0103: 84, - 0xE0104: 84, - 0xE0105: 84, - 0xE0106: 84, - 0xE0107: 84, - 0xE0108: 84, - 0xE0109: 84, - 0xE010A: 84, - 0xE010B: 84, - 0xE010C: 84, - 0xE010D: 84, - 0xE010E: 84, - 0xE010F: 84, - 0xE0110: 84, - 0xE0111: 84, - 0xE0112: 84, - 0xE0113: 84, - 0xE0114: 84, - 0xE0115: 84, - 0xE0116: 84, - 0xE0117: 84, - 0xE0118: 84, - 0xE0119: 84, - 0xE011A: 84, - 0xE011B: 84, - 0xE011C: 84, - 0xE011D: 84, - 0xE011E: 84, - 0xE011F: 84, - 0xE0120: 84, - 0xE0121: 84, - 0xE0122: 84, - 0xE0123: 84, - 0xE0124: 84, - 0xE0125: 84, - 0xE0126: 84, - 0xE0127: 84, - 0xE0128: 84, - 0xE0129: 84, - 0xE012A: 84, - 0xE012B: 84, - 0xE012C: 84, - 0xE012D: 84, - 0xE012E: 84, - 0xE012F: 84, - 0xE0130: 84, - 0xE0131: 84, - 0xE0132: 84, - 0xE0133: 84, - 0xE0134: 84, - 0xE0135: 84, - 0xE0136: 84, - 0xE0137: 84, - 0xE0138: 84, - 0xE0139: 84, - 0xE013A: 84, - 0xE013B: 84, - 0xE013C: 84, - 0xE013D: 84, - 0xE013E: 84, - 0xE013F: 84, - 0xE0140: 84, - 0xE0141: 84, - 0xE0142: 84, - 0xE0143: 84, - 0xE0144: 84, - 0xE0145: 84, - 0xE0146: 84, - 0xE0147: 84, - 0xE0148: 84, - 0xE0149: 84, - 0xE014A: 84, - 0xE014B: 84, - 0xE014C: 84, - 0xE014D: 84, - 0xE014E: 84, - 0xE014F: 84, - 0xE0150: 84, - 0xE0151: 84, - 0xE0152: 84, - 0xE0153: 84, - 0xE0154: 84, - 0xE0155: 84, - 0xE0156: 84, - 0xE0157: 84, - 0xE0158: 84, - 0xE0159: 84, - 0xE015A: 84, - 0xE015B: 84, - 0xE015C: 84, - 0xE015D: 84, - 0xE015E: 84, - 0xE015F: 84, - 0xE0160: 84, - 0xE0161: 84, - 0xE0162: 84, - 0xE0163: 84, - 0xE0164: 84, - 0xE0165: 84, - 0xE0166: 84, - 0xE0167: 84, - 0xE0168: 84, - 0xE0169: 84, - 0xE016A: 84, - 0xE016B: 84, - 0xE016C: 84, - 0xE016D: 84, - 0xE016E: 84, - 0xE016F: 84, - 0xE0170: 84, - 0xE0171: 84, - 0xE0172: 84, - 0xE0173: 84, - 0xE0174: 84, - 0xE0175: 84, - 0xE0176: 84, - 0xE0177: 84, - 0xE0178: 84, - 0xE0179: 84, - 0xE017A: 84, - 0xE017B: 84, - 0xE017C: 84, - 0xE017D: 84, - 0xE017E: 84, - 0xE017F: 84, - 0xE0180: 84, - 0xE0181: 84, - 0xE0182: 84, - 0xE0183: 84, - 0xE0184: 84, - 0xE0185: 84, - 0xE0186: 84, - 0xE0187: 84, - 0xE0188: 84, - 0xE0189: 84, - 0xE018A: 84, - 0xE018B: 84, - 0xE018C: 84, - 0xE018D: 84, - 0xE018E: 84, - 0xE018F: 84, - 0xE0190: 84, - 0xE0191: 84, - 0xE0192: 84, - 0xE0193: 84, - 0xE0194: 84, - 0xE0195: 84, - 0xE0196: 84, - 0xE0197: 84, - 0xE0198: 84, - 0xE0199: 84, - 0xE019A: 84, - 0xE019B: 84, - 0xE019C: 84, - 0xE019D: 84, - 0xE019E: 84, - 0xE019F: 84, - 0xE01A0: 84, - 0xE01A1: 84, - 0xE01A2: 84, - 0xE01A3: 84, - 0xE01A4: 84, - 0xE01A5: 84, - 0xE01A6: 84, - 0xE01A7: 84, - 0xE01A8: 84, - 0xE01A9: 84, - 0xE01AA: 84, - 0xE01AB: 84, - 0xE01AC: 84, - 0xE01AD: 84, - 0xE01AE: 84, - 0xE01AF: 84, - 0xE01B0: 84, - 0xE01B1: 84, - 0xE01B2: 84, - 0xE01B3: 84, - 0xE01B4: 84, - 0xE01B5: 84, - 0xE01B6: 84, - 0xE01B7: 84, - 0xE01B8: 84, - 0xE01B9: 84, - 0xE01BA: 84, - 0xE01BB: 84, - 0xE01BC: 84, - 0xE01BD: 84, - 0xE01BE: 84, - 0xE01BF: 84, - 0xE01C0: 84, - 0xE01C1: 84, - 0xE01C2: 84, - 0xE01C3: 84, - 0xE01C4: 84, - 0xE01C5: 84, - 0xE01C6: 84, - 0xE01C7: 84, - 0xE01C8: 84, - 0xE01C9: 84, - 0xE01CA: 84, - 0xE01CB: 84, - 0xE01CC: 84, - 0xE01CD: 84, - 0xE01CE: 84, - 0xE01CF: 84, - 0xE01D0: 84, - 0xE01D1: 84, - 0xE01D2: 84, - 0xE01D3: 84, - 0xE01D4: 84, - 0xE01D5: 84, - 0xE01D6: 84, - 0xE01D7: 84, - 0xE01D8: 84, - 0xE01D9: 84, - 0xE01DA: 84, - 0xE01DB: 84, - 0xE01DC: 84, - 0xE01DD: 84, - 0xE01DE: 84, - 0xE01DF: 84, - 0xE01E0: 84, - 0xE01E1: 84, - 0xE01E2: 84, - 0xE01E3: 84, - 0xE01E4: 84, - 0xE01E5: 84, - 0xE01E6: 84, - 0xE01E7: 84, - 0xE01E8: 84, - 0xE01E9: 84, - 0xE01EA: 84, - 0xE01EB: 84, - 0xE01EC: 84, - 0xE01ED: 84, - 0xE01EE: 84, - 0xE01EF: 84, -} -codepoint_classes = { - "PVALID": ( - 0x2D0000002E, - 0x300000003A, - 0x610000007B, - 0xDF000000F7, - 0xF800000100, - 0x10100000102, - 0x10300000104, - 0x10500000106, - 0x10700000108, - 0x1090000010A, - 0x10B0000010C, - 0x10D0000010E, - 0x10F00000110, - 0x11100000112, - 0x11300000114, - 0x11500000116, - 0x11700000118, - 0x1190000011A, - 0x11B0000011C, - 0x11D0000011E, - 0x11F00000120, - 0x12100000122, - 0x12300000124, - 0x12500000126, - 0x12700000128, - 0x1290000012A, - 0x12B0000012C, - 0x12D0000012E, - 0x12F00000130, - 0x13100000132, - 0x13500000136, - 0x13700000139, - 0x13A0000013B, - 0x13C0000013D, - 0x13E0000013F, - 0x14200000143, - 0x14400000145, - 0x14600000147, - 0x14800000149, - 0x14B0000014C, - 0x14D0000014E, - 0x14F00000150, - 0x15100000152, - 0x15300000154, - 0x15500000156, - 0x15700000158, - 0x1590000015A, - 0x15B0000015C, - 0x15D0000015E, - 0x15F00000160, - 0x16100000162, - 0x16300000164, - 0x16500000166, - 0x16700000168, - 0x1690000016A, - 0x16B0000016C, - 0x16D0000016E, - 0x16F00000170, - 0x17100000172, - 0x17300000174, - 0x17500000176, - 0x17700000178, - 0x17A0000017B, - 0x17C0000017D, - 0x17E0000017F, - 0x18000000181, - 0x18300000184, - 0x18500000186, - 0x18800000189, - 0x18C0000018E, - 0x19200000193, - 0x19500000196, - 0x1990000019C, - 0x19E0000019F, - 0x1A1000001A2, - 0x1A3000001A4, - 0x1A5000001A6, - 0x1A8000001A9, - 0x1AA000001AC, - 0x1AD000001AE, - 0x1B0000001B1, - 0x1B4000001B5, - 0x1B6000001B7, - 0x1B9000001BC, - 0x1BD000001C4, - 0x1CE000001CF, - 0x1D0000001D1, - 0x1D2000001D3, - 0x1D4000001D5, - 0x1D6000001D7, - 0x1D8000001D9, - 0x1DA000001DB, - 0x1DC000001DE, - 0x1DF000001E0, - 0x1E1000001E2, - 0x1E3000001E4, - 0x1E5000001E6, - 0x1E7000001E8, - 0x1E9000001EA, - 0x1EB000001EC, - 0x1ED000001EE, - 0x1EF000001F1, - 0x1F5000001F6, - 0x1F9000001FA, - 0x1FB000001FC, - 0x1FD000001FE, - 0x1FF00000200, - 0x20100000202, - 0x20300000204, - 0x20500000206, - 0x20700000208, - 0x2090000020A, - 0x20B0000020C, - 0x20D0000020E, - 0x20F00000210, - 0x21100000212, - 0x21300000214, - 0x21500000216, - 0x21700000218, - 0x2190000021A, - 0x21B0000021C, - 0x21D0000021E, - 0x21F00000220, - 0x22100000222, - 0x22300000224, - 0x22500000226, - 0x22700000228, - 0x2290000022A, - 0x22B0000022C, - 0x22D0000022E, - 0x22F00000230, - 0x23100000232, - 0x2330000023A, - 0x23C0000023D, - 0x23F00000241, - 0x24200000243, - 0x24700000248, - 0x2490000024A, - 0x24B0000024C, - 0x24D0000024E, - 0x24F000002B0, - 0x2B9000002C2, - 0x2C6000002D2, - 0x2EC000002ED, - 0x2EE000002EF, - 0x30000000340, - 0x34200000343, - 0x3460000034F, - 0x35000000370, - 0x37100000372, - 0x37300000374, - 0x37700000378, - 0x37B0000037E, - 0x39000000391, - 0x3AC000003CF, - 0x3D7000003D8, - 0x3D9000003DA, - 0x3DB000003DC, - 0x3DD000003DE, - 0x3DF000003E0, - 0x3E1000003E2, - 0x3E3000003E4, - 0x3E5000003E6, - 0x3E7000003E8, - 0x3E9000003EA, - 0x3EB000003EC, - 0x3ED000003EE, - 0x3EF000003F0, - 0x3F3000003F4, - 0x3F8000003F9, - 0x3FB000003FD, - 0x43000000460, - 0x46100000462, - 0x46300000464, - 0x46500000466, - 0x46700000468, - 0x4690000046A, - 0x46B0000046C, - 0x46D0000046E, - 0x46F00000470, - 0x47100000472, - 0x47300000474, - 0x47500000476, - 0x47700000478, - 0x4790000047A, - 0x47B0000047C, - 0x47D0000047E, - 0x47F00000480, - 0x48100000482, - 0x48300000488, - 0x48B0000048C, - 0x48D0000048E, - 0x48F00000490, - 0x49100000492, - 0x49300000494, - 0x49500000496, - 0x49700000498, - 0x4990000049A, - 0x49B0000049C, - 0x49D0000049E, - 0x49F000004A0, - 0x4A1000004A2, - 0x4A3000004A4, - 0x4A5000004A6, - 0x4A7000004A8, - 0x4A9000004AA, - 0x4AB000004AC, - 0x4AD000004AE, - 0x4AF000004B0, - 0x4B1000004B2, - 0x4B3000004B4, - 0x4B5000004B6, - 0x4B7000004B8, - 0x4B9000004BA, - 0x4BB000004BC, - 0x4BD000004BE, - 0x4BF000004C0, - 0x4C2000004C3, - 0x4C4000004C5, - 0x4C6000004C7, - 0x4C8000004C9, - 0x4CA000004CB, - 0x4CC000004CD, - 0x4CE000004D0, - 0x4D1000004D2, - 0x4D3000004D4, - 0x4D5000004D6, - 0x4D7000004D8, - 0x4D9000004DA, - 0x4DB000004DC, - 0x4DD000004DE, - 0x4DF000004E0, - 0x4E1000004E2, - 0x4E3000004E4, - 0x4E5000004E6, - 0x4E7000004E8, - 0x4E9000004EA, - 0x4EB000004EC, - 0x4ED000004EE, - 0x4EF000004F0, - 0x4F1000004F2, - 0x4F3000004F4, - 0x4F5000004F6, - 0x4F7000004F8, - 0x4F9000004FA, - 0x4FB000004FC, - 0x4FD000004FE, - 0x4FF00000500, - 0x50100000502, - 0x50300000504, - 0x50500000506, - 0x50700000508, - 0x5090000050A, - 0x50B0000050C, - 0x50D0000050E, - 0x50F00000510, - 0x51100000512, - 0x51300000514, - 0x51500000516, - 0x51700000518, - 0x5190000051A, - 0x51B0000051C, - 0x51D0000051E, - 0x51F00000520, - 0x52100000522, - 0x52300000524, - 0x52500000526, - 0x52700000528, - 0x5290000052A, - 0x52B0000052C, - 0x52D0000052E, - 0x52F00000530, - 0x5590000055A, - 0x56000000587, - 0x58800000589, - 0x591000005BE, - 0x5BF000005C0, - 0x5C1000005C3, - 0x5C4000005C6, - 0x5C7000005C8, - 0x5D0000005EB, - 0x5EF000005F3, - 0x6100000061B, - 0x62000000640, - 0x64100000660, - 0x66E00000675, - 0x679000006D4, - 0x6D5000006DD, - 0x6DF000006E9, - 0x6EA000006F0, - 0x6FA00000700, - 0x7100000074B, - 0x74D000007B2, - 0x7C0000007F6, - 0x7FD000007FE, - 0x8000000082E, - 0x8400000085C, - 0x8600000086B, - 0x87000000888, - 0x8890000088F, - 0x898000008E2, - 0x8E300000958, - 0x96000000964, - 0x96600000970, - 0x97100000984, - 0x9850000098D, - 0x98F00000991, - 0x993000009A9, - 0x9AA000009B1, - 0x9B2000009B3, - 0x9B6000009BA, - 0x9BC000009C5, - 0x9C7000009C9, - 0x9CB000009CF, - 0x9D7000009D8, - 0x9E0000009E4, - 0x9E6000009F2, - 0x9FC000009FD, - 0x9FE000009FF, - 0xA0100000A04, - 0xA0500000A0B, - 0xA0F00000A11, - 0xA1300000A29, - 0xA2A00000A31, - 0xA3200000A33, - 0xA3500000A36, - 0xA3800000A3A, - 0xA3C00000A3D, - 0xA3E00000A43, - 0xA4700000A49, - 0xA4B00000A4E, - 0xA5100000A52, - 0xA5C00000A5D, - 0xA6600000A76, - 0xA8100000A84, - 0xA8500000A8E, - 0xA8F00000A92, - 0xA9300000AA9, - 0xAAA00000AB1, - 0xAB200000AB4, - 0xAB500000ABA, - 0xABC00000AC6, - 0xAC700000ACA, - 0xACB00000ACE, - 0xAD000000AD1, - 0xAE000000AE4, - 0xAE600000AF0, - 0xAF900000B00, - 0xB0100000B04, - 0xB0500000B0D, - 0xB0F00000B11, - 0xB1300000B29, - 0xB2A00000B31, - 0xB3200000B34, - 0xB3500000B3A, - 0xB3C00000B45, - 0xB4700000B49, - 0xB4B00000B4E, - 0xB5500000B58, - 0xB5F00000B64, - 0xB6600000B70, - 0xB7100000B72, - 0xB8200000B84, - 0xB8500000B8B, - 0xB8E00000B91, - 0xB9200000B96, - 0xB9900000B9B, - 0xB9C00000B9D, - 0xB9E00000BA0, - 0xBA300000BA5, - 0xBA800000BAB, - 0xBAE00000BBA, - 0xBBE00000BC3, - 0xBC600000BC9, - 0xBCA00000BCE, - 0xBD000000BD1, - 0xBD700000BD8, - 0xBE600000BF0, - 0xC0000000C0D, - 0xC0E00000C11, - 0xC1200000C29, - 0xC2A00000C3A, - 0xC3C00000C45, - 0xC4600000C49, - 0xC4A00000C4E, - 0xC5500000C57, - 0xC5800000C5B, - 0xC5D00000C5E, - 0xC6000000C64, - 0xC6600000C70, - 0xC8000000C84, - 0xC8500000C8D, - 0xC8E00000C91, - 0xC9200000CA9, - 0xCAA00000CB4, - 0xCB500000CBA, - 0xCBC00000CC5, - 0xCC600000CC9, - 0xCCA00000CCE, - 0xCD500000CD7, - 0xCDD00000CDF, - 0xCE000000CE4, - 0xCE600000CF0, - 0xCF100000CF4, - 0xD0000000D0D, - 0xD0E00000D11, - 0xD1200000D45, - 0xD4600000D49, - 0xD4A00000D4F, - 0xD5400000D58, - 0xD5F00000D64, - 0xD6600000D70, - 0xD7A00000D80, - 0xD8100000D84, - 0xD8500000D97, - 0xD9A00000DB2, - 0xDB300000DBC, - 0xDBD00000DBE, - 0xDC000000DC7, - 0xDCA00000DCB, - 0xDCF00000DD5, - 0xDD600000DD7, - 0xDD800000DE0, - 0xDE600000DF0, - 0xDF200000DF4, - 0xE0100000E33, - 0xE3400000E3B, - 0xE4000000E4F, - 0xE5000000E5A, - 0xE8100000E83, - 0xE8400000E85, - 0xE8600000E8B, - 0xE8C00000EA4, - 0xEA500000EA6, - 0xEA700000EB3, - 0xEB400000EBE, - 0xEC000000EC5, - 0xEC600000EC7, - 0xEC800000ECF, - 0xED000000EDA, - 0xEDE00000EE0, - 0xF0000000F01, - 0xF0B00000F0C, - 0xF1800000F1A, - 0xF2000000F2A, - 0xF3500000F36, - 0xF3700000F38, - 0xF3900000F3A, - 0xF3E00000F43, - 0xF4400000F48, - 0xF4900000F4D, - 0xF4E00000F52, - 0xF5300000F57, - 0xF5800000F5C, - 0xF5D00000F69, - 0xF6A00000F6D, - 0xF7100000F73, - 0xF7400000F75, - 0xF7A00000F81, - 0xF8200000F85, - 0xF8600000F93, - 0xF9400000F98, - 0xF9900000F9D, - 0xF9E00000FA2, - 0xFA300000FA7, - 0xFA800000FAC, - 0xFAD00000FB9, - 0xFBA00000FBD, - 0xFC600000FC7, - 0x10000000104A, - 0x10500000109E, - 0x10D0000010FB, - 0x10FD00001100, - 0x120000001249, - 0x124A0000124E, - 0x125000001257, - 0x125800001259, - 0x125A0000125E, - 0x126000001289, - 0x128A0000128E, - 0x1290000012B1, - 0x12B2000012B6, - 0x12B8000012BF, - 0x12C0000012C1, - 0x12C2000012C6, - 0x12C8000012D7, - 0x12D800001311, - 0x131200001316, - 0x13180000135B, - 0x135D00001360, - 0x138000001390, - 0x13A0000013F6, - 0x14010000166D, - 0x166F00001680, - 0x16810000169B, - 0x16A0000016EB, - 0x16F1000016F9, - 0x170000001716, - 0x171F00001735, - 0x174000001754, - 0x17600000176D, - 0x176E00001771, - 0x177200001774, - 0x1780000017B4, - 0x17B6000017D4, - 0x17D7000017D8, - 0x17DC000017DE, - 0x17E0000017EA, - 0x18100000181A, - 0x182000001879, - 0x1880000018AB, - 0x18B0000018F6, - 0x19000000191F, - 0x19200000192C, - 0x19300000193C, - 0x19460000196E, - 0x197000001975, - 0x1980000019AC, - 0x19B0000019CA, - 0x19D0000019DA, - 0x1A0000001A1C, - 0x1A2000001A5F, - 0x1A6000001A7D, - 0x1A7F00001A8A, - 0x1A9000001A9A, - 0x1AA700001AA8, - 0x1AB000001ABE, - 0x1ABF00001ACF, - 0x1B0000001B4D, - 0x1B5000001B5A, - 0x1B6B00001B74, - 0x1B8000001BF4, - 0x1C0000001C38, - 0x1C4000001C4A, - 0x1C4D00001C7E, - 0x1CD000001CD3, - 0x1CD400001CFB, - 0x1D0000001D2C, - 0x1D2F00001D30, - 0x1D3B00001D3C, - 0x1D4E00001D4F, - 0x1D6B00001D78, - 0x1D7900001D9B, - 0x1DC000001E00, - 0x1E0100001E02, - 0x1E0300001E04, - 0x1E0500001E06, - 0x1E0700001E08, - 0x1E0900001E0A, - 0x1E0B00001E0C, - 0x1E0D00001E0E, - 0x1E0F00001E10, - 0x1E1100001E12, - 0x1E1300001E14, - 0x1E1500001E16, - 0x1E1700001E18, - 0x1E1900001E1A, - 0x1E1B00001E1C, - 0x1E1D00001E1E, - 0x1E1F00001E20, - 0x1E2100001E22, - 0x1E2300001E24, - 0x1E2500001E26, - 0x1E2700001E28, - 0x1E2900001E2A, - 0x1E2B00001E2C, - 0x1E2D00001E2E, - 0x1E2F00001E30, - 0x1E3100001E32, - 0x1E3300001E34, - 0x1E3500001E36, - 0x1E3700001E38, - 0x1E3900001E3A, - 0x1E3B00001E3C, - 0x1E3D00001E3E, - 0x1E3F00001E40, - 0x1E4100001E42, - 0x1E4300001E44, - 0x1E4500001E46, - 0x1E4700001E48, - 0x1E4900001E4A, - 0x1E4B00001E4C, - 0x1E4D00001E4E, - 0x1E4F00001E50, - 0x1E5100001E52, - 0x1E5300001E54, - 0x1E5500001E56, - 0x1E5700001E58, - 0x1E5900001E5A, - 0x1E5B00001E5C, - 0x1E5D00001E5E, - 0x1E5F00001E60, - 0x1E6100001E62, - 0x1E6300001E64, - 0x1E6500001E66, - 0x1E6700001E68, - 0x1E6900001E6A, - 0x1E6B00001E6C, - 0x1E6D00001E6E, - 0x1E6F00001E70, - 0x1E7100001E72, - 0x1E7300001E74, - 0x1E7500001E76, - 0x1E7700001E78, - 0x1E7900001E7A, - 0x1E7B00001E7C, - 0x1E7D00001E7E, - 0x1E7F00001E80, - 0x1E8100001E82, - 0x1E8300001E84, - 0x1E8500001E86, - 0x1E8700001E88, - 0x1E8900001E8A, - 0x1E8B00001E8C, - 0x1E8D00001E8E, - 0x1E8F00001E90, - 0x1E9100001E92, - 0x1E9300001E94, - 0x1E9500001E9A, - 0x1E9C00001E9E, - 0x1E9F00001EA0, - 0x1EA100001EA2, - 0x1EA300001EA4, - 0x1EA500001EA6, - 0x1EA700001EA8, - 0x1EA900001EAA, - 0x1EAB00001EAC, - 0x1EAD00001EAE, - 0x1EAF00001EB0, - 0x1EB100001EB2, - 0x1EB300001EB4, - 0x1EB500001EB6, - 0x1EB700001EB8, - 0x1EB900001EBA, - 0x1EBB00001EBC, - 0x1EBD00001EBE, - 0x1EBF00001EC0, - 0x1EC100001EC2, - 0x1EC300001EC4, - 0x1EC500001EC6, - 0x1EC700001EC8, - 0x1EC900001ECA, - 0x1ECB00001ECC, - 0x1ECD00001ECE, - 0x1ECF00001ED0, - 0x1ED100001ED2, - 0x1ED300001ED4, - 0x1ED500001ED6, - 0x1ED700001ED8, - 0x1ED900001EDA, - 0x1EDB00001EDC, - 0x1EDD00001EDE, - 0x1EDF00001EE0, - 0x1EE100001EE2, - 0x1EE300001EE4, - 0x1EE500001EE6, - 0x1EE700001EE8, - 0x1EE900001EEA, - 0x1EEB00001EEC, - 0x1EED00001EEE, - 0x1EEF00001EF0, - 0x1EF100001EF2, - 0x1EF300001EF4, - 0x1EF500001EF6, - 0x1EF700001EF8, - 0x1EF900001EFA, - 0x1EFB00001EFC, - 0x1EFD00001EFE, - 0x1EFF00001F08, - 0x1F1000001F16, - 0x1F2000001F28, - 0x1F3000001F38, - 0x1F4000001F46, - 0x1F5000001F58, - 0x1F6000001F68, - 0x1F7000001F71, - 0x1F7200001F73, - 0x1F7400001F75, - 0x1F7600001F77, - 0x1F7800001F79, - 0x1F7A00001F7B, - 0x1F7C00001F7D, - 0x1FB000001FB2, - 0x1FB600001FB7, - 0x1FC600001FC7, - 0x1FD000001FD3, - 0x1FD600001FD8, - 0x1FE000001FE3, - 0x1FE400001FE8, - 0x1FF600001FF7, - 0x214E0000214F, - 0x218400002185, - 0x2C3000002C60, - 0x2C6100002C62, - 0x2C6500002C67, - 0x2C6800002C69, - 0x2C6A00002C6B, - 0x2C6C00002C6D, - 0x2C7100002C72, - 0x2C7300002C75, - 0x2C7600002C7C, - 0x2C8100002C82, - 0x2C8300002C84, - 0x2C8500002C86, - 0x2C8700002C88, - 0x2C8900002C8A, - 0x2C8B00002C8C, - 0x2C8D00002C8E, - 0x2C8F00002C90, - 0x2C9100002C92, - 0x2C9300002C94, - 0x2C9500002C96, - 0x2C9700002C98, - 0x2C9900002C9A, - 0x2C9B00002C9C, - 0x2C9D00002C9E, - 0x2C9F00002CA0, - 0x2CA100002CA2, - 0x2CA300002CA4, - 0x2CA500002CA6, - 0x2CA700002CA8, - 0x2CA900002CAA, - 0x2CAB00002CAC, - 0x2CAD00002CAE, - 0x2CAF00002CB0, - 0x2CB100002CB2, - 0x2CB300002CB4, - 0x2CB500002CB6, - 0x2CB700002CB8, - 0x2CB900002CBA, - 0x2CBB00002CBC, - 0x2CBD00002CBE, - 0x2CBF00002CC0, - 0x2CC100002CC2, - 0x2CC300002CC4, - 0x2CC500002CC6, - 0x2CC700002CC8, - 0x2CC900002CCA, - 0x2CCB00002CCC, - 0x2CCD00002CCE, - 0x2CCF00002CD0, - 0x2CD100002CD2, - 0x2CD300002CD4, - 0x2CD500002CD6, - 0x2CD700002CD8, - 0x2CD900002CDA, - 0x2CDB00002CDC, - 0x2CDD00002CDE, - 0x2CDF00002CE0, - 0x2CE100002CE2, - 0x2CE300002CE5, - 0x2CEC00002CED, - 0x2CEE00002CF2, - 0x2CF300002CF4, - 0x2D0000002D26, - 0x2D2700002D28, - 0x2D2D00002D2E, - 0x2D3000002D68, - 0x2D7F00002D97, - 0x2DA000002DA7, - 0x2DA800002DAF, - 0x2DB000002DB7, - 0x2DB800002DBF, - 0x2DC000002DC7, - 0x2DC800002DCF, - 0x2DD000002DD7, - 0x2DD800002DDF, - 0x2DE000002E00, - 0x2E2F00002E30, - 0x300500003008, - 0x302A0000302E, - 0x303C0000303D, - 0x304100003097, - 0x30990000309B, - 0x309D0000309F, - 0x30A1000030FB, - 0x30FC000030FF, - 0x310500003130, - 0x31A0000031C0, - 0x31F000003200, - 0x340000004DC0, - 0x4E000000A48D, - 0xA4D00000A4FE, - 0xA5000000A60D, - 0xA6100000A62C, - 0xA6410000A642, - 0xA6430000A644, - 0xA6450000A646, - 0xA6470000A648, - 0xA6490000A64A, - 0xA64B0000A64C, - 0xA64D0000A64E, - 0xA64F0000A650, - 0xA6510000A652, - 0xA6530000A654, - 0xA6550000A656, - 0xA6570000A658, - 0xA6590000A65A, - 0xA65B0000A65C, - 0xA65D0000A65E, - 0xA65F0000A660, - 0xA6610000A662, - 0xA6630000A664, - 0xA6650000A666, - 0xA6670000A668, - 0xA6690000A66A, - 0xA66B0000A66C, - 0xA66D0000A670, - 0xA6740000A67E, - 0xA67F0000A680, - 0xA6810000A682, - 0xA6830000A684, - 0xA6850000A686, - 0xA6870000A688, - 0xA6890000A68A, - 0xA68B0000A68C, - 0xA68D0000A68E, - 0xA68F0000A690, - 0xA6910000A692, - 0xA6930000A694, - 0xA6950000A696, - 0xA6970000A698, - 0xA6990000A69A, - 0xA69B0000A69C, - 0xA69E0000A6E6, - 0xA6F00000A6F2, - 0xA7170000A720, - 0xA7230000A724, - 0xA7250000A726, - 0xA7270000A728, - 0xA7290000A72A, - 0xA72B0000A72C, - 0xA72D0000A72E, - 0xA72F0000A732, - 0xA7330000A734, - 0xA7350000A736, - 0xA7370000A738, - 0xA7390000A73A, - 0xA73B0000A73C, - 0xA73D0000A73E, - 0xA73F0000A740, - 0xA7410000A742, - 0xA7430000A744, - 0xA7450000A746, - 0xA7470000A748, - 0xA7490000A74A, - 0xA74B0000A74C, - 0xA74D0000A74E, - 0xA74F0000A750, - 0xA7510000A752, - 0xA7530000A754, - 0xA7550000A756, - 0xA7570000A758, - 0xA7590000A75A, - 0xA75B0000A75C, - 0xA75D0000A75E, - 0xA75F0000A760, - 0xA7610000A762, - 0xA7630000A764, - 0xA7650000A766, - 0xA7670000A768, - 0xA7690000A76A, - 0xA76B0000A76C, - 0xA76D0000A76E, - 0xA76F0000A770, - 0xA7710000A779, - 0xA77A0000A77B, - 0xA77C0000A77D, - 0xA77F0000A780, - 0xA7810000A782, - 0xA7830000A784, - 0xA7850000A786, - 0xA7870000A789, - 0xA78C0000A78D, - 0xA78E0000A790, - 0xA7910000A792, - 0xA7930000A796, - 0xA7970000A798, - 0xA7990000A79A, - 0xA79B0000A79C, - 0xA79D0000A79E, - 0xA79F0000A7A0, - 0xA7A10000A7A2, - 0xA7A30000A7A4, - 0xA7A50000A7A6, - 0xA7A70000A7A8, - 0xA7A90000A7AA, - 0xA7AF0000A7B0, - 0xA7B50000A7B6, - 0xA7B70000A7B8, - 0xA7B90000A7BA, - 0xA7BB0000A7BC, - 0xA7BD0000A7BE, - 0xA7BF0000A7C0, - 0xA7C10000A7C2, - 0xA7C30000A7C4, - 0xA7C80000A7C9, - 0xA7CA0000A7CB, - 0xA7D10000A7D2, - 0xA7D30000A7D4, - 0xA7D50000A7D6, - 0xA7D70000A7D8, - 0xA7D90000A7DA, - 0xA7F60000A7F8, - 0xA7FA0000A828, - 0xA82C0000A82D, - 0xA8400000A874, - 0xA8800000A8C6, - 0xA8D00000A8DA, - 0xA8E00000A8F8, - 0xA8FB0000A8FC, - 0xA8FD0000A92E, - 0xA9300000A954, - 0xA9800000A9C1, - 0xA9CF0000A9DA, - 0xA9E00000A9FF, - 0xAA000000AA37, - 0xAA400000AA4E, - 0xAA500000AA5A, - 0xAA600000AA77, - 0xAA7A0000AAC3, - 0xAADB0000AADE, - 0xAAE00000AAF0, - 0xAAF20000AAF7, - 0xAB010000AB07, - 0xAB090000AB0F, - 0xAB110000AB17, - 0xAB200000AB27, - 0xAB280000AB2F, - 0xAB300000AB5B, - 0xAB600000AB69, - 0xABC00000ABEB, - 0xABEC0000ABEE, - 0xABF00000ABFA, - 0xAC000000D7A4, - 0xFA0E0000FA10, - 0xFA110000FA12, - 0xFA130000FA15, - 0xFA1F0000FA20, - 0xFA210000FA22, - 0xFA230000FA25, - 0xFA270000FA2A, - 0xFB1E0000FB1F, - 0xFE200000FE30, - 0xFE730000FE74, - 0x100000001000C, - 0x1000D00010027, - 0x100280001003B, - 0x1003C0001003E, - 0x1003F0001004E, - 0x100500001005E, - 0x10080000100FB, - 0x101FD000101FE, - 0x102800001029D, - 0x102A0000102D1, - 0x102E0000102E1, - 0x1030000010320, - 0x1032D00010341, - 0x103420001034A, - 0x103500001037B, - 0x103800001039E, - 0x103A0000103C4, - 0x103C8000103D0, - 0x104280001049E, - 0x104A0000104AA, - 0x104D8000104FC, - 0x1050000010528, - 0x1053000010564, - 0x10597000105A2, - 0x105A3000105B2, - 0x105B3000105BA, - 0x105BB000105BD, - 0x1060000010737, - 0x1074000010756, - 0x1076000010768, - 0x1078000010781, - 0x1080000010806, - 0x1080800010809, - 0x1080A00010836, - 0x1083700010839, - 0x1083C0001083D, - 0x1083F00010856, - 0x1086000010877, - 0x108800001089F, - 0x108E0000108F3, - 0x108F4000108F6, - 0x1090000010916, - 0x109200001093A, - 0x10980000109B8, - 0x109BE000109C0, - 0x10A0000010A04, - 0x10A0500010A07, - 0x10A0C00010A14, - 0x10A1500010A18, - 0x10A1900010A36, - 0x10A3800010A3B, - 0x10A3F00010A40, - 0x10A6000010A7D, - 0x10A8000010A9D, - 0x10AC000010AC8, - 0x10AC900010AE7, - 0x10B0000010B36, - 0x10B4000010B56, - 0x10B6000010B73, - 0x10B8000010B92, - 0x10C0000010C49, - 0x10CC000010CF3, - 0x10D0000010D28, - 0x10D3000010D3A, - 0x10E8000010EAA, - 0x10EAB00010EAD, - 0x10EB000010EB2, - 0x10EFD00010F1D, - 0x10F2700010F28, - 0x10F3000010F51, - 0x10F7000010F86, - 0x10FB000010FC5, - 0x10FE000010FF7, - 0x1100000011047, - 0x1106600011076, - 0x1107F000110BB, - 0x110C2000110C3, - 0x110D0000110E9, - 0x110F0000110FA, - 0x1110000011135, - 0x1113600011140, - 0x1114400011148, - 0x1115000011174, - 0x1117600011177, - 0x11180000111C5, - 0x111C9000111CD, - 0x111CE000111DB, - 0x111DC000111DD, - 0x1120000011212, - 0x1121300011238, - 0x1123E00011242, - 0x1128000011287, - 0x1128800011289, - 0x1128A0001128E, - 0x1128F0001129E, - 0x1129F000112A9, - 0x112B0000112EB, - 0x112F0000112FA, - 0x1130000011304, - 0x113050001130D, - 0x1130F00011311, - 0x1131300011329, - 0x1132A00011331, - 0x1133200011334, - 0x113350001133A, - 0x1133B00011345, - 0x1134700011349, - 0x1134B0001134E, - 0x1135000011351, - 0x1135700011358, - 0x1135D00011364, - 0x113660001136D, - 0x1137000011375, - 0x114000001144B, - 0x114500001145A, - 0x1145E00011462, - 0x11480000114C6, - 0x114C7000114C8, - 0x114D0000114DA, - 0x11580000115B6, - 0x115B8000115C1, - 0x115D8000115DE, - 0x1160000011641, - 0x1164400011645, - 0x116500001165A, - 0x11680000116B9, - 0x116C0000116CA, - 0x117000001171B, - 0x1171D0001172C, - 0x117300001173A, - 0x1174000011747, - 0x118000001183B, - 0x118C0000118EA, - 0x118FF00011907, - 0x119090001190A, - 0x1190C00011914, - 0x1191500011917, - 0x1191800011936, - 0x1193700011939, - 0x1193B00011944, - 0x119500001195A, - 0x119A0000119A8, - 0x119AA000119D8, - 0x119DA000119E2, - 0x119E3000119E5, - 0x11A0000011A3F, - 0x11A4700011A48, - 0x11A5000011A9A, - 0x11A9D00011A9E, - 0x11AB000011AF9, - 0x11C0000011C09, - 0x11C0A00011C37, - 0x11C3800011C41, - 0x11C5000011C5A, - 0x11C7200011C90, - 0x11C9200011CA8, - 0x11CA900011CB7, - 0x11D0000011D07, - 0x11D0800011D0A, - 0x11D0B00011D37, - 0x11D3A00011D3B, - 0x11D3C00011D3E, - 0x11D3F00011D48, - 0x11D5000011D5A, - 0x11D6000011D66, - 0x11D6700011D69, - 0x11D6A00011D8F, - 0x11D9000011D92, - 0x11D9300011D99, - 0x11DA000011DAA, - 0x11EE000011EF7, - 0x11F0000011F11, - 0x11F1200011F3B, - 0x11F3E00011F43, - 0x11F5000011F5A, - 0x11FB000011FB1, - 0x120000001239A, - 0x1248000012544, - 0x12F9000012FF1, - 0x1300000013430, - 0x1344000013456, - 0x1440000014647, - 0x1680000016A39, - 0x16A4000016A5F, - 0x16A6000016A6A, - 0x16A7000016ABF, - 0x16AC000016ACA, - 0x16AD000016AEE, - 0x16AF000016AF5, - 0x16B0000016B37, - 0x16B4000016B44, - 0x16B5000016B5A, - 0x16B6300016B78, - 0x16B7D00016B90, - 0x16E6000016E80, - 0x16F0000016F4B, - 0x16F4F00016F88, - 0x16F8F00016FA0, - 0x16FE000016FE2, - 0x16FE300016FE5, - 0x16FF000016FF2, - 0x17000000187F8, - 0x1880000018CD6, - 0x18D0000018D09, - 0x1AFF00001AFF4, - 0x1AFF50001AFFC, - 0x1AFFD0001AFFF, - 0x1B0000001B123, - 0x1B1320001B133, - 0x1B1500001B153, - 0x1B1550001B156, - 0x1B1640001B168, - 0x1B1700001B2FC, - 0x1BC000001BC6B, - 0x1BC700001BC7D, - 0x1BC800001BC89, - 0x1BC900001BC9A, - 0x1BC9D0001BC9F, - 0x1CF000001CF2E, - 0x1CF300001CF47, - 0x1DA000001DA37, - 0x1DA3B0001DA6D, - 0x1DA750001DA76, - 0x1DA840001DA85, - 0x1DA9B0001DAA0, - 0x1DAA10001DAB0, - 0x1DF000001DF1F, - 0x1DF250001DF2B, - 0x1E0000001E007, - 0x1E0080001E019, - 0x1E01B0001E022, - 0x1E0230001E025, - 0x1E0260001E02B, - 0x1E08F0001E090, - 0x1E1000001E12D, - 0x1E1300001E13E, - 0x1E1400001E14A, - 0x1E14E0001E14F, - 0x1E2900001E2AF, - 0x1E2C00001E2FA, - 0x1E4D00001E4FA, - 0x1E7E00001E7E7, - 0x1E7E80001E7EC, - 0x1E7ED0001E7EF, - 0x1E7F00001E7FF, - 0x1E8000001E8C5, - 0x1E8D00001E8D7, - 0x1E9220001E94C, - 0x1E9500001E95A, - 0x200000002A6E0, - 0x2A7000002B73A, - 0x2B7400002B81E, - 0x2B8200002CEA2, - 0x2CEB00002EBE1, - 0x2EBF00002EE5E, - 0x300000003134B, - 0x31350000323B0, - ), - "CONTEXTJ": (0x200C0000200E,), - "CONTEXTO": ( - 0xB7000000B8, - 0x37500000376, - 0x5F3000005F5, - 0x6600000066A, - 0x6F0000006FA, - 0x30FB000030FC, - ), -} diff --git a/.venv/Lib/site-packages/idna/intranges.py b/.venv/Lib/site-packages/idna/intranges.py deleted file mode 100644 index 7bfaa8d..0000000 --- a/.venv/Lib/site-packages/idna/intranges.py +++ /dev/null @@ -1,57 +0,0 @@ -""" -Given a list of integers, made up of (hopefully) a small number of long runs -of consecutive integers, compute a representation of the form -((start1, end1), (start2, end2) ...). Then answer the question "was x present -in the original list?" in time O(log(# runs)). -""" - -import bisect -from typing import List, Tuple - - -def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: - """Represent a list of integers as a sequence of ranges: - ((start_0, end_0), (start_1, end_1), ...), such that the original - integers are exactly those x such that start_i <= x < end_i for some i. - - Ranges are encoded as single integers (start << 32 | end), not as tuples. - """ - - sorted_list = sorted(list_) - ranges = [] - last_write = -1 - for i in range(len(sorted_list)): - if i + 1 < len(sorted_list): - if sorted_list[i] == sorted_list[i + 1] - 1: - continue - current_range = sorted_list[last_write + 1 : i + 1] - ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) - last_write = i - - return tuple(ranges) - - -def _encode_range(start: int, end: int) -> int: - return (start << 32) | end - - -def _decode_range(r: int) -> Tuple[int, int]: - return (r >> 32), (r & ((1 << 32) - 1)) - - -def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: - """Determine if `int_` falls into one of the ranges in `ranges`.""" - tuple_ = _encode_range(int_, 0) - pos = bisect.bisect_left(ranges, tuple_) - # we could be immediately ahead of a tuple (start, end) - # with start < int_ <= end - if pos > 0: - left, right = _decode_range(ranges[pos - 1]) - if left <= int_ < right: - return True - # or we could be immediately behind a tuple (int_, end) - if pos < len(ranges): - left, _ = _decode_range(ranges[pos]) - if left == int_: - return True - return False diff --git a/.venv/Lib/site-packages/idna/package_data.py b/.venv/Lib/site-packages/idna/package_data.py deleted file mode 100644 index 514ff7e..0000000 --- a/.venv/Lib/site-packages/idna/package_data.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "3.10" diff --git a/.venv/Lib/site-packages/idna/py.typed b/.venv/Lib/site-packages/idna/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/idna/uts46data.py b/.venv/Lib/site-packages/idna/uts46data.py deleted file mode 100644 index eb89432..0000000 --- a/.venv/Lib/site-packages/idna/uts46data.py +++ /dev/null @@ -1,8681 +0,0 @@ -# This file is automatically generated by tools/idna-data -# vim: set fileencoding=utf-8 : - -from typing import List, Tuple, Union - -"""IDNA Mapping Table from UTS46.""" - - -__version__ = "15.1.0" - - -def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x0, "3"), - (0x1, "3"), - (0x2, "3"), - (0x3, "3"), - (0x4, "3"), - (0x5, "3"), - (0x6, "3"), - (0x7, "3"), - (0x8, "3"), - (0x9, "3"), - (0xA, "3"), - (0xB, "3"), - (0xC, "3"), - (0xD, "3"), - (0xE, "3"), - (0xF, "3"), - (0x10, "3"), - (0x11, "3"), - (0x12, "3"), - (0x13, "3"), - (0x14, "3"), - (0x15, "3"), - (0x16, "3"), - (0x17, "3"), - (0x18, "3"), - (0x19, "3"), - (0x1A, "3"), - (0x1B, "3"), - (0x1C, "3"), - (0x1D, "3"), - (0x1E, "3"), - (0x1F, "3"), - (0x20, "3"), - (0x21, "3"), - (0x22, "3"), - (0x23, "3"), - (0x24, "3"), - (0x25, "3"), - (0x26, "3"), - (0x27, "3"), - (0x28, "3"), - (0x29, "3"), - (0x2A, "3"), - (0x2B, "3"), - (0x2C, "3"), - (0x2D, "V"), - (0x2E, "V"), - (0x2F, "3"), - (0x30, "V"), - (0x31, "V"), - (0x32, "V"), - (0x33, "V"), - (0x34, "V"), - (0x35, "V"), - (0x36, "V"), - (0x37, "V"), - (0x38, "V"), - (0x39, "V"), - (0x3A, "3"), - (0x3B, "3"), - (0x3C, "3"), - (0x3D, "3"), - (0x3E, "3"), - (0x3F, "3"), - (0x40, "3"), - (0x41, "M", "a"), - (0x42, "M", "b"), - (0x43, "M", "c"), - (0x44, "M", "d"), - (0x45, "M", "e"), - (0x46, "M", "f"), - (0x47, "M", "g"), - (0x48, "M", "h"), - (0x49, "M", "i"), - (0x4A, "M", "j"), - (0x4B, "M", "k"), - (0x4C, "M", "l"), - (0x4D, "M", "m"), - (0x4E, "M", "n"), - (0x4F, "M", "o"), - (0x50, "M", "p"), - (0x51, "M", "q"), - (0x52, "M", "r"), - (0x53, "M", "s"), - (0x54, "M", "t"), - (0x55, "M", "u"), - (0x56, "M", "v"), - (0x57, "M", "w"), - (0x58, "M", "x"), - (0x59, "M", "y"), - (0x5A, "M", "z"), - (0x5B, "3"), - (0x5C, "3"), - (0x5D, "3"), - (0x5E, "3"), - (0x5F, "3"), - (0x60, "3"), - (0x61, "V"), - (0x62, "V"), - (0x63, "V"), - ] - - -def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x64, "V"), - (0x65, "V"), - (0x66, "V"), - (0x67, "V"), - (0x68, "V"), - (0x69, "V"), - (0x6A, "V"), - (0x6B, "V"), - (0x6C, "V"), - (0x6D, "V"), - (0x6E, "V"), - (0x6F, "V"), - (0x70, "V"), - (0x71, "V"), - (0x72, "V"), - (0x73, "V"), - (0x74, "V"), - (0x75, "V"), - (0x76, "V"), - (0x77, "V"), - (0x78, "V"), - (0x79, "V"), - (0x7A, "V"), - (0x7B, "3"), - (0x7C, "3"), - (0x7D, "3"), - (0x7E, "3"), - (0x7F, "3"), - (0x80, "X"), - (0x81, "X"), - (0x82, "X"), - (0x83, "X"), - (0x84, "X"), - (0x85, "X"), - (0x86, "X"), - (0x87, "X"), - (0x88, "X"), - (0x89, "X"), - (0x8A, "X"), - (0x8B, "X"), - (0x8C, "X"), - (0x8D, "X"), - (0x8E, "X"), - (0x8F, "X"), - (0x90, "X"), - (0x91, "X"), - (0x92, "X"), - (0x93, "X"), - (0x94, "X"), - (0x95, "X"), - (0x96, "X"), - (0x97, "X"), - (0x98, "X"), - (0x99, "X"), - (0x9A, "X"), - (0x9B, "X"), - (0x9C, "X"), - (0x9D, "X"), - (0x9E, "X"), - (0x9F, "X"), - (0xA0, "3", " "), - (0xA1, "V"), - (0xA2, "V"), - (0xA3, "V"), - (0xA4, "V"), - (0xA5, "V"), - (0xA6, "V"), - (0xA7, "V"), - (0xA8, "3", " ̈"), - (0xA9, "V"), - (0xAA, "M", "a"), - (0xAB, "V"), - (0xAC, "V"), - (0xAD, "I"), - (0xAE, "V"), - (0xAF, "3", " ̄"), - (0xB0, "V"), - (0xB1, "V"), - (0xB2, "M", "2"), - (0xB3, "M", "3"), - (0xB4, "3", " ́"), - (0xB5, "M", "μ"), - (0xB6, "V"), - (0xB7, "V"), - (0xB8, "3", " ̧"), - (0xB9, "M", "1"), - (0xBA, "M", "o"), - (0xBB, "V"), - (0xBC, "M", "1⁄4"), - (0xBD, "M", "1⁄2"), - (0xBE, "M", "3⁄4"), - (0xBF, "V"), - (0xC0, "M", "à"), - (0xC1, "M", "á"), - (0xC2, "M", "â"), - (0xC3, "M", "ã"), - (0xC4, "M", "ä"), - (0xC5, "M", "å"), - (0xC6, "M", "æ"), - (0xC7, "M", "ç"), - ] - - -def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC8, "M", "è"), - (0xC9, "M", "é"), - (0xCA, "M", "ê"), - (0xCB, "M", "ë"), - (0xCC, "M", "ì"), - (0xCD, "M", "í"), - (0xCE, "M", "î"), - (0xCF, "M", "ï"), - (0xD0, "M", "ð"), - (0xD1, "M", "ñ"), - (0xD2, "M", "ò"), - (0xD3, "M", "ó"), - (0xD4, "M", "ô"), - (0xD5, "M", "õ"), - (0xD6, "M", "ö"), - (0xD7, "V"), - (0xD8, "M", "ø"), - (0xD9, "M", "ù"), - (0xDA, "M", "ú"), - (0xDB, "M", "û"), - (0xDC, "M", "ü"), - (0xDD, "M", "ý"), - (0xDE, "M", "þ"), - (0xDF, "D", "ss"), - (0xE0, "V"), - (0xE1, "V"), - (0xE2, "V"), - (0xE3, "V"), - (0xE4, "V"), - (0xE5, "V"), - (0xE6, "V"), - (0xE7, "V"), - (0xE8, "V"), - (0xE9, "V"), - (0xEA, "V"), - (0xEB, "V"), - (0xEC, "V"), - (0xED, "V"), - (0xEE, "V"), - (0xEF, "V"), - (0xF0, "V"), - (0xF1, "V"), - (0xF2, "V"), - (0xF3, "V"), - (0xF4, "V"), - (0xF5, "V"), - (0xF6, "V"), - (0xF7, "V"), - (0xF8, "V"), - (0xF9, "V"), - (0xFA, "V"), - (0xFB, "V"), - (0xFC, "V"), - (0xFD, "V"), - (0xFE, "V"), - (0xFF, "V"), - (0x100, "M", "ā"), - (0x101, "V"), - (0x102, "M", "ă"), - (0x103, "V"), - (0x104, "M", "ą"), - (0x105, "V"), - (0x106, "M", "ć"), - (0x107, "V"), - (0x108, "M", "ĉ"), - (0x109, "V"), - (0x10A, "M", "ċ"), - (0x10B, "V"), - (0x10C, "M", "č"), - (0x10D, "V"), - (0x10E, "M", "ď"), - (0x10F, "V"), - (0x110, "M", "đ"), - (0x111, "V"), - (0x112, "M", "ē"), - (0x113, "V"), - (0x114, "M", "ĕ"), - (0x115, "V"), - (0x116, "M", "ė"), - (0x117, "V"), - (0x118, "M", "ę"), - (0x119, "V"), - (0x11A, "M", "ě"), - (0x11B, "V"), - (0x11C, "M", "ĝ"), - (0x11D, "V"), - (0x11E, "M", "ğ"), - (0x11F, "V"), - (0x120, "M", "ġ"), - (0x121, "V"), - (0x122, "M", "ģ"), - (0x123, "V"), - (0x124, "M", "ĥ"), - (0x125, "V"), - (0x126, "M", "ħ"), - (0x127, "V"), - (0x128, "M", "ĩ"), - (0x129, "V"), - (0x12A, "M", "ī"), - (0x12B, "V"), - ] - - -def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x12C, "M", "ĭ"), - (0x12D, "V"), - (0x12E, "M", "į"), - (0x12F, "V"), - (0x130, "M", "i̇"), - (0x131, "V"), - (0x132, "M", "ij"), - (0x134, "M", "ĵ"), - (0x135, "V"), - (0x136, "M", "ķ"), - (0x137, "V"), - (0x139, "M", "ĺ"), - (0x13A, "V"), - (0x13B, "M", "ļ"), - (0x13C, "V"), - (0x13D, "M", "ľ"), - (0x13E, "V"), - (0x13F, "M", "l·"), - (0x141, "M", "ł"), - (0x142, "V"), - (0x143, "M", "ń"), - (0x144, "V"), - (0x145, "M", "ņ"), - (0x146, "V"), - (0x147, "M", "ň"), - (0x148, "V"), - (0x149, "M", "ʼn"), - (0x14A, "M", "ŋ"), - (0x14B, "V"), - (0x14C, "M", "ō"), - (0x14D, "V"), - (0x14E, "M", "ŏ"), - (0x14F, "V"), - (0x150, "M", "ő"), - (0x151, "V"), - (0x152, "M", "œ"), - (0x153, "V"), - (0x154, "M", "ŕ"), - (0x155, "V"), - (0x156, "M", "ŗ"), - (0x157, "V"), - (0x158, "M", "ř"), - (0x159, "V"), - (0x15A, "M", "ś"), - (0x15B, "V"), - (0x15C, "M", "ŝ"), - (0x15D, "V"), - (0x15E, "M", "ş"), - (0x15F, "V"), - (0x160, "M", "š"), - (0x161, "V"), - (0x162, "M", "ţ"), - (0x163, "V"), - (0x164, "M", "ť"), - (0x165, "V"), - (0x166, "M", "ŧ"), - (0x167, "V"), - (0x168, "M", "ũ"), - (0x169, "V"), - (0x16A, "M", "ū"), - (0x16B, "V"), - (0x16C, "M", "ŭ"), - (0x16D, "V"), - (0x16E, "M", "ů"), - (0x16F, "V"), - (0x170, "M", "ű"), - (0x171, "V"), - (0x172, "M", "ų"), - (0x173, "V"), - (0x174, "M", "ŵ"), - (0x175, "V"), - (0x176, "M", "ŷ"), - (0x177, "V"), - (0x178, "M", "ÿ"), - (0x179, "M", "ź"), - (0x17A, "V"), - (0x17B, "M", "ż"), - (0x17C, "V"), - (0x17D, "M", "ž"), - (0x17E, "V"), - (0x17F, "M", "s"), - (0x180, "V"), - (0x181, "M", "ɓ"), - (0x182, "M", "ƃ"), - (0x183, "V"), - (0x184, "M", "ƅ"), - (0x185, "V"), - (0x186, "M", "ɔ"), - (0x187, "M", "ƈ"), - (0x188, "V"), - (0x189, "M", "ɖ"), - (0x18A, "M", "ɗ"), - (0x18B, "M", "ƌ"), - (0x18C, "V"), - (0x18E, "M", "ǝ"), - (0x18F, "M", "ə"), - (0x190, "M", "ɛ"), - (0x191, "M", "ƒ"), - (0x192, "V"), - (0x193, "M", "ɠ"), - ] - - -def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x194, "M", "ɣ"), - (0x195, "V"), - (0x196, "M", "ɩ"), - (0x197, "M", "ɨ"), - (0x198, "M", "ƙ"), - (0x199, "V"), - (0x19C, "M", "ɯ"), - (0x19D, "M", "ɲ"), - (0x19E, "V"), - (0x19F, "M", "ɵ"), - (0x1A0, "M", "ơ"), - (0x1A1, "V"), - (0x1A2, "M", "ƣ"), - (0x1A3, "V"), - (0x1A4, "M", "ƥ"), - (0x1A5, "V"), - (0x1A6, "M", "ʀ"), - (0x1A7, "M", "ƨ"), - (0x1A8, "V"), - (0x1A9, "M", "ʃ"), - (0x1AA, "V"), - (0x1AC, "M", "ƭ"), - (0x1AD, "V"), - (0x1AE, "M", "ʈ"), - (0x1AF, "M", "ư"), - (0x1B0, "V"), - (0x1B1, "M", "ʊ"), - (0x1B2, "M", "ʋ"), - (0x1B3, "M", "ƴ"), - (0x1B4, "V"), - (0x1B5, "M", "ƶ"), - (0x1B6, "V"), - (0x1B7, "M", "ʒ"), - (0x1B8, "M", "ƹ"), - (0x1B9, "V"), - (0x1BC, "M", "ƽ"), - (0x1BD, "V"), - (0x1C4, "M", "dž"), - (0x1C7, "M", "lj"), - (0x1CA, "M", "nj"), - (0x1CD, "M", "ǎ"), - (0x1CE, "V"), - (0x1CF, "M", "ǐ"), - (0x1D0, "V"), - (0x1D1, "M", "ǒ"), - (0x1D2, "V"), - (0x1D3, "M", "ǔ"), - (0x1D4, "V"), - (0x1D5, "M", "ǖ"), - (0x1D6, "V"), - (0x1D7, "M", "ǘ"), - (0x1D8, "V"), - (0x1D9, "M", "ǚ"), - (0x1DA, "V"), - (0x1DB, "M", "ǜ"), - (0x1DC, "V"), - (0x1DE, "M", "ǟ"), - (0x1DF, "V"), - (0x1E0, "M", "ǡ"), - (0x1E1, "V"), - (0x1E2, "M", "ǣ"), - (0x1E3, "V"), - (0x1E4, "M", "ǥ"), - (0x1E5, "V"), - (0x1E6, "M", "ǧ"), - (0x1E7, "V"), - (0x1E8, "M", "ǩ"), - (0x1E9, "V"), - (0x1EA, "M", "ǫ"), - (0x1EB, "V"), - (0x1EC, "M", "ǭ"), - (0x1ED, "V"), - (0x1EE, "M", "ǯ"), - (0x1EF, "V"), - (0x1F1, "M", "dz"), - (0x1F4, "M", "ǵ"), - (0x1F5, "V"), - (0x1F6, "M", "ƕ"), - (0x1F7, "M", "ƿ"), - (0x1F8, "M", "ǹ"), - (0x1F9, "V"), - (0x1FA, "M", "ǻ"), - (0x1FB, "V"), - (0x1FC, "M", "ǽ"), - (0x1FD, "V"), - (0x1FE, "M", "ǿ"), - (0x1FF, "V"), - (0x200, "M", "ȁ"), - (0x201, "V"), - (0x202, "M", "ȃ"), - (0x203, "V"), - (0x204, "M", "ȅ"), - (0x205, "V"), - (0x206, "M", "ȇ"), - (0x207, "V"), - (0x208, "M", "ȉ"), - (0x209, "V"), - (0x20A, "M", "ȋ"), - (0x20B, "V"), - (0x20C, "M", "ȍ"), - ] - - -def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x20D, "V"), - (0x20E, "M", "ȏ"), - (0x20F, "V"), - (0x210, "M", "ȑ"), - (0x211, "V"), - (0x212, "M", "ȓ"), - (0x213, "V"), - (0x214, "M", "ȕ"), - (0x215, "V"), - (0x216, "M", "ȗ"), - (0x217, "V"), - (0x218, "M", "ș"), - (0x219, "V"), - (0x21A, "M", "ț"), - (0x21B, "V"), - (0x21C, "M", "ȝ"), - (0x21D, "V"), - (0x21E, "M", "ȟ"), - (0x21F, "V"), - (0x220, "M", "ƞ"), - (0x221, "V"), - (0x222, "M", "ȣ"), - (0x223, "V"), - (0x224, "M", "ȥ"), - (0x225, "V"), - (0x226, "M", "ȧ"), - (0x227, "V"), - (0x228, "M", "ȩ"), - (0x229, "V"), - (0x22A, "M", "ȫ"), - (0x22B, "V"), - (0x22C, "M", "ȭ"), - (0x22D, "V"), - (0x22E, "M", "ȯ"), - (0x22F, "V"), - (0x230, "M", "ȱ"), - (0x231, "V"), - (0x232, "M", "ȳ"), - (0x233, "V"), - (0x23A, "M", "ⱥ"), - (0x23B, "M", "ȼ"), - (0x23C, "V"), - (0x23D, "M", "ƚ"), - (0x23E, "M", "ⱦ"), - (0x23F, "V"), - (0x241, "M", "ɂ"), - (0x242, "V"), - (0x243, "M", "ƀ"), - (0x244, "M", "ʉ"), - (0x245, "M", "ʌ"), - (0x246, "M", "ɇ"), - (0x247, "V"), - (0x248, "M", "ɉ"), - (0x249, "V"), - (0x24A, "M", "ɋ"), - (0x24B, "V"), - (0x24C, "M", "ɍ"), - (0x24D, "V"), - (0x24E, "M", "ɏ"), - (0x24F, "V"), - (0x2B0, "M", "h"), - (0x2B1, "M", "ɦ"), - (0x2B2, "M", "j"), - (0x2B3, "M", "r"), - (0x2B4, "M", "ɹ"), - (0x2B5, "M", "ɻ"), - (0x2B6, "M", "ʁ"), - (0x2B7, "M", "w"), - (0x2B8, "M", "y"), - (0x2B9, "V"), - (0x2D8, "3", " ̆"), - (0x2D9, "3", " ̇"), - (0x2DA, "3", " ̊"), - (0x2DB, "3", " ̨"), - (0x2DC, "3", " ̃"), - (0x2DD, "3", " ̋"), - (0x2DE, "V"), - (0x2E0, "M", "ɣ"), - (0x2E1, "M", "l"), - (0x2E2, "M", "s"), - (0x2E3, "M", "x"), - (0x2E4, "M", "ʕ"), - (0x2E5, "V"), - (0x340, "M", "̀"), - (0x341, "M", "́"), - (0x342, "V"), - (0x343, "M", "̓"), - (0x344, "M", "̈́"), - (0x345, "M", "ι"), - (0x346, "V"), - (0x34F, "I"), - (0x350, "V"), - (0x370, "M", "ͱ"), - (0x371, "V"), - (0x372, "M", "ͳ"), - (0x373, "V"), - (0x374, "M", "ʹ"), - (0x375, "V"), - (0x376, "M", "ͷ"), - (0x377, "V"), - ] - - -def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x378, "X"), - (0x37A, "3", " ι"), - (0x37B, "V"), - (0x37E, "3", ";"), - (0x37F, "M", "ϳ"), - (0x380, "X"), - (0x384, "3", " ́"), - (0x385, "3", " ̈́"), - (0x386, "M", "ά"), - (0x387, "M", "·"), - (0x388, "M", "έ"), - (0x389, "M", "ή"), - (0x38A, "M", "ί"), - (0x38B, "X"), - (0x38C, "M", "ό"), - (0x38D, "X"), - (0x38E, "M", "ύ"), - (0x38F, "M", "ώ"), - (0x390, "V"), - (0x391, "M", "α"), - (0x392, "M", "β"), - (0x393, "M", "γ"), - (0x394, "M", "δ"), - (0x395, "M", "ε"), - (0x396, "M", "ζ"), - (0x397, "M", "η"), - (0x398, "M", "θ"), - (0x399, "M", "ι"), - (0x39A, "M", "κ"), - (0x39B, "M", "λ"), - (0x39C, "M", "μ"), - (0x39D, "M", "ν"), - (0x39E, "M", "ξ"), - (0x39F, "M", "ο"), - (0x3A0, "M", "π"), - (0x3A1, "M", "ρ"), - (0x3A2, "X"), - (0x3A3, "M", "σ"), - (0x3A4, "M", "τ"), - (0x3A5, "M", "υ"), - (0x3A6, "M", "φ"), - (0x3A7, "M", "χ"), - (0x3A8, "M", "ψ"), - (0x3A9, "M", "ω"), - (0x3AA, "M", "ϊ"), - (0x3AB, "M", "ϋ"), - (0x3AC, "V"), - (0x3C2, "D", "σ"), - (0x3C3, "V"), - (0x3CF, "M", "ϗ"), - (0x3D0, "M", "β"), - (0x3D1, "M", "θ"), - (0x3D2, "M", "υ"), - (0x3D3, "M", "ύ"), - (0x3D4, "M", "ϋ"), - (0x3D5, "M", "φ"), - (0x3D6, "M", "π"), - (0x3D7, "V"), - (0x3D8, "M", "ϙ"), - (0x3D9, "V"), - (0x3DA, "M", "ϛ"), - (0x3DB, "V"), - (0x3DC, "M", "ϝ"), - (0x3DD, "V"), - (0x3DE, "M", "ϟ"), - (0x3DF, "V"), - (0x3E0, "M", "ϡ"), - (0x3E1, "V"), - (0x3E2, "M", "ϣ"), - (0x3E3, "V"), - (0x3E4, "M", "ϥ"), - (0x3E5, "V"), - (0x3E6, "M", "ϧ"), - (0x3E7, "V"), - (0x3E8, "M", "ϩ"), - (0x3E9, "V"), - (0x3EA, "M", "ϫ"), - (0x3EB, "V"), - (0x3EC, "M", "ϭ"), - (0x3ED, "V"), - (0x3EE, "M", "ϯ"), - (0x3EF, "V"), - (0x3F0, "M", "κ"), - (0x3F1, "M", "ρ"), - (0x3F2, "M", "σ"), - (0x3F3, "V"), - (0x3F4, "M", "θ"), - (0x3F5, "M", "ε"), - (0x3F6, "V"), - (0x3F7, "M", "ϸ"), - (0x3F8, "V"), - (0x3F9, "M", "σ"), - (0x3FA, "M", "ϻ"), - (0x3FB, "V"), - (0x3FD, "M", "ͻ"), - (0x3FE, "M", "ͼ"), - (0x3FF, "M", "ͽ"), - (0x400, "M", "ѐ"), - (0x401, "M", "ё"), - (0x402, "M", "ђ"), - ] - - -def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x403, "M", "ѓ"), - (0x404, "M", "є"), - (0x405, "M", "ѕ"), - (0x406, "M", "і"), - (0x407, "M", "ї"), - (0x408, "M", "ј"), - (0x409, "M", "љ"), - (0x40A, "M", "њ"), - (0x40B, "M", "ћ"), - (0x40C, "M", "ќ"), - (0x40D, "M", "ѝ"), - (0x40E, "M", "ў"), - (0x40F, "M", "џ"), - (0x410, "M", "а"), - (0x411, "M", "б"), - (0x412, "M", "в"), - (0x413, "M", "г"), - (0x414, "M", "д"), - (0x415, "M", "е"), - (0x416, "M", "ж"), - (0x417, "M", "з"), - (0x418, "M", "и"), - (0x419, "M", "й"), - (0x41A, "M", "к"), - (0x41B, "M", "л"), - (0x41C, "M", "м"), - (0x41D, "M", "н"), - (0x41E, "M", "о"), - (0x41F, "M", "п"), - (0x420, "M", "р"), - (0x421, "M", "с"), - (0x422, "M", "т"), - (0x423, "M", "у"), - (0x424, "M", "ф"), - (0x425, "M", "х"), - (0x426, "M", "ц"), - (0x427, "M", "ч"), - (0x428, "M", "ш"), - (0x429, "M", "щ"), - (0x42A, "M", "ъ"), - (0x42B, "M", "ы"), - (0x42C, "M", "ь"), - (0x42D, "M", "э"), - (0x42E, "M", "ю"), - (0x42F, "M", "я"), - (0x430, "V"), - (0x460, "M", "ѡ"), - (0x461, "V"), - (0x462, "M", "ѣ"), - (0x463, "V"), - (0x464, "M", "ѥ"), - (0x465, "V"), - (0x466, "M", "ѧ"), - (0x467, "V"), - (0x468, "M", "ѩ"), - (0x469, "V"), - (0x46A, "M", "ѫ"), - (0x46B, "V"), - (0x46C, "M", "ѭ"), - (0x46D, "V"), - (0x46E, "M", "ѯ"), - (0x46F, "V"), - (0x470, "M", "ѱ"), - (0x471, "V"), - (0x472, "M", "ѳ"), - (0x473, "V"), - (0x474, "M", "ѵ"), - (0x475, "V"), - (0x476, "M", "ѷ"), - (0x477, "V"), - (0x478, "M", "ѹ"), - (0x479, "V"), - (0x47A, "M", "ѻ"), - (0x47B, "V"), - (0x47C, "M", "ѽ"), - (0x47D, "V"), - (0x47E, "M", "ѿ"), - (0x47F, "V"), - (0x480, "M", "ҁ"), - (0x481, "V"), - (0x48A, "M", "ҋ"), - (0x48B, "V"), - (0x48C, "M", "ҍ"), - (0x48D, "V"), - (0x48E, "M", "ҏ"), - (0x48F, "V"), - (0x490, "M", "ґ"), - (0x491, "V"), - (0x492, "M", "ғ"), - (0x493, "V"), - (0x494, "M", "ҕ"), - (0x495, "V"), - (0x496, "M", "җ"), - (0x497, "V"), - (0x498, "M", "ҙ"), - (0x499, "V"), - (0x49A, "M", "қ"), - (0x49B, "V"), - (0x49C, "M", "ҝ"), - (0x49D, "V"), - ] - - -def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x49E, "M", "ҟ"), - (0x49F, "V"), - (0x4A0, "M", "ҡ"), - (0x4A1, "V"), - (0x4A2, "M", "ң"), - (0x4A3, "V"), - (0x4A4, "M", "ҥ"), - (0x4A5, "V"), - (0x4A6, "M", "ҧ"), - (0x4A7, "V"), - (0x4A8, "M", "ҩ"), - (0x4A9, "V"), - (0x4AA, "M", "ҫ"), - (0x4AB, "V"), - (0x4AC, "M", "ҭ"), - (0x4AD, "V"), - (0x4AE, "M", "ү"), - (0x4AF, "V"), - (0x4B0, "M", "ұ"), - (0x4B1, "V"), - (0x4B2, "M", "ҳ"), - (0x4B3, "V"), - (0x4B4, "M", "ҵ"), - (0x4B5, "V"), - (0x4B6, "M", "ҷ"), - (0x4B7, "V"), - (0x4B8, "M", "ҹ"), - (0x4B9, "V"), - (0x4BA, "M", "һ"), - (0x4BB, "V"), - (0x4BC, "M", "ҽ"), - (0x4BD, "V"), - (0x4BE, "M", "ҿ"), - (0x4BF, "V"), - (0x4C0, "X"), - (0x4C1, "M", "ӂ"), - (0x4C2, "V"), - (0x4C3, "M", "ӄ"), - (0x4C4, "V"), - (0x4C5, "M", "ӆ"), - (0x4C6, "V"), - (0x4C7, "M", "ӈ"), - (0x4C8, "V"), - (0x4C9, "M", "ӊ"), - (0x4CA, "V"), - (0x4CB, "M", "ӌ"), - (0x4CC, "V"), - (0x4CD, "M", "ӎ"), - (0x4CE, "V"), - (0x4D0, "M", "ӑ"), - (0x4D1, "V"), - (0x4D2, "M", "ӓ"), - (0x4D3, "V"), - (0x4D4, "M", "ӕ"), - (0x4D5, "V"), - (0x4D6, "M", "ӗ"), - (0x4D7, "V"), - (0x4D8, "M", "ә"), - (0x4D9, "V"), - (0x4DA, "M", "ӛ"), - (0x4DB, "V"), - (0x4DC, "M", "ӝ"), - (0x4DD, "V"), - (0x4DE, "M", "ӟ"), - (0x4DF, "V"), - (0x4E0, "M", "ӡ"), - (0x4E1, "V"), - (0x4E2, "M", "ӣ"), - (0x4E3, "V"), - (0x4E4, "M", "ӥ"), - (0x4E5, "V"), - (0x4E6, "M", "ӧ"), - (0x4E7, "V"), - (0x4E8, "M", "ө"), - (0x4E9, "V"), - (0x4EA, "M", "ӫ"), - (0x4EB, "V"), - (0x4EC, "M", "ӭ"), - (0x4ED, "V"), - (0x4EE, "M", "ӯ"), - (0x4EF, "V"), - (0x4F0, "M", "ӱ"), - (0x4F1, "V"), - (0x4F2, "M", "ӳ"), - (0x4F3, "V"), - (0x4F4, "M", "ӵ"), - (0x4F5, "V"), - (0x4F6, "M", "ӷ"), - (0x4F7, "V"), - (0x4F8, "M", "ӹ"), - (0x4F9, "V"), - (0x4FA, "M", "ӻ"), - (0x4FB, "V"), - (0x4FC, "M", "ӽ"), - (0x4FD, "V"), - (0x4FE, "M", "ӿ"), - (0x4FF, "V"), - (0x500, "M", "ԁ"), - (0x501, "V"), - (0x502, "M", "ԃ"), - ] - - -def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x503, "V"), - (0x504, "M", "ԅ"), - (0x505, "V"), - (0x506, "M", "ԇ"), - (0x507, "V"), - (0x508, "M", "ԉ"), - (0x509, "V"), - (0x50A, "M", "ԋ"), - (0x50B, "V"), - (0x50C, "M", "ԍ"), - (0x50D, "V"), - (0x50E, "M", "ԏ"), - (0x50F, "V"), - (0x510, "M", "ԑ"), - (0x511, "V"), - (0x512, "M", "ԓ"), - (0x513, "V"), - (0x514, "M", "ԕ"), - (0x515, "V"), - (0x516, "M", "ԗ"), - (0x517, "V"), - (0x518, "M", "ԙ"), - (0x519, "V"), - (0x51A, "M", "ԛ"), - (0x51B, "V"), - (0x51C, "M", "ԝ"), - (0x51D, "V"), - (0x51E, "M", "ԟ"), - (0x51F, "V"), - (0x520, "M", "ԡ"), - (0x521, "V"), - (0x522, "M", "ԣ"), - (0x523, "V"), - (0x524, "M", "ԥ"), - (0x525, "V"), - (0x526, "M", "ԧ"), - (0x527, "V"), - (0x528, "M", "ԩ"), - (0x529, "V"), - (0x52A, "M", "ԫ"), - (0x52B, "V"), - (0x52C, "M", "ԭ"), - (0x52D, "V"), - (0x52E, "M", "ԯ"), - (0x52F, "V"), - (0x530, "X"), - (0x531, "M", "ա"), - (0x532, "M", "բ"), - (0x533, "M", "գ"), - (0x534, "M", "դ"), - (0x535, "M", "ե"), - (0x536, "M", "զ"), - (0x537, "M", "է"), - (0x538, "M", "ը"), - (0x539, "M", "թ"), - (0x53A, "M", "ժ"), - (0x53B, "M", "ի"), - (0x53C, "M", "լ"), - (0x53D, "M", "խ"), - (0x53E, "M", "ծ"), - (0x53F, "M", "կ"), - (0x540, "M", "հ"), - (0x541, "M", "ձ"), - (0x542, "M", "ղ"), - (0x543, "M", "ճ"), - (0x544, "M", "մ"), - (0x545, "M", "յ"), - (0x546, "M", "ն"), - (0x547, "M", "շ"), - (0x548, "M", "ո"), - (0x549, "M", "չ"), - (0x54A, "M", "պ"), - (0x54B, "M", "ջ"), - (0x54C, "M", "ռ"), - (0x54D, "M", "ս"), - (0x54E, "M", "վ"), - (0x54F, "M", "տ"), - (0x550, "M", "ր"), - (0x551, "M", "ց"), - (0x552, "M", "ւ"), - (0x553, "M", "փ"), - (0x554, "M", "ք"), - (0x555, "M", "օ"), - (0x556, "M", "ֆ"), - (0x557, "X"), - (0x559, "V"), - (0x587, "M", "եւ"), - (0x588, "V"), - (0x58B, "X"), - (0x58D, "V"), - (0x590, "X"), - (0x591, "V"), - (0x5C8, "X"), - (0x5D0, "V"), - (0x5EB, "X"), - (0x5EF, "V"), - (0x5F5, "X"), - (0x606, "V"), - (0x61C, "X"), - (0x61D, "V"), - ] - - -def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x675, "M", "اٴ"), - (0x676, "M", "وٴ"), - (0x677, "M", "ۇٴ"), - (0x678, "M", "يٴ"), - (0x679, "V"), - (0x6DD, "X"), - (0x6DE, "V"), - (0x70E, "X"), - (0x710, "V"), - (0x74B, "X"), - (0x74D, "V"), - (0x7B2, "X"), - (0x7C0, "V"), - (0x7FB, "X"), - (0x7FD, "V"), - (0x82E, "X"), - (0x830, "V"), - (0x83F, "X"), - (0x840, "V"), - (0x85C, "X"), - (0x85E, "V"), - (0x85F, "X"), - (0x860, "V"), - (0x86B, "X"), - (0x870, "V"), - (0x88F, "X"), - (0x898, "V"), - (0x8E2, "X"), - (0x8E3, "V"), - (0x958, "M", "क़"), - (0x959, "M", "ख़"), - (0x95A, "M", "ग़"), - (0x95B, "M", "ज़"), - (0x95C, "M", "ड़"), - (0x95D, "M", "ढ़"), - (0x95E, "M", "फ़"), - (0x95F, "M", "य़"), - (0x960, "V"), - (0x984, "X"), - (0x985, "V"), - (0x98D, "X"), - (0x98F, "V"), - (0x991, "X"), - (0x993, "V"), - (0x9A9, "X"), - (0x9AA, "V"), - (0x9B1, "X"), - (0x9B2, "V"), - (0x9B3, "X"), - (0x9B6, "V"), - (0x9BA, "X"), - (0x9BC, "V"), - (0x9C5, "X"), - (0x9C7, "V"), - (0x9C9, "X"), - (0x9CB, "V"), - (0x9CF, "X"), - (0x9D7, "V"), - (0x9D8, "X"), - (0x9DC, "M", "ড়"), - (0x9DD, "M", "ঢ়"), - (0x9DE, "X"), - (0x9DF, "M", "য়"), - (0x9E0, "V"), - (0x9E4, "X"), - (0x9E6, "V"), - (0x9FF, "X"), - (0xA01, "V"), - (0xA04, "X"), - (0xA05, "V"), - (0xA0B, "X"), - (0xA0F, "V"), - (0xA11, "X"), - (0xA13, "V"), - (0xA29, "X"), - (0xA2A, "V"), - (0xA31, "X"), - (0xA32, "V"), - (0xA33, "M", "ਲ਼"), - (0xA34, "X"), - (0xA35, "V"), - (0xA36, "M", "ਸ਼"), - (0xA37, "X"), - (0xA38, "V"), - (0xA3A, "X"), - (0xA3C, "V"), - (0xA3D, "X"), - (0xA3E, "V"), - (0xA43, "X"), - (0xA47, "V"), - (0xA49, "X"), - (0xA4B, "V"), - (0xA4E, "X"), - (0xA51, "V"), - (0xA52, "X"), - (0xA59, "M", "ਖ਼"), - (0xA5A, "M", "ਗ਼"), - (0xA5B, "M", "ਜ਼"), - (0xA5C, "V"), - (0xA5D, "X"), - ] - - -def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA5E, "M", "ਫ਼"), - (0xA5F, "X"), - (0xA66, "V"), - (0xA77, "X"), - (0xA81, "V"), - (0xA84, "X"), - (0xA85, "V"), - (0xA8E, "X"), - (0xA8F, "V"), - (0xA92, "X"), - (0xA93, "V"), - (0xAA9, "X"), - (0xAAA, "V"), - (0xAB1, "X"), - (0xAB2, "V"), - (0xAB4, "X"), - (0xAB5, "V"), - (0xABA, "X"), - (0xABC, "V"), - (0xAC6, "X"), - (0xAC7, "V"), - (0xACA, "X"), - (0xACB, "V"), - (0xACE, "X"), - (0xAD0, "V"), - (0xAD1, "X"), - (0xAE0, "V"), - (0xAE4, "X"), - (0xAE6, "V"), - (0xAF2, "X"), - (0xAF9, "V"), - (0xB00, "X"), - (0xB01, "V"), - (0xB04, "X"), - (0xB05, "V"), - (0xB0D, "X"), - (0xB0F, "V"), - (0xB11, "X"), - (0xB13, "V"), - (0xB29, "X"), - (0xB2A, "V"), - (0xB31, "X"), - (0xB32, "V"), - (0xB34, "X"), - (0xB35, "V"), - (0xB3A, "X"), - (0xB3C, "V"), - (0xB45, "X"), - (0xB47, "V"), - (0xB49, "X"), - (0xB4B, "V"), - (0xB4E, "X"), - (0xB55, "V"), - (0xB58, "X"), - (0xB5C, "M", "ଡ଼"), - (0xB5D, "M", "ଢ଼"), - (0xB5E, "X"), - (0xB5F, "V"), - (0xB64, "X"), - (0xB66, "V"), - (0xB78, "X"), - (0xB82, "V"), - (0xB84, "X"), - (0xB85, "V"), - (0xB8B, "X"), - (0xB8E, "V"), - (0xB91, "X"), - (0xB92, "V"), - (0xB96, "X"), - (0xB99, "V"), - (0xB9B, "X"), - (0xB9C, "V"), - (0xB9D, "X"), - (0xB9E, "V"), - (0xBA0, "X"), - (0xBA3, "V"), - (0xBA5, "X"), - (0xBA8, "V"), - (0xBAB, "X"), - (0xBAE, "V"), - (0xBBA, "X"), - (0xBBE, "V"), - (0xBC3, "X"), - (0xBC6, "V"), - (0xBC9, "X"), - (0xBCA, "V"), - (0xBCE, "X"), - (0xBD0, "V"), - (0xBD1, "X"), - (0xBD7, "V"), - (0xBD8, "X"), - (0xBE6, "V"), - (0xBFB, "X"), - (0xC00, "V"), - (0xC0D, "X"), - (0xC0E, "V"), - (0xC11, "X"), - (0xC12, "V"), - (0xC29, "X"), - (0xC2A, "V"), - ] - - -def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC3A, "X"), - (0xC3C, "V"), - (0xC45, "X"), - (0xC46, "V"), - (0xC49, "X"), - (0xC4A, "V"), - (0xC4E, "X"), - (0xC55, "V"), - (0xC57, "X"), - (0xC58, "V"), - (0xC5B, "X"), - (0xC5D, "V"), - (0xC5E, "X"), - (0xC60, "V"), - (0xC64, "X"), - (0xC66, "V"), - (0xC70, "X"), - (0xC77, "V"), - (0xC8D, "X"), - (0xC8E, "V"), - (0xC91, "X"), - (0xC92, "V"), - (0xCA9, "X"), - (0xCAA, "V"), - (0xCB4, "X"), - (0xCB5, "V"), - (0xCBA, "X"), - (0xCBC, "V"), - (0xCC5, "X"), - (0xCC6, "V"), - (0xCC9, "X"), - (0xCCA, "V"), - (0xCCE, "X"), - (0xCD5, "V"), - (0xCD7, "X"), - (0xCDD, "V"), - (0xCDF, "X"), - (0xCE0, "V"), - (0xCE4, "X"), - (0xCE6, "V"), - (0xCF0, "X"), - (0xCF1, "V"), - (0xCF4, "X"), - (0xD00, "V"), - (0xD0D, "X"), - (0xD0E, "V"), - (0xD11, "X"), - (0xD12, "V"), - (0xD45, "X"), - (0xD46, "V"), - (0xD49, "X"), - (0xD4A, "V"), - (0xD50, "X"), - (0xD54, "V"), - (0xD64, "X"), - (0xD66, "V"), - (0xD80, "X"), - (0xD81, "V"), - (0xD84, "X"), - (0xD85, "V"), - (0xD97, "X"), - (0xD9A, "V"), - (0xDB2, "X"), - (0xDB3, "V"), - (0xDBC, "X"), - (0xDBD, "V"), - (0xDBE, "X"), - (0xDC0, "V"), - (0xDC7, "X"), - (0xDCA, "V"), - (0xDCB, "X"), - (0xDCF, "V"), - (0xDD5, "X"), - (0xDD6, "V"), - (0xDD7, "X"), - (0xDD8, "V"), - (0xDE0, "X"), - (0xDE6, "V"), - (0xDF0, "X"), - (0xDF2, "V"), - (0xDF5, "X"), - (0xE01, "V"), - (0xE33, "M", "ํา"), - (0xE34, "V"), - (0xE3B, "X"), - (0xE3F, "V"), - (0xE5C, "X"), - (0xE81, "V"), - (0xE83, "X"), - (0xE84, "V"), - (0xE85, "X"), - (0xE86, "V"), - (0xE8B, "X"), - (0xE8C, "V"), - (0xEA4, "X"), - (0xEA5, "V"), - (0xEA6, "X"), - (0xEA7, "V"), - (0xEB3, "M", "ໍາ"), - (0xEB4, "V"), - ] - - -def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xEBE, "X"), - (0xEC0, "V"), - (0xEC5, "X"), - (0xEC6, "V"), - (0xEC7, "X"), - (0xEC8, "V"), - (0xECF, "X"), - (0xED0, "V"), - (0xEDA, "X"), - (0xEDC, "M", "ຫນ"), - (0xEDD, "M", "ຫມ"), - (0xEDE, "V"), - (0xEE0, "X"), - (0xF00, "V"), - (0xF0C, "M", "་"), - (0xF0D, "V"), - (0xF43, "M", "གྷ"), - (0xF44, "V"), - (0xF48, "X"), - (0xF49, "V"), - (0xF4D, "M", "ཌྷ"), - (0xF4E, "V"), - (0xF52, "M", "དྷ"), - (0xF53, "V"), - (0xF57, "M", "བྷ"), - (0xF58, "V"), - (0xF5C, "M", "ཛྷ"), - (0xF5D, "V"), - (0xF69, "M", "ཀྵ"), - (0xF6A, "V"), - (0xF6D, "X"), - (0xF71, "V"), - (0xF73, "M", "ཱི"), - (0xF74, "V"), - (0xF75, "M", "ཱུ"), - (0xF76, "M", "ྲྀ"), - (0xF77, "M", "ྲཱྀ"), - (0xF78, "M", "ླྀ"), - (0xF79, "M", "ླཱྀ"), - (0xF7A, "V"), - (0xF81, "M", "ཱྀ"), - (0xF82, "V"), - (0xF93, "M", "ྒྷ"), - (0xF94, "V"), - (0xF98, "X"), - (0xF99, "V"), - (0xF9D, "M", "ྜྷ"), - (0xF9E, "V"), - (0xFA2, "M", "ྡྷ"), - (0xFA3, "V"), - (0xFA7, "M", "ྦྷ"), - (0xFA8, "V"), - (0xFAC, "M", "ྫྷ"), - (0xFAD, "V"), - (0xFB9, "M", "ྐྵ"), - (0xFBA, "V"), - (0xFBD, "X"), - (0xFBE, "V"), - (0xFCD, "X"), - (0xFCE, "V"), - (0xFDB, "X"), - (0x1000, "V"), - (0x10A0, "X"), - (0x10C7, "M", "ⴧ"), - (0x10C8, "X"), - (0x10CD, "M", "ⴭ"), - (0x10CE, "X"), - (0x10D0, "V"), - (0x10FC, "M", "ნ"), - (0x10FD, "V"), - (0x115F, "X"), - (0x1161, "V"), - (0x1249, "X"), - (0x124A, "V"), - (0x124E, "X"), - (0x1250, "V"), - (0x1257, "X"), - (0x1258, "V"), - (0x1259, "X"), - (0x125A, "V"), - (0x125E, "X"), - (0x1260, "V"), - (0x1289, "X"), - (0x128A, "V"), - (0x128E, "X"), - (0x1290, "V"), - (0x12B1, "X"), - (0x12B2, "V"), - (0x12B6, "X"), - (0x12B8, "V"), - (0x12BF, "X"), - (0x12C0, "V"), - (0x12C1, "X"), - (0x12C2, "V"), - (0x12C6, "X"), - (0x12C8, "V"), - (0x12D7, "X"), - (0x12D8, "V"), - (0x1311, "X"), - (0x1312, "V"), - ] - - -def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1316, "X"), - (0x1318, "V"), - (0x135B, "X"), - (0x135D, "V"), - (0x137D, "X"), - (0x1380, "V"), - (0x139A, "X"), - (0x13A0, "V"), - (0x13F6, "X"), - (0x13F8, "M", "Ᏸ"), - (0x13F9, "M", "Ᏹ"), - (0x13FA, "M", "Ᏺ"), - (0x13FB, "M", "Ᏻ"), - (0x13FC, "M", "Ᏼ"), - (0x13FD, "M", "Ᏽ"), - (0x13FE, "X"), - (0x1400, "V"), - (0x1680, "X"), - (0x1681, "V"), - (0x169D, "X"), - (0x16A0, "V"), - (0x16F9, "X"), - (0x1700, "V"), - (0x1716, "X"), - (0x171F, "V"), - (0x1737, "X"), - (0x1740, "V"), - (0x1754, "X"), - (0x1760, "V"), - (0x176D, "X"), - (0x176E, "V"), - (0x1771, "X"), - (0x1772, "V"), - (0x1774, "X"), - (0x1780, "V"), - (0x17B4, "X"), - (0x17B6, "V"), - (0x17DE, "X"), - (0x17E0, "V"), - (0x17EA, "X"), - (0x17F0, "V"), - (0x17FA, "X"), - (0x1800, "V"), - (0x1806, "X"), - (0x1807, "V"), - (0x180B, "I"), - (0x180E, "X"), - (0x180F, "I"), - (0x1810, "V"), - (0x181A, "X"), - (0x1820, "V"), - (0x1879, "X"), - (0x1880, "V"), - (0x18AB, "X"), - (0x18B0, "V"), - (0x18F6, "X"), - (0x1900, "V"), - (0x191F, "X"), - (0x1920, "V"), - (0x192C, "X"), - (0x1930, "V"), - (0x193C, "X"), - (0x1940, "V"), - (0x1941, "X"), - (0x1944, "V"), - (0x196E, "X"), - (0x1970, "V"), - (0x1975, "X"), - (0x1980, "V"), - (0x19AC, "X"), - (0x19B0, "V"), - (0x19CA, "X"), - (0x19D0, "V"), - (0x19DB, "X"), - (0x19DE, "V"), - (0x1A1C, "X"), - (0x1A1E, "V"), - (0x1A5F, "X"), - (0x1A60, "V"), - (0x1A7D, "X"), - (0x1A7F, "V"), - (0x1A8A, "X"), - (0x1A90, "V"), - (0x1A9A, "X"), - (0x1AA0, "V"), - (0x1AAE, "X"), - (0x1AB0, "V"), - (0x1ACF, "X"), - (0x1B00, "V"), - (0x1B4D, "X"), - (0x1B50, "V"), - (0x1B7F, "X"), - (0x1B80, "V"), - (0x1BF4, "X"), - (0x1BFC, "V"), - (0x1C38, "X"), - (0x1C3B, "V"), - (0x1C4A, "X"), - (0x1C4D, "V"), - (0x1C80, "M", "в"), - ] - - -def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1C81, "M", "д"), - (0x1C82, "M", "о"), - (0x1C83, "M", "с"), - (0x1C84, "M", "т"), - (0x1C86, "M", "ъ"), - (0x1C87, "M", "ѣ"), - (0x1C88, "M", "ꙋ"), - (0x1C89, "X"), - (0x1C90, "M", "ა"), - (0x1C91, "M", "ბ"), - (0x1C92, "M", "გ"), - (0x1C93, "M", "დ"), - (0x1C94, "M", "ე"), - (0x1C95, "M", "ვ"), - (0x1C96, "M", "ზ"), - (0x1C97, "M", "თ"), - (0x1C98, "M", "ი"), - (0x1C99, "M", "კ"), - (0x1C9A, "M", "ლ"), - (0x1C9B, "M", "მ"), - (0x1C9C, "M", "ნ"), - (0x1C9D, "M", "ო"), - (0x1C9E, "M", "პ"), - (0x1C9F, "M", "ჟ"), - (0x1CA0, "M", "რ"), - (0x1CA1, "M", "ს"), - (0x1CA2, "M", "ტ"), - (0x1CA3, "M", "უ"), - (0x1CA4, "M", "ფ"), - (0x1CA5, "M", "ქ"), - (0x1CA6, "M", "ღ"), - (0x1CA7, "M", "ყ"), - (0x1CA8, "M", "შ"), - (0x1CA9, "M", "ჩ"), - (0x1CAA, "M", "ც"), - (0x1CAB, "M", "ძ"), - (0x1CAC, "M", "წ"), - (0x1CAD, "M", "ჭ"), - (0x1CAE, "M", "ხ"), - (0x1CAF, "M", "ჯ"), - (0x1CB0, "M", "ჰ"), - (0x1CB1, "M", "ჱ"), - (0x1CB2, "M", "ჲ"), - (0x1CB3, "M", "ჳ"), - (0x1CB4, "M", "ჴ"), - (0x1CB5, "M", "ჵ"), - (0x1CB6, "M", "ჶ"), - (0x1CB7, "M", "ჷ"), - (0x1CB8, "M", "ჸ"), - (0x1CB9, "M", "ჹ"), - (0x1CBA, "M", "ჺ"), - (0x1CBB, "X"), - (0x1CBD, "M", "ჽ"), - (0x1CBE, "M", "ჾ"), - (0x1CBF, "M", "ჿ"), - (0x1CC0, "V"), - (0x1CC8, "X"), - (0x1CD0, "V"), - (0x1CFB, "X"), - (0x1D00, "V"), - (0x1D2C, "M", "a"), - (0x1D2D, "M", "æ"), - (0x1D2E, "M", "b"), - (0x1D2F, "V"), - (0x1D30, "M", "d"), - (0x1D31, "M", "e"), - (0x1D32, "M", "ǝ"), - (0x1D33, "M", "g"), - (0x1D34, "M", "h"), - (0x1D35, "M", "i"), - (0x1D36, "M", "j"), - (0x1D37, "M", "k"), - (0x1D38, "M", "l"), - (0x1D39, "M", "m"), - (0x1D3A, "M", "n"), - (0x1D3B, "V"), - (0x1D3C, "M", "o"), - (0x1D3D, "M", "ȣ"), - (0x1D3E, "M", "p"), - (0x1D3F, "M", "r"), - (0x1D40, "M", "t"), - (0x1D41, "M", "u"), - (0x1D42, "M", "w"), - (0x1D43, "M", "a"), - (0x1D44, "M", "ɐ"), - (0x1D45, "M", "ɑ"), - (0x1D46, "M", "ᴂ"), - (0x1D47, "M", "b"), - (0x1D48, "M", "d"), - (0x1D49, "M", "e"), - (0x1D4A, "M", "ə"), - (0x1D4B, "M", "ɛ"), - (0x1D4C, "M", "ɜ"), - (0x1D4D, "M", "g"), - (0x1D4E, "V"), - (0x1D4F, "M", "k"), - (0x1D50, "M", "m"), - (0x1D51, "M", "ŋ"), - (0x1D52, "M", "o"), - (0x1D53, "M", "ɔ"), - ] - - -def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D54, "M", "ᴖ"), - (0x1D55, "M", "ᴗ"), - (0x1D56, "M", "p"), - (0x1D57, "M", "t"), - (0x1D58, "M", "u"), - (0x1D59, "M", "ᴝ"), - (0x1D5A, "M", "ɯ"), - (0x1D5B, "M", "v"), - (0x1D5C, "M", "ᴥ"), - (0x1D5D, "M", "β"), - (0x1D5E, "M", "γ"), - (0x1D5F, "M", "δ"), - (0x1D60, "M", "φ"), - (0x1D61, "M", "χ"), - (0x1D62, "M", "i"), - (0x1D63, "M", "r"), - (0x1D64, "M", "u"), - (0x1D65, "M", "v"), - (0x1D66, "M", "β"), - (0x1D67, "M", "γ"), - (0x1D68, "M", "ρ"), - (0x1D69, "M", "φ"), - (0x1D6A, "M", "χ"), - (0x1D6B, "V"), - (0x1D78, "M", "н"), - (0x1D79, "V"), - (0x1D9B, "M", "ɒ"), - (0x1D9C, "M", "c"), - (0x1D9D, "M", "ɕ"), - (0x1D9E, "M", "ð"), - (0x1D9F, "M", "ɜ"), - (0x1DA0, "M", "f"), - (0x1DA1, "M", "ɟ"), - (0x1DA2, "M", "ɡ"), - (0x1DA3, "M", "ɥ"), - (0x1DA4, "M", "ɨ"), - (0x1DA5, "M", "ɩ"), - (0x1DA6, "M", "ɪ"), - (0x1DA7, "M", "ᵻ"), - (0x1DA8, "M", "ʝ"), - (0x1DA9, "M", "ɭ"), - (0x1DAA, "M", "ᶅ"), - (0x1DAB, "M", "ʟ"), - (0x1DAC, "M", "ɱ"), - (0x1DAD, "M", "ɰ"), - (0x1DAE, "M", "ɲ"), - (0x1DAF, "M", "ɳ"), - (0x1DB0, "M", "ɴ"), - (0x1DB1, "M", "ɵ"), - (0x1DB2, "M", "ɸ"), - (0x1DB3, "M", "ʂ"), - (0x1DB4, "M", "ʃ"), - (0x1DB5, "M", "ƫ"), - (0x1DB6, "M", "ʉ"), - (0x1DB7, "M", "ʊ"), - (0x1DB8, "M", "ᴜ"), - (0x1DB9, "M", "ʋ"), - (0x1DBA, "M", "ʌ"), - (0x1DBB, "M", "z"), - (0x1DBC, "M", "ʐ"), - (0x1DBD, "M", "ʑ"), - (0x1DBE, "M", "ʒ"), - (0x1DBF, "M", "θ"), - (0x1DC0, "V"), - (0x1E00, "M", "ḁ"), - (0x1E01, "V"), - (0x1E02, "M", "ḃ"), - (0x1E03, "V"), - (0x1E04, "M", "ḅ"), - (0x1E05, "V"), - (0x1E06, "M", "ḇ"), - (0x1E07, "V"), - (0x1E08, "M", "ḉ"), - (0x1E09, "V"), - (0x1E0A, "M", "ḋ"), - (0x1E0B, "V"), - (0x1E0C, "M", "ḍ"), - (0x1E0D, "V"), - (0x1E0E, "M", "ḏ"), - (0x1E0F, "V"), - (0x1E10, "M", "ḑ"), - (0x1E11, "V"), - (0x1E12, "M", "ḓ"), - (0x1E13, "V"), - (0x1E14, "M", "ḕ"), - (0x1E15, "V"), - (0x1E16, "M", "ḗ"), - (0x1E17, "V"), - (0x1E18, "M", "ḙ"), - (0x1E19, "V"), - (0x1E1A, "M", "ḛ"), - (0x1E1B, "V"), - (0x1E1C, "M", "ḝ"), - (0x1E1D, "V"), - (0x1E1E, "M", "ḟ"), - (0x1E1F, "V"), - (0x1E20, "M", "ḡ"), - (0x1E21, "V"), - (0x1E22, "M", "ḣ"), - (0x1E23, "V"), - ] - - -def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E24, "M", "ḥ"), - (0x1E25, "V"), - (0x1E26, "M", "ḧ"), - (0x1E27, "V"), - (0x1E28, "M", "ḩ"), - (0x1E29, "V"), - (0x1E2A, "M", "ḫ"), - (0x1E2B, "V"), - (0x1E2C, "M", "ḭ"), - (0x1E2D, "V"), - (0x1E2E, "M", "ḯ"), - (0x1E2F, "V"), - (0x1E30, "M", "ḱ"), - (0x1E31, "V"), - (0x1E32, "M", "ḳ"), - (0x1E33, "V"), - (0x1E34, "M", "ḵ"), - (0x1E35, "V"), - (0x1E36, "M", "ḷ"), - (0x1E37, "V"), - (0x1E38, "M", "ḹ"), - (0x1E39, "V"), - (0x1E3A, "M", "ḻ"), - (0x1E3B, "V"), - (0x1E3C, "M", "ḽ"), - (0x1E3D, "V"), - (0x1E3E, "M", "ḿ"), - (0x1E3F, "V"), - (0x1E40, "M", "ṁ"), - (0x1E41, "V"), - (0x1E42, "M", "ṃ"), - (0x1E43, "V"), - (0x1E44, "M", "ṅ"), - (0x1E45, "V"), - (0x1E46, "M", "ṇ"), - (0x1E47, "V"), - (0x1E48, "M", "ṉ"), - (0x1E49, "V"), - (0x1E4A, "M", "ṋ"), - (0x1E4B, "V"), - (0x1E4C, "M", "ṍ"), - (0x1E4D, "V"), - (0x1E4E, "M", "ṏ"), - (0x1E4F, "V"), - (0x1E50, "M", "ṑ"), - (0x1E51, "V"), - (0x1E52, "M", "ṓ"), - (0x1E53, "V"), - (0x1E54, "M", "ṕ"), - (0x1E55, "V"), - (0x1E56, "M", "ṗ"), - (0x1E57, "V"), - (0x1E58, "M", "ṙ"), - (0x1E59, "V"), - (0x1E5A, "M", "ṛ"), - (0x1E5B, "V"), - (0x1E5C, "M", "ṝ"), - (0x1E5D, "V"), - (0x1E5E, "M", "ṟ"), - (0x1E5F, "V"), - (0x1E60, "M", "ṡ"), - (0x1E61, "V"), - (0x1E62, "M", "ṣ"), - (0x1E63, "V"), - (0x1E64, "M", "ṥ"), - (0x1E65, "V"), - (0x1E66, "M", "ṧ"), - (0x1E67, "V"), - (0x1E68, "M", "ṩ"), - (0x1E69, "V"), - (0x1E6A, "M", "ṫ"), - (0x1E6B, "V"), - (0x1E6C, "M", "ṭ"), - (0x1E6D, "V"), - (0x1E6E, "M", "ṯ"), - (0x1E6F, "V"), - (0x1E70, "M", "ṱ"), - (0x1E71, "V"), - (0x1E72, "M", "ṳ"), - (0x1E73, "V"), - (0x1E74, "M", "ṵ"), - (0x1E75, "V"), - (0x1E76, "M", "ṷ"), - (0x1E77, "V"), - (0x1E78, "M", "ṹ"), - (0x1E79, "V"), - (0x1E7A, "M", "ṻ"), - (0x1E7B, "V"), - (0x1E7C, "M", "ṽ"), - (0x1E7D, "V"), - (0x1E7E, "M", "ṿ"), - (0x1E7F, "V"), - (0x1E80, "M", "ẁ"), - (0x1E81, "V"), - (0x1E82, "M", "ẃ"), - (0x1E83, "V"), - (0x1E84, "M", "ẅ"), - (0x1E85, "V"), - (0x1E86, "M", "ẇ"), - (0x1E87, "V"), - ] - - -def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E88, "M", "ẉ"), - (0x1E89, "V"), - (0x1E8A, "M", "ẋ"), - (0x1E8B, "V"), - (0x1E8C, "M", "ẍ"), - (0x1E8D, "V"), - (0x1E8E, "M", "ẏ"), - (0x1E8F, "V"), - (0x1E90, "M", "ẑ"), - (0x1E91, "V"), - (0x1E92, "M", "ẓ"), - (0x1E93, "V"), - (0x1E94, "M", "ẕ"), - (0x1E95, "V"), - (0x1E9A, "M", "aʾ"), - (0x1E9B, "M", "ṡ"), - (0x1E9C, "V"), - (0x1E9E, "M", "ß"), - (0x1E9F, "V"), - (0x1EA0, "M", "ạ"), - (0x1EA1, "V"), - (0x1EA2, "M", "ả"), - (0x1EA3, "V"), - (0x1EA4, "M", "ấ"), - (0x1EA5, "V"), - (0x1EA6, "M", "ầ"), - (0x1EA7, "V"), - (0x1EA8, "M", "ẩ"), - (0x1EA9, "V"), - (0x1EAA, "M", "ẫ"), - (0x1EAB, "V"), - (0x1EAC, "M", "ậ"), - (0x1EAD, "V"), - (0x1EAE, "M", "ắ"), - (0x1EAF, "V"), - (0x1EB0, "M", "ằ"), - (0x1EB1, "V"), - (0x1EB2, "M", "ẳ"), - (0x1EB3, "V"), - (0x1EB4, "M", "ẵ"), - (0x1EB5, "V"), - (0x1EB6, "M", "ặ"), - (0x1EB7, "V"), - (0x1EB8, "M", "ẹ"), - (0x1EB9, "V"), - (0x1EBA, "M", "ẻ"), - (0x1EBB, "V"), - (0x1EBC, "M", "ẽ"), - (0x1EBD, "V"), - (0x1EBE, "M", "ế"), - (0x1EBF, "V"), - (0x1EC0, "M", "ề"), - (0x1EC1, "V"), - (0x1EC2, "M", "ể"), - (0x1EC3, "V"), - (0x1EC4, "M", "ễ"), - (0x1EC5, "V"), - (0x1EC6, "M", "ệ"), - (0x1EC7, "V"), - (0x1EC8, "M", "ỉ"), - (0x1EC9, "V"), - (0x1ECA, "M", "ị"), - (0x1ECB, "V"), - (0x1ECC, "M", "ọ"), - (0x1ECD, "V"), - (0x1ECE, "M", "ỏ"), - (0x1ECF, "V"), - (0x1ED0, "M", "ố"), - (0x1ED1, "V"), - (0x1ED2, "M", "ồ"), - (0x1ED3, "V"), - (0x1ED4, "M", "ổ"), - (0x1ED5, "V"), - (0x1ED6, "M", "ỗ"), - (0x1ED7, "V"), - (0x1ED8, "M", "ộ"), - (0x1ED9, "V"), - (0x1EDA, "M", "ớ"), - (0x1EDB, "V"), - (0x1EDC, "M", "ờ"), - (0x1EDD, "V"), - (0x1EDE, "M", "ở"), - (0x1EDF, "V"), - (0x1EE0, "M", "ỡ"), - (0x1EE1, "V"), - (0x1EE2, "M", "ợ"), - (0x1EE3, "V"), - (0x1EE4, "M", "ụ"), - (0x1EE5, "V"), - (0x1EE6, "M", "ủ"), - (0x1EE7, "V"), - (0x1EE8, "M", "ứ"), - (0x1EE9, "V"), - (0x1EEA, "M", "ừ"), - (0x1EEB, "V"), - (0x1EEC, "M", "ử"), - (0x1EED, "V"), - (0x1EEE, "M", "ữ"), - (0x1EEF, "V"), - (0x1EF0, "M", "ự"), - ] - - -def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EF1, "V"), - (0x1EF2, "M", "ỳ"), - (0x1EF3, "V"), - (0x1EF4, "M", "ỵ"), - (0x1EF5, "V"), - (0x1EF6, "M", "ỷ"), - (0x1EF7, "V"), - (0x1EF8, "M", "ỹ"), - (0x1EF9, "V"), - (0x1EFA, "M", "ỻ"), - (0x1EFB, "V"), - (0x1EFC, "M", "ỽ"), - (0x1EFD, "V"), - (0x1EFE, "M", "ỿ"), - (0x1EFF, "V"), - (0x1F08, "M", "ἀ"), - (0x1F09, "M", "ἁ"), - (0x1F0A, "M", "ἂ"), - (0x1F0B, "M", "ἃ"), - (0x1F0C, "M", "ἄ"), - (0x1F0D, "M", "ἅ"), - (0x1F0E, "M", "ἆ"), - (0x1F0F, "M", "ἇ"), - (0x1F10, "V"), - (0x1F16, "X"), - (0x1F18, "M", "ἐ"), - (0x1F19, "M", "ἑ"), - (0x1F1A, "M", "ἒ"), - (0x1F1B, "M", "ἓ"), - (0x1F1C, "M", "ἔ"), - (0x1F1D, "M", "ἕ"), - (0x1F1E, "X"), - (0x1F20, "V"), - (0x1F28, "M", "ἠ"), - (0x1F29, "M", "ἡ"), - (0x1F2A, "M", "ἢ"), - (0x1F2B, "M", "ἣ"), - (0x1F2C, "M", "ἤ"), - (0x1F2D, "M", "ἥ"), - (0x1F2E, "M", "ἦ"), - (0x1F2F, "M", "ἧ"), - (0x1F30, "V"), - (0x1F38, "M", "ἰ"), - (0x1F39, "M", "ἱ"), - (0x1F3A, "M", "ἲ"), - (0x1F3B, "M", "ἳ"), - (0x1F3C, "M", "ἴ"), - (0x1F3D, "M", "ἵ"), - (0x1F3E, "M", "ἶ"), - (0x1F3F, "M", "ἷ"), - (0x1F40, "V"), - (0x1F46, "X"), - (0x1F48, "M", "ὀ"), - (0x1F49, "M", "ὁ"), - (0x1F4A, "M", "ὂ"), - (0x1F4B, "M", "ὃ"), - (0x1F4C, "M", "ὄ"), - (0x1F4D, "M", "ὅ"), - (0x1F4E, "X"), - (0x1F50, "V"), - (0x1F58, "X"), - (0x1F59, "M", "ὑ"), - (0x1F5A, "X"), - (0x1F5B, "M", "ὓ"), - (0x1F5C, "X"), - (0x1F5D, "M", "ὕ"), - (0x1F5E, "X"), - (0x1F5F, "M", "ὗ"), - (0x1F60, "V"), - (0x1F68, "M", "ὠ"), - (0x1F69, "M", "ὡ"), - (0x1F6A, "M", "ὢ"), - (0x1F6B, "M", "ὣ"), - (0x1F6C, "M", "ὤ"), - (0x1F6D, "M", "ὥ"), - (0x1F6E, "M", "ὦ"), - (0x1F6F, "M", "ὧ"), - (0x1F70, "V"), - (0x1F71, "M", "ά"), - (0x1F72, "V"), - (0x1F73, "M", "έ"), - (0x1F74, "V"), - (0x1F75, "M", "ή"), - (0x1F76, "V"), - (0x1F77, "M", "ί"), - (0x1F78, "V"), - (0x1F79, "M", "ό"), - (0x1F7A, "V"), - (0x1F7B, "M", "ύ"), - (0x1F7C, "V"), - (0x1F7D, "M", "ώ"), - (0x1F7E, "X"), - (0x1F80, "M", "ἀι"), - (0x1F81, "M", "ἁι"), - (0x1F82, "M", "ἂι"), - (0x1F83, "M", "ἃι"), - (0x1F84, "M", "ἄι"), - (0x1F85, "M", "ἅι"), - (0x1F86, "M", "ἆι"), - (0x1F87, "M", "ἇι"), - ] - - -def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F88, "M", "ἀι"), - (0x1F89, "M", "ἁι"), - (0x1F8A, "M", "ἂι"), - (0x1F8B, "M", "ἃι"), - (0x1F8C, "M", "ἄι"), - (0x1F8D, "M", "ἅι"), - (0x1F8E, "M", "ἆι"), - (0x1F8F, "M", "ἇι"), - (0x1F90, "M", "ἠι"), - (0x1F91, "M", "ἡι"), - (0x1F92, "M", "ἢι"), - (0x1F93, "M", "ἣι"), - (0x1F94, "M", "ἤι"), - (0x1F95, "M", "ἥι"), - (0x1F96, "M", "ἦι"), - (0x1F97, "M", "ἧι"), - (0x1F98, "M", "ἠι"), - (0x1F99, "M", "ἡι"), - (0x1F9A, "M", "ἢι"), - (0x1F9B, "M", "ἣι"), - (0x1F9C, "M", "ἤι"), - (0x1F9D, "M", "ἥι"), - (0x1F9E, "M", "ἦι"), - (0x1F9F, "M", "ἧι"), - (0x1FA0, "M", "ὠι"), - (0x1FA1, "M", "ὡι"), - (0x1FA2, "M", "ὢι"), - (0x1FA3, "M", "ὣι"), - (0x1FA4, "M", "ὤι"), - (0x1FA5, "M", "ὥι"), - (0x1FA6, "M", "ὦι"), - (0x1FA7, "M", "ὧι"), - (0x1FA8, "M", "ὠι"), - (0x1FA9, "M", "ὡι"), - (0x1FAA, "M", "ὢι"), - (0x1FAB, "M", "ὣι"), - (0x1FAC, "M", "ὤι"), - (0x1FAD, "M", "ὥι"), - (0x1FAE, "M", "ὦι"), - (0x1FAF, "M", "ὧι"), - (0x1FB0, "V"), - (0x1FB2, "M", "ὰι"), - (0x1FB3, "M", "αι"), - (0x1FB4, "M", "άι"), - (0x1FB5, "X"), - (0x1FB6, "V"), - (0x1FB7, "M", "ᾶι"), - (0x1FB8, "M", "ᾰ"), - (0x1FB9, "M", "ᾱ"), - (0x1FBA, "M", "ὰ"), - (0x1FBB, "M", "ά"), - (0x1FBC, "M", "αι"), - (0x1FBD, "3", " ̓"), - (0x1FBE, "M", "ι"), - (0x1FBF, "3", " ̓"), - (0x1FC0, "3", " ͂"), - (0x1FC1, "3", " ̈͂"), - (0x1FC2, "M", "ὴι"), - (0x1FC3, "M", "ηι"), - (0x1FC4, "M", "ήι"), - (0x1FC5, "X"), - (0x1FC6, "V"), - (0x1FC7, "M", "ῆι"), - (0x1FC8, "M", "ὲ"), - (0x1FC9, "M", "έ"), - (0x1FCA, "M", "ὴ"), - (0x1FCB, "M", "ή"), - (0x1FCC, "M", "ηι"), - (0x1FCD, "3", " ̓̀"), - (0x1FCE, "3", " ̓́"), - (0x1FCF, "3", " ̓͂"), - (0x1FD0, "V"), - (0x1FD3, "M", "ΐ"), - (0x1FD4, "X"), - (0x1FD6, "V"), - (0x1FD8, "M", "ῐ"), - (0x1FD9, "M", "ῑ"), - (0x1FDA, "M", "ὶ"), - (0x1FDB, "M", "ί"), - (0x1FDC, "X"), - (0x1FDD, "3", " ̔̀"), - (0x1FDE, "3", " ̔́"), - (0x1FDF, "3", " ̔͂"), - (0x1FE0, "V"), - (0x1FE3, "M", "ΰ"), - (0x1FE4, "V"), - (0x1FE8, "M", "ῠ"), - (0x1FE9, "M", "ῡ"), - (0x1FEA, "M", "ὺ"), - (0x1FEB, "M", "ύ"), - (0x1FEC, "M", "ῥ"), - (0x1FED, "3", " ̈̀"), - (0x1FEE, "3", " ̈́"), - (0x1FEF, "3", "`"), - (0x1FF0, "X"), - (0x1FF2, "M", "ὼι"), - (0x1FF3, "M", "ωι"), - (0x1FF4, "M", "ώι"), - (0x1FF5, "X"), - (0x1FF6, "V"), - ] - - -def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FF7, "M", "ῶι"), - (0x1FF8, "M", "ὸ"), - (0x1FF9, "M", "ό"), - (0x1FFA, "M", "ὼ"), - (0x1FFB, "M", "ώ"), - (0x1FFC, "M", "ωι"), - (0x1FFD, "3", " ́"), - (0x1FFE, "3", " ̔"), - (0x1FFF, "X"), - (0x2000, "3", " "), - (0x200B, "I"), - (0x200C, "D", ""), - (0x200E, "X"), - (0x2010, "V"), - (0x2011, "M", "‐"), - (0x2012, "V"), - (0x2017, "3", " ̳"), - (0x2018, "V"), - (0x2024, "X"), - (0x2027, "V"), - (0x2028, "X"), - (0x202F, "3", " "), - (0x2030, "V"), - (0x2033, "M", "′′"), - (0x2034, "M", "′′′"), - (0x2035, "V"), - (0x2036, "M", "‵‵"), - (0x2037, "M", "‵‵‵"), - (0x2038, "V"), - (0x203C, "3", "!!"), - (0x203D, "V"), - (0x203E, "3", " ̅"), - (0x203F, "V"), - (0x2047, "3", "??"), - (0x2048, "3", "?!"), - (0x2049, "3", "!?"), - (0x204A, "V"), - (0x2057, "M", "′′′′"), - (0x2058, "V"), - (0x205F, "3", " "), - (0x2060, "I"), - (0x2061, "X"), - (0x2064, "I"), - (0x2065, "X"), - (0x2070, "M", "0"), - (0x2071, "M", "i"), - (0x2072, "X"), - (0x2074, "M", "4"), - (0x2075, "M", "5"), - (0x2076, "M", "6"), - (0x2077, "M", "7"), - (0x2078, "M", "8"), - (0x2079, "M", "9"), - (0x207A, "3", "+"), - (0x207B, "M", "−"), - (0x207C, "3", "="), - (0x207D, "3", "("), - (0x207E, "3", ")"), - (0x207F, "M", "n"), - (0x2080, "M", "0"), - (0x2081, "M", "1"), - (0x2082, "M", "2"), - (0x2083, "M", "3"), - (0x2084, "M", "4"), - (0x2085, "M", "5"), - (0x2086, "M", "6"), - (0x2087, "M", "7"), - (0x2088, "M", "8"), - (0x2089, "M", "9"), - (0x208A, "3", "+"), - (0x208B, "M", "−"), - (0x208C, "3", "="), - (0x208D, "3", "("), - (0x208E, "3", ")"), - (0x208F, "X"), - (0x2090, "M", "a"), - (0x2091, "M", "e"), - (0x2092, "M", "o"), - (0x2093, "M", "x"), - (0x2094, "M", "ə"), - (0x2095, "M", "h"), - (0x2096, "M", "k"), - (0x2097, "M", "l"), - (0x2098, "M", "m"), - (0x2099, "M", "n"), - (0x209A, "M", "p"), - (0x209B, "M", "s"), - (0x209C, "M", "t"), - (0x209D, "X"), - (0x20A0, "V"), - (0x20A8, "M", "rs"), - (0x20A9, "V"), - (0x20C1, "X"), - (0x20D0, "V"), - (0x20F1, "X"), - (0x2100, "3", "a/c"), - (0x2101, "3", "a/s"), - (0x2102, "M", "c"), - (0x2103, "M", "°c"), - (0x2104, "V"), - ] - - -def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2105, "3", "c/o"), - (0x2106, "3", "c/u"), - (0x2107, "M", "ɛ"), - (0x2108, "V"), - (0x2109, "M", "°f"), - (0x210A, "M", "g"), - (0x210B, "M", "h"), - (0x210F, "M", "ħ"), - (0x2110, "M", "i"), - (0x2112, "M", "l"), - (0x2114, "V"), - (0x2115, "M", "n"), - (0x2116, "M", "no"), - (0x2117, "V"), - (0x2119, "M", "p"), - (0x211A, "M", "q"), - (0x211B, "M", "r"), - (0x211E, "V"), - (0x2120, "M", "sm"), - (0x2121, "M", "tel"), - (0x2122, "M", "tm"), - (0x2123, "V"), - (0x2124, "M", "z"), - (0x2125, "V"), - (0x2126, "M", "ω"), - (0x2127, "V"), - (0x2128, "M", "z"), - (0x2129, "V"), - (0x212A, "M", "k"), - (0x212B, "M", "å"), - (0x212C, "M", "b"), - (0x212D, "M", "c"), - (0x212E, "V"), - (0x212F, "M", "e"), - (0x2131, "M", "f"), - (0x2132, "X"), - (0x2133, "M", "m"), - (0x2134, "M", "o"), - (0x2135, "M", "א"), - (0x2136, "M", "ב"), - (0x2137, "M", "ג"), - (0x2138, "M", "ד"), - (0x2139, "M", "i"), - (0x213A, "V"), - (0x213B, "M", "fax"), - (0x213C, "M", "π"), - (0x213D, "M", "γ"), - (0x213F, "M", "π"), - (0x2140, "M", "∑"), - (0x2141, "V"), - (0x2145, "M", "d"), - (0x2147, "M", "e"), - (0x2148, "M", "i"), - (0x2149, "M", "j"), - (0x214A, "V"), - (0x2150, "M", "1⁄7"), - (0x2151, "M", "1⁄9"), - (0x2152, "M", "1⁄10"), - (0x2153, "M", "1⁄3"), - (0x2154, "M", "2⁄3"), - (0x2155, "M", "1⁄5"), - (0x2156, "M", "2⁄5"), - (0x2157, "M", "3⁄5"), - (0x2158, "M", "4⁄5"), - (0x2159, "M", "1⁄6"), - (0x215A, "M", "5⁄6"), - (0x215B, "M", "1⁄8"), - (0x215C, "M", "3⁄8"), - (0x215D, "M", "5⁄8"), - (0x215E, "M", "7⁄8"), - (0x215F, "M", "1⁄"), - (0x2160, "M", "i"), - (0x2161, "M", "ii"), - (0x2162, "M", "iii"), - (0x2163, "M", "iv"), - (0x2164, "M", "v"), - (0x2165, "M", "vi"), - (0x2166, "M", "vii"), - (0x2167, "M", "viii"), - (0x2168, "M", "ix"), - (0x2169, "M", "x"), - (0x216A, "M", "xi"), - (0x216B, "M", "xii"), - (0x216C, "M", "l"), - (0x216D, "M", "c"), - (0x216E, "M", "d"), - (0x216F, "M", "m"), - (0x2170, "M", "i"), - (0x2171, "M", "ii"), - (0x2172, "M", "iii"), - (0x2173, "M", "iv"), - (0x2174, "M", "v"), - (0x2175, "M", "vi"), - (0x2176, "M", "vii"), - (0x2177, "M", "viii"), - (0x2178, "M", "ix"), - (0x2179, "M", "x"), - (0x217A, "M", "xi"), - (0x217B, "M", "xii"), - (0x217C, "M", "l"), - ] - - -def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x217D, "M", "c"), - (0x217E, "M", "d"), - (0x217F, "M", "m"), - (0x2180, "V"), - (0x2183, "X"), - (0x2184, "V"), - (0x2189, "M", "0⁄3"), - (0x218A, "V"), - (0x218C, "X"), - (0x2190, "V"), - (0x222C, "M", "∫∫"), - (0x222D, "M", "∫∫∫"), - (0x222E, "V"), - (0x222F, "M", "∮∮"), - (0x2230, "M", "∮∮∮"), - (0x2231, "V"), - (0x2329, "M", "〈"), - (0x232A, "M", "〉"), - (0x232B, "V"), - (0x2427, "X"), - (0x2440, "V"), - (0x244B, "X"), - (0x2460, "M", "1"), - (0x2461, "M", "2"), - (0x2462, "M", "3"), - (0x2463, "M", "4"), - (0x2464, "M", "5"), - (0x2465, "M", "6"), - (0x2466, "M", "7"), - (0x2467, "M", "8"), - (0x2468, "M", "9"), - (0x2469, "M", "10"), - (0x246A, "M", "11"), - (0x246B, "M", "12"), - (0x246C, "M", "13"), - (0x246D, "M", "14"), - (0x246E, "M", "15"), - (0x246F, "M", "16"), - (0x2470, "M", "17"), - (0x2471, "M", "18"), - (0x2472, "M", "19"), - (0x2473, "M", "20"), - (0x2474, "3", "(1)"), - (0x2475, "3", "(2)"), - (0x2476, "3", "(3)"), - (0x2477, "3", "(4)"), - (0x2478, "3", "(5)"), - (0x2479, "3", "(6)"), - (0x247A, "3", "(7)"), - (0x247B, "3", "(8)"), - (0x247C, "3", "(9)"), - (0x247D, "3", "(10)"), - (0x247E, "3", "(11)"), - (0x247F, "3", "(12)"), - (0x2480, "3", "(13)"), - (0x2481, "3", "(14)"), - (0x2482, "3", "(15)"), - (0x2483, "3", "(16)"), - (0x2484, "3", "(17)"), - (0x2485, "3", "(18)"), - (0x2486, "3", "(19)"), - (0x2487, "3", "(20)"), - (0x2488, "X"), - (0x249C, "3", "(a)"), - (0x249D, "3", "(b)"), - (0x249E, "3", "(c)"), - (0x249F, "3", "(d)"), - (0x24A0, "3", "(e)"), - (0x24A1, "3", "(f)"), - (0x24A2, "3", "(g)"), - (0x24A3, "3", "(h)"), - (0x24A4, "3", "(i)"), - (0x24A5, "3", "(j)"), - (0x24A6, "3", "(k)"), - (0x24A7, "3", "(l)"), - (0x24A8, "3", "(m)"), - (0x24A9, "3", "(n)"), - (0x24AA, "3", "(o)"), - (0x24AB, "3", "(p)"), - (0x24AC, "3", "(q)"), - (0x24AD, "3", "(r)"), - (0x24AE, "3", "(s)"), - (0x24AF, "3", "(t)"), - (0x24B0, "3", "(u)"), - (0x24B1, "3", "(v)"), - (0x24B2, "3", "(w)"), - (0x24B3, "3", "(x)"), - (0x24B4, "3", "(y)"), - (0x24B5, "3", "(z)"), - (0x24B6, "M", "a"), - (0x24B7, "M", "b"), - (0x24B8, "M", "c"), - (0x24B9, "M", "d"), - (0x24BA, "M", "e"), - (0x24BB, "M", "f"), - (0x24BC, "M", "g"), - (0x24BD, "M", "h"), - (0x24BE, "M", "i"), - (0x24BF, "M", "j"), - (0x24C0, "M", "k"), - ] - - -def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x24C1, "M", "l"), - (0x24C2, "M", "m"), - (0x24C3, "M", "n"), - (0x24C4, "M", "o"), - (0x24C5, "M", "p"), - (0x24C6, "M", "q"), - (0x24C7, "M", "r"), - (0x24C8, "M", "s"), - (0x24C9, "M", "t"), - (0x24CA, "M", "u"), - (0x24CB, "M", "v"), - (0x24CC, "M", "w"), - (0x24CD, "M", "x"), - (0x24CE, "M", "y"), - (0x24CF, "M", "z"), - (0x24D0, "M", "a"), - (0x24D1, "M", "b"), - (0x24D2, "M", "c"), - (0x24D3, "M", "d"), - (0x24D4, "M", "e"), - (0x24D5, "M", "f"), - (0x24D6, "M", "g"), - (0x24D7, "M", "h"), - (0x24D8, "M", "i"), - (0x24D9, "M", "j"), - (0x24DA, "M", "k"), - (0x24DB, "M", "l"), - (0x24DC, "M", "m"), - (0x24DD, "M", "n"), - (0x24DE, "M", "o"), - (0x24DF, "M", "p"), - (0x24E0, "M", "q"), - (0x24E1, "M", "r"), - (0x24E2, "M", "s"), - (0x24E3, "M", "t"), - (0x24E4, "M", "u"), - (0x24E5, "M", "v"), - (0x24E6, "M", "w"), - (0x24E7, "M", "x"), - (0x24E8, "M", "y"), - (0x24E9, "M", "z"), - (0x24EA, "M", "0"), - (0x24EB, "V"), - (0x2A0C, "M", "∫∫∫∫"), - (0x2A0D, "V"), - (0x2A74, "3", "::="), - (0x2A75, "3", "=="), - (0x2A76, "3", "==="), - (0x2A77, "V"), - (0x2ADC, "M", "⫝̸"), - (0x2ADD, "V"), - (0x2B74, "X"), - (0x2B76, "V"), - (0x2B96, "X"), - (0x2B97, "V"), - (0x2C00, "M", "ⰰ"), - (0x2C01, "M", "ⰱ"), - (0x2C02, "M", "ⰲ"), - (0x2C03, "M", "ⰳ"), - (0x2C04, "M", "ⰴ"), - (0x2C05, "M", "ⰵ"), - (0x2C06, "M", "ⰶ"), - (0x2C07, "M", "ⰷ"), - (0x2C08, "M", "ⰸ"), - (0x2C09, "M", "ⰹ"), - (0x2C0A, "M", "ⰺ"), - (0x2C0B, "M", "ⰻ"), - (0x2C0C, "M", "ⰼ"), - (0x2C0D, "M", "ⰽ"), - (0x2C0E, "M", "ⰾ"), - (0x2C0F, "M", "ⰿ"), - (0x2C10, "M", "ⱀ"), - (0x2C11, "M", "ⱁ"), - (0x2C12, "M", "ⱂ"), - (0x2C13, "M", "ⱃ"), - (0x2C14, "M", "ⱄ"), - (0x2C15, "M", "ⱅ"), - (0x2C16, "M", "ⱆ"), - (0x2C17, "M", "ⱇ"), - (0x2C18, "M", "ⱈ"), - (0x2C19, "M", "ⱉ"), - (0x2C1A, "M", "ⱊ"), - (0x2C1B, "M", "ⱋ"), - (0x2C1C, "M", "ⱌ"), - (0x2C1D, "M", "ⱍ"), - (0x2C1E, "M", "ⱎ"), - (0x2C1F, "M", "ⱏ"), - (0x2C20, "M", "ⱐ"), - (0x2C21, "M", "ⱑ"), - (0x2C22, "M", "ⱒ"), - (0x2C23, "M", "ⱓ"), - (0x2C24, "M", "ⱔ"), - (0x2C25, "M", "ⱕ"), - (0x2C26, "M", "ⱖ"), - (0x2C27, "M", "ⱗ"), - (0x2C28, "M", "ⱘ"), - (0x2C29, "M", "ⱙ"), - (0x2C2A, "M", "ⱚ"), - (0x2C2B, "M", "ⱛ"), - (0x2C2C, "M", "ⱜ"), - ] - - -def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2C2D, "M", "ⱝ"), - (0x2C2E, "M", "ⱞ"), - (0x2C2F, "M", "ⱟ"), - (0x2C30, "V"), - (0x2C60, "M", "ⱡ"), - (0x2C61, "V"), - (0x2C62, "M", "ɫ"), - (0x2C63, "M", "ᵽ"), - (0x2C64, "M", "ɽ"), - (0x2C65, "V"), - (0x2C67, "M", "ⱨ"), - (0x2C68, "V"), - (0x2C69, "M", "ⱪ"), - (0x2C6A, "V"), - (0x2C6B, "M", "ⱬ"), - (0x2C6C, "V"), - (0x2C6D, "M", "ɑ"), - (0x2C6E, "M", "ɱ"), - (0x2C6F, "M", "ɐ"), - (0x2C70, "M", "ɒ"), - (0x2C71, "V"), - (0x2C72, "M", "ⱳ"), - (0x2C73, "V"), - (0x2C75, "M", "ⱶ"), - (0x2C76, "V"), - (0x2C7C, "M", "j"), - (0x2C7D, "M", "v"), - (0x2C7E, "M", "ȿ"), - (0x2C7F, "M", "ɀ"), - (0x2C80, "M", "ⲁ"), - (0x2C81, "V"), - (0x2C82, "M", "ⲃ"), - (0x2C83, "V"), - (0x2C84, "M", "ⲅ"), - (0x2C85, "V"), - (0x2C86, "M", "ⲇ"), - (0x2C87, "V"), - (0x2C88, "M", "ⲉ"), - (0x2C89, "V"), - (0x2C8A, "M", "ⲋ"), - (0x2C8B, "V"), - (0x2C8C, "M", "ⲍ"), - (0x2C8D, "V"), - (0x2C8E, "M", "ⲏ"), - (0x2C8F, "V"), - (0x2C90, "M", "ⲑ"), - (0x2C91, "V"), - (0x2C92, "M", "ⲓ"), - (0x2C93, "V"), - (0x2C94, "M", "ⲕ"), - (0x2C95, "V"), - (0x2C96, "M", "ⲗ"), - (0x2C97, "V"), - (0x2C98, "M", "ⲙ"), - (0x2C99, "V"), - (0x2C9A, "M", "ⲛ"), - (0x2C9B, "V"), - (0x2C9C, "M", "ⲝ"), - (0x2C9D, "V"), - (0x2C9E, "M", "ⲟ"), - (0x2C9F, "V"), - (0x2CA0, "M", "ⲡ"), - (0x2CA1, "V"), - (0x2CA2, "M", "ⲣ"), - (0x2CA3, "V"), - (0x2CA4, "M", "ⲥ"), - (0x2CA5, "V"), - (0x2CA6, "M", "ⲧ"), - (0x2CA7, "V"), - (0x2CA8, "M", "ⲩ"), - (0x2CA9, "V"), - (0x2CAA, "M", "ⲫ"), - (0x2CAB, "V"), - (0x2CAC, "M", "ⲭ"), - (0x2CAD, "V"), - (0x2CAE, "M", "ⲯ"), - (0x2CAF, "V"), - (0x2CB0, "M", "ⲱ"), - (0x2CB1, "V"), - (0x2CB2, "M", "ⲳ"), - (0x2CB3, "V"), - (0x2CB4, "M", "ⲵ"), - (0x2CB5, "V"), - (0x2CB6, "M", "ⲷ"), - (0x2CB7, "V"), - (0x2CB8, "M", "ⲹ"), - (0x2CB9, "V"), - (0x2CBA, "M", "ⲻ"), - (0x2CBB, "V"), - (0x2CBC, "M", "ⲽ"), - (0x2CBD, "V"), - (0x2CBE, "M", "ⲿ"), - (0x2CBF, "V"), - (0x2CC0, "M", "ⳁ"), - (0x2CC1, "V"), - (0x2CC2, "M", "ⳃ"), - (0x2CC3, "V"), - (0x2CC4, "M", "ⳅ"), - (0x2CC5, "V"), - (0x2CC6, "M", "ⳇ"), - ] - - -def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2CC7, "V"), - (0x2CC8, "M", "ⳉ"), - (0x2CC9, "V"), - (0x2CCA, "M", "ⳋ"), - (0x2CCB, "V"), - (0x2CCC, "M", "ⳍ"), - (0x2CCD, "V"), - (0x2CCE, "M", "ⳏ"), - (0x2CCF, "V"), - (0x2CD0, "M", "ⳑ"), - (0x2CD1, "V"), - (0x2CD2, "M", "ⳓ"), - (0x2CD3, "V"), - (0x2CD4, "M", "ⳕ"), - (0x2CD5, "V"), - (0x2CD6, "M", "ⳗ"), - (0x2CD7, "V"), - (0x2CD8, "M", "ⳙ"), - (0x2CD9, "V"), - (0x2CDA, "M", "ⳛ"), - (0x2CDB, "V"), - (0x2CDC, "M", "ⳝ"), - (0x2CDD, "V"), - (0x2CDE, "M", "ⳟ"), - (0x2CDF, "V"), - (0x2CE0, "M", "ⳡ"), - (0x2CE1, "V"), - (0x2CE2, "M", "ⳣ"), - (0x2CE3, "V"), - (0x2CEB, "M", "ⳬ"), - (0x2CEC, "V"), - (0x2CED, "M", "ⳮ"), - (0x2CEE, "V"), - (0x2CF2, "M", "ⳳ"), - (0x2CF3, "V"), - (0x2CF4, "X"), - (0x2CF9, "V"), - (0x2D26, "X"), - (0x2D27, "V"), - (0x2D28, "X"), - (0x2D2D, "V"), - (0x2D2E, "X"), - (0x2D30, "V"), - (0x2D68, "X"), - (0x2D6F, "M", "ⵡ"), - (0x2D70, "V"), - (0x2D71, "X"), - (0x2D7F, "V"), - (0x2D97, "X"), - (0x2DA0, "V"), - (0x2DA7, "X"), - (0x2DA8, "V"), - (0x2DAF, "X"), - (0x2DB0, "V"), - (0x2DB7, "X"), - (0x2DB8, "V"), - (0x2DBF, "X"), - (0x2DC0, "V"), - (0x2DC7, "X"), - (0x2DC8, "V"), - (0x2DCF, "X"), - (0x2DD0, "V"), - (0x2DD7, "X"), - (0x2DD8, "V"), - (0x2DDF, "X"), - (0x2DE0, "V"), - (0x2E5E, "X"), - (0x2E80, "V"), - (0x2E9A, "X"), - (0x2E9B, "V"), - (0x2E9F, "M", "母"), - (0x2EA0, "V"), - (0x2EF3, "M", "龟"), - (0x2EF4, "X"), - (0x2F00, "M", "一"), - (0x2F01, "M", "丨"), - (0x2F02, "M", "丶"), - (0x2F03, "M", "丿"), - (0x2F04, "M", "乙"), - (0x2F05, "M", "亅"), - (0x2F06, "M", "二"), - (0x2F07, "M", "亠"), - (0x2F08, "M", "人"), - (0x2F09, "M", "儿"), - (0x2F0A, "M", "入"), - (0x2F0B, "M", "八"), - (0x2F0C, "M", "冂"), - (0x2F0D, "M", "冖"), - (0x2F0E, "M", "冫"), - (0x2F0F, "M", "几"), - (0x2F10, "M", "凵"), - (0x2F11, "M", "刀"), - (0x2F12, "M", "力"), - (0x2F13, "M", "勹"), - (0x2F14, "M", "匕"), - (0x2F15, "M", "匚"), - (0x2F16, "M", "匸"), - (0x2F17, "M", "十"), - (0x2F18, "M", "卜"), - (0x2F19, "M", "卩"), - ] - - -def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F1A, "M", "厂"), - (0x2F1B, "M", "厶"), - (0x2F1C, "M", "又"), - (0x2F1D, "M", "口"), - (0x2F1E, "M", "囗"), - (0x2F1F, "M", "土"), - (0x2F20, "M", "士"), - (0x2F21, "M", "夂"), - (0x2F22, "M", "夊"), - (0x2F23, "M", "夕"), - (0x2F24, "M", "大"), - (0x2F25, "M", "女"), - (0x2F26, "M", "子"), - (0x2F27, "M", "宀"), - (0x2F28, "M", "寸"), - (0x2F29, "M", "小"), - (0x2F2A, "M", "尢"), - (0x2F2B, "M", "尸"), - (0x2F2C, "M", "屮"), - (0x2F2D, "M", "山"), - (0x2F2E, "M", "巛"), - (0x2F2F, "M", "工"), - (0x2F30, "M", "己"), - (0x2F31, "M", "巾"), - (0x2F32, "M", "干"), - (0x2F33, "M", "幺"), - (0x2F34, "M", "广"), - (0x2F35, "M", "廴"), - (0x2F36, "M", "廾"), - (0x2F37, "M", "弋"), - (0x2F38, "M", "弓"), - (0x2F39, "M", "彐"), - (0x2F3A, "M", "彡"), - (0x2F3B, "M", "彳"), - (0x2F3C, "M", "心"), - (0x2F3D, "M", "戈"), - (0x2F3E, "M", "戶"), - (0x2F3F, "M", "手"), - (0x2F40, "M", "支"), - (0x2F41, "M", "攴"), - (0x2F42, "M", "文"), - (0x2F43, "M", "斗"), - (0x2F44, "M", "斤"), - (0x2F45, "M", "方"), - (0x2F46, "M", "无"), - (0x2F47, "M", "日"), - (0x2F48, "M", "曰"), - (0x2F49, "M", "月"), - (0x2F4A, "M", "木"), - (0x2F4B, "M", "欠"), - (0x2F4C, "M", "止"), - (0x2F4D, "M", "歹"), - (0x2F4E, "M", "殳"), - (0x2F4F, "M", "毋"), - (0x2F50, "M", "比"), - (0x2F51, "M", "毛"), - (0x2F52, "M", "氏"), - (0x2F53, "M", "气"), - (0x2F54, "M", "水"), - (0x2F55, "M", "火"), - (0x2F56, "M", "爪"), - (0x2F57, "M", "父"), - (0x2F58, "M", "爻"), - (0x2F59, "M", "爿"), - (0x2F5A, "M", "片"), - (0x2F5B, "M", "牙"), - (0x2F5C, "M", "牛"), - (0x2F5D, "M", "犬"), - (0x2F5E, "M", "玄"), - (0x2F5F, "M", "玉"), - (0x2F60, "M", "瓜"), - (0x2F61, "M", "瓦"), - (0x2F62, "M", "甘"), - (0x2F63, "M", "生"), - (0x2F64, "M", "用"), - (0x2F65, "M", "田"), - (0x2F66, "M", "疋"), - (0x2F67, "M", "疒"), - (0x2F68, "M", "癶"), - (0x2F69, "M", "白"), - (0x2F6A, "M", "皮"), - (0x2F6B, "M", "皿"), - (0x2F6C, "M", "目"), - (0x2F6D, "M", "矛"), - (0x2F6E, "M", "矢"), - (0x2F6F, "M", "石"), - (0x2F70, "M", "示"), - (0x2F71, "M", "禸"), - (0x2F72, "M", "禾"), - (0x2F73, "M", "穴"), - (0x2F74, "M", "立"), - (0x2F75, "M", "竹"), - (0x2F76, "M", "米"), - (0x2F77, "M", "糸"), - (0x2F78, "M", "缶"), - (0x2F79, "M", "网"), - (0x2F7A, "M", "羊"), - (0x2F7B, "M", "羽"), - (0x2F7C, "M", "老"), - (0x2F7D, "M", "而"), - ] - - -def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F7E, "M", "耒"), - (0x2F7F, "M", "耳"), - (0x2F80, "M", "聿"), - (0x2F81, "M", "肉"), - (0x2F82, "M", "臣"), - (0x2F83, "M", "自"), - (0x2F84, "M", "至"), - (0x2F85, "M", "臼"), - (0x2F86, "M", "舌"), - (0x2F87, "M", "舛"), - (0x2F88, "M", "舟"), - (0x2F89, "M", "艮"), - (0x2F8A, "M", "色"), - (0x2F8B, "M", "艸"), - (0x2F8C, "M", "虍"), - (0x2F8D, "M", "虫"), - (0x2F8E, "M", "血"), - (0x2F8F, "M", "行"), - (0x2F90, "M", "衣"), - (0x2F91, "M", "襾"), - (0x2F92, "M", "見"), - (0x2F93, "M", "角"), - (0x2F94, "M", "言"), - (0x2F95, "M", "谷"), - (0x2F96, "M", "豆"), - (0x2F97, "M", "豕"), - (0x2F98, "M", "豸"), - (0x2F99, "M", "貝"), - (0x2F9A, "M", "赤"), - (0x2F9B, "M", "走"), - (0x2F9C, "M", "足"), - (0x2F9D, "M", "身"), - (0x2F9E, "M", "車"), - (0x2F9F, "M", "辛"), - (0x2FA0, "M", "辰"), - (0x2FA1, "M", "辵"), - (0x2FA2, "M", "邑"), - (0x2FA3, "M", "酉"), - (0x2FA4, "M", "釆"), - (0x2FA5, "M", "里"), - (0x2FA6, "M", "金"), - (0x2FA7, "M", "長"), - (0x2FA8, "M", "門"), - (0x2FA9, "M", "阜"), - (0x2FAA, "M", "隶"), - (0x2FAB, "M", "隹"), - (0x2FAC, "M", "雨"), - (0x2FAD, "M", "靑"), - (0x2FAE, "M", "非"), - (0x2FAF, "M", "面"), - (0x2FB0, "M", "革"), - (0x2FB1, "M", "韋"), - (0x2FB2, "M", "韭"), - (0x2FB3, "M", "音"), - (0x2FB4, "M", "頁"), - (0x2FB5, "M", "風"), - (0x2FB6, "M", "飛"), - (0x2FB7, "M", "食"), - (0x2FB8, "M", "首"), - (0x2FB9, "M", "香"), - (0x2FBA, "M", "馬"), - (0x2FBB, "M", "骨"), - (0x2FBC, "M", "高"), - (0x2FBD, "M", "髟"), - (0x2FBE, "M", "鬥"), - (0x2FBF, "M", "鬯"), - (0x2FC0, "M", "鬲"), - (0x2FC1, "M", "鬼"), - (0x2FC2, "M", "魚"), - (0x2FC3, "M", "鳥"), - (0x2FC4, "M", "鹵"), - (0x2FC5, "M", "鹿"), - (0x2FC6, "M", "麥"), - (0x2FC7, "M", "麻"), - (0x2FC8, "M", "黃"), - (0x2FC9, "M", "黍"), - (0x2FCA, "M", "黑"), - (0x2FCB, "M", "黹"), - (0x2FCC, "M", "黽"), - (0x2FCD, "M", "鼎"), - (0x2FCE, "M", "鼓"), - (0x2FCF, "M", "鼠"), - (0x2FD0, "M", "鼻"), - (0x2FD1, "M", "齊"), - (0x2FD2, "M", "齒"), - (0x2FD3, "M", "龍"), - (0x2FD4, "M", "龜"), - (0x2FD5, "M", "龠"), - (0x2FD6, "X"), - (0x3000, "3", " "), - (0x3001, "V"), - (0x3002, "M", "."), - (0x3003, "V"), - (0x3036, "M", "〒"), - (0x3037, "V"), - (0x3038, "M", "十"), - (0x3039, "M", "卄"), - (0x303A, "M", "卅"), - (0x303B, "V"), - (0x3040, "X"), - ] - - -def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3041, "V"), - (0x3097, "X"), - (0x3099, "V"), - (0x309B, "3", " ゙"), - (0x309C, "3", " ゚"), - (0x309D, "V"), - (0x309F, "M", "より"), - (0x30A0, "V"), - (0x30FF, "M", "コト"), - (0x3100, "X"), - (0x3105, "V"), - (0x3130, "X"), - (0x3131, "M", "ᄀ"), - (0x3132, "M", "ᄁ"), - (0x3133, "M", "ᆪ"), - (0x3134, "M", "ᄂ"), - (0x3135, "M", "ᆬ"), - (0x3136, "M", "ᆭ"), - (0x3137, "M", "ᄃ"), - (0x3138, "M", "ᄄ"), - (0x3139, "M", "ᄅ"), - (0x313A, "M", "ᆰ"), - (0x313B, "M", "ᆱ"), - (0x313C, "M", "ᆲ"), - (0x313D, "M", "ᆳ"), - (0x313E, "M", "ᆴ"), - (0x313F, "M", "ᆵ"), - (0x3140, "M", "ᄚ"), - (0x3141, "M", "ᄆ"), - (0x3142, "M", "ᄇ"), - (0x3143, "M", "ᄈ"), - (0x3144, "M", "ᄡ"), - (0x3145, "M", "ᄉ"), - (0x3146, "M", "ᄊ"), - (0x3147, "M", "ᄋ"), - (0x3148, "M", "ᄌ"), - (0x3149, "M", "ᄍ"), - (0x314A, "M", "ᄎ"), - (0x314B, "M", "ᄏ"), - (0x314C, "M", "ᄐ"), - (0x314D, "M", "ᄑ"), - (0x314E, "M", "ᄒ"), - (0x314F, "M", "ᅡ"), - (0x3150, "M", "ᅢ"), - (0x3151, "M", "ᅣ"), - (0x3152, "M", "ᅤ"), - (0x3153, "M", "ᅥ"), - (0x3154, "M", "ᅦ"), - (0x3155, "M", "ᅧ"), - (0x3156, "M", "ᅨ"), - (0x3157, "M", "ᅩ"), - (0x3158, "M", "ᅪ"), - (0x3159, "M", "ᅫ"), - (0x315A, "M", "ᅬ"), - (0x315B, "M", "ᅭ"), - (0x315C, "M", "ᅮ"), - (0x315D, "M", "ᅯ"), - (0x315E, "M", "ᅰ"), - (0x315F, "M", "ᅱ"), - (0x3160, "M", "ᅲ"), - (0x3161, "M", "ᅳ"), - (0x3162, "M", "ᅴ"), - (0x3163, "M", "ᅵ"), - (0x3164, "X"), - (0x3165, "M", "ᄔ"), - (0x3166, "M", "ᄕ"), - (0x3167, "M", "ᇇ"), - (0x3168, "M", "ᇈ"), - (0x3169, "M", "ᇌ"), - (0x316A, "M", "ᇎ"), - (0x316B, "M", "ᇓ"), - (0x316C, "M", "ᇗ"), - (0x316D, "M", "ᇙ"), - (0x316E, "M", "ᄜ"), - (0x316F, "M", "ᇝ"), - (0x3170, "M", "ᇟ"), - (0x3171, "M", "ᄝ"), - (0x3172, "M", "ᄞ"), - (0x3173, "M", "ᄠ"), - (0x3174, "M", "ᄢ"), - (0x3175, "M", "ᄣ"), - (0x3176, "M", "ᄧ"), - (0x3177, "M", "ᄩ"), - (0x3178, "M", "ᄫ"), - (0x3179, "M", "ᄬ"), - (0x317A, "M", "ᄭ"), - (0x317B, "M", "ᄮ"), - (0x317C, "M", "ᄯ"), - (0x317D, "M", "ᄲ"), - (0x317E, "M", "ᄶ"), - (0x317F, "M", "ᅀ"), - (0x3180, "M", "ᅇ"), - (0x3181, "M", "ᅌ"), - (0x3182, "M", "ᇱ"), - (0x3183, "M", "ᇲ"), - (0x3184, "M", "ᅗ"), - (0x3185, "M", "ᅘ"), - (0x3186, "M", "ᅙ"), - (0x3187, "M", "ᆄ"), - (0x3188, "M", "ᆅ"), - ] - - -def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3189, "M", "ᆈ"), - (0x318A, "M", "ᆑ"), - (0x318B, "M", "ᆒ"), - (0x318C, "M", "ᆔ"), - (0x318D, "M", "ᆞ"), - (0x318E, "M", "ᆡ"), - (0x318F, "X"), - (0x3190, "V"), - (0x3192, "M", "一"), - (0x3193, "M", "二"), - (0x3194, "M", "三"), - (0x3195, "M", "四"), - (0x3196, "M", "上"), - (0x3197, "M", "中"), - (0x3198, "M", "下"), - (0x3199, "M", "甲"), - (0x319A, "M", "乙"), - (0x319B, "M", "丙"), - (0x319C, "M", "丁"), - (0x319D, "M", "天"), - (0x319E, "M", "地"), - (0x319F, "M", "人"), - (0x31A0, "V"), - (0x31E4, "X"), - (0x31F0, "V"), - (0x3200, "3", "(ᄀ)"), - (0x3201, "3", "(ᄂ)"), - (0x3202, "3", "(ᄃ)"), - (0x3203, "3", "(ᄅ)"), - (0x3204, "3", "(ᄆ)"), - (0x3205, "3", "(ᄇ)"), - (0x3206, "3", "(ᄉ)"), - (0x3207, "3", "(ᄋ)"), - (0x3208, "3", "(ᄌ)"), - (0x3209, "3", "(ᄎ)"), - (0x320A, "3", "(ᄏ)"), - (0x320B, "3", "(ᄐ)"), - (0x320C, "3", "(ᄑ)"), - (0x320D, "3", "(ᄒ)"), - (0x320E, "3", "(가)"), - (0x320F, "3", "(나)"), - (0x3210, "3", "(다)"), - (0x3211, "3", "(라)"), - (0x3212, "3", "(마)"), - (0x3213, "3", "(바)"), - (0x3214, "3", "(사)"), - (0x3215, "3", "(아)"), - (0x3216, "3", "(자)"), - (0x3217, "3", "(차)"), - (0x3218, "3", "(카)"), - (0x3219, "3", "(타)"), - (0x321A, "3", "(파)"), - (0x321B, "3", "(하)"), - (0x321C, "3", "(주)"), - (0x321D, "3", "(오전)"), - (0x321E, "3", "(오후)"), - (0x321F, "X"), - (0x3220, "3", "(一)"), - (0x3221, "3", "(二)"), - (0x3222, "3", "(三)"), - (0x3223, "3", "(四)"), - (0x3224, "3", "(五)"), - (0x3225, "3", "(六)"), - (0x3226, "3", "(七)"), - (0x3227, "3", "(八)"), - (0x3228, "3", "(九)"), - (0x3229, "3", "(十)"), - (0x322A, "3", "(月)"), - (0x322B, "3", "(火)"), - (0x322C, "3", "(水)"), - (0x322D, "3", "(木)"), - (0x322E, "3", "(金)"), - (0x322F, "3", "(土)"), - (0x3230, "3", "(日)"), - (0x3231, "3", "(株)"), - (0x3232, "3", "(有)"), - (0x3233, "3", "(社)"), - (0x3234, "3", "(名)"), - (0x3235, "3", "(特)"), - (0x3236, "3", "(財)"), - (0x3237, "3", "(祝)"), - (0x3238, "3", "(労)"), - (0x3239, "3", "(代)"), - (0x323A, "3", "(呼)"), - (0x323B, "3", "(学)"), - (0x323C, "3", "(監)"), - (0x323D, "3", "(企)"), - (0x323E, "3", "(資)"), - (0x323F, "3", "(協)"), - (0x3240, "3", "(祭)"), - (0x3241, "3", "(休)"), - (0x3242, "3", "(自)"), - (0x3243, "3", "(至)"), - (0x3244, "M", "問"), - (0x3245, "M", "幼"), - (0x3246, "M", "文"), - (0x3247, "M", "箏"), - (0x3248, "V"), - (0x3250, "M", "pte"), - (0x3251, "M", "21"), - ] - - -def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3252, "M", "22"), - (0x3253, "M", "23"), - (0x3254, "M", "24"), - (0x3255, "M", "25"), - (0x3256, "M", "26"), - (0x3257, "M", "27"), - (0x3258, "M", "28"), - (0x3259, "M", "29"), - (0x325A, "M", "30"), - (0x325B, "M", "31"), - (0x325C, "M", "32"), - (0x325D, "M", "33"), - (0x325E, "M", "34"), - (0x325F, "M", "35"), - (0x3260, "M", "ᄀ"), - (0x3261, "M", "ᄂ"), - (0x3262, "M", "ᄃ"), - (0x3263, "M", "ᄅ"), - (0x3264, "M", "ᄆ"), - (0x3265, "M", "ᄇ"), - (0x3266, "M", "ᄉ"), - (0x3267, "M", "ᄋ"), - (0x3268, "M", "ᄌ"), - (0x3269, "M", "ᄎ"), - (0x326A, "M", "ᄏ"), - (0x326B, "M", "ᄐ"), - (0x326C, "M", "ᄑ"), - (0x326D, "M", "ᄒ"), - (0x326E, "M", "가"), - (0x326F, "M", "나"), - (0x3270, "M", "다"), - (0x3271, "M", "라"), - (0x3272, "M", "마"), - (0x3273, "M", "바"), - (0x3274, "M", "사"), - (0x3275, "M", "아"), - (0x3276, "M", "자"), - (0x3277, "M", "차"), - (0x3278, "M", "카"), - (0x3279, "M", "타"), - (0x327A, "M", "파"), - (0x327B, "M", "하"), - (0x327C, "M", "참고"), - (0x327D, "M", "주의"), - (0x327E, "M", "우"), - (0x327F, "V"), - (0x3280, "M", "一"), - (0x3281, "M", "二"), - (0x3282, "M", "三"), - (0x3283, "M", "四"), - (0x3284, "M", "五"), - (0x3285, "M", "六"), - (0x3286, "M", "七"), - (0x3287, "M", "八"), - (0x3288, "M", "九"), - (0x3289, "M", "十"), - (0x328A, "M", "月"), - (0x328B, "M", "火"), - (0x328C, "M", "水"), - (0x328D, "M", "木"), - (0x328E, "M", "金"), - (0x328F, "M", "土"), - (0x3290, "M", "日"), - (0x3291, "M", "株"), - (0x3292, "M", "有"), - (0x3293, "M", "社"), - (0x3294, "M", "名"), - (0x3295, "M", "特"), - (0x3296, "M", "財"), - (0x3297, "M", "祝"), - (0x3298, "M", "労"), - (0x3299, "M", "秘"), - (0x329A, "M", "男"), - (0x329B, "M", "女"), - (0x329C, "M", "適"), - (0x329D, "M", "優"), - (0x329E, "M", "印"), - (0x329F, "M", "注"), - (0x32A0, "M", "項"), - (0x32A1, "M", "休"), - (0x32A2, "M", "写"), - (0x32A3, "M", "正"), - (0x32A4, "M", "上"), - (0x32A5, "M", "中"), - (0x32A6, "M", "下"), - (0x32A7, "M", "左"), - (0x32A8, "M", "右"), - (0x32A9, "M", "医"), - (0x32AA, "M", "宗"), - (0x32AB, "M", "学"), - (0x32AC, "M", "監"), - (0x32AD, "M", "企"), - (0x32AE, "M", "資"), - (0x32AF, "M", "協"), - (0x32B0, "M", "夜"), - (0x32B1, "M", "36"), - (0x32B2, "M", "37"), - (0x32B3, "M", "38"), - (0x32B4, "M", "39"), - (0x32B5, "M", "40"), - ] - - -def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x32B6, "M", "41"), - (0x32B7, "M", "42"), - (0x32B8, "M", "43"), - (0x32B9, "M", "44"), - (0x32BA, "M", "45"), - (0x32BB, "M", "46"), - (0x32BC, "M", "47"), - (0x32BD, "M", "48"), - (0x32BE, "M", "49"), - (0x32BF, "M", "50"), - (0x32C0, "M", "1月"), - (0x32C1, "M", "2月"), - (0x32C2, "M", "3月"), - (0x32C3, "M", "4月"), - (0x32C4, "M", "5月"), - (0x32C5, "M", "6月"), - (0x32C6, "M", "7月"), - (0x32C7, "M", "8月"), - (0x32C8, "M", "9月"), - (0x32C9, "M", "10月"), - (0x32CA, "M", "11月"), - (0x32CB, "M", "12月"), - (0x32CC, "M", "hg"), - (0x32CD, "M", "erg"), - (0x32CE, "M", "ev"), - (0x32CF, "M", "ltd"), - (0x32D0, "M", "ア"), - (0x32D1, "M", "イ"), - (0x32D2, "M", "ウ"), - (0x32D3, "M", "エ"), - (0x32D4, "M", "オ"), - (0x32D5, "M", "カ"), - (0x32D6, "M", "キ"), - (0x32D7, "M", "ク"), - (0x32D8, "M", "ケ"), - (0x32D9, "M", "コ"), - (0x32DA, "M", "サ"), - (0x32DB, "M", "シ"), - (0x32DC, "M", "ス"), - (0x32DD, "M", "セ"), - (0x32DE, "M", "ソ"), - (0x32DF, "M", "タ"), - (0x32E0, "M", "チ"), - (0x32E1, "M", "ツ"), - (0x32E2, "M", "テ"), - (0x32E3, "M", "ト"), - (0x32E4, "M", "ナ"), - (0x32E5, "M", "ニ"), - (0x32E6, "M", "ヌ"), - (0x32E7, "M", "ネ"), - (0x32E8, "M", "ノ"), - (0x32E9, "M", "ハ"), - (0x32EA, "M", "ヒ"), - (0x32EB, "M", "フ"), - (0x32EC, "M", "ヘ"), - (0x32ED, "M", "ホ"), - (0x32EE, "M", "マ"), - (0x32EF, "M", "ミ"), - (0x32F0, "M", "ム"), - (0x32F1, "M", "メ"), - (0x32F2, "M", "モ"), - (0x32F3, "M", "ヤ"), - (0x32F4, "M", "ユ"), - (0x32F5, "M", "ヨ"), - (0x32F6, "M", "ラ"), - (0x32F7, "M", "リ"), - (0x32F8, "M", "ル"), - (0x32F9, "M", "レ"), - (0x32FA, "M", "ロ"), - (0x32FB, "M", "ワ"), - (0x32FC, "M", "ヰ"), - (0x32FD, "M", "ヱ"), - (0x32FE, "M", "ヲ"), - (0x32FF, "M", "令和"), - (0x3300, "M", "アパート"), - (0x3301, "M", "アルファ"), - (0x3302, "M", "アンペア"), - (0x3303, "M", "アール"), - (0x3304, "M", "イニング"), - (0x3305, "M", "インチ"), - (0x3306, "M", "ウォン"), - (0x3307, "M", "エスクード"), - (0x3308, "M", "エーカー"), - (0x3309, "M", "オンス"), - (0x330A, "M", "オーム"), - (0x330B, "M", "カイリ"), - (0x330C, "M", "カラット"), - (0x330D, "M", "カロリー"), - (0x330E, "M", "ガロン"), - (0x330F, "M", "ガンマ"), - (0x3310, "M", "ギガ"), - (0x3311, "M", "ギニー"), - (0x3312, "M", "キュリー"), - (0x3313, "M", "ギルダー"), - (0x3314, "M", "キロ"), - (0x3315, "M", "キログラム"), - (0x3316, "M", "キロメートル"), - (0x3317, "M", "キロワット"), - (0x3318, "M", "グラム"), - (0x3319, "M", "グラムトン"), - ] - - -def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x331A, "M", "クルゼイロ"), - (0x331B, "M", "クローネ"), - (0x331C, "M", "ケース"), - (0x331D, "M", "コルナ"), - (0x331E, "M", "コーポ"), - (0x331F, "M", "サイクル"), - (0x3320, "M", "サンチーム"), - (0x3321, "M", "シリング"), - (0x3322, "M", "センチ"), - (0x3323, "M", "セント"), - (0x3324, "M", "ダース"), - (0x3325, "M", "デシ"), - (0x3326, "M", "ドル"), - (0x3327, "M", "トン"), - (0x3328, "M", "ナノ"), - (0x3329, "M", "ノット"), - (0x332A, "M", "ハイツ"), - (0x332B, "M", "パーセント"), - (0x332C, "M", "パーツ"), - (0x332D, "M", "バーレル"), - (0x332E, "M", "ピアストル"), - (0x332F, "M", "ピクル"), - (0x3330, "M", "ピコ"), - (0x3331, "M", "ビル"), - (0x3332, "M", "ファラッド"), - (0x3333, "M", "フィート"), - (0x3334, "M", "ブッシェル"), - (0x3335, "M", "フラン"), - (0x3336, "M", "ヘクタール"), - (0x3337, "M", "ペソ"), - (0x3338, "M", "ペニヒ"), - (0x3339, "M", "ヘルツ"), - (0x333A, "M", "ペンス"), - (0x333B, "M", "ページ"), - (0x333C, "M", "ベータ"), - (0x333D, "M", "ポイント"), - (0x333E, "M", "ボルト"), - (0x333F, "M", "ホン"), - (0x3340, "M", "ポンド"), - (0x3341, "M", "ホール"), - (0x3342, "M", "ホーン"), - (0x3343, "M", "マイクロ"), - (0x3344, "M", "マイル"), - (0x3345, "M", "マッハ"), - (0x3346, "M", "マルク"), - (0x3347, "M", "マンション"), - (0x3348, "M", "ミクロン"), - (0x3349, "M", "ミリ"), - (0x334A, "M", "ミリバール"), - (0x334B, "M", "メガ"), - (0x334C, "M", "メガトン"), - (0x334D, "M", "メートル"), - (0x334E, "M", "ヤード"), - (0x334F, "M", "ヤール"), - (0x3350, "M", "ユアン"), - (0x3351, "M", "リットル"), - (0x3352, "M", "リラ"), - (0x3353, "M", "ルピー"), - (0x3354, "M", "ルーブル"), - (0x3355, "M", "レム"), - (0x3356, "M", "レントゲン"), - (0x3357, "M", "ワット"), - (0x3358, "M", "0点"), - (0x3359, "M", "1点"), - (0x335A, "M", "2点"), - (0x335B, "M", "3点"), - (0x335C, "M", "4点"), - (0x335D, "M", "5点"), - (0x335E, "M", "6点"), - (0x335F, "M", "7点"), - (0x3360, "M", "8点"), - (0x3361, "M", "9点"), - (0x3362, "M", "10点"), - (0x3363, "M", "11点"), - (0x3364, "M", "12点"), - (0x3365, "M", "13点"), - (0x3366, "M", "14点"), - (0x3367, "M", "15点"), - (0x3368, "M", "16点"), - (0x3369, "M", "17点"), - (0x336A, "M", "18点"), - (0x336B, "M", "19点"), - (0x336C, "M", "20点"), - (0x336D, "M", "21点"), - (0x336E, "M", "22点"), - (0x336F, "M", "23点"), - (0x3370, "M", "24点"), - (0x3371, "M", "hpa"), - (0x3372, "M", "da"), - (0x3373, "M", "au"), - (0x3374, "M", "bar"), - (0x3375, "M", "ov"), - (0x3376, "M", "pc"), - (0x3377, "M", "dm"), - (0x3378, "M", "dm2"), - (0x3379, "M", "dm3"), - (0x337A, "M", "iu"), - (0x337B, "M", "平成"), - (0x337C, "M", "昭和"), - (0x337D, "M", "大正"), - ] - - -def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x337E, "M", "明治"), - (0x337F, "M", "株式会社"), - (0x3380, "M", "pa"), - (0x3381, "M", "na"), - (0x3382, "M", "μa"), - (0x3383, "M", "ma"), - (0x3384, "M", "ka"), - (0x3385, "M", "kb"), - (0x3386, "M", "mb"), - (0x3387, "M", "gb"), - (0x3388, "M", "cal"), - (0x3389, "M", "kcal"), - (0x338A, "M", "pf"), - (0x338B, "M", "nf"), - (0x338C, "M", "μf"), - (0x338D, "M", "μg"), - (0x338E, "M", "mg"), - (0x338F, "M", "kg"), - (0x3390, "M", "hz"), - (0x3391, "M", "khz"), - (0x3392, "M", "mhz"), - (0x3393, "M", "ghz"), - (0x3394, "M", "thz"), - (0x3395, "M", "μl"), - (0x3396, "M", "ml"), - (0x3397, "M", "dl"), - (0x3398, "M", "kl"), - (0x3399, "M", "fm"), - (0x339A, "M", "nm"), - (0x339B, "M", "μm"), - (0x339C, "M", "mm"), - (0x339D, "M", "cm"), - (0x339E, "M", "km"), - (0x339F, "M", "mm2"), - (0x33A0, "M", "cm2"), - (0x33A1, "M", "m2"), - (0x33A2, "M", "km2"), - (0x33A3, "M", "mm3"), - (0x33A4, "M", "cm3"), - (0x33A5, "M", "m3"), - (0x33A6, "M", "km3"), - (0x33A7, "M", "m∕s"), - (0x33A8, "M", "m∕s2"), - (0x33A9, "M", "pa"), - (0x33AA, "M", "kpa"), - (0x33AB, "M", "mpa"), - (0x33AC, "M", "gpa"), - (0x33AD, "M", "rad"), - (0x33AE, "M", "rad∕s"), - (0x33AF, "M", "rad∕s2"), - (0x33B0, "M", "ps"), - (0x33B1, "M", "ns"), - (0x33B2, "M", "μs"), - (0x33B3, "M", "ms"), - (0x33B4, "M", "pv"), - (0x33B5, "M", "nv"), - (0x33B6, "M", "μv"), - (0x33B7, "M", "mv"), - (0x33B8, "M", "kv"), - (0x33B9, "M", "mv"), - (0x33BA, "M", "pw"), - (0x33BB, "M", "nw"), - (0x33BC, "M", "μw"), - (0x33BD, "M", "mw"), - (0x33BE, "M", "kw"), - (0x33BF, "M", "mw"), - (0x33C0, "M", "kω"), - (0x33C1, "M", "mω"), - (0x33C2, "X"), - (0x33C3, "M", "bq"), - (0x33C4, "M", "cc"), - (0x33C5, "M", "cd"), - (0x33C6, "M", "c∕kg"), - (0x33C7, "X"), - (0x33C8, "M", "db"), - (0x33C9, "M", "gy"), - (0x33CA, "M", "ha"), - (0x33CB, "M", "hp"), - (0x33CC, "M", "in"), - (0x33CD, "M", "kk"), - (0x33CE, "M", "km"), - (0x33CF, "M", "kt"), - (0x33D0, "M", "lm"), - (0x33D1, "M", "ln"), - (0x33D2, "M", "log"), - (0x33D3, "M", "lx"), - (0x33D4, "M", "mb"), - (0x33D5, "M", "mil"), - (0x33D6, "M", "mol"), - (0x33D7, "M", "ph"), - (0x33D8, "X"), - (0x33D9, "M", "ppm"), - (0x33DA, "M", "pr"), - (0x33DB, "M", "sr"), - (0x33DC, "M", "sv"), - (0x33DD, "M", "wb"), - (0x33DE, "M", "v∕m"), - (0x33DF, "M", "a∕m"), - (0x33E0, "M", "1日"), - (0x33E1, "M", "2日"), - ] - - -def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x33E2, "M", "3日"), - (0x33E3, "M", "4日"), - (0x33E4, "M", "5日"), - (0x33E5, "M", "6日"), - (0x33E6, "M", "7日"), - (0x33E7, "M", "8日"), - (0x33E8, "M", "9日"), - (0x33E9, "M", "10日"), - (0x33EA, "M", "11日"), - (0x33EB, "M", "12日"), - (0x33EC, "M", "13日"), - (0x33ED, "M", "14日"), - (0x33EE, "M", "15日"), - (0x33EF, "M", "16日"), - (0x33F0, "M", "17日"), - (0x33F1, "M", "18日"), - (0x33F2, "M", "19日"), - (0x33F3, "M", "20日"), - (0x33F4, "M", "21日"), - (0x33F5, "M", "22日"), - (0x33F6, "M", "23日"), - (0x33F7, "M", "24日"), - (0x33F8, "M", "25日"), - (0x33F9, "M", "26日"), - (0x33FA, "M", "27日"), - (0x33FB, "M", "28日"), - (0x33FC, "M", "29日"), - (0x33FD, "M", "30日"), - (0x33FE, "M", "31日"), - (0x33FF, "M", "gal"), - (0x3400, "V"), - (0xA48D, "X"), - (0xA490, "V"), - (0xA4C7, "X"), - (0xA4D0, "V"), - (0xA62C, "X"), - (0xA640, "M", "ꙁ"), - (0xA641, "V"), - (0xA642, "M", "ꙃ"), - (0xA643, "V"), - (0xA644, "M", "ꙅ"), - (0xA645, "V"), - (0xA646, "M", "ꙇ"), - (0xA647, "V"), - (0xA648, "M", "ꙉ"), - (0xA649, "V"), - (0xA64A, "M", "ꙋ"), - (0xA64B, "V"), - (0xA64C, "M", "ꙍ"), - (0xA64D, "V"), - (0xA64E, "M", "ꙏ"), - (0xA64F, "V"), - (0xA650, "M", "ꙑ"), - (0xA651, "V"), - (0xA652, "M", "ꙓ"), - (0xA653, "V"), - (0xA654, "M", "ꙕ"), - (0xA655, "V"), - (0xA656, "M", "ꙗ"), - (0xA657, "V"), - (0xA658, "M", "ꙙ"), - (0xA659, "V"), - (0xA65A, "M", "ꙛ"), - (0xA65B, "V"), - (0xA65C, "M", "ꙝ"), - (0xA65D, "V"), - (0xA65E, "M", "ꙟ"), - (0xA65F, "V"), - (0xA660, "M", "ꙡ"), - (0xA661, "V"), - (0xA662, "M", "ꙣ"), - (0xA663, "V"), - (0xA664, "M", "ꙥ"), - (0xA665, "V"), - (0xA666, "M", "ꙧ"), - (0xA667, "V"), - (0xA668, "M", "ꙩ"), - (0xA669, "V"), - (0xA66A, "M", "ꙫ"), - (0xA66B, "V"), - (0xA66C, "M", "ꙭ"), - (0xA66D, "V"), - (0xA680, "M", "ꚁ"), - (0xA681, "V"), - (0xA682, "M", "ꚃ"), - (0xA683, "V"), - (0xA684, "M", "ꚅ"), - (0xA685, "V"), - (0xA686, "M", "ꚇ"), - (0xA687, "V"), - (0xA688, "M", "ꚉ"), - (0xA689, "V"), - (0xA68A, "M", "ꚋ"), - (0xA68B, "V"), - (0xA68C, "M", "ꚍ"), - (0xA68D, "V"), - (0xA68E, "M", "ꚏ"), - (0xA68F, "V"), - (0xA690, "M", "ꚑ"), - (0xA691, "V"), - ] - - -def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA692, "M", "ꚓ"), - (0xA693, "V"), - (0xA694, "M", "ꚕ"), - (0xA695, "V"), - (0xA696, "M", "ꚗ"), - (0xA697, "V"), - (0xA698, "M", "ꚙ"), - (0xA699, "V"), - (0xA69A, "M", "ꚛ"), - (0xA69B, "V"), - (0xA69C, "M", "ъ"), - (0xA69D, "M", "ь"), - (0xA69E, "V"), - (0xA6F8, "X"), - (0xA700, "V"), - (0xA722, "M", "ꜣ"), - (0xA723, "V"), - (0xA724, "M", "ꜥ"), - (0xA725, "V"), - (0xA726, "M", "ꜧ"), - (0xA727, "V"), - (0xA728, "M", "ꜩ"), - (0xA729, "V"), - (0xA72A, "M", "ꜫ"), - (0xA72B, "V"), - (0xA72C, "M", "ꜭ"), - (0xA72D, "V"), - (0xA72E, "M", "ꜯ"), - (0xA72F, "V"), - (0xA732, "M", "ꜳ"), - (0xA733, "V"), - (0xA734, "M", "ꜵ"), - (0xA735, "V"), - (0xA736, "M", "ꜷ"), - (0xA737, "V"), - (0xA738, "M", "ꜹ"), - (0xA739, "V"), - (0xA73A, "M", "ꜻ"), - (0xA73B, "V"), - (0xA73C, "M", "ꜽ"), - (0xA73D, "V"), - (0xA73E, "M", "ꜿ"), - (0xA73F, "V"), - (0xA740, "M", "ꝁ"), - (0xA741, "V"), - (0xA742, "M", "ꝃ"), - (0xA743, "V"), - (0xA744, "M", "ꝅ"), - (0xA745, "V"), - (0xA746, "M", "ꝇ"), - (0xA747, "V"), - (0xA748, "M", "ꝉ"), - (0xA749, "V"), - (0xA74A, "M", "ꝋ"), - (0xA74B, "V"), - (0xA74C, "M", "ꝍ"), - (0xA74D, "V"), - (0xA74E, "M", "ꝏ"), - (0xA74F, "V"), - (0xA750, "M", "ꝑ"), - (0xA751, "V"), - (0xA752, "M", "ꝓ"), - (0xA753, "V"), - (0xA754, "M", "ꝕ"), - (0xA755, "V"), - (0xA756, "M", "ꝗ"), - (0xA757, "V"), - (0xA758, "M", "ꝙ"), - (0xA759, "V"), - (0xA75A, "M", "ꝛ"), - (0xA75B, "V"), - (0xA75C, "M", "ꝝ"), - (0xA75D, "V"), - (0xA75E, "M", "ꝟ"), - (0xA75F, "V"), - (0xA760, "M", "ꝡ"), - (0xA761, "V"), - (0xA762, "M", "ꝣ"), - (0xA763, "V"), - (0xA764, "M", "ꝥ"), - (0xA765, "V"), - (0xA766, "M", "ꝧ"), - (0xA767, "V"), - (0xA768, "M", "ꝩ"), - (0xA769, "V"), - (0xA76A, "M", "ꝫ"), - (0xA76B, "V"), - (0xA76C, "M", "ꝭ"), - (0xA76D, "V"), - (0xA76E, "M", "ꝯ"), - (0xA76F, "V"), - (0xA770, "M", "ꝯ"), - (0xA771, "V"), - (0xA779, "M", "ꝺ"), - (0xA77A, "V"), - (0xA77B, "M", "ꝼ"), - (0xA77C, "V"), - (0xA77D, "M", "ᵹ"), - (0xA77E, "M", "ꝿ"), - (0xA77F, "V"), - ] - - -def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA780, "M", "ꞁ"), - (0xA781, "V"), - (0xA782, "M", "ꞃ"), - (0xA783, "V"), - (0xA784, "M", "ꞅ"), - (0xA785, "V"), - (0xA786, "M", "ꞇ"), - (0xA787, "V"), - (0xA78B, "M", "ꞌ"), - (0xA78C, "V"), - (0xA78D, "M", "ɥ"), - (0xA78E, "V"), - (0xA790, "M", "ꞑ"), - (0xA791, "V"), - (0xA792, "M", "ꞓ"), - (0xA793, "V"), - (0xA796, "M", "ꞗ"), - (0xA797, "V"), - (0xA798, "M", "ꞙ"), - (0xA799, "V"), - (0xA79A, "M", "ꞛ"), - (0xA79B, "V"), - (0xA79C, "M", "ꞝ"), - (0xA79D, "V"), - (0xA79E, "M", "ꞟ"), - (0xA79F, "V"), - (0xA7A0, "M", "ꞡ"), - (0xA7A1, "V"), - (0xA7A2, "M", "ꞣ"), - (0xA7A3, "V"), - (0xA7A4, "M", "ꞥ"), - (0xA7A5, "V"), - (0xA7A6, "M", "ꞧ"), - (0xA7A7, "V"), - (0xA7A8, "M", "ꞩ"), - (0xA7A9, "V"), - (0xA7AA, "M", "ɦ"), - (0xA7AB, "M", "ɜ"), - (0xA7AC, "M", "ɡ"), - (0xA7AD, "M", "ɬ"), - (0xA7AE, "M", "ɪ"), - (0xA7AF, "V"), - (0xA7B0, "M", "ʞ"), - (0xA7B1, "M", "ʇ"), - (0xA7B2, "M", "ʝ"), - (0xA7B3, "M", "ꭓ"), - (0xA7B4, "M", "ꞵ"), - (0xA7B5, "V"), - (0xA7B6, "M", "ꞷ"), - (0xA7B7, "V"), - (0xA7B8, "M", "ꞹ"), - (0xA7B9, "V"), - (0xA7BA, "M", "ꞻ"), - (0xA7BB, "V"), - (0xA7BC, "M", "ꞽ"), - (0xA7BD, "V"), - (0xA7BE, "M", "ꞿ"), - (0xA7BF, "V"), - (0xA7C0, "M", "ꟁ"), - (0xA7C1, "V"), - (0xA7C2, "M", "ꟃ"), - (0xA7C3, "V"), - (0xA7C4, "M", "ꞔ"), - (0xA7C5, "M", "ʂ"), - (0xA7C6, "M", "ᶎ"), - (0xA7C7, "M", "ꟈ"), - (0xA7C8, "V"), - (0xA7C9, "M", "ꟊ"), - (0xA7CA, "V"), - (0xA7CB, "X"), - (0xA7D0, "M", "ꟑ"), - (0xA7D1, "V"), - (0xA7D2, "X"), - (0xA7D3, "V"), - (0xA7D4, "X"), - (0xA7D5, "V"), - (0xA7D6, "M", "ꟗ"), - (0xA7D7, "V"), - (0xA7D8, "M", "ꟙ"), - (0xA7D9, "V"), - (0xA7DA, "X"), - (0xA7F2, "M", "c"), - (0xA7F3, "M", "f"), - (0xA7F4, "M", "q"), - (0xA7F5, "M", "ꟶ"), - (0xA7F6, "V"), - (0xA7F8, "M", "ħ"), - (0xA7F9, "M", "œ"), - (0xA7FA, "V"), - (0xA82D, "X"), - (0xA830, "V"), - (0xA83A, "X"), - (0xA840, "V"), - (0xA878, "X"), - (0xA880, "V"), - (0xA8C6, "X"), - (0xA8CE, "V"), - (0xA8DA, "X"), - (0xA8E0, "V"), - (0xA954, "X"), - ] - - -def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA95F, "V"), - (0xA97D, "X"), - (0xA980, "V"), - (0xA9CE, "X"), - (0xA9CF, "V"), - (0xA9DA, "X"), - (0xA9DE, "V"), - (0xA9FF, "X"), - (0xAA00, "V"), - (0xAA37, "X"), - (0xAA40, "V"), - (0xAA4E, "X"), - (0xAA50, "V"), - (0xAA5A, "X"), - (0xAA5C, "V"), - (0xAAC3, "X"), - (0xAADB, "V"), - (0xAAF7, "X"), - (0xAB01, "V"), - (0xAB07, "X"), - (0xAB09, "V"), - (0xAB0F, "X"), - (0xAB11, "V"), - (0xAB17, "X"), - (0xAB20, "V"), - (0xAB27, "X"), - (0xAB28, "V"), - (0xAB2F, "X"), - (0xAB30, "V"), - (0xAB5C, "M", "ꜧ"), - (0xAB5D, "M", "ꬷ"), - (0xAB5E, "M", "ɫ"), - (0xAB5F, "M", "ꭒ"), - (0xAB60, "V"), - (0xAB69, "M", "ʍ"), - (0xAB6A, "V"), - (0xAB6C, "X"), - (0xAB70, "M", "Ꭰ"), - (0xAB71, "M", "Ꭱ"), - (0xAB72, "M", "Ꭲ"), - (0xAB73, "M", "Ꭳ"), - (0xAB74, "M", "Ꭴ"), - (0xAB75, "M", "Ꭵ"), - (0xAB76, "M", "Ꭶ"), - (0xAB77, "M", "Ꭷ"), - (0xAB78, "M", "Ꭸ"), - (0xAB79, "M", "Ꭹ"), - (0xAB7A, "M", "Ꭺ"), - (0xAB7B, "M", "Ꭻ"), - (0xAB7C, "M", "Ꭼ"), - (0xAB7D, "M", "Ꭽ"), - (0xAB7E, "M", "Ꭾ"), - (0xAB7F, "M", "Ꭿ"), - (0xAB80, "M", "Ꮀ"), - (0xAB81, "M", "Ꮁ"), - (0xAB82, "M", "Ꮂ"), - (0xAB83, "M", "Ꮃ"), - (0xAB84, "M", "Ꮄ"), - (0xAB85, "M", "Ꮅ"), - (0xAB86, "M", "Ꮆ"), - (0xAB87, "M", "Ꮇ"), - (0xAB88, "M", "Ꮈ"), - (0xAB89, "M", "Ꮉ"), - (0xAB8A, "M", "Ꮊ"), - (0xAB8B, "M", "Ꮋ"), - (0xAB8C, "M", "Ꮌ"), - (0xAB8D, "M", "Ꮍ"), - (0xAB8E, "M", "Ꮎ"), - (0xAB8F, "M", "Ꮏ"), - (0xAB90, "M", "Ꮐ"), - (0xAB91, "M", "Ꮑ"), - (0xAB92, "M", "Ꮒ"), - (0xAB93, "M", "Ꮓ"), - (0xAB94, "M", "Ꮔ"), - (0xAB95, "M", "Ꮕ"), - (0xAB96, "M", "Ꮖ"), - (0xAB97, "M", "Ꮗ"), - (0xAB98, "M", "Ꮘ"), - (0xAB99, "M", "Ꮙ"), - (0xAB9A, "M", "Ꮚ"), - (0xAB9B, "M", "Ꮛ"), - (0xAB9C, "M", "Ꮜ"), - (0xAB9D, "M", "Ꮝ"), - (0xAB9E, "M", "Ꮞ"), - (0xAB9F, "M", "Ꮟ"), - (0xABA0, "M", "Ꮠ"), - (0xABA1, "M", "Ꮡ"), - (0xABA2, "M", "Ꮢ"), - (0xABA3, "M", "Ꮣ"), - (0xABA4, "M", "Ꮤ"), - (0xABA5, "M", "Ꮥ"), - (0xABA6, "M", "Ꮦ"), - (0xABA7, "M", "Ꮧ"), - (0xABA8, "M", "Ꮨ"), - (0xABA9, "M", "Ꮩ"), - (0xABAA, "M", "Ꮪ"), - (0xABAB, "M", "Ꮫ"), - (0xABAC, "M", "Ꮬ"), - (0xABAD, "M", "Ꮭ"), - (0xABAE, "M", "Ꮮ"), - ] - - -def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xABAF, "M", "Ꮯ"), - (0xABB0, "M", "Ꮰ"), - (0xABB1, "M", "Ꮱ"), - (0xABB2, "M", "Ꮲ"), - (0xABB3, "M", "Ꮳ"), - (0xABB4, "M", "Ꮴ"), - (0xABB5, "M", "Ꮵ"), - (0xABB6, "M", "Ꮶ"), - (0xABB7, "M", "Ꮷ"), - (0xABB8, "M", "Ꮸ"), - (0xABB9, "M", "Ꮹ"), - (0xABBA, "M", "Ꮺ"), - (0xABBB, "M", "Ꮻ"), - (0xABBC, "M", "Ꮼ"), - (0xABBD, "M", "Ꮽ"), - (0xABBE, "M", "Ꮾ"), - (0xABBF, "M", "Ꮿ"), - (0xABC0, "V"), - (0xABEE, "X"), - (0xABF0, "V"), - (0xABFA, "X"), - (0xAC00, "V"), - (0xD7A4, "X"), - (0xD7B0, "V"), - (0xD7C7, "X"), - (0xD7CB, "V"), - (0xD7FC, "X"), - (0xF900, "M", "豈"), - (0xF901, "M", "更"), - (0xF902, "M", "車"), - (0xF903, "M", "賈"), - (0xF904, "M", "滑"), - (0xF905, "M", "串"), - (0xF906, "M", "句"), - (0xF907, "M", "龜"), - (0xF909, "M", "契"), - (0xF90A, "M", "金"), - (0xF90B, "M", "喇"), - (0xF90C, "M", "奈"), - (0xF90D, "M", "懶"), - (0xF90E, "M", "癩"), - (0xF90F, "M", "羅"), - (0xF910, "M", "蘿"), - (0xF911, "M", "螺"), - (0xF912, "M", "裸"), - (0xF913, "M", "邏"), - (0xF914, "M", "樂"), - (0xF915, "M", "洛"), - (0xF916, "M", "烙"), - (0xF917, "M", "珞"), - (0xF918, "M", "落"), - (0xF919, "M", "酪"), - (0xF91A, "M", "駱"), - (0xF91B, "M", "亂"), - (0xF91C, "M", "卵"), - (0xF91D, "M", "欄"), - (0xF91E, "M", "爛"), - (0xF91F, "M", "蘭"), - (0xF920, "M", "鸞"), - (0xF921, "M", "嵐"), - (0xF922, "M", "濫"), - (0xF923, "M", "藍"), - (0xF924, "M", "襤"), - (0xF925, "M", "拉"), - (0xF926, "M", "臘"), - (0xF927, "M", "蠟"), - (0xF928, "M", "廊"), - (0xF929, "M", "朗"), - (0xF92A, "M", "浪"), - (0xF92B, "M", "狼"), - (0xF92C, "M", "郎"), - (0xF92D, "M", "來"), - (0xF92E, "M", "冷"), - (0xF92F, "M", "勞"), - (0xF930, "M", "擄"), - (0xF931, "M", "櫓"), - (0xF932, "M", "爐"), - (0xF933, "M", "盧"), - (0xF934, "M", "老"), - (0xF935, "M", "蘆"), - (0xF936, "M", "虜"), - (0xF937, "M", "路"), - (0xF938, "M", "露"), - (0xF939, "M", "魯"), - (0xF93A, "M", "鷺"), - (0xF93B, "M", "碌"), - (0xF93C, "M", "祿"), - (0xF93D, "M", "綠"), - (0xF93E, "M", "菉"), - (0xF93F, "M", "錄"), - (0xF940, "M", "鹿"), - (0xF941, "M", "論"), - (0xF942, "M", "壟"), - (0xF943, "M", "弄"), - (0xF944, "M", "籠"), - (0xF945, "M", "聾"), - (0xF946, "M", "牢"), - (0xF947, "M", "磊"), - (0xF948, "M", "賂"), - (0xF949, "M", "雷"), - ] - - -def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF94A, "M", "壘"), - (0xF94B, "M", "屢"), - (0xF94C, "M", "樓"), - (0xF94D, "M", "淚"), - (0xF94E, "M", "漏"), - (0xF94F, "M", "累"), - (0xF950, "M", "縷"), - (0xF951, "M", "陋"), - (0xF952, "M", "勒"), - (0xF953, "M", "肋"), - (0xF954, "M", "凜"), - (0xF955, "M", "凌"), - (0xF956, "M", "稜"), - (0xF957, "M", "綾"), - (0xF958, "M", "菱"), - (0xF959, "M", "陵"), - (0xF95A, "M", "讀"), - (0xF95B, "M", "拏"), - (0xF95C, "M", "樂"), - (0xF95D, "M", "諾"), - (0xF95E, "M", "丹"), - (0xF95F, "M", "寧"), - (0xF960, "M", "怒"), - (0xF961, "M", "率"), - (0xF962, "M", "異"), - (0xF963, "M", "北"), - (0xF964, "M", "磻"), - (0xF965, "M", "便"), - (0xF966, "M", "復"), - (0xF967, "M", "不"), - (0xF968, "M", "泌"), - (0xF969, "M", "數"), - (0xF96A, "M", "索"), - (0xF96B, "M", "參"), - (0xF96C, "M", "塞"), - (0xF96D, "M", "省"), - (0xF96E, "M", "葉"), - (0xF96F, "M", "說"), - (0xF970, "M", "殺"), - (0xF971, "M", "辰"), - (0xF972, "M", "沈"), - (0xF973, "M", "拾"), - (0xF974, "M", "若"), - (0xF975, "M", "掠"), - (0xF976, "M", "略"), - (0xF977, "M", "亮"), - (0xF978, "M", "兩"), - (0xF979, "M", "凉"), - (0xF97A, "M", "梁"), - (0xF97B, "M", "糧"), - (0xF97C, "M", "良"), - (0xF97D, "M", "諒"), - (0xF97E, "M", "量"), - (0xF97F, "M", "勵"), - (0xF980, "M", "呂"), - (0xF981, "M", "女"), - (0xF982, "M", "廬"), - (0xF983, "M", "旅"), - (0xF984, "M", "濾"), - (0xF985, "M", "礪"), - (0xF986, "M", "閭"), - (0xF987, "M", "驪"), - (0xF988, "M", "麗"), - (0xF989, "M", "黎"), - (0xF98A, "M", "力"), - (0xF98B, "M", "曆"), - (0xF98C, "M", "歷"), - (0xF98D, "M", "轢"), - (0xF98E, "M", "年"), - (0xF98F, "M", "憐"), - (0xF990, "M", "戀"), - (0xF991, "M", "撚"), - (0xF992, "M", "漣"), - (0xF993, "M", "煉"), - (0xF994, "M", "璉"), - (0xF995, "M", "秊"), - (0xF996, "M", "練"), - (0xF997, "M", "聯"), - (0xF998, "M", "輦"), - (0xF999, "M", "蓮"), - (0xF99A, "M", "連"), - (0xF99B, "M", "鍊"), - (0xF99C, "M", "列"), - (0xF99D, "M", "劣"), - (0xF99E, "M", "咽"), - (0xF99F, "M", "烈"), - (0xF9A0, "M", "裂"), - (0xF9A1, "M", "說"), - (0xF9A2, "M", "廉"), - (0xF9A3, "M", "念"), - (0xF9A4, "M", "捻"), - (0xF9A5, "M", "殮"), - (0xF9A6, "M", "簾"), - (0xF9A7, "M", "獵"), - (0xF9A8, "M", "令"), - (0xF9A9, "M", "囹"), - (0xF9AA, "M", "寧"), - (0xF9AB, "M", "嶺"), - (0xF9AC, "M", "怜"), - (0xF9AD, "M", "玲"), - ] - - -def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF9AE, "M", "瑩"), - (0xF9AF, "M", "羚"), - (0xF9B0, "M", "聆"), - (0xF9B1, "M", "鈴"), - (0xF9B2, "M", "零"), - (0xF9B3, "M", "靈"), - (0xF9B4, "M", "領"), - (0xF9B5, "M", "例"), - (0xF9B6, "M", "禮"), - (0xF9B7, "M", "醴"), - (0xF9B8, "M", "隸"), - (0xF9B9, "M", "惡"), - (0xF9BA, "M", "了"), - (0xF9BB, "M", "僚"), - (0xF9BC, "M", "寮"), - (0xF9BD, "M", "尿"), - (0xF9BE, "M", "料"), - (0xF9BF, "M", "樂"), - (0xF9C0, "M", "燎"), - (0xF9C1, "M", "療"), - (0xF9C2, "M", "蓼"), - (0xF9C3, "M", "遼"), - (0xF9C4, "M", "龍"), - (0xF9C5, "M", "暈"), - (0xF9C6, "M", "阮"), - (0xF9C7, "M", "劉"), - (0xF9C8, "M", "杻"), - (0xF9C9, "M", "柳"), - (0xF9CA, "M", "流"), - (0xF9CB, "M", "溜"), - (0xF9CC, "M", "琉"), - (0xF9CD, "M", "留"), - (0xF9CE, "M", "硫"), - (0xF9CF, "M", "紐"), - (0xF9D0, "M", "類"), - (0xF9D1, "M", "六"), - (0xF9D2, "M", "戮"), - (0xF9D3, "M", "陸"), - (0xF9D4, "M", "倫"), - (0xF9D5, "M", "崙"), - (0xF9D6, "M", "淪"), - (0xF9D7, "M", "輪"), - (0xF9D8, "M", "律"), - (0xF9D9, "M", "慄"), - (0xF9DA, "M", "栗"), - (0xF9DB, "M", "率"), - (0xF9DC, "M", "隆"), - (0xF9DD, "M", "利"), - (0xF9DE, "M", "吏"), - (0xF9DF, "M", "履"), - (0xF9E0, "M", "易"), - (0xF9E1, "M", "李"), - (0xF9E2, "M", "梨"), - (0xF9E3, "M", "泥"), - (0xF9E4, "M", "理"), - (0xF9E5, "M", "痢"), - (0xF9E6, "M", "罹"), - (0xF9E7, "M", "裏"), - (0xF9E8, "M", "裡"), - (0xF9E9, "M", "里"), - (0xF9EA, "M", "離"), - (0xF9EB, "M", "匿"), - (0xF9EC, "M", "溺"), - (0xF9ED, "M", "吝"), - (0xF9EE, "M", "燐"), - (0xF9EF, "M", "璘"), - (0xF9F0, "M", "藺"), - (0xF9F1, "M", "隣"), - (0xF9F2, "M", "鱗"), - (0xF9F3, "M", "麟"), - (0xF9F4, "M", "林"), - (0xF9F5, "M", "淋"), - (0xF9F6, "M", "臨"), - (0xF9F7, "M", "立"), - (0xF9F8, "M", "笠"), - (0xF9F9, "M", "粒"), - (0xF9FA, "M", "狀"), - (0xF9FB, "M", "炙"), - (0xF9FC, "M", "識"), - (0xF9FD, "M", "什"), - (0xF9FE, "M", "茶"), - (0xF9FF, "M", "刺"), - (0xFA00, "M", "切"), - (0xFA01, "M", "度"), - (0xFA02, "M", "拓"), - (0xFA03, "M", "糖"), - (0xFA04, "M", "宅"), - (0xFA05, "M", "洞"), - (0xFA06, "M", "暴"), - (0xFA07, "M", "輻"), - (0xFA08, "M", "行"), - (0xFA09, "M", "降"), - (0xFA0A, "M", "見"), - (0xFA0B, "M", "廓"), - (0xFA0C, "M", "兀"), - (0xFA0D, "M", "嗀"), - (0xFA0E, "V"), - (0xFA10, "M", "塚"), - (0xFA11, "V"), - (0xFA12, "M", "晴"), - ] - - -def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA13, "V"), - (0xFA15, "M", "凞"), - (0xFA16, "M", "猪"), - (0xFA17, "M", "益"), - (0xFA18, "M", "礼"), - (0xFA19, "M", "神"), - (0xFA1A, "M", "祥"), - (0xFA1B, "M", "福"), - (0xFA1C, "M", "靖"), - (0xFA1D, "M", "精"), - (0xFA1E, "M", "羽"), - (0xFA1F, "V"), - (0xFA20, "M", "蘒"), - (0xFA21, "V"), - (0xFA22, "M", "諸"), - (0xFA23, "V"), - (0xFA25, "M", "逸"), - (0xFA26, "M", "都"), - (0xFA27, "V"), - (0xFA2A, "M", "飯"), - (0xFA2B, "M", "飼"), - (0xFA2C, "M", "館"), - (0xFA2D, "M", "鶴"), - (0xFA2E, "M", "郞"), - (0xFA2F, "M", "隷"), - (0xFA30, "M", "侮"), - (0xFA31, "M", "僧"), - (0xFA32, "M", "免"), - (0xFA33, "M", "勉"), - (0xFA34, "M", "勤"), - (0xFA35, "M", "卑"), - (0xFA36, "M", "喝"), - (0xFA37, "M", "嘆"), - (0xFA38, "M", "器"), - (0xFA39, "M", "塀"), - (0xFA3A, "M", "墨"), - (0xFA3B, "M", "層"), - (0xFA3C, "M", "屮"), - (0xFA3D, "M", "悔"), - (0xFA3E, "M", "慨"), - (0xFA3F, "M", "憎"), - (0xFA40, "M", "懲"), - (0xFA41, "M", "敏"), - (0xFA42, "M", "既"), - (0xFA43, "M", "暑"), - (0xFA44, "M", "梅"), - (0xFA45, "M", "海"), - (0xFA46, "M", "渚"), - (0xFA47, "M", "漢"), - (0xFA48, "M", "煮"), - (0xFA49, "M", "爫"), - (0xFA4A, "M", "琢"), - (0xFA4B, "M", "碑"), - (0xFA4C, "M", "社"), - (0xFA4D, "M", "祉"), - (0xFA4E, "M", "祈"), - (0xFA4F, "M", "祐"), - (0xFA50, "M", "祖"), - (0xFA51, "M", "祝"), - (0xFA52, "M", "禍"), - (0xFA53, "M", "禎"), - (0xFA54, "M", "穀"), - (0xFA55, "M", "突"), - (0xFA56, "M", "節"), - (0xFA57, "M", "練"), - (0xFA58, "M", "縉"), - (0xFA59, "M", "繁"), - (0xFA5A, "M", "署"), - (0xFA5B, "M", "者"), - (0xFA5C, "M", "臭"), - (0xFA5D, "M", "艹"), - (0xFA5F, "M", "著"), - (0xFA60, "M", "褐"), - (0xFA61, "M", "視"), - (0xFA62, "M", "謁"), - (0xFA63, "M", "謹"), - (0xFA64, "M", "賓"), - (0xFA65, "M", "贈"), - (0xFA66, "M", "辶"), - (0xFA67, "M", "逸"), - (0xFA68, "M", "難"), - (0xFA69, "M", "響"), - (0xFA6A, "M", "頻"), - (0xFA6B, "M", "恵"), - (0xFA6C, "M", "𤋮"), - (0xFA6D, "M", "舘"), - (0xFA6E, "X"), - (0xFA70, "M", "並"), - (0xFA71, "M", "况"), - (0xFA72, "M", "全"), - (0xFA73, "M", "侀"), - (0xFA74, "M", "充"), - (0xFA75, "M", "冀"), - (0xFA76, "M", "勇"), - (0xFA77, "M", "勺"), - (0xFA78, "M", "喝"), - (0xFA79, "M", "啕"), - (0xFA7A, "M", "喙"), - (0xFA7B, "M", "嗢"), - (0xFA7C, "M", "塚"), - ] - - -def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA7D, "M", "墳"), - (0xFA7E, "M", "奄"), - (0xFA7F, "M", "奔"), - (0xFA80, "M", "婢"), - (0xFA81, "M", "嬨"), - (0xFA82, "M", "廒"), - (0xFA83, "M", "廙"), - (0xFA84, "M", "彩"), - (0xFA85, "M", "徭"), - (0xFA86, "M", "惘"), - (0xFA87, "M", "慎"), - (0xFA88, "M", "愈"), - (0xFA89, "M", "憎"), - (0xFA8A, "M", "慠"), - (0xFA8B, "M", "懲"), - (0xFA8C, "M", "戴"), - (0xFA8D, "M", "揄"), - (0xFA8E, "M", "搜"), - (0xFA8F, "M", "摒"), - (0xFA90, "M", "敖"), - (0xFA91, "M", "晴"), - (0xFA92, "M", "朗"), - (0xFA93, "M", "望"), - (0xFA94, "M", "杖"), - (0xFA95, "M", "歹"), - (0xFA96, "M", "殺"), - (0xFA97, "M", "流"), - (0xFA98, "M", "滛"), - (0xFA99, "M", "滋"), - (0xFA9A, "M", "漢"), - (0xFA9B, "M", "瀞"), - (0xFA9C, "M", "煮"), - (0xFA9D, "M", "瞧"), - (0xFA9E, "M", "爵"), - (0xFA9F, "M", "犯"), - (0xFAA0, "M", "猪"), - (0xFAA1, "M", "瑱"), - (0xFAA2, "M", "甆"), - (0xFAA3, "M", "画"), - (0xFAA4, "M", "瘝"), - (0xFAA5, "M", "瘟"), - (0xFAA6, "M", "益"), - (0xFAA7, "M", "盛"), - (0xFAA8, "M", "直"), - (0xFAA9, "M", "睊"), - (0xFAAA, "M", "着"), - (0xFAAB, "M", "磌"), - (0xFAAC, "M", "窱"), - (0xFAAD, "M", "節"), - (0xFAAE, "M", "类"), - (0xFAAF, "M", "絛"), - (0xFAB0, "M", "練"), - (0xFAB1, "M", "缾"), - (0xFAB2, "M", "者"), - (0xFAB3, "M", "荒"), - (0xFAB4, "M", "華"), - (0xFAB5, "M", "蝹"), - (0xFAB6, "M", "襁"), - (0xFAB7, "M", "覆"), - (0xFAB8, "M", "視"), - (0xFAB9, "M", "調"), - (0xFABA, "M", "諸"), - (0xFABB, "M", "請"), - (0xFABC, "M", "謁"), - (0xFABD, "M", "諾"), - (0xFABE, "M", "諭"), - (0xFABF, "M", "謹"), - (0xFAC0, "M", "變"), - (0xFAC1, "M", "贈"), - (0xFAC2, "M", "輸"), - (0xFAC3, "M", "遲"), - (0xFAC4, "M", "醙"), - (0xFAC5, "M", "鉶"), - (0xFAC6, "M", "陼"), - (0xFAC7, "M", "難"), - (0xFAC8, "M", "靖"), - (0xFAC9, "M", "韛"), - (0xFACA, "M", "響"), - (0xFACB, "M", "頋"), - (0xFACC, "M", "頻"), - (0xFACD, "M", "鬒"), - (0xFACE, "M", "龜"), - (0xFACF, "M", "𢡊"), - (0xFAD0, "M", "𢡄"), - (0xFAD1, "M", "𣏕"), - (0xFAD2, "M", "㮝"), - (0xFAD3, "M", "䀘"), - (0xFAD4, "M", "䀹"), - (0xFAD5, "M", "𥉉"), - (0xFAD6, "M", "𥳐"), - (0xFAD7, "M", "𧻓"), - (0xFAD8, "M", "齃"), - (0xFAD9, "M", "龎"), - (0xFADA, "X"), - (0xFB00, "M", "ff"), - (0xFB01, "M", "fi"), - (0xFB02, "M", "fl"), - (0xFB03, "M", "ffi"), - (0xFB04, "M", "ffl"), - (0xFB05, "M", "st"), - ] - - -def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFB07, "X"), - (0xFB13, "M", "մն"), - (0xFB14, "M", "մե"), - (0xFB15, "M", "մի"), - (0xFB16, "M", "վն"), - (0xFB17, "M", "մխ"), - (0xFB18, "X"), - (0xFB1D, "M", "יִ"), - (0xFB1E, "V"), - (0xFB1F, "M", "ײַ"), - (0xFB20, "M", "ע"), - (0xFB21, "M", "א"), - (0xFB22, "M", "ד"), - (0xFB23, "M", "ה"), - (0xFB24, "M", "כ"), - (0xFB25, "M", "ל"), - (0xFB26, "M", "ם"), - (0xFB27, "M", "ר"), - (0xFB28, "M", "ת"), - (0xFB29, "3", "+"), - (0xFB2A, "M", "שׁ"), - (0xFB2B, "M", "שׂ"), - (0xFB2C, "M", "שּׁ"), - (0xFB2D, "M", "שּׂ"), - (0xFB2E, "M", "אַ"), - (0xFB2F, "M", "אָ"), - (0xFB30, "M", "אּ"), - (0xFB31, "M", "בּ"), - (0xFB32, "M", "גּ"), - (0xFB33, "M", "דּ"), - (0xFB34, "M", "הּ"), - (0xFB35, "M", "וּ"), - (0xFB36, "M", "זּ"), - (0xFB37, "X"), - (0xFB38, "M", "טּ"), - (0xFB39, "M", "יּ"), - (0xFB3A, "M", "ךּ"), - (0xFB3B, "M", "כּ"), - (0xFB3C, "M", "לּ"), - (0xFB3D, "X"), - (0xFB3E, "M", "מּ"), - (0xFB3F, "X"), - (0xFB40, "M", "נּ"), - (0xFB41, "M", "סּ"), - (0xFB42, "X"), - (0xFB43, "M", "ףּ"), - (0xFB44, "M", "פּ"), - (0xFB45, "X"), - (0xFB46, "M", "צּ"), - (0xFB47, "M", "קּ"), - (0xFB48, "M", "רּ"), - (0xFB49, "M", "שּ"), - (0xFB4A, "M", "תּ"), - (0xFB4B, "M", "וֹ"), - (0xFB4C, "M", "בֿ"), - (0xFB4D, "M", "כֿ"), - (0xFB4E, "M", "פֿ"), - (0xFB4F, "M", "אל"), - (0xFB50, "M", "ٱ"), - (0xFB52, "M", "ٻ"), - (0xFB56, "M", "پ"), - (0xFB5A, "M", "ڀ"), - (0xFB5E, "M", "ٺ"), - (0xFB62, "M", "ٿ"), - (0xFB66, "M", "ٹ"), - (0xFB6A, "M", "ڤ"), - (0xFB6E, "M", "ڦ"), - (0xFB72, "M", "ڄ"), - (0xFB76, "M", "ڃ"), - (0xFB7A, "M", "چ"), - (0xFB7E, "M", "ڇ"), - (0xFB82, "M", "ڍ"), - (0xFB84, "M", "ڌ"), - (0xFB86, "M", "ڎ"), - (0xFB88, "M", "ڈ"), - (0xFB8A, "M", "ژ"), - (0xFB8C, "M", "ڑ"), - (0xFB8E, "M", "ک"), - (0xFB92, "M", "گ"), - (0xFB96, "M", "ڳ"), - (0xFB9A, "M", "ڱ"), - (0xFB9E, "M", "ں"), - (0xFBA0, "M", "ڻ"), - (0xFBA4, "M", "ۀ"), - (0xFBA6, "M", "ہ"), - (0xFBAA, "M", "ھ"), - (0xFBAE, "M", "ے"), - (0xFBB0, "M", "ۓ"), - (0xFBB2, "V"), - (0xFBC3, "X"), - (0xFBD3, "M", "ڭ"), - (0xFBD7, "M", "ۇ"), - (0xFBD9, "M", "ۆ"), - (0xFBDB, "M", "ۈ"), - (0xFBDD, "M", "ۇٴ"), - (0xFBDE, "M", "ۋ"), - (0xFBE0, "M", "ۅ"), - (0xFBE2, "M", "ۉ"), - (0xFBE4, "M", "ې"), - (0xFBE8, "M", "ى"), - ] - - -def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFBEA, "M", "ئا"), - (0xFBEC, "M", "ئە"), - (0xFBEE, "M", "ئو"), - (0xFBF0, "M", "ئۇ"), - (0xFBF2, "M", "ئۆ"), - (0xFBF4, "M", "ئۈ"), - (0xFBF6, "M", "ئې"), - (0xFBF9, "M", "ئى"), - (0xFBFC, "M", "ی"), - (0xFC00, "M", "ئج"), - (0xFC01, "M", "ئح"), - (0xFC02, "M", "ئم"), - (0xFC03, "M", "ئى"), - (0xFC04, "M", "ئي"), - (0xFC05, "M", "بج"), - (0xFC06, "M", "بح"), - (0xFC07, "M", "بخ"), - (0xFC08, "M", "بم"), - (0xFC09, "M", "بى"), - (0xFC0A, "M", "بي"), - (0xFC0B, "M", "تج"), - (0xFC0C, "M", "تح"), - (0xFC0D, "M", "تخ"), - (0xFC0E, "M", "تم"), - (0xFC0F, "M", "تى"), - (0xFC10, "M", "تي"), - (0xFC11, "M", "ثج"), - (0xFC12, "M", "ثم"), - (0xFC13, "M", "ثى"), - (0xFC14, "M", "ثي"), - (0xFC15, "M", "جح"), - (0xFC16, "M", "جم"), - (0xFC17, "M", "حج"), - (0xFC18, "M", "حم"), - (0xFC19, "M", "خج"), - (0xFC1A, "M", "خح"), - (0xFC1B, "M", "خم"), - (0xFC1C, "M", "سج"), - (0xFC1D, "M", "سح"), - (0xFC1E, "M", "سخ"), - (0xFC1F, "M", "سم"), - (0xFC20, "M", "صح"), - (0xFC21, "M", "صم"), - (0xFC22, "M", "ضج"), - (0xFC23, "M", "ضح"), - (0xFC24, "M", "ضخ"), - (0xFC25, "M", "ضم"), - (0xFC26, "M", "طح"), - (0xFC27, "M", "طم"), - (0xFC28, "M", "ظم"), - (0xFC29, "M", "عج"), - (0xFC2A, "M", "عم"), - (0xFC2B, "M", "غج"), - (0xFC2C, "M", "غم"), - (0xFC2D, "M", "فج"), - (0xFC2E, "M", "فح"), - (0xFC2F, "M", "فخ"), - (0xFC30, "M", "فم"), - (0xFC31, "M", "فى"), - (0xFC32, "M", "في"), - (0xFC33, "M", "قح"), - (0xFC34, "M", "قم"), - (0xFC35, "M", "قى"), - (0xFC36, "M", "قي"), - (0xFC37, "M", "كا"), - (0xFC38, "M", "كج"), - (0xFC39, "M", "كح"), - (0xFC3A, "M", "كخ"), - (0xFC3B, "M", "كل"), - (0xFC3C, "M", "كم"), - (0xFC3D, "M", "كى"), - (0xFC3E, "M", "كي"), - (0xFC3F, "M", "لج"), - (0xFC40, "M", "لح"), - (0xFC41, "M", "لخ"), - (0xFC42, "M", "لم"), - (0xFC43, "M", "لى"), - (0xFC44, "M", "لي"), - (0xFC45, "M", "مج"), - (0xFC46, "M", "مح"), - (0xFC47, "M", "مخ"), - (0xFC48, "M", "مم"), - (0xFC49, "M", "مى"), - (0xFC4A, "M", "مي"), - (0xFC4B, "M", "نج"), - (0xFC4C, "M", "نح"), - (0xFC4D, "M", "نخ"), - (0xFC4E, "M", "نم"), - (0xFC4F, "M", "نى"), - (0xFC50, "M", "ني"), - (0xFC51, "M", "هج"), - (0xFC52, "M", "هم"), - (0xFC53, "M", "هى"), - (0xFC54, "M", "هي"), - (0xFC55, "M", "يج"), - (0xFC56, "M", "يح"), - (0xFC57, "M", "يخ"), - (0xFC58, "M", "يم"), - (0xFC59, "M", "يى"), - (0xFC5A, "M", "يي"), - ] - - -def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFC5B, "M", "ذٰ"), - (0xFC5C, "M", "رٰ"), - (0xFC5D, "M", "ىٰ"), - (0xFC5E, "3", " ٌّ"), - (0xFC5F, "3", " ٍّ"), - (0xFC60, "3", " َّ"), - (0xFC61, "3", " ُّ"), - (0xFC62, "3", " ِّ"), - (0xFC63, "3", " ّٰ"), - (0xFC64, "M", "ئر"), - (0xFC65, "M", "ئز"), - (0xFC66, "M", "ئم"), - (0xFC67, "M", "ئن"), - (0xFC68, "M", "ئى"), - (0xFC69, "M", "ئي"), - (0xFC6A, "M", "بر"), - (0xFC6B, "M", "بز"), - (0xFC6C, "M", "بم"), - (0xFC6D, "M", "بن"), - (0xFC6E, "M", "بى"), - (0xFC6F, "M", "بي"), - (0xFC70, "M", "تر"), - (0xFC71, "M", "تز"), - (0xFC72, "M", "تم"), - (0xFC73, "M", "تن"), - (0xFC74, "M", "تى"), - (0xFC75, "M", "تي"), - (0xFC76, "M", "ثر"), - (0xFC77, "M", "ثز"), - (0xFC78, "M", "ثم"), - (0xFC79, "M", "ثن"), - (0xFC7A, "M", "ثى"), - (0xFC7B, "M", "ثي"), - (0xFC7C, "M", "فى"), - (0xFC7D, "M", "في"), - (0xFC7E, "M", "قى"), - (0xFC7F, "M", "قي"), - (0xFC80, "M", "كا"), - (0xFC81, "M", "كل"), - (0xFC82, "M", "كم"), - (0xFC83, "M", "كى"), - (0xFC84, "M", "كي"), - (0xFC85, "M", "لم"), - (0xFC86, "M", "لى"), - (0xFC87, "M", "لي"), - (0xFC88, "M", "ما"), - (0xFC89, "M", "مم"), - (0xFC8A, "M", "نر"), - (0xFC8B, "M", "نز"), - (0xFC8C, "M", "نم"), - (0xFC8D, "M", "نن"), - (0xFC8E, "M", "نى"), - (0xFC8F, "M", "ني"), - (0xFC90, "M", "ىٰ"), - (0xFC91, "M", "ير"), - (0xFC92, "M", "يز"), - (0xFC93, "M", "يم"), - (0xFC94, "M", "ين"), - (0xFC95, "M", "يى"), - (0xFC96, "M", "يي"), - (0xFC97, "M", "ئج"), - (0xFC98, "M", "ئح"), - (0xFC99, "M", "ئخ"), - (0xFC9A, "M", "ئم"), - (0xFC9B, "M", "ئه"), - (0xFC9C, "M", "بج"), - (0xFC9D, "M", "بح"), - (0xFC9E, "M", "بخ"), - (0xFC9F, "M", "بم"), - (0xFCA0, "M", "به"), - (0xFCA1, "M", "تج"), - (0xFCA2, "M", "تح"), - (0xFCA3, "M", "تخ"), - (0xFCA4, "M", "تم"), - (0xFCA5, "M", "ته"), - (0xFCA6, "M", "ثم"), - (0xFCA7, "M", "جح"), - (0xFCA8, "M", "جم"), - (0xFCA9, "M", "حج"), - (0xFCAA, "M", "حم"), - (0xFCAB, "M", "خج"), - (0xFCAC, "M", "خم"), - (0xFCAD, "M", "سج"), - (0xFCAE, "M", "سح"), - (0xFCAF, "M", "سخ"), - (0xFCB0, "M", "سم"), - (0xFCB1, "M", "صح"), - (0xFCB2, "M", "صخ"), - (0xFCB3, "M", "صم"), - (0xFCB4, "M", "ضج"), - (0xFCB5, "M", "ضح"), - (0xFCB6, "M", "ضخ"), - (0xFCB7, "M", "ضم"), - (0xFCB8, "M", "طح"), - (0xFCB9, "M", "ظم"), - (0xFCBA, "M", "عج"), - (0xFCBB, "M", "عم"), - (0xFCBC, "M", "غج"), - (0xFCBD, "M", "غم"), - (0xFCBE, "M", "فج"), - ] - - -def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFCBF, "M", "فح"), - (0xFCC0, "M", "فخ"), - (0xFCC1, "M", "فم"), - (0xFCC2, "M", "قح"), - (0xFCC3, "M", "قم"), - (0xFCC4, "M", "كج"), - (0xFCC5, "M", "كح"), - (0xFCC6, "M", "كخ"), - (0xFCC7, "M", "كل"), - (0xFCC8, "M", "كم"), - (0xFCC9, "M", "لج"), - (0xFCCA, "M", "لح"), - (0xFCCB, "M", "لخ"), - (0xFCCC, "M", "لم"), - (0xFCCD, "M", "له"), - (0xFCCE, "M", "مج"), - (0xFCCF, "M", "مح"), - (0xFCD0, "M", "مخ"), - (0xFCD1, "M", "مم"), - (0xFCD2, "M", "نج"), - (0xFCD3, "M", "نح"), - (0xFCD4, "M", "نخ"), - (0xFCD5, "M", "نم"), - (0xFCD6, "M", "نه"), - (0xFCD7, "M", "هج"), - (0xFCD8, "M", "هم"), - (0xFCD9, "M", "هٰ"), - (0xFCDA, "M", "يج"), - (0xFCDB, "M", "يح"), - (0xFCDC, "M", "يخ"), - (0xFCDD, "M", "يم"), - (0xFCDE, "M", "يه"), - (0xFCDF, "M", "ئم"), - (0xFCE0, "M", "ئه"), - (0xFCE1, "M", "بم"), - (0xFCE2, "M", "به"), - (0xFCE3, "M", "تم"), - (0xFCE4, "M", "ته"), - (0xFCE5, "M", "ثم"), - (0xFCE6, "M", "ثه"), - (0xFCE7, "M", "سم"), - (0xFCE8, "M", "سه"), - (0xFCE9, "M", "شم"), - (0xFCEA, "M", "شه"), - (0xFCEB, "M", "كل"), - (0xFCEC, "M", "كم"), - (0xFCED, "M", "لم"), - (0xFCEE, "M", "نم"), - (0xFCEF, "M", "نه"), - (0xFCF0, "M", "يم"), - (0xFCF1, "M", "يه"), - (0xFCF2, "M", "ـَّ"), - (0xFCF3, "M", "ـُّ"), - (0xFCF4, "M", "ـِّ"), - (0xFCF5, "M", "طى"), - (0xFCF6, "M", "طي"), - (0xFCF7, "M", "عى"), - (0xFCF8, "M", "عي"), - (0xFCF9, "M", "غى"), - (0xFCFA, "M", "غي"), - (0xFCFB, "M", "سى"), - (0xFCFC, "M", "سي"), - (0xFCFD, "M", "شى"), - (0xFCFE, "M", "شي"), - (0xFCFF, "M", "حى"), - (0xFD00, "M", "حي"), - (0xFD01, "M", "جى"), - (0xFD02, "M", "جي"), - (0xFD03, "M", "خى"), - (0xFD04, "M", "خي"), - (0xFD05, "M", "صى"), - (0xFD06, "M", "صي"), - (0xFD07, "M", "ضى"), - (0xFD08, "M", "ضي"), - (0xFD09, "M", "شج"), - (0xFD0A, "M", "شح"), - (0xFD0B, "M", "شخ"), - (0xFD0C, "M", "شم"), - (0xFD0D, "M", "شر"), - (0xFD0E, "M", "سر"), - (0xFD0F, "M", "صر"), - (0xFD10, "M", "ضر"), - (0xFD11, "M", "طى"), - (0xFD12, "M", "طي"), - (0xFD13, "M", "عى"), - (0xFD14, "M", "عي"), - (0xFD15, "M", "غى"), - (0xFD16, "M", "غي"), - (0xFD17, "M", "سى"), - (0xFD18, "M", "سي"), - (0xFD19, "M", "شى"), - (0xFD1A, "M", "شي"), - (0xFD1B, "M", "حى"), - (0xFD1C, "M", "حي"), - (0xFD1D, "M", "جى"), - (0xFD1E, "M", "جي"), - (0xFD1F, "M", "خى"), - (0xFD20, "M", "خي"), - (0xFD21, "M", "صى"), - (0xFD22, "M", "صي"), - ] - - -def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFD23, "M", "ضى"), - (0xFD24, "M", "ضي"), - (0xFD25, "M", "شج"), - (0xFD26, "M", "شح"), - (0xFD27, "M", "شخ"), - (0xFD28, "M", "شم"), - (0xFD29, "M", "شر"), - (0xFD2A, "M", "سر"), - (0xFD2B, "M", "صر"), - (0xFD2C, "M", "ضر"), - (0xFD2D, "M", "شج"), - (0xFD2E, "M", "شح"), - (0xFD2F, "M", "شخ"), - (0xFD30, "M", "شم"), - (0xFD31, "M", "سه"), - (0xFD32, "M", "شه"), - (0xFD33, "M", "طم"), - (0xFD34, "M", "سج"), - (0xFD35, "M", "سح"), - (0xFD36, "M", "سخ"), - (0xFD37, "M", "شج"), - (0xFD38, "M", "شح"), - (0xFD39, "M", "شخ"), - (0xFD3A, "M", "طم"), - (0xFD3B, "M", "ظم"), - (0xFD3C, "M", "اً"), - (0xFD3E, "V"), - (0xFD50, "M", "تجم"), - (0xFD51, "M", "تحج"), - (0xFD53, "M", "تحم"), - (0xFD54, "M", "تخم"), - (0xFD55, "M", "تمج"), - (0xFD56, "M", "تمح"), - (0xFD57, "M", "تمخ"), - (0xFD58, "M", "جمح"), - (0xFD5A, "M", "حمي"), - (0xFD5B, "M", "حمى"), - (0xFD5C, "M", "سحج"), - (0xFD5D, "M", "سجح"), - (0xFD5E, "M", "سجى"), - (0xFD5F, "M", "سمح"), - (0xFD61, "M", "سمج"), - (0xFD62, "M", "سمم"), - (0xFD64, "M", "صحح"), - (0xFD66, "M", "صمم"), - (0xFD67, "M", "شحم"), - (0xFD69, "M", "شجي"), - (0xFD6A, "M", "شمخ"), - (0xFD6C, "M", "شمم"), - (0xFD6E, "M", "ضحى"), - (0xFD6F, "M", "ضخم"), - (0xFD71, "M", "طمح"), - (0xFD73, "M", "طمم"), - (0xFD74, "M", "طمي"), - (0xFD75, "M", "عجم"), - (0xFD76, "M", "عمم"), - (0xFD78, "M", "عمى"), - (0xFD79, "M", "غمم"), - (0xFD7A, "M", "غمي"), - (0xFD7B, "M", "غمى"), - (0xFD7C, "M", "فخم"), - (0xFD7E, "M", "قمح"), - (0xFD7F, "M", "قمم"), - (0xFD80, "M", "لحم"), - (0xFD81, "M", "لحي"), - (0xFD82, "M", "لحى"), - (0xFD83, "M", "لجج"), - (0xFD85, "M", "لخم"), - (0xFD87, "M", "لمح"), - (0xFD89, "M", "محج"), - (0xFD8A, "M", "محم"), - (0xFD8B, "M", "محي"), - (0xFD8C, "M", "مجح"), - (0xFD8D, "M", "مجم"), - (0xFD8E, "M", "مخج"), - (0xFD8F, "M", "مخم"), - (0xFD90, "X"), - (0xFD92, "M", "مجخ"), - (0xFD93, "M", "همج"), - (0xFD94, "M", "همم"), - (0xFD95, "M", "نحم"), - (0xFD96, "M", "نحى"), - (0xFD97, "M", "نجم"), - (0xFD99, "M", "نجى"), - (0xFD9A, "M", "نمي"), - (0xFD9B, "M", "نمى"), - (0xFD9C, "M", "يمم"), - (0xFD9E, "M", "بخي"), - (0xFD9F, "M", "تجي"), - (0xFDA0, "M", "تجى"), - (0xFDA1, "M", "تخي"), - (0xFDA2, "M", "تخى"), - (0xFDA3, "M", "تمي"), - (0xFDA4, "M", "تمى"), - (0xFDA5, "M", "جمي"), - (0xFDA6, "M", "جحى"), - (0xFDA7, "M", "جمى"), - (0xFDA8, "M", "سخى"), - (0xFDA9, "M", "صحي"), - (0xFDAA, "M", "شحي"), - ] - - -def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFDAB, "M", "ضحي"), - (0xFDAC, "M", "لجي"), - (0xFDAD, "M", "لمي"), - (0xFDAE, "M", "يحي"), - (0xFDAF, "M", "يجي"), - (0xFDB0, "M", "يمي"), - (0xFDB1, "M", "ممي"), - (0xFDB2, "M", "قمي"), - (0xFDB3, "M", "نحي"), - (0xFDB4, "M", "قمح"), - (0xFDB5, "M", "لحم"), - (0xFDB6, "M", "عمي"), - (0xFDB7, "M", "كمي"), - (0xFDB8, "M", "نجح"), - (0xFDB9, "M", "مخي"), - (0xFDBA, "M", "لجم"), - (0xFDBB, "M", "كمم"), - (0xFDBC, "M", "لجم"), - (0xFDBD, "M", "نجح"), - (0xFDBE, "M", "جحي"), - (0xFDBF, "M", "حجي"), - (0xFDC0, "M", "مجي"), - (0xFDC1, "M", "فمي"), - (0xFDC2, "M", "بحي"), - (0xFDC3, "M", "كمم"), - (0xFDC4, "M", "عجم"), - (0xFDC5, "M", "صمم"), - (0xFDC6, "M", "سخي"), - (0xFDC7, "M", "نجي"), - (0xFDC8, "X"), - (0xFDCF, "V"), - (0xFDD0, "X"), - (0xFDF0, "M", "صلے"), - (0xFDF1, "M", "قلے"), - (0xFDF2, "M", "الله"), - (0xFDF3, "M", "اكبر"), - (0xFDF4, "M", "محمد"), - (0xFDF5, "M", "صلعم"), - (0xFDF6, "M", "رسول"), - (0xFDF7, "M", "عليه"), - (0xFDF8, "M", "وسلم"), - (0xFDF9, "M", "صلى"), - (0xFDFA, "3", "صلى الله عليه وسلم"), - (0xFDFB, "3", "جل جلاله"), - (0xFDFC, "M", "ریال"), - (0xFDFD, "V"), - (0xFE00, "I"), - (0xFE10, "3", ","), - (0xFE11, "M", "、"), - (0xFE12, "X"), - (0xFE13, "3", ":"), - (0xFE14, "3", ";"), - (0xFE15, "3", "!"), - (0xFE16, "3", "?"), - (0xFE17, "M", "〖"), - (0xFE18, "M", "〗"), - (0xFE19, "X"), - (0xFE20, "V"), - (0xFE30, "X"), - (0xFE31, "M", "—"), - (0xFE32, "M", "–"), - (0xFE33, "3", "_"), - (0xFE35, "3", "("), - (0xFE36, "3", ")"), - (0xFE37, "3", "{"), - (0xFE38, "3", "}"), - (0xFE39, "M", "〔"), - (0xFE3A, "M", "〕"), - (0xFE3B, "M", "【"), - (0xFE3C, "M", "】"), - (0xFE3D, "M", "《"), - (0xFE3E, "M", "》"), - (0xFE3F, "M", "〈"), - (0xFE40, "M", "〉"), - (0xFE41, "M", "「"), - (0xFE42, "M", "」"), - (0xFE43, "M", "『"), - (0xFE44, "M", "』"), - (0xFE45, "V"), - (0xFE47, "3", "["), - (0xFE48, "3", "]"), - (0xFE49, "3", " ̅"), - (0xFE4D, "3", "_"), - (0xFE50, "3", ","), - (0xFE51, "M", "、"), - (0xFE52, "X"), - (0xFE54, "3", ";"), - (0xFE55, "3", ":"), - (0xFE56, "3", "?"), - (0xFE57, "3", "!"), - (0xFE58, "M", "—"), - (0xFE59, "3", "("), - (0xFE5A, "3", ")"), - (0xFE5B, "3", "{"), - (0xFE5C, "3", "}"), - (0xFE5D, "M", "〔"), - (0xFE5E, "M", "〕"), - (0xFE5F, "3", "#"), - (0xFE60, "3", "&"), - (0xFE61, "3", "*"), - ] - - -def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFE62, "3", "+"), - (0xFE63, "M", "-"), - (0xFE64, "3", "<"), - (0xFE65, "3", ">"), - (0xFE66, "3", "="), - (0xFE67, "X"), - (0xFE68, "3", "\\"), - (0xFE69, "3", "$"), - (0xFE6A, "3", "%"), - (0xFE6B, "3", "@"), - (0xFE6C, "X"), - (0xFE70, "3", " ً"), - (0xFE71, "M", "ـً"), - (0xFE72, "3", " ٌ"), - (0xFE73, "V"), - (0xFE74, "3", " ٍ"), - (0xFE75, "X"), - (0xFE76, "3", " َ"), - (0xFE77, "M", "ـَ"), - (0xFE78, "3", " ُ"), - (0xFE79, "M", "ـُ"), - (0xFE7A, "3", " ِ"), - (0xFE7B, "M", "ـِ"), - (0xFE7C, "3", " ّ"), - (0xFE7D, "M", "ـّ"), - (0xFE7E, "3", " ْ"), - (0xFE7F, "M", "ـْ"), - (0xFE80, "M", "ء"), - (0xFE81, "M", "آ"), - (0xFE83, "M", "أ"), - (0xFE85, "M", "ؤ"), - (0xFE87, "M", "إ"), - (0xFE89, "M", "ئ"), - (0xFE8D, "M", "ا"), - (0xFE8F, "M", "ب"), - (0xFE93, "M", "ة"), - (0xFE95, "M", "ت"), - (0xFE99, "M", "ث"), - (0xFE9D, "M", "ج"), - (0xFEA1, "M", "ح"), - (0xFEA5, "M", "خ"), - (0xFEA9, "M", "د"), - (0xFEAB, "M", "ذ"), - (0xFEAD, "M", "ر"), - (0xFEAF, "M", "ز"), - (0xFEB1, "M", "س"), - (0xFEB5, "M", "ش"), - (0xFEB9, "M", "ص"), - (0xFEBD, "M", "ض"), - (0xFEC1, "M", "ط"), - (0xFEC5, "M", "ظ"), - (0xFEC9, "M", "ع"), - (0xFECD, "M", "غ"), - (0xFED1, "M", "ف"), - (0xFED5, "M", "ق"), - (0xFED9, "M", "ك"), - (0xFEDD, "M", "ل"), - (0xFEE1, "M", "م"), - (0xFEE5, "M", "ن"), - (0xFEE9, "M", "ه"), - (0xFEED, "M", "و"), - (0xFEEF, "M", "ى"), - (0xFEF1, "M", "ي"), - (0xFEF5, "M", "لآ"), - (0xFEF7, "M", "لأ"), - (0xFEF9, "M", "لإ"), - (0xFEFB, "M", "لا"), - (0xFEFD, "X"), - (0xFEFF, "I"), - (0xFF00, "X"), - (0xFF01, "3", "!"), - (0xFF02, "3", '"'), - (0xFF03, "3", "#"), - (0xFF04, "3", "$"), - (0xFF05, "3", "%"), - (0xFF06, "3", "&"), - (0xFF07, "3", "'"), - (0xFF08, "3", "("), - (0xFF09, "3", ")"), - (0xFF0A, "3", "*"), - (0xFF0B, "3", "+"), - (0xFF0C, "3", ","), - (0xFF0D, "M", "-"), - (0xFF0E, "M", "."), - (0xFF0F, "3", "/"), - (0xFF10, "M", "0"), - (0xFF11, "M", "1"), - (0xFF12, "M", "2"), - (0xFF13, "M", "3"), - (0xFF14, "M", "4"), - (0xFF15, "M", "5"), - (0xFF16, "M", "6"), - (0xFF17, "M", "7"), - (0xFF18, "M", "8"), - (0xFF19, "M", "9"), - (0xFF1A, "3", ":"), - (0xFF1B, "3", ";"), - (0xFF1C, "3", "<"), - (0xFF1D, "3", "="), - (0xFF1E, "3", ">"), - ] - - -def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF1F, "3", "?"), - (0xFF20, "3", "@"), - (0xFF21, "M", "a"), - (0xFF22, "M", "b"), - (0xFF23, "M", "c"), - (0xFF24, "M", "d"), - (0xFF25, "M", "e"), - (0xFF26, "M", "f"), - (0xFF27, "M", "g"), - (0xFF28, "M", "h"), - (0xFF29, "M", "i"), - (0xFF2A, "M", "j"), - (0xFF2B, "M", "k"), - (0xFF2C, "M", "l"), - (0xFF2D, "M", "m"), - (0xFF2E, "M", "n"), - (0xFF2F, "M", "o"), - (0xFF30, "M", "p"), - (0xFF31, "M", "q"), - (0xFF32, "M", "r"), - (0xFF33, "M", "s"), - (0xFF34, "M", "t"), - (0xFF35, "M", "u"), - (0xFF36, "M", "v"), - (0xFF37, "M", "w"), - (0xFF38, "M", "x"), - (0xFF39, "M", "y"), - (0xFF3A, "M", "z"), - (0xFF3B, "3", "["), - (0xFF3C, "3", "\\"), - (0xFF3D, "3", "]"), - (0xFF3E, "3", "^"), - (0xFF3F, "3", "_"), - (0xFF40, "3", "`"), - (0xFF41, "M", "a"), - (0xFF42, "M", "b"), - (0xFF43, "M", "c"), - (0xFF44, "M", "d"), - (0xFF45, "M", "e"), - (0xFF46, "M", "f"), - (0xFF47, "M", "g"), - (0xFF48, "M", "h"), - (0xFF49, "M", "i"), - (0xFF4A, "M", "j"), - (0xFF4B, "M", "k"), - (0xFF4C, "M", "l"), - (0xFF4D, "M", "m"), - (0xFF4E, "M", "n"), - (0xFF4F, "M", "o"), - (0xFF50, "M", "p"), - (0xFF51, "M", "q"), - (0xFF52, "M", "r"), - (0xFF53, "M", "s"), - (0xFF54, "M", "t"), - (0xFF55, "M", "u"), - (0xFF56, "M", "v"), - (0xFF57, "M", "w"), - (0xFF58, "M", "x"), - (0xFF59, "M", "y"), - (0xFF5A, "M", "z"), - (0xFF5B, "3", "{"), - (0xFF5C, "3", "|"), - (0xFF5D, "3", "}"), - (0xFF5E, "3", "~"), - (0xFF5F, "M", "⦅"), - (0xFF60, "M", "⦆"), - (0xFF61, "M", "."), - (0xFF62, "M", "「"), - (0xFF63, "M", "」"), - (0xFF64, "M", "、"), - (0xFF65, "M", "・"), - (0xFF66, "M", "ヲ"), - (0xFF67, "M", "ァ"), - (0xFF68, "M", "ィ"), - (0xFF69, "M", "ゥ"), - (0xFF6A, "M", "ェ"), - (0xFF6B, "M", "ォ"), - (0xFF6C, "M", "ャ"), - (0xFF6D, "M", "ュ"), - (0xFF6E, "M", "ョ"), - (0xFF6F, "M", "ッ"), - (0xFF70, "M", "ー"), - (0xFF71, "M", "ア"), - (0xFF72, "M", "イ"), - (0xFF73, "M", "ウ"), - (0xFF74, "M", "エ"), - (0xFF75, "M", "オ"), - (0xFF76, "M", "カ"), - (0xFF77, "M", "キ"), - (0xFF78, "M", "ク"), - (0xFF79, "M", "ケ"), - (0xFF7A, "M", "コ"), - (0xFF7B, "M", "サ"), - (0xFF7C, "M", "シ"), - (0xFF7D, "M", "ス"), - (0xFF7E, "M", "セ"), - (0xFF7F, "M", "ソ"), - (0xFF80, "M", "タ"), - (0xFF81, "M", "チ"), - (0xFF82, "M", "ツ"), - ] - - -def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF83, "M", "テ"), - (0xFF84, "M", "ト"), - (0xFF85, "M", "ナ"), - (0xFF86, "M", "ニ"), - (0xFF87, "M", "ヌ"), - (0xFF88, "M", "ネ"), - (0xFF89, "M", "ノ"), - (0xFF8A, "M", "ハ"), - (0xFF8B, "M", "ヒ"), - (0xFF8C, "M", "フ"), - (0xFF8D, "M", "ヘ"), - (0xFF8E, "M", "ホ"), - (0xFF8F, "M", "マ"), - (0xFF90, "M", "ミ"), - (0xFF91, "M", "ム"), - (0xFF92, "M", "メ"), - (0xFF93, "M", "モ"), - (0xFF94, "M", "ヤ"), - (0xFF95, "M", "ユ"), - (0xFF96, "M", "ヨ"), - (0xFF97, "M", "ラ"), - (0xFF98, "M", "リ"), - (0xFF99, "M", "ル"), - (0xFF9A, "M", "レ"), - (0xFF9B, "M", "ロ"), - (0xFF9C, "M", "ワ"), - (0xFF9D, "M", "ン"), - (0xFF9E, "M", "゙"), - (0xFF9F, "M", "゚"), - (0xFFA0, "X"), - (0xFFA1, "M", "ᄀ"), - (0xFFA2, "M", "ᄁ"), - (0xFFA3, "M", "ᆪ"), - (0xFFA4, "M", "ᄂ"), - (0xFFA5, "M", "ᆬ"), - (0xFFA6, "M", "ᆭ"), - (0xFFA7, "M", "ᄃ"), - (0xFFA8, "M", "ᄄ"), - (0xFFA9, "M", "ᄅ"), - (0xFFAA, "M", "ᆰ"), - (0xFFAB, "M", "ᆱ"), - (0xFFAC, "M", "ᆲ"), - (0xFFAD, "M", "ᆳ"), - (0xFFAE, "M", "ᆴ"), - (0xFFAF, "M", "ᆵ"), - (0xFFB0, "M", "ᄚ"), - (0xFFB1, "M", "ᄆ"), - (0xFFB2, "M", "ᄇ"), - (0xFFB3, "M", "ᄈ"), - (0xFFB4, "M", "ᄡ"), - (0xFFB5, "M", "ᄉ"), - (0xFFB6, "M", "ᄊ"), - (0xFFB7, "M", "ᄋ"), - (0xFFB8, "M", "ᄌ"), - (0xFFB9, "M", "ᄍ"), - (0xFFBA, "M", "ᄎ"), - (0xFFBB, "M", "ᄏ"), - (0xFFBC, "M", "ᄐ"), - (0xFFBD, "M", "ᄑ"), - (0xFFBE, "M", "ᄒ"), - (0xFFBF, "X"), - (0xFFC2, "M", "ᅡ"), - (0xFFC3, "M", "ᅢ"), - (0xFFC4, "M", "ᅣ"), - (0xFFC5, "M", "ᅤ"), - (0xFFC6, "M", "ᅥ"), - (0xFFC7, "M", "ᅦ"), - (0xFFC8, "X"), - (0xFFCA, "M", "ᅧ"), - (0xFFCB, "M", "ᅨ"), - (0xFFCC, "M", "ᅩ"), - (0xFFCD, "M", "ᅪ"), - (0xFFCE, "M", "ᅫ"), - (0xFFCF, "M", "ᅬ"), - (0xFFD0, "X"), - (0xFFD2, "M", "ᅭ"), - (0xFFD3, "M", "ᅮ"), - (0xFFD4, "M", "ᅯ"), - (0xFFD5, "M", "ᅰ"), - (0xFFD6, "M", "ᅱ"), - (0xFFD7, "M", "ᅲ"), - (0xFFD8, "X"), - (0xFFDA, "M", "ᅳ"), - (0xFFDB, "M", "ᅴ"), - (0xFFDC, "M", "ᅵ"), - (0xFFDD, "X"), - (0xFFE0, "M", "¢"), - (0xFFE1, "M", "£"), - (0xFFE2, "M", "¬"), - (0xFFE3, "3", " ̄"), - (0xFFE4, "M", "¦"), - (0xFFE5, "M", "¥"), - (0xFFE6, "M", "₩"), - (0xFFE7, "X"), - (0xFFE8, "M", "│"), - (0xFFE9, "M", "←"), - (0xFFEA, "M", "↑"), - (0xFFEB, "M", "→"), - (0xFFEC, "M", "↓"), - (0xFFED, "M", "■"), - ] - - -def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFFEE, "M", "○"), - (0xFFEF, "X"), - (0x10000, "V"), - (0x1000C, "X"), - (0x1000D, "V"), - (0x10027, "X"), - (0x10028, "V"), - (0x1003B, "X"), - (0x1003C, "V"), - (0x1003E, "X"), - (0x1003F, "V"), - (0x1004E, "X"), - (0x10050, "V"), - (0x1005E, "X"), - (0x10080, "V"), - (0x100FB, "X"), - (0x10100, "V"), - (0x10103, "X"), - (0x10107, "V"), - (0x10134, "X"), - (0x10137, "V"), - (0x1018F, "X"), - (0x10190, "V"), - (0x1019D, "X"), - (0x101A0, "V"), - (0x101A1, "X"), - (0x101D0, "V"), - (0x101FE, "X"), - (0x10280, "V"), - (0x1029D, "X"), - (0x102A0, "V"), - (0x102D1, "X"), - (0x102E0, "V"), - (0x102FC, "X"), - (0x10300, "V"), - (0x10324, "X"), - (0x1032D, "V"), - (0x1034B, "X"), - (0x10350, "V"), - (0x1037B, "X"), - (0x10380, "V"), - (0x1039E, "X"), - (0x1039F, "V"), - (0x103C4, "X"), - (0x103C8, "V"), - (0x103D6, "X"), - (0x10400, "M", "𐐨"), - (0x10401, "M", "𐐩"), - (0x10402, "M", "𐐪"), - (0x10403, "M", "𐐫"), - (0x10404, "M", "𐐬"), - (0x10405, "M", "𐐭"), - (0x10406, "M", "𐐮"), - (0x10407, "M", "𐐯"), - (0x10408, "M", "𐐰"), - (0x10409, "M", "𐐱"), - (0x1040A, "M", "𐐲"), - (0x1040B, "M", "𐐳"), - (0x1040C, "M", "𐐴"), - (0x1040D, "M", "𐐵"), - (0x1040E, "M", "𐐶"), - (0x1040F, "M", "𐐷"), - (0x10410, "M", "𐐸"), - (0x10411, "M", "𐐹"), - (0x10412, "M", "𐐺"), - (0x10413, "M", "𐐻"), - (0x10414, "M", "𐐼"), - (0x10415, "M", "𐐽"), - (0x10416, "M", "𐐾"), - (0x10417, "M", "𐐿"), - (0x10418, "M", "𐑀"), - (0x10419, "M", "𐑁"), - (0x1041A, "M", "𐑂"), - (0x1041B, "M", "𐑃"), - (0x1041C, "M", "𐑄"), - (0x1041D, "M", "𐑅"), - (0x1041E, "M", "𐑆"), - (0x1041F, "M", "𐑇"), - (0x10420, "M", "𐑈"), - (0x10421, "M", "𐑉"), - (0x10422, "M", "𐑊"), - (0x10423, "M", "𐑋"), - (0x10424, "M", "𐑌"), - (0x10425, "M", "𐑍"), - (0x10426, "M", "𐑎"), - (0x10427, "M", "𐑏"), - (0x10428, "V"), - (0x1049E, "X"), - (0x104A0, "V"), - (0x104AA, "X"), - (0x104B0, "M", "𐓘"), - (0x104B1, "M", "𐓙"), - (0x104B2, "M", "𐓚"), - (0x104B3, "M", "𐓛"), - (0x104B4, "M", "𐓜"), - (0x104B5, "M", "𐓝"), - (0x104B6, "M", "𐓞"), - (0x104B7, "M", "𐓟"), - (0x104B8, "M", "𐓠"), - (0x104B9, "M", "𐓡"), - ] - - -def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x104BA, "M", "𐓢"), - (0x104BB, "M", "𐓣"), - (0x104BC, "M", "𐓤"), - (0x104BD, "M", "𐓥"), - (0x104BE, "M", "𐓦"), - (0x104BF, "M", "𐓧"), - (0x104C0, "M", "𐓨"), - (0x104C1, "M", "𐓩"), - (0x104C2, "M", "𐓪"), - (0x104C3, "M", "𐓫"), - (0x104C4, "M", "𐓬"), - (0x104C5, "M", "𐓭"), - (0x104C6, "M", "𐓮"), - (0x104C7, "M", "𐓯"), - (0x104C8, "M", "𐓰"), - (0x104C9, "M", "𐓱"), - (0x104CA, "M", "𐓲"), - (0x104CB, "M", "𐓳"), - (0x104CC, "M", "𐓴"), - (0x104CD, "M", "𐓵"), - (0x104CE, "M", "𐓶"), - (0x104CF, "M", "𐓷"), - (0x104D0, "M", "𐓸"), - (0x104D1, "M", "𐓹"), - (0x104D2, "M", "𐓺"), - (0x104D3, "M", "𐓻"), - (0x104D4, "X"), - (0x104D8, "V"), - (0x104FC, "X"), - (0x10500, "V"), - (0x10528, "X"), - (0x10530, "V"), - (0x10564, "X"), - (0x1056F, "V"), - (0x10570, "M", "𐖗"), - (0x10571, "M", "𐖘"), - (0x10572, "M", "𐖙"), - (0x10573, "M", "𐖚"), - (0x10574, "M", "𐖛"), - (0x10575, "M", "𐖜"), - (0x10576, "M", "𐖝"), - (0x10577, "M", "𐖞"), - (0x10578, "M", "𐖟"), - (0x10579, "M", "𐖠"), - (0x1057A, "M", "𐖡"), - (0x1057B, "X"), - (0x1057C, "M", "𐖣"), - (0x1057D, "M", "𐖤"), - (0x1057E, "M", "𐖥"), - (0x1057F, "M", "𐖦"), - (0x10580, "M", "𐖧"), - (0x10581, "M", "𐖨"), - (0x10582, "M", "𐖩"), - (0x10583, "M", "𐖪"), - (0x10584, "M", "𐖫"), - (0x10585, "M", "𐖬"), - (0x10586, "M", "𐖭"), - (0x10587, "M", "𐖮"), - (0x10588, "M", "𐖯"), - (0x10589, "M", "𐖰"), - (0x1058A, "M", "𐖱"), - (0x1058B, "X"), - (0x1058C, "M", "𐖳"), - (0x1058D, "M", "𐖴"), - (0x1058E, "M", "𐖵"), - (0x1058F, "M", "𐖶"), - (0x10590, "M", "𐖷"), - (0x10591, "M", "𐖸"), - (0x10592, "M", "𐖹"), - (0x10593, "X"), - (0x10594, "M", "𐖻"), - (0x10595, "M", "𐖼"), - (0x10596, "X"), - (0x10597, "V"), - (0x105A2, "X"), - (0x105A3, "V"), - (0x105B2, "X"), - (0x105B3, "V"), - (0x105BA, "X"), - (0x105BB, "V"), - (0x105BD, "X"), - (0x10600, "V"), - (0x10737, "X"), - (0x10740, "V"), - (0x10756, "X"), - (0x10760, "V"), - (0x10768, "X"), - (0x10780, "V"), - (0x10781, "M", "ː"), - (0x10782, "M", "ˑ"), - (0x10783, "M", "æ"), - (0x10784, "M", "ʙ"), - (0x10785, "M", "ɓ"), - (0x10786, "X"), - (0x10787, "M", "ʣ"), - (0x10788, "M", "ꭦ"), - (0x10789, "M", "ʥ"), - (0x1078A, "M", "ʤ"), - (0x1078B, "M", "ɖ"), - (0x1078C, "M", "ɗ"), - ] - - -def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1078D, "M", "ᶑ"), - (0x1078E, "M", "ɘ"), - (0x1078F, "M", "ɞ"), - (0x10790, "M", "ʩ"), - (0x10791, "M", "ɤ"), - (0x10792, "M", "ɢ"), - (0x10793, "M", "ɠ"), - (0x10794, "M", "ʛ"), - (0x10795, "M", "ħ"), - (0x10796, "M", "ʜ"), - (0x10797, "M", "ɧ"), - (0x10798, "M", "ʄ"), - (0x10799, "M", "ʪ"), - (0x1079A, "M", "ʫ"), - (0x1079B, "M", "ɬ"), - (0x1079C, "M", "𝼄"), - (0x1079D, "M", "ꞎ"), - (0x1079E, "M", "ɮ"), - (0x1079F, "M", "𝼅"), - (0x107A0, "M", "ʎ"), - (0x107A1, "M", "𝼆"), - (0x107A2, "M", "ø"), - (0x107A3, "M", "ɶ"), - (0x107A4, "M", "ɷ"), - (0x107A5, "M", "q"), - (0x107A6, "M", "ɺ"), - (0x107A7, "M", "𝼈"), - (0x107A8, "M", "ɽ"), - (0x107A9, "M", "ɾ"), - (0x107AA, "M", "ʀ"), - (0x107AB, "M", "ʨ"), - (0x107AC, "M", "ʦ"), - (0x107AD, "M", "ꭧ"), - (0x107AE, "M", "ʧ"), - (0x107AF, "M", "ʈ"), - (0x107B0, "M", "ⱱ"), - (0x107B1, "X"), - (0x107B2, "M", "ʏ"), - (0x107B3, "M", "ʡ"), - (0x107B4, "M", "ʢ"), - (0x107B5, "M", "ʘ"), - (0x107B6, "M", "ǀ"), - (0x107B7, "M", "ǁ"), - (0x107B8, "M", "ǂ"), - (0x107B9, "M", "𝼊"), - (0x107BA, "M", "𝼞"), - (0x107BB, "X"), - (0x10800, "V"), - (0x10806, "X"), - (0x10808, "V"), - (0x10809, "X"), - (0x1080A, "V"), - (0x10836, "X"), - (0x10837, "V"), - (0x10839, "X"), - (0x1083C, "V"), - (0x1083D, "X"), - (0x1083F, "V"), - (0x10856, "X"), - (0x10857, "V"), - (0x1089F, "X"), - (0x108A7, "V"), - (0x108B0, "X"), - (0x108E0, "V"), - (0x108F3, "X"), - (0x108F4, "V"), - (0x108F6, "X"), - (0x108FB, "V"), - (0x1091C, "X"), - (0x1091F, "V"), - (0x1093A, "X"), - (0x1093F, "V"), - (0x10940, "X"), - (0x10980, "V"), - (0x109B8, "X"), - (0x109BC, "V"), - (0x109D0, "X"), - (0x109D2, "V"), - (0x10A04, "X"), - (0x10A05, "V"), - (0x10A07, "X"), - (0x10A0C, "V"), - (0x10A14, "X"), - (0x10A15, "V"), - (0x10A18, "X"), - (0x10A19, "V"), - (0x10A36, "X"), - (0x10A38, "V"), - (0x10A3B, "X"), - (0x10A3F, "V"), - (0x10A49, "X"), - (0x10A50, "V"), - (0x10A59, "X"), - (0x10A60, "V"), - (0x10AA0, "X"), - (0x10AC0, "V"), - (0x10AE7, "X"), - (0x10AEB, "V"), - (0x10AF7, "X"), - (0x10B00, "V"), - ] - - -def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10B36, "X"), - (0x10B39, "V"), - (0x10B56, "X"), - (0x10B58, "V"), - (0x10B73, "X"), - (0x10B78, "V"), - (0x10B92, "X"), - (0x10B99, "V"), - (0x10B9D, "X"), - (0x10BA9, "V"), - (0x10BB0, "X"), - (0x10C00, "V"), - (0x10C49, "X"), - (0x10C80, "M", "𐳀"), - (0x10C81, "M", "𐳁"), - (0x10C82, "M", "𐳂"), - (0x10C83, "M", "𐳃"), - (0x10C84, "M", "𐳄"), - (0x10C85, "M", "𐳅"), - (0x10C86, "M", "𐳆"), - (0x10C87, "M", "𐳇"), - (0x10C88, "M", "𐳈"), - (0x10C89, "M", "𐳉"), - (0x10C8A, "M", "𐳊"), - (0x10C8B, "M", "𐳋"), - (0x10C8C, "M", "𐳌"), - (0x10C8D, "M", "𐳍"), - (0x10C8E, "M", "𐳎"), - (0x10C8F, "M", "𐳏"), - (0x10C90, "M", "𐳐"), - (0x10C91, "M", "𐳑"), - (0x10C92, "M", "𐳒"), - (0x10C93, "M", "𐳓"), - (0x10C94, "M", "𐳔"), - (0x10C95, "M", "𐳕"), - (0x10C96, "M", "𐳖"), - (0x10C97, "M", "𐳗"), - (0x10C98, "M", "𐳘"), - (0x10C99, "M", "𐳙"), - (0x10C9A, "M", "𐳚"), - (0x10C9B, "M", "𐳛"), - (0x10C9C, "M", "𐳜"), - (0x10C9D, "M", "𐳝"), - (0x10C9E, "M", "𐳞"), - (0x10C9F, "M", "𐳟"), - (0x10CA0, "M", "𐳠"), - (0x10CA1, "M", "𐳡"), - (0x10CA2, "M", "𐳢"), - (0x10CA3, "M", "𐳣"), - (0x10CA4, "M", "𐳤"), - (0x10CA5, "M", "𐳥"), - (0x10CA6, "M", "𐳦"), - (0x10CA7, "M", "𐳧"), - (0x10CA8, "M", "𐳨"), - (0x10CA9, "M", "𐳩"), - (0x10CAA, "M", "𐳪"), - (0x10CAB, "M", "𐳫"), - (0x10CAC, "M", "𐳬"), - (0x10CAD, "M", "𐳭"), - (0x10CAE, "M", "𐳮"), - (0x10CAF, "M", "𐳯"), - (0x10CB0, "M", "𐳰"), - (0x10CB1, "M", "𐳱"), - (0x10CB2, "M", "𐳲"), - (0x10CB3, "X"), - (0x10CC0, "V"), - (0x10CF3, "X"), - (0x10CFA, "V"), - (0x10D28, "X"), - (0x10D30, "V"), - (0x10D3A, "X"), - (0x10E60, "V"), - (0x10E7F, "X"), - (0x10E80, "V"), - (0x10EAA, "X"), - (0x10EAB, "V"), - (0x10EAE, "X"), - (0x10EB0, "V"), - (0x10EB2, "X"), - (0x10EFD, "V"), - (0x10F28, "X"), - (0x10F30, "V"), - (0x10F5A, "X"), - (0x10F70, "V"), - (0x10F8A, "X"), - (0x10FB0, "V"), - (0x10FCC, "X"), - (0x10FE0, "V"), - (0x10FF7, "X"), - (0x11000, "V"), - (0x1104E, "X"), - (0x11052, "V"), - (0x11076, "X"), - (0x1107F, "V"), - (0x110BD, "X"), - (0x110BE, "V"), - (0x110C3, "X"), - (0x110D0, "V"), - (0x110E9, "X"), - (0x110F0, "V"), - ] - - -def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x110FA, "X"), - (0x11100, "V"), - (0x11135, "X"), - (0x11136, "V"), - (0x11148, "X"), - (0x11150, "V"), - (0x11177, "X"), - (0x11180, "V"), - (0x111E0, "X"), - (0x111E1, "V"), - (0x111F5, "X"), - (0x11200, "V"), - (0x11212, "X"), - (0x11213, "V"), - (0x11242, "X"), - (0x11280, "V"), - (0x11287, "X"), - (0x11288, "V"), - (0x11289, "X"), - (0x1128A, "V"), - (0x1128E, "X"), - (0x1128F, "V"), - (0x1129E, "X"), - (0x1129F, "V"), - (0x112AA, "X"), - (0x112B0, "V"), - (0x112EB, "X"), - (0x112F0, "V"), - (0x112FA, "X"), - (0x11300, "V"), - (0x11304, "X"), - (0x11305, "V"), - (0x1130D, "X"), - (0x1130F, "V"), - (0x11311, "X"), - (0x11313, "V"), - (0x11329, "X"), - (0x1132A, "V"), - (0x11331, "X"), - (0x11332, "V"), - (0x11334, "X"), - (0x11335, "V"), - (0x1133A, "X"), - (0x1133B, "V"), - (0x11345, "X"), - (0x11347, "V"), - (0x11349, "X"), - (0x1134B, "V"), - (0x1134E, "X"), - (0x11350, "V"), - (0x11351, "X"), - (0x11357, "V"), - (0x11358, "X"), - (0x1135D, "V"), - (0x11364, "X"), - (0x11366, "V"), - (0x1136D, "X"), - (0x11370, "V"), - (0x11375, "X"), - (0x11400, "V"), - (0x1145C, "X"), - (0x1145D, "V"), - (0x11462, "X"), - (0x11480, "V"), - (0x114C8, "X"), - (0x114D0, "V"), - (0x114DA, "X"), - (0x11580, "V"), - (0x115B6, "X"), - (0x115B8, "V"), - (0x115DE, "X"), - (0x11600, "V"), - (0x11645, "X"), - (0x11650, "V"), - (0x1165A, "X"), - (0x11660, "V"), - (0x1166D, "X"), - (0x11680, "V"), - (0x116BA, "X"), - (0x116C0, "V"), - (0x116CA, "X"), - (0x11700, "V"), - (0x1171B, "X"), - (0x1171D, "V"), - (0x1172C, "X"), - (0x11730, "V"), - (0x11747, "X"), - (0x11800, "V"), - (0x1183C, "X"), - (0x118A0, "M", "𑣀"), - (0x118A1, "M", "𑣁"), - (0x118A2, "M", "𑣂"), - (0x118A3, "M", "𑣃"), - (0x118A4, "M", "𑣄"), - (0x118A5, "M", "𑣅"), - (0x118A6, "M", "𑣆"), - (0x118A7, "M", "𑣇"), - (0x118A8, "M", "𑣈"), - (0x118A9, "M", "𑣉"), - (0x118AA, "M", "𑣊"), - ] - - -def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x118AB, "M", "𑣋"), - (0x118AC, "M", "𑣌"), - (0x118AD, "M", "𑣍"), - (0x118AE, "M", "𑣎"), - (0x118AF, "M", "𑣏"), - (0x118B0, "M", "𑣐"), - (0x118B1, "M", "𑣑"), - (0x118B2, "M", "𑣒"), - (0x118B3, "M", "𑣓"), - (0x118B4, "M", "𑣔"), - (0x118B5, "M", "𑣕"), - (0x118B6, "M", "𑣖"), - (0x118B7, "M", "𑣗"), - (0x118B8, "M", "𑣘"), - (0x118B9, "M", "𑣙"), - (0x118BA, "M", "𑣚"), - (0x118BB, "M", "𑣛"), - (0x118BC, "M", "𑣜"), - (0x118BD, "M", "𑣝"), - (0x118BE, "M", "𑣞"), - (0x118BF, "M", "𑣟"), - (0x118C0, "V"), - (0x118F3, "X"), - (0x118FF, "V"), - (0x11907, "X"), - (0x11909, "V"), - (0x1190A, "X"), - (0x1190C, "V"), - (0x11914, "X"), - (0x11915, "V"), - (0x11917, "X"), - (0x11918, "V"), - (0x11936, "X"), - (0x11937, "V"), - (0x11939, "X"), - (0x1193B, "V"), - (0x11947, "X"), - (0x11950, "V"), - (0x1195A, "X"), - (0x119A0, "V"), - (0x119A8, "X"), - (0x119AA, "V"), - (0x119D8, "X"), - (0x119DA, "V"), - (0x119E5, "X"), - (0x11A00, "V"), - (0x11A48, "X"), - (0x11A50, "V"), - (0x11AA3, "X"), - (0x11AB0, "V"), - (0x11AF9, "X"), - (0x11B00, "V"), - (0x11B0A, "X"), - (0x11C00, "V"), - (0x11C09, "X"), - (0x11C0A, "V"), - (0x11C37, "X"), - (0x11C38, "V"), - (0x11C46, "X"), - (0x11C50, "V"), - (0x11C6D, "X"), - (0x11C70, "V"), - (0x11C90, "X"), - (0x11C92, "V"), - (0x11CA8, "X"), - (0x11CA9, "V"), - (0x11CB7, "X"), - (0x11D00, "V"), - (0x11D07, "X"), - (0x11D08, "V"), - (0x11D0A, "X"), - (0x11D0B, "V"), - (0x11D37, "X"), - (0x11D3A, "V"), - (0x11D3B, "X"), - (0x11D3C, "V"), - (0x11D3E, "X"), - (0x11D3F, "V"), - (0x11D48, "X"), - (0x11D50, "V"), - (0x11D5A, "X"), - (0x11D60, "V"), - (0x11D66, "X"), - (0x11D67, "V"), - (0x11D69, "X"), - (0x11D6A, "V"), - (0x11D8F, "X"), - (0x11D90, "V"), - (0x11D92, "X"), - (0x11D93, "V"), - (0x11D99, "X"), - (0x11DA0, "V"), - (0x11DAA, "X"), - (0x11EE0, "V"), - (0x11EF9, "X"), - (0x11F00, "V"), - (0x11F11, "X"), - (0x11F12, "V"), - (0x11F3B, "X"), - (0x11F3E, "V"), - ] - - -def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x11F5A, "X"), - (0x11FB0, "V"), - (0x11FB1, "X"), - (0x11FC0, "V"), - (0x11FF2, "X"), - (0x11FFF, "V"), - (0x1239A, "X"), - (0x12400, "V"), - (0x1246F, "X"), - (0x12470, "V"), - (0x12475, "X"), - (0x12480, "V"), - (0x12544, "X"), - (0x12F90, "V"), - (0x12FF3, "X"), - (0x13000, "V"), - (0x13430, "X"), - (0x13440, "V"), - (0x13456, "X"), - (0x14400, "V"), - (0x14647, "X"), - (0x16800, "V"), - (0x16A39, "X"), - (0x16A40, "V"), - (0x16A5F, "X"), - (0x16A60, "V"), - (0x16A6A, "X"), - (0x16A6E, "V"), - (0x16ABF, "X"), - (0x16AC0, "V"), - (0x16ACA, "X"), - (0x16AD0, "V"), - (0x16AEE, "X"), - (0x16AF0, "V"), - (0x16AF6, "X"), - (0x16B00, "V"), - (0x16B46, "X"), - (0x16B50, "V"), - (0x16B5A, "X"), - (0x16B5B, "V"), - (0x16B62, "X"), - (0x16B63, "V"), - (0x16B78, "X"), - (0x16B7D, "V"), - (0x16B90, "X"), - (0x16E40, "M", "𖹠"), - (0x16E41, "M", "𖹡"), - (0x16E42, "M", "𖹢"), - (0x16E43, "M", "𖹣"), - (0x16E44, "M", "𖹤"), - (0x16E45, "M", "𖹥"), - (0x16E46, "M", "𖹦"), - (0x16E47, "M", "𖹧"), - (0x16E48, "M", "𖹨"), - (0x16E49, "M", "𖹩"), - (0x16E4A, "M", "𖹪"), - (0x16E4B, "M", "𖹫"), - (0x16E4C, "M", "𖹬"), - (0x16E4D, "M", "𖹭"), - (0x16E4E, "M", "𖹮"), - (0x16E4F, "M", "𖹯"), - (0x16E50, "M", "𖹰"), - (0x16E51, "M", "𖹱"), - (0x16E52, "M", "𖹲"), - (0x16E53, "M", "𖹳"), - (0x16E54, "M", "𖹴"), - (0x16E55, "M", "𖹵"), - (0x16E56, "M", "𖹶"), - (0x16E57, "M", "𖹷"), - (0x16E58, "M", "𖹸"), - (0x16E59, "M", "𖹹"), - (0x16E5A, "M", "𖹺"), - (0x16E5B, "M", "𖹻"), - (0x16E5C, "M", "𖹼"), - (0x16E5D, "M", "𖹽"), - (0x16E5E, "M", "𖹾"), - (0x16E5F, "M", "𖹿"), - (0x16E60, "V"), - (0x16E9B, "X"), - (0x16F00, "V"), - (0x16F4B, "X"), - (0x16F4F, "V"), - (0x16F88, "X"), - (0x16F8F, "V"), - (0x16FA0, "X"), - (0x16FE0, "V"), - (0x16FE5, "X"), - (0x16FF0, "V"), - (0x16FF2, "X"), - (0x17000, "V"), - (0x187F8, "X"), - (0x18800, "V"), - (0x18CD6, "X"), - (0x18D00, "V"), - (0x18D09, "X"), - (0x1AFF0, "V"), - (0x1AFF4, "X"), - (0x1AFF5, "V"), - (0x1AFFC, "X"), - (0x1AFFD, "V"), - ] - - -def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1AFFF, "X"), - (0x1B000, "V"), - (0x1B123, "X"), - (0x1B132, "V"), - (0x1B133, "X"), - (0x1B150, "V"), - (0x1B153, "X"), - (0x1B155, "V"), - (0x1B156, "X"), - (0x1B164, "V"), - (0x1B168, "X"), - (0x1B170, "V"), - (0x1B2FC, "X"), - (0x1BC00, "V"), - (0x1BC6B, "X"), - (0x1BC70, "V"), - (0x1BC7D, "X"), - (0x1BC80, "V"), - (0x1BC89, "X"), - (0x1BC90, "V"), - (0x1BC9A, "X"), - (0x1BC9C, "V"), - (0x1BCA0, "I"), - (0x1BCA4, "X"), - (0x1CF00, "V"), - (0x1CF2E, "X"), - (0x1CF30, "V"), - (0x1CF47, "X"), - (0x1CF50, "V"), - (0x1CFC4, "X"), - (0x1D000, "V"), - (0x1D0F6, "X"), - (0x1D100, "V"), - (0x1D127, "X"), - (0x1D129, "V"), - (0x1D15E, "M", "𝅗𝅥"), - (0x1D15F, "M", "𝅘𝅥"), - (0x1D160, "M", "𝅘𝅥𝅮"), - (0x1D161, "M", "𝅘𝅥𝅯"), - (0x1D162, "M", "𝅘𝅥𝅰"), - (0x1D163, "M", "𝅘𝅥𝅱"), - (0x1D164, "M", "𝅘𝅥𝅲"), - (0x1D165, "V"), - (0x1D173, "X"), - (0x1D17B, "V"), - (0x1D1BB, "M", "𝆹𝅥"), - (0x1D1BC, "M", "𝆺𝅥"), - (0x1D1BD, "M", "𝆹𝅥𝅮"), - (0x1D1BE, "M", "𝆺𝅥𝅮"), - (0x1D1BF, "M", "𝆹𝅥𝅯"), - (0x1D1C0, "M", "𝆺𝅥𝅯"), - (0x1D1C1, "V"), - (0x1D1EB, "X"), - (0x1D200, "V"), - (0x1D246, "X"), - (0x1D2C0, "V"), - (0x1D2D4, "X"), - (0x1D2E0, "V"), - (0x1D2F4, "X"), - (0x1D300, "V"), - (0x1D357, "X"), - (0x1D360, "V"), - (0x1D379, "X"), - (0x1D400, "M", "a"), - (0x1D401, "M", "b"), - (0x1D402, "M", "c"), - (0x1D403, "M", "d"), - (0x1D404, "M", "e"), - (0x1D405, "M", "f"), - (0x1D406, "M", "g"), - (0x1D407, "M", "h"), - (0x1D408, "M", "i"), - (0x1D409, "M", "j"), - (0x1D40A, "M", "k"), - (0x1D40B, "M", "l"), - (0x1D40C, "M", "m"), - (0x1D40D, "M", "n"), - (0x1D40E, "M", "o"), - (0x1D40F, "M", "p"), - (0x1D410, "M", "q"), - (0x1D411, "M", "r"), - (0x1D412, "M", "s"), - (0x1D413, "M", "t"), - (0x1D414, "M", "u"), - (0x1D415, "M", "v"), - (0x1D416, "M", "w"), - (0x1D417, "M", "x"), - (0x1D418, "M", "y"), - (0x1D419, "M", "z"), - (0x1D41A, "M", "a"), - (0x1D41B, "M", "b"), - (0x1D41C, "M", "c"), - (0x1D41D, "M", "d"), - (0x1D41E, "M", "e"), - (0x1D41F, "M", "f"), - (0x1D420, "M", "g"), - (0x1D421, "M", "h"), - (0x1D422, "M", "i"), - (0x1D423, "M", "j"), - (0x1D424, "M", "k"), - ] - - -def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D425, "M", "l"), - (0x1D426, "M", "m"), - (0x1D427, "M", "n"), - (0x1D428, "M", "o"), - (0x1D429, "M", "p"), - (0x1D42A, "M", "q"), - (0x1D42B, "M", "r"), - (0x1D42C, "M", "s"), - (0x1D42D, "M", "t"), - (0x1D42E, "M", "u"), - (0x1D42F, "M", "v"), - (0x1D430, "M", "w"), - (0x1D431, "M", "x"), - (0x1D432, "M", "y"), - (0x1D433, "M", "z"), - (0x1D434, "M", "a"), - (0x1D435, "M", "b"), - (0x1D436, "M", "c"), - (0x1D437, "M", "d"), - (0x1D438, "M", "e"), - (0x1D439, "M", "f"), - (0x1D43A, "M", "g"), - (0x1D43B, "M", "h"), - (0x1D43C, "M", "i"), - (0x1D43D, "M", "j"), - (0x1D43E, "M", "k"), - (0x1D43F, "M", "l"), - (0x1D440, "M", "m"), - (0x1D441, "M", "n"), - (0x1D442, "M", "o"), - (0x1D443, "M", "p"), - (0x1D444, "M", "q"), - (0x1D445, "M", "r"), - (0x1D446, "M", "s"), - (0x1D447, "M", "t"), - (0x1D448, "M", "u"), - (0x1D449, "M", "v"), - (0x1D44A, "M", "w"), - (0x1D44B, "M", "x"), - (0x1D44C, "M", "y"), - (0x1D44D, "M", "z"), - (0x1D44E, "M", "a"), - (0x1D44F, "M", "b"), - (0x1D450, "M", "c"), - (0x1D451, "M", "d"), - (0x1D452, "M", "e"), - (0x1D453, "M", "f"), - (0x1D454, "M", "g"), - (0x1D455, "X"), - (0x1D456, "M", "i"), - (0x1D457, "M", "j"), - (0x1D458, "M", "k"), - (0x1D459, "M", "l"), - (0x1D45A, "M", "m"), - (0x1D45B, "M", "n"), - (0x1D45C, "M", "o"), - (0x1D45D, "M", "p"), - (0x1D45E, "M", "q"), - (0x1D45F, "M", "r"), - (0x1D460, "M", "s"), - (0x1D461, "M", "t"), - (0x1D462, "M", "u"), - (0x1D463, "M", "v"), - (0x1D464, "M", "w"), - (0x1D465, "M", "x"), - (0x1D466, "M", "y"), - (0x1D467, "M", "z"), - (0x1D468, "M", "a"), - (0x1D469, "M", "b"), - (0x1D46A, "M", "c"), - (0x1D46B, "M", "d"), - (0x1D46C, "M", "e"), - (0x1D46D, "M", "f"), - (0x1D46E, "M", "g"), - (0x1D46F, "M", "h"), - (0x1D470, "M", "i"), - (0x1D471, "M", "j"), - (0x1D472, "M", "k"), - (0x1D473, "M", "l"), - (0x1D474, "M", "m"), - (0x1D475, "M", "n"), - (0x1D476, "M", "o"), - (0x1D477, "M", "p"), - (0x1D478, "M", "q"), - (0x1D479, "M", "r"), - (0x1D47A, "M", "s"), - (0x1D47B, "M", "t"), - (0x1D47C, "M", "u"), - (0x1D47D, "M", "v"), - (0x1D47E, "M", "w"), - (0x1D47F, "M", "x"), - (0x1D480, "M", "y"), - (0x1D481, "M", "z"), - (0x1D482, "M", "a"), - (0x1D483, "M", "b"), - (0x1D484, "M", "c"), - (0x1D485, "M", "d"), - (0x1D486, "M", "e"), - (0x1D487, "M", "f"), - (0x1D488, "M", "g"), - ] - - -def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D489, "M", "h"), - (0x1D48A, "M", "i"), - (0x1D48B, "M", "j"), - (0x1D48C, "M", "k"), - (0x1D48D, "M", "l"), - (0x1D48E, "M", "m"), - (0x1D48F, "M", "n"), - (0x1D490, "M", "o"), - (0x1D491, "M", "p"), - (0x1D492, "M", "q"), - (0x1D493, "M", "r"), - (0x1D494, "M", "s"), - (0x1D495, "M", "t"), - (0x1D496, "M", "u"), - (0x1D497, "M", "v"), - (0x1D498, "M", "w"), - (0x1D499, "M", "x"), - (0x1D49A, "M", "y"), - (0x1D49B, "M", "z"), - (0x1D49C, "M", "a"), - (0x1D49D, "X"), - (0x1D49E, "M", "c"), - (0x1D49F, "M", "d"), - (0x1D4A0, "X"), - (0x1D4A2, "M", "g"), - (0x1D4A3, "X"), - (0x1D4A5, "M", "j"), - (0x1D4A6, "M", "k"), - (0x1D4A7, "X"), - (0x1D4A9, "M", "n"), - (0x1D4AA, "M", "o"), - (0x1D4AB, "M", "p"), - (0x1D4AC, "M", "q"), - (0x1D4AD, "X"), - (0x1D4AE, "M", "s"), - (0x1D4AF, "M", "t"), - (0x1D4B0, "M", "u"), - (0x1D4B1, "M", "v"), - (0x1D4B2, "M", "w"), - (0x1D4B3, "M", "x"), - (0x1D4B4, "M", "y"), - (0x1D4B5, "M", "z"), - (0x1D4B6, "M", "a"), - (0x1D4B7, "M", "b"), - (0x1D4B8, "M", "c"), - (0x1D4B9, "M", "d"), - (0x1D4BA, "X"), - (0x1D4BB, "M", "f"), - (0x1D4BC, "X"), - (0x1D4BD, "M", "h"), - (0x1D4BE, "M", "i"), - (0x1D4BF, "M", "j"), - (0x1D4C0, "M", "k"), - (0x1D4C1, "M", "l"), - (0x1D4C2, "M", "m"), - (0x1D4C3, "M", "n"), - (0x1D4C4, "X"), - (0x1D4C5, "M", "p"), - (0x1D4C6, "M", "q"), - (0x1D4C7, "M", "r"), - (0x1D4C8, "M", "s"), - (0x1D4C9, "M", "t"), - (0x1D4CA, "M", "u"), - (0x1D4CB, "M", "v"), - (0x1D4CC, "M", "w"), - (0x1D4CD, "M", "x"), - (0x1D4CE, "M", "y"), - (0x1D4CF, "M", "z"), - (0x1D4D0, "M", "a"), - (0x1D4D1, "M", "b"), - (0x1D4D2, "M", "c"), - (0x1D4D3, "M", "d"), - (0x1D4D4, "M", "e"), - (0x1D4D5, "M", "f"), - (0x1D4D6, "M", "g"), - (0x1D4D7, "M", "h"), - (0x1D4D8, "M", "i"), - (0x1D4D9, "M", "j"), - (0x1D4DA, "M", "k"), - (0x1D4DB, "M", "l"), - (0x1D4DC, "M", "m"), - (0x1D4DD, "M", "n"), - (0x1D4DE, "M", "o"), - (0x1D4DF, "M", "p"), - (0x1D4E0, "M", "q"), - (0x1D4E1, "M", "r"), - (0x1D4E2, "M", "s"), - (0x1D4E3, "M", "t"), - (0x1D4E4, "M", "u"), - (0x1D4E5, "M", "v"), - (0x1D4E6, "M", "w"), - (0x1D4E7, "M", "x"), - (0x1D4E8, "M", "y"), - (0x1D4E9, "M", "z"), - (0x1D4EA, "M", "a"), - (0x1D4EB, "M", "b"), - (0x1D4EC, "M", "c"), - (0x1D4ED, "M", "d"), - (0x1D4EE, "M", "e"), - (0x1D4EF, "M", "f"), - ] - - -def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D4F0, "M", "g"), - (0x1D4F1, "M", "h"), - (0x1D4F2, "M", "i"), - (0x1D4F3, "M", "j"), - (0x1D4F4, "M", "k"), - (0x1D4F5, "M", "l"), - (0x1D4F6, "M", "m"), - (0x1D4F7, "M", "n"), - (0x1D4F8, "M", "o"), - (0x1D4F9, "M", "p"), - (0x1D4FA, "M", "q"), - (0x1D4FB, "M", "r"), - (0x1D4FC, "M", "s"), - (0x1D4FD, "M", "t"), - (0x1D4FE, "M", "u"), - (0x1D4FF, "M", "v"), - (0x1D500, "M", "w"), - (0x1D501, "M", "x"), - (0x1D502, "M", "y"), - (0x1D503, "M", "z"), - (0x1D504, "M", "a"), - (0x1D505, "M", "b"), - (0x1D506, "X"), - (0x1D507, "M", "d"), - (0x1D508, "M", "e"), - (0x1D509, "M", "f"), - (0x1D50A, "M", "g"), - (0x1D50B, "X"), - (0x1D50D, "M", "j"), - (0x1D50E, "M", "k"), - (0x1D50F, "M", "l"), - (0x1D510, "M", "m"), - (0x1D511, "M", "n"), - (0x1D512, "M", "o"), - (0x1D513, "M", "p"), - (0x1D514, "M", "q"), - (0x1D515, "X"), - (0x1D516, "M", "s"), - (0x1D517, "M", "t"), - (0x1D518, "M", "u"), - (0x1D519, "M", "v"), - (0x1D51A, "M", "w"), - (0x1D51B, "M", "x"), - (0x1D51C, "M", "y"), - (0x1D51D, "X"), - (0x1D51E, "M", "a"), - (0x1D51F, "M", "b"), - (0x1D520, "M", "c"), - (0x1D521, "M", "d"), - (0x1D522, "M", "e"), - (0x1D523, "M", "f"), - (0x1D524, "M", "g"), - (0x1D525, "M", "h"), - (0x1D526, "M", "i"), - (0x1D527, "M", "j"), - (0x1D528, "M", "k"), - (0x1D529, "M", "l"), - (0x1D52A, "M", "m"), - (0x1D52B, "M", "n"), - (0x1D52C, "M", "o"), - (0x1D52D, "M", "p"), - (0x1D52E, "M", "q"), - (0x1D52F, "M", "r"), - (0x1D530, "M", "s"), - (0x1D531, "M", "t"), - (0x1D532, "M", "u"), - (0x1D533, "M", "v"), - (0x1D534, "M", "w"), - (0x1D535, "M", "x"), - (0x1D536, "M", "y"), - (0x1D537, "M", "z"), - (0x1D538, "M", "a"), - (0x1D539, "M", "b"), - (0x1D53A, "X"), - (0x1D53B, "M", "d"), - (0x1D53C, "M", "e"), - (0x1D53D, "M", "f"), - (0x1D53E, "M", "g"), - (0x1D53F, "X"), - (0x1D540, "M", "i"), - (0x1D541, "M", "j"), - (0x1D542, "M", "k"), - (0x1D543, "M", "l"), - (0x1D544, "M", "m"), - (0x1D545, "X"), - (0x1D546, "M", "o"), - (0x1D547, "X"), - (0x1D54A, "M", "s"), - (0x1D54B, "M", "t"), - (0x1D54C, "M", "u"), - (0x1D54D, "M", "v"), - (0x1D54E, "M", "w"), - (0x1D54F, "M", "x"), - (0x1D550, "M", "y"), - (0x1D551, "X"), - (0x1D552, "M", "a"), - (0x1D553, "M", "b"), - (0x1D554, "M", "c"), - (0x1D555, "M", "d"), - (0x1D556, "M", "e"), - ] - - -def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D557, "M", "f"), - (0x1D558, "M", "g"), - (0x1D559, "M", "h"), - (0x1D55A, "M", "i"), - (0x1D55B, "M", "j"), - (0x1D55C, "M", "k"), - (0x1D55D, "M", "l"), - (0x1D55E, "M", "m"), - (0x1D55F, "M", "n"), - (0x1D560, "M", "o"), - (0x1D561, "M", "p"), - (0x1D562, "M", "q"), - (0x1D563, "M", "r"), - (0x1D564, "M", "s"), - (0x1D565, "M", "t"), - (0x1D566, "M", "u"), - (0x1D567, "M", "v"), - (0x1D568, "M", "w"), - (0x1D569, "M", "x"), - (0x1D56A, "M", "y"), - (0x1D56B, "M", "z"), - (0x1D56C, "M", "a"), - (0x1D56D, "M", "b"), - (0x1D56E, "M", "c"), - (0x1D56F, "M", "d"), - (0x1D570, "M", "e"), - (0x1D571, "M", "f"), - (0x1D572, "M", "g"), - (0x1D573, "M", "h"), - (0x1D574, "M", "i"), - (0x1D575, "M", "j"), - (0x1D576, "M", "k"), - (0x1D577, "M", "l"), - (0x1D578, "M", "m"), - (0x1D579, "M", "n"), - (0x1D57A, "M", "o"), - (0x1D57B, "M", "p"), - (0x1D57C, "M", "q"), - (0x1D57D, "M", "r"), - (0x1D57E, "M", "s"), - (0x1D57F, "M", "t"), - (0x1D580, "M", "u"), - (0x1D581, "M", "v"), - (0x1D582, "M", "w"), - (0x1D583, "M", "x"), - (0x1D584, "M", "y"), - (0x1D585, "M", "z"), - (0x1D586, "M", "a"), - (0x1D587, "M", "b"), - (0x1D588, "M", "c"), - (0x1D589, "M", "d"), - (0x1D58A, "M", "e"), - (0x1D58B, "M", "f"), - (0x1D58C, "M", "g"), - (0x1D58D, "M", "h"), - (0x1D58E, "M", "i"), - (0x1D58F, "M", "j"), - (0x1D590, "M", "k"), - (0x1D591, "M", "l"), - (0x1D592, "M", "m"), - (0x1D593, "M", "n"), - (0x1D594, "M", "o"), - (0x1D595, "M", "p"), - (0x1D596, "M", "q"), - (0x1D597, "M", "r"), - (0x1D598, "M", "s"), - (0x1D599, "M", "t"), - (0x1D59A, "M", "u"), - (0x1D59B, "M", "v"), - (0x1D59C, "M", "w"), - (0x1D59D, "M", "x"), - (0x1D59E, "M", "y"), - (0x1D59F, "M", "z"), - (0x1D5A0, "M", "a"), - (0x1D5A1, "M", "b"), - (0x1D5A2, "M", "c"), - (0x1D5A3, "M", "d"), - (0x1D5A4, "M", "e"), - (0x1D5A5, "M", "f"), - (0x1D5A6, "M", "g"), - (0x1D5A7, "M", "h"), - (0x1D5A8, "M", "i"), - (0x1D5A9, "M", "j"), - (0x1D5AA, "M", "k"), - (0x1D5AB, "M", "l"), - (0x1D5AC, "M", "m"), - (0x1D5AD, "M", "n"), - (0x1D5AE, "M", "o"), - (0x1D5AF, "M", "p"), - (0x1D5B0, "M", "q"), - (0x1D5B1, "M", "r"), - (0x1D5B2, "M", "s"), - (0x1D5B3, "M", "t"), - (0x1D5B4, "M", "u"), - (0x1D5B5, "M", "v"), - (0x1D5B6, "M", "w"), - (0x1D5B7, "M", "x"), - (0x1D5B8, "M", "y"), - (0x1D5B9, "M", "z"), - (0x1D5BA, "M", "a"), - ] - - -def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D5BB, "M", "b"), - (0x1D5BC, "M", "c"), - (0x1D5BD, "M", "d"), - (0x1D5BE, "M", "e"), - (0x1D5BF, "M", "f"), - (0x1D5C0, "M", "g"), - (0x1D5C1, "M", "h"), - (0x1D5C2, "M", "i"), - (0x1D5C3, "M", "j"), - (0x1D5C4, "M", "k"), - (0x1D5C5, "M", "l"), - (0x1D5C6, "M", "m"), - (0x1D5C7, "M", "n"), - (0x1D5C8, "M", "o"), - (0x1D5C9, "M", "p"), - (0x1D5CA, "M", "q"), - (0x1D5CB, "M", "r"), - (0x1D5CC, "M", "s"), - (0x1D5CD, "M", "t"), - (0x1D5CE, "M", "u"), - (0x1D5CF, "M", "v"), - (0x1D5D0, "M", "w"), - (0x1D5D1, "M", "x"), - (0x1D5D2, "M", "y"), - (0x1D5D3, "M", "z"), - (0x1D5D4, "M", "a"), - (0x1D5D5, "M", "b"), - (0x1D5D6, "M", "c"), - (0x1D5D7, "M", "d"), - (0x1D5D8, "M", "e"), - (0x1D5D9, "M", "f"), - (0x1D5DA, "M", "g"), - (0x1D5DB, "M", "h"), - (0x1D5DC, "M", "i"), - (0x1D5DD, "M", "j"), - (0x1D5DE, "M", "k"), - (0x1D5DF, "M", "l"), - (0x1D5E0, "M", "m"), - (0x1D5E1, "M", "n"), - (0x1D5E2, "M", "o"), - (0x1D5E3, "M", "p"), - (0x1D5E4, "M", "q"), - (0x1D5E5, "M", "r"), - (0x1D5E6, "M", "s"), - (0x1D5E7, "M", "t"), - (0x1D5E8, "M", "u"), - (0x1D5E9, "M", "v"), - (0x1D5EA, "M", "w"), - (0x1D5EB, "M", "x"), - (0x1D5EC, "M", "y"), - (0x1D5ED, "M", "z"), - (0x1D5EE, "M", "a"), - (0x1D5EF, "M", "b"), - (0x1D5F0, "M", "c"), - (0x1D5F1, "M", "d"), - (0x1D5F2, "M", "e"), - (0x1D5F3, "M", "f"), - (0x1D5F4, "M", "g"), - (0x1D5F5, "M", "h"), - (0x1D5F6, "M", "i"), - (0x1D5F7, "M", "j"), - (0x1D5F8, "M", "k"), - (0x1D5F9, "M", "l"), - (0x1D5FA, "M", "m"), - (0x1D5FB, "M", "n"), - (0x1D5FC, "M", "o"), - (0x1D5FD, "M", "p"), - (0x1D5FE, "M", "q"), - (0x1D5FF, "M", "r"), - (0x1D600, "M", "s"), - (0x1D601, "M", "t"), - (0x1D602, "M", "u"), - (0x1D603, "M", "v"), - (0x1D604, "M", "w"), - (0x1D605, "M", "x"), - (0x1D606, "M", "y"), - (0x1D607, "M", "z"), - (0x1D608, "M", "a"), - (0x1D609, "M", "b"), - (0x1D60A, "M", "c"), - (0x1D60B, "M", "d"), - (0x1D60C, "M", "e"), - (0x1D60D, "M", "f"), - (0x1D60E, "M", "g"), - (0x1D60F, "M", "h"), - (0x1D610, "M", "i"), - (0x1D611, "M", "j"), - (0x1D612, "M", "k"), - (0x1D613, "M", "l"), - (0x1D614, "M", "m"), - (0x1D615, "M", "n"), - (0x1D616, "M", "o"), - (0x1D617, "M", "p"), - (0x1D618, "M", "q"), - (0x1D619, "M", "r"), - (0x1D61A, "M", "s"), - (0x1D61B, "M", "t"), - (0x1D61C, "M", "u"), - (0x1D61D, "M", "v"), - (0x1D61E, "M", "w"), - ] - - -def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D61F, "M", "x"), - (0x1D620, "M", "y"), - (0x1D621, "M", "z"), - (0x1D622, "M", "a"), - (0x1D623, "M", "b"), - (0x1D624, "M", "c"), - (0x1D625, "M", "d"), - (0x1D626, "M", "e"), - (0x1D627, "M", "f"), - (0x1D628, "M", "g"), - (0x1D629, "M", "h"), - (0x1D62A, "M", "i"), - (0x1D62B, "M", "j"), - (0x1D62C, "M", "k"), - (0x1D62D, "M", "l"), - (0x1D62E, "M", "m"), - (0x1D62F, "M", "n"), - (0x1D630, "M", "o"), - (0x1D631, "M", "p"), - (0x1D632, "M", "q"), - (0x1D633, "M", "r"), - (0x1D634, "M", "s"), - (0x1D635, "M", "t"), - (0x1D636, "M", "u"), - (0x1D637, "M", "v"), - (0x1D638, "M", "w"), - (0x1D639, "M", "x"), - (0x1D63A, "M", "y"), - (0x1D63B, "M", "z"), - (0x1D63C, "M", "a"), - (0x1D63D, "M", "b"), - (0x1D63E, "M", "c"), - (0x1D63F, "M", "d"), - (0x1D640, "M", "e"), - (0x1D641, "M", "f"), - (0x1D642, "M", "g"), - (0x1D643, "M", "h"), - (0x1D644, "M", "i"), - (0x1D645, "M", "j"), - (0x1D646, "M", "k"), - (0x1D647, "M", "l"), - (0x1D648, "M", "m"), - (0x1D649, "M", "n"), - (0x1D64A, "M", "o"), - (0x1D64B, "M", "p"), - (0x1D64C, "M", "q"), - (0x1D64D, "M", "r"), - (0x1D64E, "M", "s"), - (0x1D64F, "M", "t"), - (0x1D650, "M", "u"), - (0x1D651, "M", "v"), - (0x1D652, "M", "w"), - (0x1D653, "M", "x"), - (0x1D654, "M", "y"), - (0x1D655, "M", "z"), - (0x1D656, "M", "a"), - (0x1D657, "M", "b"), - (0x1D658, "M", "c"), - (0x1D659, "M", "d"), - (0x1D65A, "M", "e"), - (0x1D65B, "M", "f"), - (0x1D65C, "M", "g"), - (0x1D65D, "M", "h"), - (0x1D65E, "M", "i"), - (0x1D65F, "M", "j"), - (0x1D660, "M", "k"), - (0x1D661, "M", "l"), - (0x1D662, "M", "m"), - (0x1D663, "M", "n"), - (0x1D664, "M", "o"), - (0x1D665, "M", "p"), - (0x1D666, "M", "q"), - (0x1D667, "M", "r"), - (0x1D668, "M", "s"), - (0x1D669, "M", "t"), - (0x1D66A, "M", "u"), - (0x1D66B, "M", "v"), - (0x1D66C, "M", "w"), - (0x1D66D, "M", "x"), - (0x1D66E, "M", "y"), - (0x1D66F, "M", "z"), - (0x1D670, "M", "a"), - (0x1D671, "M", "b"), - (0x1D672, "M", "c"), - (0x1D673, "M", "d"), - (0x1D674, "M", "e"), - (0x1D675, "M", "f"), - (0x1D676, "M", "g"), - (0x1D677, "M", "h"), - (0x1D678, "M", "i"), - (0x1D679, "M", "j"), - (0x1D67A, "M", "k"), - (0x1D67B, "M", "l"), - (0x1D67C, "M", "m"), - (0x1D67D, "M", "n"), - (0x1D67E, "M", "o"), - (0x1D67F, "M", "p"), - (0x1D680, "M", "q"), - (0x1D681, "M", "r"), - (0x1D682, "M", "s"), - ] - - -def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D683, "M", "t"), - (0x1D684, "M", "u"), - (0x1D685, "M", "v"), - (0x1D686, "M", "w"), - (0x1D687, "M", "x"), - (0x1D688, "M", "y"), - (0x1D689, "M", "z"), - (0x1D68A, "M", "a"), - (0x1D68B, "M", "b"), - (0x1D68C, "M", "c"), - (0x1D68D, "M", "d"), - (0x1D68E, "M", "e"), - (0x1D68F, "M", "f"), - (0x1D690, "M", "g"), - (0x1D691, "M", "h"), - (0x1D692, "M", "i"), - (0x1D693, "M", "j"), - (0x1D694, "M", "k"), - (0x1D695, "M", "l"), - (0x1D696, "M", "m"), - (0x1D697, "M", "n"), - (0x1D698, "M", "o"), - (0x1D699, "M", "p"), - (0x1D69A, "M", "q"), - (0x1D69B, "M", "r"), - (0x1D69C, "M", "s"), - (0x1D69D, "M", "t"), - (0x1D69E, "M", "u"), - (0x1D69F, "M", "v"), - (0x1D6A0, "M", "w"), - (0x1D6A1, "M", "x"), - (0x1D6A2, "M", "y"), - (0x1D6A3, "M", "z"), - (0x1D6A4, "M", "ı"), - (0x1D6A5, "M", "ȷ"), - (0x1D6A6, "X"), - (0x1D6A8, "M", "α"), - (0x1D6A9, "M", "β"), - (0x1D6AA, "M", "γ"), - (0x1D6AB, "M", "δ"), - (0x1D6AC, "M", "ε"), - (0x1D6AD, "M", "ζ"), - (0x1D6AE, "M", "η"), - (0x1D6AF, "M", "θ"), - (0x1D6B0, "M", "ι"), - (0x1D6B1, "M", "κ"), - (0x1D6B2, "M", "λ"), - (0x1D6B3, "M", "μ"), - (0x1D6B4, "M", "ν"), - (0x1D6B5, "M", "ξ"), - (0x1D6B6, "M", "ο"), - (0x1D6B7, "M", "π"), - (0x1D6B8, "M", "ρ"), - (0x1D6B9, "M", "θ"), - (0x1D6BA, "M", "σ"), - (0x1D6BB, "M", "τ"), - (0x1D6BC, "M", "υ"), - (0x1D6BD, "M", "φ"), - (0x1D6BE, "M", "χ"), - (0x1D6BF, "M", "ψ"), - (0x1D6C0, "M", "ω"), - (0x1D6C1, "M", "∇"), - (0x1D6C2, "M", "α"), - (0x1D6C3, "M", "β"), - (0x1D6C4, "M", "γ"), - (0x1D6C5, "M", "δ"), - (0x1D6C6, "M", "ε"), - (0x1D6C7, "M", "ζ"), - (0x1D6C8, "M", "η"), - (0x1D6C9, "M", "θ"), - (0x1D6CA, "M", "ι"), - (0x1D6CB, "M", "κ"), - (0x1D6CC, "M", "λ"), - (0x1D6CD, "M", "μ"), - (0x1D6CE, "M", "ν"), - (0x1D6CF, "M", "ξ"), - (0x1D6D0, "M", "ο"), - (0x1D6D1, "M", "π"), - (0x1D6D2, "M", "ρ"), - (0x1D6D3, "M", "σ"), - (0x1D6D5, "M", "τ"), - (0x1D6D6, "M", "υ"), - (0x1D6D7, "M", "φ"), - (0x1D6D8, "M", "χ"), - (0x1D6D9, "M", "ψ"), - (0x1D6DA, "M", "ω"), - (0x1D6DB, "M", "∂"), - (0x1D6DC, "M", "ε"), - (0x1D6DD, "M", "θ"), - (0x1D6DE, "M", "κ"), - (0x1D6DF, "M", "φ"), - (0x1D6E0, "M", "ρ"), - (0x1D6E1, "M", "π"), - (0x1D6E2, "M", "α"), - (0x1D6E3, "M", "β"), - (0x1D6E4, "M", "γ"), - (0x1D6E5, "M", "δ"), - (0x1D6E6, "M", "ε"), - (0x1D6E7, "M", "ζ"), - (0x1D6E8, "M", "η"), - ] - - -def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D6E9, "M", "θ"), - (0x1D6EA, "M", "ι"), - (0x1D6EB, "M", "κ"), - (0x1D6EC, "M", "λ"), - (0x1D6ED, "M", "μ"), - (0x1D6EE, "M", "ν"), - (0x1D6EF, "M", "ξ"), - (0x1D6F0, "M", "ο"), - (0x1D6F1, "M", "π"), - (0x1D6F2, "M", "ρ"), - (0x1D6F3, "M", "θ"), - (0x1D6F4, "M", "σ"), - (0x1D6F5, "M", "τ"), - (0x1D6F6, "M", "υ"), - (0x1D6F7, "M", "φ"), - (0x1D6F8, "M", "χ"), - (0x1D6F9, "M", "ψ"), - (0x1D6FA, "M", "ω"), - (0x1D6FB, "M", "∇"), - (0x1D6FC, "M", "α"), - (0x1D6FD, "M", "β"), - (0x1D6FE, "M", "γ"), - (0x1D6FF, "M", "δ"), - (0x1D700, "M", "ε"), - (0x1D701, "M", "ζ"), - (0x1D702, "M", "η"), - (0x1D703, "M", "θ"), - (0x1D704, "M", "ι"), - (0x1D705, "M", "κ"), - (0x1D706, "M", "λ"), - (0x1D707, "M", "μ"), - (0x1D708, "M", "ν"), - (0x1D709, "M", "ξ"), - (0x1D70A, "M", "ο"), - (0x1D70B, "M", "π"), - (0x1D70C, "M", "ρ"), - (0x1D70D, "M", "σ"), - (0x1D70F, "M", "τ"), - (0x1D710, "M", "υ"), - (0x1D711, "M", "φ"), - (0x1D712, "M", "χ"), - (0x1D713, "M", "ψ"), - (0x1D714, "M", "ω"), - (0x1D715, "M", "∂"), - (0x1D716, "M", "ε"), - (0x1D717, "M", "θ"), - (0x1D718, "M", "κ"), - (0x1D719, "M", "φ"), - (0x1D71A, "M", "ρ"), - (0x1D71B, "M", "π"), - (0x1D71C, "M", "α"), - (0x1D71D, "M", "β"), - (0x1D71E, "M", "γ"), - (0x1D71F, "M", "δ"), - (0x1D720, "M", "ε"), - (0x1D721, "M", "ζ"), - (0x1D722, "M", "η"), - (0x1D723, "M", "θ"), - (0x1D724, "M", "ι"), - (0x1D725, "M", "κ"), - (0x1D726, "M", "λ"), - (0x1D727, "M", "μ"), - (0x1D728, "M", "ν"), - (0x1D729, "M", "ξ"), - (0x1D72A, "M", "ο"), - (0x1D72B, "M", "π"), - (0x1D72C, "M", "ρ"), - (0x1D72D, "M", "θ"), - (0x1D72E, "M", "σ"), - (0x1D72F, "M", "τ"), - (0x1D730, "M", "υ"), - (0x1D731, "M", "φ"), - (0x1D732, "M", "χ"), - (0x1D733, "M", "ψ"), - (0x1D734, "M", "ω"), - (0x1D735, "M", "∇"), - (0x1D736, "M", "α"), - (0x1D737, "M", "β"), - (0x1D738, "M", "γ"), - (0x1D739, "M", "δ"), - (0x1D73A, "M", "ε"), - (0x1D73B, "M", "ζ"), - (0x1D73C, "M", "η"), - (0x1D73D, "M", "θ"), - (0x1D73E, "M", "ι"), - (0x1D73F, "M", "κ"), - (0x1D740, "M", "λ"), - (0x1D741, "M", "μ"), - (0x1D742, "M", "ν"), - (0x1D743, "M", "ξ"), - (0x1D744, "M", "ο"), - (0x1D745, "M", "π"), - (0x1D746, "M", "ρ"), - (0x1D747, "M", "σ"), - (0x1D749, "M", "τ"), - (0x1D74A, "M", "υ"), - (0x1D74B, "M", "φ"), - (0x1D74C, "M", "χ"), - (0x1D74D, "M", "ψ"), - (0x1D74E, "M", "ω"), - ] - - -def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D74F, "M", "∂"), - (0x1D750, "M", "ε"), - (0x1D751, "M", "θ"), - (0x1D752, "M", "κ"), - (0x1D753, "M", "φ"), - (0x1D754, "M", "ρ"), - (0x1D755, "M", "π"), - (0x1D756, "M", "α"), - (0x1D757, "M", "β"), - (0x1D758, "M", "γ"), - (0x1D759, "M", "δ"), - (0x1D75A, "M", "ε"), - (0x1D75B, "M", "ζ"), - (0x1D75C, "M", "η"), - (0x1D75D, "M", "θ"), - (0x1D75E, "M", "ι"), - (0x1D75F, "M", "κ"), - (0x1D760, "M", "λ"), - (0x1D761, "M", "μ"), - (0x1D762, "M", "ν"), - (0x1D763, "M", "ξ"), - (0x1D764, "M", "ο"), - (0x1D765, "M", "π"), - (0x1D766, "M", "ρ"), - (0x1D767, "M", "θ"), - (0x1D768, "M", "σ"), - (0x1D769, "M", "τ"), - (0x1D76A, "M", "υ"), - (0x1D76B, "M", "φ"), - (0x1D76C, "M", "χ"), - (0x1D76D, "M", "ψ"), - (0x1D76E, "M", "ω"), - (0x1D76F, "M", "∇"), - (0x1D770, "M", "α"), - (0x1D771, "M", "β"), - (0x1D772, "M", "γ"), - (0x1D773, "M", "δ"), - (0x1D774, "M", "ε"), - (0x1D775, "M", "ζ"), - (0x1D776, "M", "η"), - (0x1D777, "M", "θ"), - (0x1D778, "M", "ι"), - (0x1D779, "M", "κ"), - (0x1D77A, "M", "λ"), - (0x1D77B, "M", "μ"), - (0x1D77C, "M", "ν"), - (0x1D77D, "M", "ξ"), - (0x1D77E, "M", "ο"), - (0x1D77F, "M", "π"), - (0x1D780, "M", "ρ"), - (0x1D781, "M", "σ"), - (0x1D783, "M", "τ"), - (0x1D784, "M", "υ"), - (0x1D785, "M", "φ"), - (0x1D786, "M", "χ"), - (0x1D787, "M", "ψ"), - (0x1D788, "M", "ω"), - (0x1D789, "M", "∂"), - (0x1D78A, "M", "ε"), - (0x1D78B, "M", "θ"), - (0x1D78C, "M", "κ"), - (0x1D78D, "M", "φ"), - (0x1D78E, "M", "ρ"), - (0x1D78F, "M", "π"), - (0x1D790, "M", "α"), - (0x1D791, "M", "β"), - (0x1D792, "M", "γ"), - (0x1D793, "M", "δ"), - (0x1D794, "M", "ε"), - (0x1D795, "M", "ζ"), - (0x1D796, "M", "η"), - (0x1D797, "M", "θ"), - (0x1D798, "M", "ι"), - (0x1D799, "M", "κ"), - (0x1D79A, "M", "λ"), - (0x1D79B, "M", "μ"), - (0x1D79C, "M", "ν"), - (0x1D79D, "M", "ξ"), - (0x1D79E, "M", "ο"), - (0x1D79F, "M", "π"), - (0x1D7A0, "M", "ρ"), - (0x1D7A1, "M", "θ"), - (0x1D7A2, "M", "σ"), - (0x1D7A3, "M", "τ"), - (0x1D7A4, "M", "υ"), - (0x1D7A5, "M", "φ"), - (0x1D7A6, "M", "χ"), - (0x1D7A7, "M", "ψ"), - (0x1D7A8, "M", "ω"), - (0x1D7A9, "M", "∇"), - (0x1D7AA, "M", "α"), - (0x1D7AB, "M", "β"), - (0x1D7AC, "M", "γ"), - (0x1D7AD, "M", "δ"), - (0x1D7AE, "M", "ε"), - (0x1D7AF, "M", "ζ"), - (0x1D7B0, "M", "η"), - (0x1D7B1, "M", "θ"), - (0x1D7B2, "M", "ι"), - (0x1D7B3, "M", "κ"), - ] - - -def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D7B4, "M", "λ"), - (0x1D7B5, "M", "μ"), - (0x1D7B6, "M", "ν"), - (0x1D7B7, "M", "ξ"), - (0x1D7B8, "M", "ο"), - (0x1D7B9, "M", "π"), - (0x1D7BA, "M", "ρ"), - (0x1D7BB, "M", "σ"), - (0x1D7BD, "M", "τ"), - (0x1D7BE, "M", "υ"), - (0x1D7BF, "M", "φ"), - (0x1D7C0, "M", "χ"), - (0x1D7C1, "M", "ψ"), - (0x1D7C2, "M", "ω"), - (0x1D7C3, "M", "∂"), - (0x1D7C4, "M", "ε"), - (0x1D7C5, "M", "θ"), - (0x1D7C6, "M", "κ"), - (0x1D7C7, "M", "φ"), - (0x1D7C8, "M", "ρ"), - (0x1D7C9, "M", "π"), - (0x1D7CA, "M", "ϝ"), - (0x1D7CC, "X"), - (0x1D7CE, "M", "0"), - (0x1D7CF, "M", "1"), - (0x1D7D0, "M", "2"), - (0x1D7D1, "M", "3"), - (0x1D7D2, "M", "4"), - (0x1D7D3, "M", "5"), - (0x1D7D4, "M", "6"), - (0x1D7D5, "M", "7"), - (0x1D7D6, "M", "8"), - (0x1D7D7, "M", "9"), - (0x1D7D8, "M", "0"), - (0x1D7D9, "M", "1"), - (0x1D7DA, "M", "2"), - (0x1D7DB, "M", "3"), - (0x1D7DC, "M", "4"), - (0x1D7DD, "M", "5"), - (0x1D7DE, "M", "6"), - (0x1D7DF, "M", "7"), - (0x1D7E0, "M", "8"), - (0x1D7E1, "M", "9"), - (0x1D7E2, "M", "0"), - (0x1D7E3, "M", "1"), - (0x1D7E4, "M", "2"), - (0x1D7E5, "M", "3"), - (0x1D7E6, "M", "4"), - (0x1D7E7, "M", "5"), - (0x1D7E8, "M", "6"), - (0x1D7E9, "M", "7"), - (0x1D7EA, "M", "8"), - (0x1D7EB, "M", "9"), - (0x1D7EC, "M", "0"), - (0x1D7ED, "M", "1"), - (0x1D7EE, "M", "2"), - (0x1D7EF, "M", "3"), - (0x1D7F0, "M", "4"), - (0x1D7F1, "M", "5"), - (0x1D7F2, "M", "6"), - (0x1D7F3, "M", "7"), - (0x1D7F4, "M", "8"), - (0x1D7F5, "M", "9"), - (0x1D7F6, "M", "0"), - (0x1D7F7, "M", "1"), - (0x1D7F8, "M", "2"), - (0x1D7F9, "M", "3"), - (0x1D7FA, "M", "4"), - (0x1D7FB, "M", "5"), - (0x1D7FC, "M", "6"), - (0x1D7FD, "M", "7"), - (0x1D7FE, "M", "8"), - (0x1D7FF, "M", "9"), - (0x1D800, "V"), - (0x1DA8C, "X"), - (0x1DA9B, "V"), - (0x1DAA0, "X"), - (0x1DAA1, "V"), - (0x1DAB0, "X"), - (0x1DF00, "V"), - (0x1DF1F, "X"), - (0x1DF25, "V"), - (0x1DF2B, "X"), - (0x1E000, "V"), - (0x1E007, "X"), - (0x1E008, "V"), - (0x1E019, "X"), - (0x1E01B, "V"), - (0x1E022, "X"), - (0x1E023, "V"), - (0x1E025, "X"), - (0x1E026, "V"), - (0x1E02B, "X"), - (0x1E030, "M", "а"), - (0x1E031, "M", "б"), - (0x1E032, "M", "в"), - (0x1E033, "M", "г"), - (0x1E034, "M", "д"), - (0x1E035, "M", "е"), - (0x1E036, "M", "ж"), - ] - - -def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E037, "M", "з"), - (0x1E038, "M", "и"), - (0x1E039, "M", "к"), - (0x1E03A, "M", "л"), - (0x1E03B, "M", "м"), - (0x1E03C, "M", "о"), - (0x1E03D, "M", "п"), - (0x1E03E, "M", "р"), - (0x1E03F, "M", "с"), - (0x1E040, "M", "т"), - (0x1E041, "M", "у"), - (0x1E042, "M", "ф"), - (0x1E043, "M", "х"), - (0x1E044, "M", "ц"), - (0x1E045, "M", "ч"), - (0x1E046, "M", "ш"), - (0x1E047, "M", "ы"), - (0x1E048, "M", "э"), - (0x1E049, "M", "ю"), - (0x1E04A, "M", "ꚉ"), - (0x1E04B, "M", "ә"), - (0x1E04C, "M", "і"), - (0x1E04D, "M", "ј"), - (0x1E04E, "M", "ө"), - (0x1E04F, "M", "ү"), - (0x1E050, "M", "ӏ"), - (0x1E051, "M", "а"), - (0x1E052, "M", "б"), - (0x1E053, "M", "в"), - (0x1E054, "M", "г"), - (0x1E055, "M", "д"), - (0x1E056, "M", "е"), - (0x1E057, "M", "ж"), - (0x1E058, "M", "з"), - (0x1E059, "M", "и"), - (0x1E05A, "M", "к"), - (0x1E05B, "M", "л"), - (0x1E05C, "M", "о"), - (0x1E05D, "M", "п"), - (0x1E05E, "M", "с"), - (0x1E05F, "M", "у"), - (0x1E060, "M", "ф"), - (0x1E061, "M", "х"), - (0x1E062, "M", "ц"), - (0x1E063, "M", "ч"), - (0x1E064, "M", "ш"), - (0x1E065, "M", "ъ"), - (0x1E066, "M", "ы"), - (0x1E067, "M", "ґ"), - (0x1E068, "M", "і"), - (0x1E069, "M", "ѕ"), - (0x1E06A, "M", "џ"), - (0x1E06B, "M", "ҫ"), - (0x1E06C, "M", "ꙑ"), - (0x1E06D, "M", "ұ"), - (0x1E06E, "X"), - (0x1E08F, "V"), - (0x1E090, "X"), - (0x1E100, "V"), - (0x1E12D, "X"), - (0x1E130, "V"), - (0x1E13E, "X"), - (0x1E140, "V"), - (0x1E14A, "X"), - (0x1E14E, "V"), - (0x1E150, "X"), - (0x1E290, "V"), - (0x1E2AF, "X"), - (0x1E2C0, "V"), - (0x1E2FA, "X"), - (0x1E2FF, "V"), - (0x1E300, "X"), - (0x1E4D0, "V"), - (0x1E4FA, "X"), - (0x1E7E0, "V"), - (0x1E7E7, "X"), - (0x1E7E8, "V"), - (0x1E7EC, "X"), - (0x1E7ED, "V"), - (0x1E7EF, "X"), - (0x1E7F0, "V"), - (0x1E7FF, "X"), - (0x1E800, "V"), - (0x1E8C5, "X"), - (0x1E8C7, "V"), - (0x1E8D7, "X"), - (0x1E900, "M", "𞤢"), - (0x1E901, "M", "𞤣"), - (0x1E902, "M", "𞤤"), - (0x1E903, "M", "𞤥"), - (0x1E904, "M", "𞤦"), - (0x1E905, "M", "𞤧"), - (0x1E906, "M", "𞤨"), - (0x1E907, "M", "𞤩"), - (0x1E908, "M", "𞤪"), - (0x1E909, "M", "𞤫"), - (0x1E90A, "M", "𞤬"), - (0x1E90B, "M", "𞤭"), - (0x1E90C, "M", "𞤮"), - (0x1E90D, "M", "𞤯"), - ] - - -def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E90E, "M", "𞤰"), - (0x1E90F, "M", "𞤱"), - (0x1E910, "M", "𞤲"), - (0x1E911, "M", "𞤳"), - (0x1E912, "M", "𞤴"), - (0x1E913, "M", "𞤵"), - (0x1E914, "M", "𞤶"), - (0x1E915, "M", "𞤷"), - (0x1E916, "M", "𞤸"), - (0x1E917, "M", "𞤹"), - (0x1E918, "M", "𞤺"), - (0x1E919, "M", "𞤻"), - (0x1E91A, "M", "𞤼"), - (0x1E91B, "M", "𞤽"), - (0x1E91C, "M", "𞤾"), - (0x1E91D, "M", "𞤿"), - (0x1E91E, "M", "𞥀"), - (0x1E91F, "M", "𞥁"), - (0x1E920, "M", "𞥂"), - (0x1E921, "M", "𞥃"), - (0x1E922, "V"), - (0x1E94C, "X"), - (0x1E950, "V"), - (0x1E95A, "X"), - (0x1E95E, "V"), - (0x1E960, "X"), - (0x1EC71, "V"), - (0x1ECB5, "X"), - (0x1ED01, "V"), - (0x1ED3E, "X"), - (0x1EE00, "M", "ا"), - (0x1EE01, "M", "ب"), - (0x1EE02, "M", "ج"), - (0x1EE03, "M", "د"), - (0x1EE04, "X"), - (0x1EE05, "M", "و"), - (0x1EE06, "M", "ز"), - (0x1EE07, "M", "ح"), - (0x1EE08, "M", "ط"), - (0x1EE09, "M", "ي"), - (0x1EE0A, "M", "ك"), - (0x1EE0B, "M", "ل"), - (0x1EE0C, "M", "م"), - (0x1EE0D, "M", "ن"), - (0x1EE0E, "M", "س"), - (0x1EE0F, "M", "ع"), - (0x1EE10, "M", "ف"), - (0x1EE11, "M", "ص"), - (0x1EE12, "M", "ق"), - (0x1EE13, "M", "ر"), - (0x1EE14, "M", "ش"), - (0x1EE15, "M", "ت"), - (0x1EE16, "M", "ث"), - (0x1EE17, "M", "خ"), - (0x1EE18, "M", "ذ"), - (0x1EE19, "M", "ض"), - (0x1EE1A, "M", "ظ"), - (0x1EE1B, "M", "غ"), - (0x1EE1C, "M", "ٮ"), - (0x1EE1D, "M", "ں"), - (0x1EE1E, "M", "ڡ"), - (0x1EE1F, "M", "ٯ"), - (0x1EE20, "X"), - (0x1EE21, "M", "ب"), - (0x1EE22, "M", "ج"), - (0x1EE23, "X"), - (0x1EE24, "M", "ه"), - (0x1EE25, "X"), - (0x1EE27, "M", "ح"), - (0x1EE28, "X"), - (0x1EE29, "M", "ي"), - (0x1EE2A, "M", "ك"), - (0x1EE2B, "M", "ل"), - (0x1EE2C, "M", "م"), - (0x1EE2D, "M", "ن"), - (0x1EE2E, "M", "س"), - (0x1EE2F, "M", "ع"), - (0x1EE30, "M", "ف"), - (0x1EE31, "M", "ص"), - (0x1EE32, "M", "ق"), - (0x1EE33, "X"), - (0x1EE34, "M", "ش"), - (0x1EE35, "M", "ت"), - (0x1EE36, "M", "ث"), - (0x1EE37, "M", "خ"), - (0x1EE38, "X"), - (0x1EE39, "M", "ض"), - (0x1EE3A, "X"), - (0x1EE3B, "M", "غ"), - (0x1EE3C, "X"), - (0x1EE42, "M", "ج"), - (0x1EE43, "X"), - (0x1EE47, "M", "ح"), - (0x1EE48, "X"), - (0x1EE49, "M", "ي"), - (0x1EE4A, "X"), - (0x1EE4B, "M", "ل"), - (0x1EE4C, "X"), - (0x1EE4D, "M", "ن"), - (0x1EE4E, "M", "س"), - ] - - -def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EE4F, "M", "ع"), - (0x1EE50, "X"), - (0x1EE51, "M", "ص"), - (0x1EE52, "M", "ق"), - (0x1EE53, "X"), - (0x1EE54, "M", "ش"), - (0x1EE55, "X"), - (0x1EE57, "M", "خ"), - (0x1EE58, "X"), - (0x1EE59, "M", "ض"), - (0x1EE5A, "X"), - (0x1EE5B, "M", "غ"), - (0x1EE5C, "X"), - (0x1EE5D, "M", "ں"), - (0x1EE5E, "X"), - (0x1EE5F, "M", "ٯ"), - (0x1EE60, "X"), - (0x1EE61, "M", "ب"), - (0x1EE62, "M", "ج"), - (0x1EE63, "X"), - (0x1EE64, "M", "ه"), - (0x1EE65, "X"), - (0x1EE67, "M", "ح"), - (0x1EE68, "M", "ط"), - (0x1EE69, "M", "ي"), - (0x1EE6A, "M", "ك"), - (0x1EE6B, "X"), - (0x1EE6C, "M", "م"), - (0x1EE6D, "M", "ن"), - (0x1EE6E, "M", "س"), - (0x1EE6F, "M", "ع"), - (0x1EE70, "M", "ف"), - (0x1EE71, "M", "ص"), - (0x1EE72, "M", "ق"), - (0x1EE73, "X"), - (0x1EE74, "M", "ش"), - (0x1EE75, "M", "ت"), - (0x1EE76, "M", "ث"), - (0x1EE77, "M", "خ"), - (0x1EE78, "X"), - (0x1EE79, "M", "ض"), - (0x1EE7A, "M", "ظ"), - (0x1EE7B, "M", "غ"), - (0x1EE7C, "M", "ٮ"), - (0x1EE7D, "X"), - (0x1EE7E, "M", "ڡ"), - (0x1EE7F, "X"), - (0x1EE80, "M", "ا"), - (0x1EE81, "M", "ب"), - (0x1EE82, "M", "ج"), - (0x1EE83, "M", "د"), - (0x1EE84, "M", "ه"), - (0x1EE85, "M", "و"), - (0x1EE86, "M", "ز"), - (0x1EE87, "M", "ح"), - (0x1EE88, "M", "ط"), - (0x1EE89, "M", "ي"), - (0x1EE8A, "X"), - (0x1EE8B, "M", "ل"), - (0x1EE8C, "M", "م"), - (0x1EE8D, "M", "ن"), - (0x1EE8E, "M", "س"), - (0x1EE8F, "M", "ع"), - (0x1EE90, "M", "ف"), - (0x1EE91, "M", "ص"), - (0x1EE92, "M", "ق"), - (0x1EE93, "M", "ر"), - (0x1EE94, "M", "ش"), - (0x1EE95, "M", "ت"), - (0x1EE96, "M", "ث"), - (0x1EE97, "M", "خ"), - (0x1EE98, "M", "ذ"), - (0x1EE99, "M", "ض"), - (0x1EE9A, "M", "ظ"), - (0x1EE9B, "M", "غ"), - (0x1EE9C, "X"), - (0x1EEA1, "M", "ب"), - (0x1EEA2, "M", "ج"), - (0x1EEA3, "M", "د"), - (0x1EEA4, "X"), - (0x1EEA5, "M", "و"), - (0x1EEA6, "M", "ز"), - (0x1EEA7, "M", "ح"), - (0x1EEA8, "M", "ط"), - (0x1EEA9, "M", "ي"), - (0x1EEAA, "X"), - (0x1EEAB, "M", "ل"), - (0x1EEAC, "M", "م"), - (0x1EEAD, "M", "ن"), - (0x1EEAE, "M", "س"), - (0x1EEAF, "M", "ع"), - (0x1EEB0, "M", "ف"), - (0x1EEB1, "M", "ص"), - (0x1EEB2, "M", "ق"), - (0x1EEB3, "M", "ر"), - (0x1EEB4, "M", "ش"), - (0x1EEB5, "M", "ت"), - (0x1EEB6, "M", "ث"), - (0x1EEB7, "M", "خ"), - (0x1EEB8, "M", "ذ"), - ] - - -def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EEB9, "M", "ض"), - (0x1EEBA, "M", "ظ"), - (0x1EEBB, "M", "غ"), - (0x1EEBC, "X"), - (0x1EEF0, "V"), - (0x1EEF2, "X"), - (0x1F000, "V"), - (0x1F02C, "X"), - (0x1F030, "V"), - (0x1F094, "X"), - (0x1F0A0, "V"), - (0x1F0AF, "X"), - (0x1F0B1, "V"), - (0x1F0C0, "X"), - (0x1F0C1, "V"), - (0x1F0D0, "X"), - (0x1F0D1, "V"), - (0x1F0F6, "X"), - (0x1F101, "3", "0,"), - (0x1F102, "3", "1,"), - (0x1F103, "3", "2,"), - (0x1F104, "3", "3,"), - (0x1F105, "3", "4,"), - (0x1F106, "3", "5,"), - (0x1F107, "3", "6,"), - (0x1F108, "3", "7,"), - (0x1F109, "3", "8,"), - (0x1F10A, "3", "9,"), - (0x1F10B, "V"), - (0x1F110, "3", "(a)"), - (0x1F111, "3", "(b)"), - (0x1F112, "3", "(c)"), - (0x1F113, "3", "(d)"), - (0x1F114, "3", "(e)"), - (0x1F115, "3", "(f)"), - (0x1F116, "3", "(g)"), - (0x1F117, "3", "(h)"), - (0x1F118, "3", "(i)"), - (0x1F119, "3", "(j)"), - (0x1F11A, "3", "(k)"), - (0x1F11B, "3", "(l)"), - (0x1F11C, "3", "(m)"), - (0x1F11D, "3", "(n)"), - (0x1F11E, "3", "(o)"), - (0x1F11F, "3", "(p)"), - (0x1F120, "3", "(q)"), - (0x1F121, "3", "(r)"), - (0x1F122, "3", "(s)"), - (0x1F123, "3", "(t)"), - (0x1F124, "3", "(u)"), - (0x1F125, "3", "(v)"), - (0x1F126, "3", "(w)"), - (0x1F127, "3", "(x)"), - (0x1F128, "3", "(y)"), - (0x1F129, "3", "(z)"), - (0x1F12A, "M", "〔s〕"), - (0x1F12B, "M", "c"), - (0x1F12C, "M", "r"), - (0x1F12D, "M", "cd"), - (0x1F12E, "M", "wz"), - (0x1F12F, "V"), - (0x1F130, "M", "a"), - (0x1F131, "M", "b"), - (0x1F132, "M", "c"), - (0x1F133, "M", "d"), - (0x1F134, "M", "e"), - (0x1F135, "M", "f"), - (0x1F136, "M", "g"), - (0x1F137, "M", "h"), - (0x1F138, "M", "i"), - (0x1F139, "M", "j"), - (0x1F13A, "M", "k"), - (0x1F13B, "M", "l"), - (0x1F13C, "M", "m"), - (0x1F13D, "M", "n"), - (0x1F13E, "M", "o"), - (0x1F13F, "M", "p"), - (0x1F140, "M", "q"), - (0x1F141, "M", "r"), - (0x1F142, "M", "s"), - (0x1F143, "M", "t"), - (0x1F144, "M", "u"), - (0x1F145, "M", "v"), - (0x1F146, "M", "w"), - (0x1F147, "M", "x"), - (0x1F148, "M", "y"), - (0x1F149, "M", "z"), - (0x1F14A, "M", "hv"), - (0x1F14B, "M", "mv"), - (0x1F14C, "M", "sd"), - (0x1F14D, "M", "ss"), - (0x1F14E, "M", "ppv"), - (0x1F14F, "M", "wc"), - (0x1F150, "V"), - (0x1F16A, "M", "mc"), - (0x1F16B, "M", "md"), - (0x1F16C, "M", "mr"), - (0x1F16D, "V"), - (0x1F190, "M", "dj"), - (0x1F191, "V"), - ] - - -def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F1AE, "X"), - (0x1F1E6, "V"), - (0x1F200, "M", "ほか"), - (0x1F201, "M", "ココ"), - (0x1F202, "M", "サ"), - (0x1F203, "X"), - (0x1F210, "M", "手"), - (0x1F211, "M", "字"), - (0x1F212, "M", "双"), - (0x1F213, "M", "デ"), - (0x1F214, "M", "二"), - (0x1F215, "M", "多"), - (0x1F216, "M", "解"), - (0x1F217, "M", "天"), - (0x1F218, "M", "交"), - (0x1F219, "M", "映"), - (0x1F21A, "M", "無"), - (0x1F21B, "M", "料"), - (0x1F21C, "M", "前"), - (0x1F21D, "M", "後"), - (0x1F21E, "M", "再"), - (0x1F21F, "M", "新"), - (0x1F220, "M", "初"), - (0x1F221, "M", "終"), - (0x1F222, "M", "生"), - (0x1F223, "M", "販"), - (0x1F224, "M", "声"), - (0x1F225, "M", "吹"), - (0x1F226, "M", "演"), - (0x1F227, "M", "投"), - (0x1F228, "M", "捕"), - (0x1F229, "M", "一"), - (0x1F22A, "M", "三"), - (0x1F22B, "M", "遊"), - (0x1F22C, "M", "左"), - (0x1F22D, "M", "中"), - (0x1F22E, "M", "右"), - (0x1F22F, "M", "指"), - (0x1F230, "M", "走"), - (0x1F231, "M", "打"), - (0x1F232, "M", "禁"), - (0x1F233, "M", "空"), - (0x1F234, "M", "合"), - (0x1F235, "M", "満"), - (0x1F236, "M", "有"), - (0x1F237, "M", "月"), - (0x1F238, "M", "申"), - (0x1F239, "M", "割"), - (0x1F23A, "M", "営"), - (0x1F23B, "M", "配"), - (0x1F23C, "X"), - (0x1F240, "M", "〔本〕"), - (0x1F241, "M", "〔三〕"), - (0x1F242, "M", "〔二〕"), - (0x1F243, "M", "〔安〕"), - (0x1F244, "M", "〔点〕"), - (0x1F245, "M", "〔打〕"), - (0x1F246, "M", "〔盗〕"), - (0x1F247, "M", "〔勝〕"), - (0x1F248, "M", "〔敗〕"), - (0x1F249, "X"), - (0x1F250, "M", "得"), - (0x1F251, "M", "可"), - (0x1F252, "X"), - (0x1F260, "V"), - (0x1F266, "X"), - (0x1F300, "V"), - (0x1F6D8, "X"), - (0x1F6DC, "V"), - (0x1F6ED, "X"), - (0x1F6F0, "V"), - (0x1F6FD, "X"), - (0x1F700, "V"), - (0x1F777, "X"), - (0x1F77B, "V"), - (0x1F7DA, "X"), - (0x1F7E0, "V"), - (0x1F7EC, "X"), - (0x1F7F0, "V"), - (0x1F7F1, "X"), - (0x1F800, "V"), - (0x1F80C, "X"), - (0x1F810, "V"), - (0x1F848, "X"), - (0x1F850, "V"), - (0x1F85A, "X"), - (0x1F860, "V"), - (0x1F888, "X"), - (0x1F890, "V"), - (0x1F8AE, "X"), - (0x1F8B0, "V"), - (0x1F8B2, "X"), - (0x1F900, "V"), - (0x1FA54, "X"), - (0x1FA60, "V"), - (0x1FA6E, "X"), - (0x1FA70, "V"), - (0x1FA7D, "X"), - (0x1FA80, "V"), - (0x1FA89, "X"), - ] - - -def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FA90, "V"), - (0x1FABE, "X"), - (0x1FABF, "V"), - (0x1FAC6, "X"), - (0x1FACE, "V"), - (0x1FADC, "X"), - (0x1FAE0, "V"), - (0x1FAE9, "X"), - (0x1FAF0, "V"), - (0x1FAF9, "X"), - (0x1FB00, "V"), - (0x1FB93, "X"), - (0x1FB94, "V"), - (0x1FBCB, "X"), - (0x1FBF0, "M", "0"), - (0x1FBF1, "M", "1"), - (0x1FBF2, "M", "2"), - (0x1FBF3, "M", "3"), - (0x1FBF4, "M", "4"), - (0x1FBF5, "M", "5"), - (0x1FBF6, "M", "6"), - (0x1FBF7, "M", "7"), - (0x1FBF8, "M", "8"), - (0x1FBF9, "M", "9"), - (0x1FBFA, "X"), - (0x20000, "V"), - (0x2A6E0, "X"), - (0x2A700, "V"), - (0x2B73A, "X"), - (0x2B740, "V"), - (0x2B81E, "X"), - (0x2B820, "V"), - (0x2CEA2, "X"), - (0x2CEB0, "V"), - (0x2EBE1, "X"), - (0x2EBF0, "V"), - (0x2EE5E, "X"), - (0x2F800, "M", "丽"), - (0x2F801, "M", "丸"), - (0x2F802, "M", "乁"), - (0x2F803, "M", "𠄢"), - (0x2F804, "M", "你"), - (0x2F805, "M", "侮"), - (0x2F806, "M", "侻"), - (0x2F807, "M", "倂"), - (0x2F808, "M", "偺"), - (0x2F809, "M", "備"), - (0x2F80A, "M", "僧"), - (0x2F80B, "M", "像"), - (0x2F80C, "M", "㒞"), - (0x2F80D, "M", "𠘺"), - (0x2F80E, "M", "免"), - (0x2F80F, "M", "兔"), - (0x2F810, "M", "兤"), - (0x2F811, "M", "具"), - (0x2F812, "M", "𠔜"), - (0x2F813, "M", "㒹"), - (0x2F814, "M", "內"), - (0x2F815, "M", "再"), - (0x2F816, "M", "𠕋"), - (0x2F817, "M", "冗"), - (0x2F818, "M", "冤"), - (0x2F819, "M", "仌"), - (0x2F81A, "M", "冬"), - (0x2F81B, "M", "况"), - (0x2F81C, "M", "𩇟"), - (0x2F81D, "M", "凵"), - (0x2F81E, "M", "刃"), - (0x2F81F, "M", "㓟"), - (0x2F820, "M", "刻"), - (0x2F821, "M", "剆"), - (0x2F822, "M", "割"), - (0x2F823, "M", "剷"), - (0x2F824, "M", "㔕"), - (0x2F825, "M", "勇"), - (0x2F826, "M", "勉"), - (0x2F827, "M", "勤"), - (0x2F828, "M", "勺"), - (0x2F829, "M", "包"), - (0x2F82A, "M", "匆"), - (0x2F82B, "M", "北"), - (0x2F82C, "M", "卉"), - (0x2F82D, "M", "卑"), - (0x2F82E, "M", "博"), - (0x2F82F, "M", "即"), - (0x2F830, "M", "卽"), - (0x2F831, "M", "卿"), - (0x2F834, "M", "𠨬"), - (0x2F835, "M", "灰"), - (0x2F836, "M", "及"), - (0x2F837, "M", "叟"), - (0x2F838, "M", "𠭣"), - (0x2F839, "M", "叫"), - (0x2F83A, "M", "叱"), - (0x2F83B, "M", "吆"), - (0x2F83C, "M", "咞"), - (0x2F83D, "M", "吸"), - (0x2F83E, "M", "呈"), - (0x2F83F, "M", "周"), - (0x2F840, "M", "咢"), - ] - - -def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F841, "M", "哶"), - (0x2F842, "M", "唐"), - (0x2F843, "M", "啓"), - (0x2F844, "M", "啣"), - (0x2F845, "M", "善"), - (0x2F847, "M", "喙"), - (0x2F848, "M", "喫"), - (0x2F849, "M", "喳"), - (0x2F84A, "M", "嗂"), - (0x2F84B, "M", "圖"), - (0x2F84C, "M", "嘆"), - (0x2F84D, "M", "圗"), - (0x2F84E, "M", "噑"), - (0x2F84F, "M", "噴"), - (0x2F850, "M", "切"), - (0x2F851, "M", "壮"), - (0x2F852, "M", "城"), - (0x2F853, "M", "埴"), - (0x2F854, "M", "堍"), - (0x2F855, "M", "型"), - (0x2F856, "M", "堲"), - (0x2F857, "M", "報"), - (0x2F858, "M", "墬"), - (0x2F859, "M", "𡓤"), - (0x2F85A, "M", "売"), - (0x2F85B, "M", "壷"), - (0x2F85C, "M", "夆"), - (0x2F85D, "M", "多"), - (0x2F85E, "M", "夢"), - (0x2F85F, "M", "奢"), - (0x2F860, "M", "𡚨"), - (0x2F861, "M", "𡛪"), - (0x2F862, "M", "姬"), - (0x2F863, "M", "娛"), - (0x2F864, "M", "娧"), - (0x2F865, "M", "姘"), - (0x2F866, "M", "婦"), - (0x2F867, "M", "㛮"), - (0x2F868, "X"), - (0x2F869, "M", "嬈"), - (0x2F86A, "M", "嬾"), - (0x2F86C, "M", "𡧈"), - (0x2F86D, "M", "寃"), - (0x2F86E, "M", "寘"), - (0x2F86F, "M", "寧"), - (0x2F870, "M", "寳"), - (0x2F871, "M", "𡬘"), - (0x2F872, "M", "寿"), - (0x2F873, "M", "将"), - (0x2F874, "X"), - (0x2F875, "M", "尢"), - (0x2F876, "M", "㞁"), - (0x2F877, "M", "屠"), - (0x2F878, "M", "屮"), - (0x2F879, "M", "峀"), - (0x2F87A, "M", "岍"), - (0x2F87B, "M", "𡷤"), - (0x2F87C, "M", "嵃"), - (0x2F87D, "M", "𡷦"), - (0x2F87E, "M", "嵮"), - (0x2F87F, "M", "嵫"), - (0x2F880, "M", "嵼"), - (0x2F881, "M", "巡"), - (0x2F882, "M", "巢"), - (0x2F883, "M", "㠯"), - (0x2F884, "M", "巽"), - (0x2F885, "M", "帨"), - (0x2F886, "M", "帽"), - (0x2F887, "M", "幩"), - (0x2F888, "M", "㡢"), - (0x2F889, "M", "𢆃"), - (0x2F88A, "M", "㡼"), - (0x2F88B, "M", "庰"), - (0x2F88C, "M", "庳"), - (0x2F88D, "M", "庶"), - (0x2F88E, "M", "廊"), - (0x2F88F, "M", "𪎒"), - (0x2F890, "M", "廾"), - (0x2F891, "M", "𢌱"), - (0x2F893, "M", "舁"), - (0x2F894, "M", "弢"), - (0x2F896, "M", "㣇"), - (0x2F897, "M", "𣊸"), - (0x2F898, "M", "𦇚"), - (0x2F899, "M", "形"), - (0x2F89A, "M", "彫"), - (0x2F89B, "M", "㣣"), - (0x2F89C, "M", "徚"), - (0x2F89D, "M", "忍"), - (0x2F89E, "M", "志"), - (0x2F89F, "M", "忹"), - (0x2F8A0, "M", "悁"), - (0x2F8A1, "M", "㤺"), - (0x2F8A2, "M", "㤜"), - (0x2F8A3, "M", "悔"), - (0x2F8A4, "M", "𢛔"), - (0x2F8A5, "M", "惇"), - (0x2F8A6, "M", "慈"), - (0x2F8A7, "M", "慌"), - (0x2F8A8, "M", "慎"), - ] - - -def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F8A9, "M", "慌"), - (0x2F8AA, "M", "慺"), - (0x2F8AB, "M", "憎"), - (0x2F8AC, "M", "憲"), - (0x2F8AD, "M", "憤"), - (0x2F8AE, "M", "憯"), - (0x2F8AF, "M", "懞"), - (0x2F8B0, "M", "懲"), - (0x2F8B1, "M", "懶"), - (0x2F8B2, "M", "成"), - (0x2F8B3, "M", "戛"), - (0x2F8B4, "M", "扝"), - (0x2F8B5, "M", "抱"), - (0x2F8B6, "M", "拔"), - (0x2F8B7, "M", "捐"), - (0x2F8B8, "M", "𢬌"), - (0x2F8B9, "M", "挽"), - (0x2F8BA, "M", "拼"), - (0x2F8BB, "M", "捨"), - (0x2F8BC, "M", "掃"), - (0x2F8BD, "M", "揤"), - (0x2F8BE, "M", "𢯱"), - (0x2F8BF, "M", "搢"), - (0x2F8C0, "M", "揅"), - (0x2F8C1, "M", "掩"), - (0x2F8C2, "M", "㨮"), - (0x2F8C3, "M", "摩"), - (0x2F8C4, "M", "摾"), - (0x2F8C5, "M", "撝"), - (0x2F8C6, "M", "摷"), - (0x2F8C7, "M", "㩬"), - (0x2F8C8, "M", "敏"), - (0x2F8C9, "M", "敬"), - (0x2F8CA, "M", "𣀊"), - (0x2F8CB, "M", "旣"), - (0x2F8CC, "M", "書"), - (0x2F8CD, "M", "晉"), - (0x2F8CE, "M", "㬙"), - (0x2F8CF, "M", "暑"), - (0x2F8D0, "M", "㬈"), - (0x2F8D1, "M", "㫤"), - (0x2F8D2, "M", "冒"), - (0x2F8D3, "M", "冕"), - (0x2F8D4, "M", "最"), - (0x2F8D5, "M", "暜"), - (0x2F8D6, "M", "肭"), - (0x2F8D7, "M", "䏙"), - (0x2F8D8, "M", "朗"), - (0x2F8D9, "M", "望"), - (0x2F8DA, "M", "朡"), - (0x2F8DB, "M", "杞"), - (0x2F8DC, "M", "杓"), - (0x2F8DD, "M", "𣏃"), - (0x2F8DE, "M", "㭉"), - (0x2F8DF, "M", "柺"), - (0x2F8E0, "M", "枅"), - (0x2F8E1, "M", "桒"), - (0x2F8E2, "M", "梅"), - (0x2F8E3, "M", "𣑭"), - (0x2F8E4, "M", "梎"), - (0x2F8E5, "M", "栟"), - (0x2F8E6, "M", "椔"), - (0x2F8E7, "M", "㮝"), - (0x2F8E8, "M", "楂"), - (0x2F8E9, "M", "榣"), - (0x2F8EA, "M", "槪"), - (0x2F8EB, "M", "檨"), - (0x2F8EC, "M", "𣚣"), - (0x2F8ED, "M", "櫛"), - (0x2F8EE, "M", "㰘"), - (0x2F8EF, "M", "次"), - (0x2F8F0, "M", "𣢧"), - (0x2F8F1, "M", "歔"), - (0x2F8F2, "M", "㱎"), - (0x2F8F3, "M", "歲"), - (0x2F8F4, "M", "殟"), - (0x2F8F5, "M", "殺"), - (0x2F8F6, "M", "殻"), - (0x2F8F7, "M", "𣪍"), - (0x2F8F8, "M", "𡴋"), - (0x2F8F9, "M", "𣫺"), - (0x2F8FA, "M", "汎"), - (0x2F8FB, "M", "𣲼"), - (0x2F8FC, "M", "沿"), - (0x2F8FD, "M", "泍"), - (0x2F8FE, "M", "汧"), - (0x2F8FF, "M", "洖"), - (0x2F900, "M", "派"), - (0x2F901, "M", "海"), - (0x2F902, "M", "流"), - (0x2F903, "M", "浩"), - (0x2F904, "M", "浸"), - (0x2F905, "M", "涅"), - (0x2F906, "M", "𣴞"), - (0x2F907, "M", "洴"), - (0x2F908, "M", "港"), - (0x2F909, "M", "湮"), - (0x2F90A, "M", "㴳"), - (0x2F90B, "M", "滋"), - (0x2F90C, "M", "滇"), - ] - - -def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F90D, "M", "𣻑"), - (0x2F90E, "M", "淹"), - (0x2F90F, "M", "潮"), - (0x2F910, "M", "𣽞"), - (0x2F911, "M", "𣾎"), - (0x2F912, "M", "濆"), - (0x2F913, "M", "瀹"), - (0x2F914, "M", "瀞"), - (0x2F915, "M", "瀛"), - (0x2F916, "M", "㶖"), - (0x2F917, "M", "灊"), - (0x2F918, "M", "災"), - (0x2F919, "M", "灷"), - (0x2F91A, "M", "炭"), - (0x2F91B, "M", "𠔥"), - (0x2F91C, "M", "煅"), - (0x2F91D, "M", "𤉣"), - (0x2F91E, "M", "熜"), - (0x2F91F, "X"), - (0x2F920, "M", "爨"), - (0x2F921, "M", "爵"), - (0x2F922, "M", "牐"), - (0x2F923, "M", "𤘈"), - (0x2F924, "M", "犀"), - (0x2F925, "M", "犕"), - (0x2F926, "M", "𤜵"), - (0x2F927, "M", "𤠔"), - (0x2F928, "M", "獺"), - (0x2F929, "M", "王"), - (0x2F92A, "M", "㺬"), - (0x2F92B, "M", "玥"), - (0x2F92C, "M", "㺸"), - (0x2F92E, "M", "瑇"), - (0x2F92F, "M", "瑜"), - (0x2F930, "M", "瑱"), - (0x2F931, "M", "璅"), - (0x2F932, "M", "瓊"), - (0x2F933, "M", "㼛"), - (0x2F934, "M", "甤"), - (0x2F935, "M", "𤰶"), - (0x2F936, "M", "甾"), - (0x2F937, "M", "𤲒"), - (0x2F938, "M", "異"), - (0x2F939, "M", "𢆟"), - (0x2F93A, "M", "瘐"), - (0x2F93B, "M", "𤾡"), - (0x2F93C, "M", "𤾸"), - (0x2F93D, "M", "𥁄"), - (0x2F93E, "M", "㿼"), - (0x2F93F, "M", "䀈"), - (0x2F940, "M", "直"), - (0x2F941, "M", "𥃳"), - (0x2F942, "M", "𥃲"), - (0x2F943, "M", "𥄙"), - (0x2F944, "M", "𥄳"), - (0x2F945, "M", "眞"), - (0x2F946, "M", "真"), - (0x2F948, "M", "睊"), - (0x2F949, "M", "䀹"), - (0x2F94A, "M", "瞋"), - (0x2F94B, "M", "䁆"), - (0x2F94C, "M", "䂖"), - (0x2F94D, "M", "𥐝"), - (0x2F94E, "M", "硎"), - (0x2F94F, "M", "碌"), - (0x2F950, "M", "磌"), - (0x2F951, "M", "䃣"), - (0x2F952, "M", "𥘦"), - (0x2F953, "M", "祖"), - (0x2F954, "M", "𥚚"), - (0x2F955, "M", "𥛅"), - (0x2F956, "M", "福"), - (0x2F957, "M", "秫"), - (0x2F958, "M", "䄯"), - (0x2F959, "M", "穀"), - (0x2F95A, "M", "穊"), - (0x2F95B, "M", "穏"), - (0x2F95C, "M", "𥥼"), - (0x2F95D, "M", "𥪧"), - (0x2F95F, "X"), - (0x2F960, "M", "䈂"), - (0x2F961, "M", "𥮫"), - (0x2F962, "M", "篆"), - (0x2F963, "M", "築"), - (0x2F964, "M", "䈧"), - (0x2F965, "M", "𥲀"), - (0x2F966, "M", "糒"), - (0x2F967, "M", "䊠"), - (0x2F968, "M", "糨"), - (0x2F969, "M", "糣"), - (0x2F96A, "M", "紀"), - (0x2F96B, "M", "𥾆"), - (0x2F96C, "M", "絣"), - (0x2F96D, "M", "䌁"), - (0x2F96E, "M", "緇"), - (0x2F96F, "M", "縂"), - (0x2F970, "M", "繅"), - (0x2F971, "M", "䌴"), - (0x2F972, "M", "𦈨"), - (0x2F973, "M", "𦉇"), - ] - - -def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F974, "M", "䍙"), - (0x2F975, "M", "𦋙"), - (0x2F976, "M", "罺"), - (0x2F977, "M", "𦌾"), - (0x2F978, "M", "羕"), - (0x2F979, "M", "翺"), - (0x2F97A, "M", "者"), - (0x2F97B, "M", "𦓚"), - (0x2F97C, "M", "𦔣"), - (0x2F97D, "M", "聠"), - (0x2F97E, "M", "𦖨"), - (0x2F97F, "M", "聰"), - (0x2F980, "M", "𣍟"), - (0x2F981, "M", "䏕"), - (0x2F982, "M", "育"), - (0x2F983, "M", "脃"), - (0x2F984, "M", "䐋"), - (0x2F985, "M", "脾"), - (0x2F986, "M", "媵"), - (0x2F987, "M", "𦞧"), - (0x2F988, "M", "𦞵"), - (0x2F989, "M", "𣎓"), - (0x2F98A, "M", "𣎜"), - (0x2F98B, "M", "舁"), - (0x2F98C, "M", "舄"), - (0x2F98D, "M", "辞"), - (0x2F98E, "M", "䑫"), - (0x2F98F, "M", "芑"), - (0x2F990, "M", "芋"), - (0x2F991, "M", "芝"), - (0x2F992, "M", "劳"), - (0x2F993, "M", "花"), - (0x2F994, "M", "芳"), - (0x2F995, "M", "芽"), - (0x2F996, "M", "苦"), - (0x2F997, "M", "𦬼"), - (0x2F998, "M", "若"), - (0x2F999, "M", "茝"), - (0x2F99A, "M", "荣"), - (0x2F99B, "M", "莭"), - (0x2F99C, "M", "茣"), - (0x2F99D, "M", "莽"), - (0x2F99E, "M", "菧"), - (0x2F99F, "M", "著"), - (0x2F9A0, "M", "荓"), - (0x2F9A1, "M", "菊"), - (0x2F9A2, "M", "菌"), - (0x2F9A3, "M", "菜"), - (0x2F9A4, "M", "𦰶"), - (0x2F9A5, "M", "𦵫"), - (0x2F9A6, "M", "𦳕"), - (0x2F9A7, "M", "䔫"), - (0x2F9A8, "M", "蓱"), - (0x2F9A9, "M", "蓳"), - (0x2F9AA, "M", "蔖"), - (0x2F9AB, "M", "𧏊"), - (0x2F9AC, "M", "蕤"), - (0x2F9AD, "M", "𦼬"), - (0x2F9AE, "M", "䕝"), - (0x2F9AF, "M", "䕡"), - (0x2F9B0, "M", "𦾱"), - (0x2F9B1, "M", "𧃒"), - (0x2F9B2, "M", "䕫"), - (0x2F9B3, "M", "虐"), - (0x2F9B4, "M", "虜"), - (0x2F9B5, "M", "虧"), - (0x2F9B6, "M", "虩"), - (0x2F9B7, "M", "蚩"), - (0x2F9B8, "M", "蚈"), - (0x2F9B9, "M", "蜎"), - (0x2F9BA, "M", "蛢"), - (0x2F9BB, "M", "蝹"), - (0x2F9BC, "M", "蜨"), - (0x2F9BD, "M", "蝫"), - (0x2F9BE, "M", "螆"), - (0x2F9BF, "X"), - (0x2F9C0, "M", "蟡"), - (0x2F9C1, "M", "蠁"), - (0x2F9C2, "M", "䗹"), - (0x2F9C3, "M", "衠"), - (0x2F9C4, "M", "衣"), - (0x2F9C5, "M", "𧙧"), - (0x2F9C6, "M", "裗"), - (0x2F9C7, "M", "裞"), - (0x2F9C8, "M", "䘵"), - (0x2F9C9, "M", "裺"), - (0x2F9CA, "M", "㒻"), - (0x2F9CB, "M", "𧢮"), - (0x2F9CC, "M", "𧥦"), - (0x2F9CD, "M", "䚾"), - (0x2F9CE, "M", "䛇"), - (0x2F9CF, "M", "誠"), - (0x2F9D0, "M", "諭"), - (0x2F9D1, "M", "變"), - (0x2F9D2, "M", "豕"), - (0x2F9D3, "M", "𧲨"), - (0x2F9D4, "M", "貫"), - (0x2F9D5, "M", "賁"), - (0x2F9D6, "M", "贛"), - (0x2F9D7, "M", "起"), - ] - - -def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F9D8, "M", "𧼯"), - (0x2F9D9, "M", "𠠄"), - (0x2F9DA, "M", "跋"), - (0x2F9DB, "M", "趼"), - (0x2F9DC, "M", "跰"), - (0x2F9DD, "M", "𠣞"), - (0x2F9DE, "M", "軔"), - (0x2F9DF, "M", "輸"), - (0x2F9E0, "M", "𨗒"), - (0x2F9E1, "M", "𨗭"), - (0x2F9E2, "M", "邔"), - (0x2F9E3, "M", "郱"), - (0x2F9E4, "M", "鄑"), - (0x2F9E5, "M", "𨜮"), - (0x2F9E6, "M", "鄛"), - (0x2F9E7, "M", "鈸"), - (0x2F9E8, "M", "鋗"), - (0x2F9E9, "M", "鋘"), - (0x2F9EA, "M", "鉼"), - (0x2F9EB, "M", "鏹"), - (0x2F9EC, "M", "鐕"), - (0x2F9ED, "M", "𨯺"), - (0x2F9EE, "M", "開"), - (0x2F9EF, "M", "䦕"), - (0x2F9F0, "M", "閷"), - (0x2F9F1, "M", "𨵷"), - (0x2F9F2, "M", "䧦"), - (0x2F9F3, "M", "雃"), - (0x2F9F4, "M", "嶲"), - (0x2F9F5, "M", "霣"), - (0x2F9F6, "M", "𩅅"), - (0x2F9F7, "M", "𩈚"), - (0x2F9F8, "M", "䩮"), - (0x2F9F9, "M", "䩶"), - (0x2F9FA, "M", "韠"), - (0x2F9FB, "M", "𩐊"), - (0x2F9FC, "M", "䪲"), - (0x2F9FD, "M", "𩒖"), - (0x2F9FE, "M", "頋"), - (0x2FA00, "M", "頩"), - (0x2FA01, "M", "𩖶"), - (0x2FA02, "M", "飢"), - (0x2FA03, "M", "䬳"), - (0x2FA04, "M", "餩"), - (0x2FA05, "M", "馧"), - (0x2FA06, "M", "駂"), - (0x2FA07, "M", "駾"), - (0x2FA08, "M", "䯎"), - (0x2FA09, "M", "𩬰"), - (0x2FA0A, "M", "鬒"), - (0x2FA0B, "M", "鱀"), - (0x2FA0C, "M", "鳽"), - (0x2FA0D, "M", "䳎"), - (0x2FA0E, "M", "䳭"), - (0x2FA0F, "M", "鵧"), - (0x2FA10, "M", "𪃎"), - (0x2FA11, "M", "䳸"), - (0x2FA12, "M", "𪄅"), - (0x2FA13, "M", "𪈎"), - (0x2FA14, "M", "𪊑"), - (0x2FA15, "M", "麻"), - (0x2FA16, "M", "䵖"), - (0x2FA17, "M", "黹"), - (0x2FA18, "M", "黾"), - (0x2FA19, "M", "鼅"), - (0x2FA1A, "M", "鼏"), - (0x2FA1B, "M", "鼖"), - (0x2FA1C, "M", "鼻"), - (0x2FA1D, "M", "𪘀"), - (0x2FA1E, "X"), - (0x30000, "V"), - (0x3134B, "X"), - (0x31350, "V"), - (0x323B0, "X"), - (0xE0100, "I"), - (0xE01F0, "X"), - ] - - -uts46data = tuple( - _seg_0() - + _seg_1() - + _seg_2() - + _seg_3() - + _seg_4() - + _seg_5() - + _seg_6() - + _seg_7() - + _seg_8() - + _seg_9() - + _seg_10() - + _seg_11() - + _seg_12() - + _seg_13() - + _seg_14() - + _seg_15() - + _seg_16() - + _seg_17() - + _seg_18() - + _seg_19() - + _seg_20() - + _seg_21() - + _seg_22() - + _seg_23() - + _seg_24() - + _seg_25() - + _seg_26() - + _seg_27() - + _seg_28() - + _seg_29() - + _seg_30() - + _seg_31() - + _seg_32() - + _seg_33() - + _seg_34() - + _seg_35() - + _seg_36() - + _seg_37() - + _seg_38() - + _seg_39() - + _seg_40() - + _seg_41() - + _seg_42() - + _seg_43() - + _seg_44() - + _seg_45() - + _seg_46() - + _seg_47() - + _seg_48() - + _seg_49() - + _seg_50() - + _seg_51() - + _seg_52() - + _seg_53() - + _seg_54() - + _seg_55() - + _seg_56() - + _seg_57() - + _seg_58() - + _seg_59() - + _seg_60() - + _seg_61() - + _seg_62() - + _seg_63() - + _seg_64() - + _seg_65() - + _seg_66() - + _seg_67() - + _seg_68() - + _seg_69() - + _seg_70() - + _seg_71() - + _seg_72() - + _seg_73() - + _seg_74() - + _seg_75() - + _seg_76() - + _seg_77() - + _seg_78() - + _seg_79() - + _seg_80() - + _seg_81() -) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/.venv/Lib/site-packages/jose/__init__.py b/.venv/Lib/site-packages/jose/__init__.py deleted file mode 100644 index 054baa7..0000000 --- a/.venv/Lib/site-packages/jose/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -__version__ = "3.3.0" -__author__ = "Michael Davis" -__license__ = "MIT" -__copyright__ = "Copyright 2016 Michael Davis" - - -from .exceptions import ExpiredSignatureError # noqa: F401 -from .exceptions import JOSEError # noqa: F401 -from .exceptions import JWSError # noqa: F401 -from .exceptions import JWTError # noqa: F401 diff --git a/.venv/Lib/site-packages/jose/backends/__init__.py b/.venv/Lib/site-packages/jose/backends/__init__.py deleted file mode 100644 index e7bba69..0000000 --- a/.venv/Lib/site-packages/jose/backends/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -try: - from jose.backends.cryptography_backend import get_random_bytes # noqa: F401 -except ImportError: - try: - from jose.backends.pycrypto_backend import get_random_bytes # noqa: F401 - except ImportError: - from jose.backends.native import get_random_bytes # noqa: F401 - -try: - from jose.backends.cryptography_backend import CryptographyRSAKey as RSAKey # noqa: F401 -except ImportError: - try: - from jose.backends.rsa_backend import RSAKey # noqa: F401 - except ImportError: - RSAKey = None - -try: - from jose.backends.cryptography_backend import CryptographyECKey as ECKey # noqa: F401 -except ImportError: - from jose.backends.ecdsa_backend import ECDSAECKey as ECKey # noqa: F401 - -try: - from jose.backends.cryptography_backend import CryptographyAESKey as AESKey # noqa: F401 -except ImportError: - AESKey = None - -try: - from jose.backends.cryptography_backend import CryptographyHMACKey as HMACKey # noqa: F401 -except ImportError: - from jose.backends.native import HMACKey # noqa: F401 - -from .base import DIRKey # noqa: F401 diff --git a/.venv/Lib/site-packages/jose/backends/_asn1.py b/.venv/Lib/site-packages/jose/backends/_asn1.py deleted file mode 100644 index af5fa8b..0000000 --- a/.venv/Lib/site-packages/jose/backends/_asn1.py +++ /dev/null @@ -1,83 +0,0 @@ -"""ASN1 encoding helpers for converting between PKCS1 and PKCS8. - -Required by rsa_backend but not cryptography_backend. -""" -from pyasn1.codec.der import decoder, encoder -from pyasn1.type import namedtype, univ - -RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1" - - -class RsaAlgorithmIdentifier(univ.Sequence): - """ASN1 structure for recording RSA PrivateKeyAlgorithm identifiers.""" - - componentType = namedtype.NamedTypes( - namedtype.NamedType("rsaEncryption", univ.ObjectIdentifier()), namedtype.NamedType("parameters", univ.Null()) - ) - - -class PKCS8PrivateKey(univ.Sequence): - """ASN1 structure for recording PKCS8 private keys.""" - - componentType = namedtype.NamedTypes( - namedtype.NamedType("version", univ.Integer()), - namedtype.NamedType("privateKeyAlgorithm", RsaAlgorithmIdentifier()), - namedtype.NamedType("privateKey", univ.OctetString()), - ) - - -class PublicKeyInfo(univ.Sequence): - """ASN1 structure for recording PKCS8 public keys.""" - - componentType = namedtype.NamedTypes( - namedtype.NamedType("algorithm", RsaAlgorithmIdentifier()), namedtype.NamedType("publicKey", univ.BitString()) - ) - - -def rsa_private_key_pkcs8_to_pkcs1(pkcs8_key): - """Convert a PKCS8-encoded RSA private key to PKCS1.""" - decoded_values = decoder.decode(pkcs8_key, asn1Spec=PKCS8PrivateKey()) - - try: - decoded_key = decoded_values[0] - except IndexError: - raise ValueError("Invalid private key encoding") - - return decoded_key["privateKey"] - - -def rsa_private_key_pkcs1_to_pkcs8(pkcs1_key): - """Convert a PKCS1-encoded RSA private key to PKCS8.""" - algorithm = RsaAlgorithmIdentifier() - algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID - - pkcs8_key = PKCS8PrivateKey() - pkcs8_key["version"] = 0 - pkcs8_key["privateKeyAlgorithm"] = algorithm - pkcs8_key["privateKey"] = pkcs1_key - - return encoder.encode(pkcs8_key) - - -def rsa_public_key_pkcs1_to_pkcs8(pkcs1_key): - """Convert a PKCS1-encoded RSA private key to PKCS8.""" - algorithm = RsaAlgorithmIdentifier() - algorithm["rsaEncryption"] = RSA_ENCRYPTION_ASN1_OID - - pkcs8_key = PublicKeyInfo() - pkcs8_key["algorithm"] = algorithm - pkcs8_key["publicKey"] = univ.BitString.fromOctetString(pkcs1_key) - - return encoder.encode(pkcs8_key) - - -def rsa_public_key_pkcs8_to_pkcs1(pkcs8_key): - """Convert a PKCS8-encoded RSA private key to PKCS1.""" - decoded_values = decoder.decode(pkcs8_key, asn1Spec=PublicKeyInfo()) - - try: - decoded_key = decoded_values[0] - except IndexError: - raise ValueError("Invalid public key encoding.") - - return decoded_key["publicKey"].asOctets() diff --git a/.venv/Lib/site-packages/jose/backends/base.py b/.venv/Lib/site-packages/jose/backends/base.py deleted file mode 100644 index b000a52..0000000 --- a/.venv/Lib/site-packages/jose/backends/base.py +++ /dev/null @@ -1,89 +0,0 @@ -from ..utils import base64url_encode, ensure_binary - - -class Key: - """ - A simple interface for implementing JWK keys. - """ - - def __init__(self, key, algorithm): - pass - - def sign(self, msg): - raise NotImplementedError() - - def verify(self, msg, sig): - raise NotImplementedError() - - def public_key(self): - raise NotImplementedError() - - def to_pem(self): - raise NotImplementedError() - - def to_dict(self): - raise NotImplementedError() - - def encrypt(self, plain_text, aad=None): - """ - Encrypt the plain text and generate an auth tag if appropriate - - Args: - plain_text (bytes): Data to encrypt - aad (bytes, optional): Authenticated Additional Data if key's algorithm supports auth mode - - Returns: - (bytes, bytes, bytes): IV, cipher text, and auth tag - """ - raise NotImplementedError() - - def decrypt(self, cipher_text, iv=None, aad=None, tag=None): - """ - Decrypt the cipher text and validate the auth tag if present - Args: - cipher_text (bytes): Cipher text to decrypt - iv (bytes): IV if block mode - aad (bytes): Additional Authenticated Data to verify if auth mode - tag (bytes): Authentication tag if auth mode - - Returns: - bytes: Decrypted value - """ - raise NotImplementedError() - - def wrap_key(self, key_data): - """ - Wrap the the plain text key data - - Args: - key_data (bytes): Key data to wrap - - Returns: - bytes: Wrapped key - """ - raise NotImplementedError() - - def unwrap_key(self, wrapped_key): - """ - Unwrap the the wrapped key data - - Args: - wrapped_key (bytes): Wrapped key data to unwrap - - Returns: - bytes: Unwrapped key - """ - raise NotImplementedError() - - -class DIRKey(Key): - def __init__(self, key_data, algorithm): - self._key = ensure_binary(key_data) - self._alg = algorithm - - def to_dict(self): - return { - "alg": self._alg, - "kty": "oct", - "k": base64url_encode(self._key), - } diff --git a/.venv/Lib/site-packages/jose/backends/cryptography_backend.py b/.venv/Lib/site-packages/jose/backends/cryptography_backend.py deleted file mode 100644 index abd2426..0000000 --- a/.venv/Lib/site-packages/jose/backends/cryptography_backend.py +++ /dev/null @@ -1,605 +0,0 @@ -import math -import warnings - -from cryptography.exceptions import InvalidSignature, InvalidTag -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.bindings.openssl.binding import Binding -from cryptography.hazmat.primitives import hashes, hmac, serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa -from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature -from cryptography.hazmat.primitives.ciphers import Cipher, aead, algorithms, modes -from cryptography.hazmat.primitives.keywrap import InvalidUnwrap, aes_key_unwrap, aes_key_wrap -from cryptography.hazmat.primitives.padding import PKCS7 -from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key -from cryptography.utils import int_to_bytes -from cryptography.x509 import load_pem_x509_certificate - -from ..constants import ALGORITHMS -from ..exceptions import JWEError, JWKError -from ..utils import base64_to_long, base64url_decode, base64url_encode, ensure_binary, long_to_base64 -from .base import Key - -_binding = None - - -def get_random_bytes(num_bytes): - """ - Get random bytes - - Currently, Cryptography returns OS random bytes. If you want OpenSSL - generated random bytes, you'll have to switch the RAND engine after - initializing the OpenSSL backend - Args: - num_bytes (int): Number of random bytes to generate and return - Returns: - bytes: Random bytes - """ - global _binding - - if _binding is None: - _binding = Binding() - - buf = _binding.ffi.new("char[]", num_bytes) - _binding.lib.RAND_bytes(buf, num_bytes) - rand_bytes = _binding.ffi.buffer(buf, num_bytes)[:] - return rand_bytes - - -class CryptographyECKey(Key): - SHA256 = hashes.SHA256 - SHA384 = hashes.SHA384 - SHA512 = hashes.SHA512 - - def __init__(self, key, algorithm, cryptography_backend=default_backend): - if algorithm not in ALGORITHMS.EC: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - - self.hash_alg = { - ALGORITHMS.ES256: self.SHA256, - ALGORITHMS.ES384: self.SHA384, - ALGORITHMS.ES512: self.SHA512, - }.get(algorithm) - self._algorithm = algorithm - - self.cryptography_backend = cryptography_backend - - if hasattr(key, "public_bytes") or hasattr(key, "private_bytes"): - self.prepared_key = key - return - - if hasattr(key, "to_pem"): - # convert to PEM and let cryptography below load it as PEM - key = key.to_pem().decode("utf-8") - - if isinstance(key, dict): - self.prepared_key = self._process_jwk(key) - return - - if isinstance(key, str): - key = key.encode("utf-8") - - if isinstance(key, bytes): - # Attempt to load key. We don't know if it's - # a Public Key or a Private Key, so we try - # the Public Key first. - try: - try: - key = load_pem_public_key(key, self.cryptography_backend()) - except ValueError: - key = load_pem_private_key(key, password=None, backend=self.cryptography_backend()) - except Exception as e: - raise JWKError(e) - - self.prepared_key = key - return - - raise JWKError("Unable to parse an ECKey from key: %s" % key) - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "EC": - raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get("kty")) - - if not all(k in jwk_dict for k in ["x", "y", "crv"]): - raise JWKError("Mandatory parameters are missing") - - x = base64_to_long(jwk_dict.get("x")) - y = base64_to_long(jwk_dict.get("y")) - curve = { - "P-256": ec.SECP256R1, - "P-384": ec.SECP384R1, - "P-521": ec.SECP521R1, - }[jwk_dict["crv"]] - - public = ec.EllipticCurvePublicNumbers(x, y, curve()) - - if "d" in jwk_dict: - d = base64_to_long(jwk_dict.get("d")) - private = ec.EllipticCurvePrivateNumbers(d, public) - - return private.private_key(self.cryptography_backend()) - else: - return public.public_key(self.cryptography_backend()) - - def _sig_component_length(self): - """Determine the correct serialization length for an encoded signature component. - - This is the number of bytes required to encode the maximum key value. - """ - return int(math.ceil(self.prepared_key.key_size / 8.0)) - - def _der_to_raw(self, der_signature): - """Convert signature from DER encoding to RAW encoding.""" - r, s = decode_dss_signature(der_signature) - component_length = self._sig_component_length() - return int_to_bytes(r, component_length) + int_to_bytes(s, component_length) - - def _raw_to_der(self, raw_signature): - """Convert signature from RAW encoding to DER encoding.""" - component_length = self._sig_component_length() - if len(raw_signature) != int(2 * component_length): - raise ValueError("Invalid signature") - - r_bytes = raw_signature[:component_length] - s_bytes = raw_signature[component_length:] - r = int.from_bytes(r_bytes, "big") - s = int.from_bytes(s_bytes, "big") - return encode_dss_signature(r, s) - - def sign(self, msg): - if self.hash_alg.digest_size * 8 > self.prepared_key.curve.key_size: - raise TypeError( - "this curve (%s) is too short " - "for your digest (%d)" % (self.prepared_key.curve.name, 8 * self.hash_alg.digest_size) - ) - signature = self.prepared_key.sign(msg, ec.ECDSA(self.hash_alg())) - return self._der_to_raw(signature) - - def verify(self, msg, sig): - try: - signature = self._raw_to_der(sig) - self.prepared_key.verify(signature, msg, ec.ECDSA(self.hash_alg())) - return True - except Exception: - return False - - def is_public(self): - return hasattr(self.prepared_key, "public_bytes") - - def public_key(self): - if self.is_public(): - return self - return self.__class__(self.prepared_key.public_key(), self._algorithm) - - def to_pem(self): - if self.is_public(): - pem = self.prepared_key.public_bytes( - encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo - ) - return pem - pem = self.prepared_key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ) - return pem - - def to_dict(self): - if not self.is_public(): - public_key = self.prepared_key.public_key() - else: - public_key = self.prepared_key - - crv = { - "secp256r1": "P-256", - "secp384r1": "P-384", - "secp521r1": "P-521", - }[self.prepared_key.curve.name] - - # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of - # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve - # points must be encoded as octed-strings of this length. - key_size = (self.prepared_key.curve.key_size + 7) // 8 - - data = { - "alg": self._algorithm, - "kty": "EC", - "crv": crv, - "x": long_to_base64(public_key.public_numbers().x, size=key_size).decode("ASCII"), - "y": long_to_base64(public_key.public_numbers().y, size=key_size).decode("ASCII"), - } - - if not self.is_public(): - private_value = self.prepared_key.private_numbers().private_value - data["d"] = long_to_base64(private_value, size=key_size).decode("ASCII") - - return data - - -class CryptographyRSAKey(Key): - SHA256 = hashes.SHA256 - SHA384 = hashes.SHA384 - SHA512 = hashes.SHA512 - - RSA1_5 = padding.PKCS1v15() - RSA_OAEP = padding.OAEP(padding.MGF1(hashes.SHA1()), hashes.SHA1(), None) - RSA_OAEP_256 = padding.OAEP(padding.MGF1(hashes.SHA256()), hashes.SHA256(), None) - - def __init__(self, key, algorithm, cryptography_backend=default_backend): - if algorithm not in ALGORITHMS.RSA: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - - self.hash_alg = { - ALGORITHMS.RS256: self.SHA256, - ALGORITHMS.RS384: self.SHA384, - ALGORITHMS.RS512: self.SHA512, - }.get(algorithm) - self._algorithm = algorithm - - self.padding = { - ALGORITHMS.RSA1_5: self.RSA1_5, - ALGORITHMS.RSA_OAEP: self.RSA_OAEP, - ALGORITHMS.RSA_OAEP_256: self.RSA_OAEP_256, - }.get(algorithm) - - self.cryptography_backend = cryptography_backend - - # if it conforms to RSAPublicKey interface - if hasattr(key, "public_bytes") and hasattr(key, "public_numbers"): - self.prepared_key = key - return - - if isinstance(key, dict): - self.prepared_key = self._process_jwk(key) - return - - if isinstance(key, str): - key = key.encode("utf-8") - - if isinstance(key, bytes): - try: - if key.startswith(b"-----BEGIN CERTIFICATE-----"): - self._process_cert(key) - return - - try: - self.prepared_key = load_pem_public_key(key, self.cryptography_backend()) - except ValueError: - self.prepared_key = load_pem_private_key(key, password=None, backend=self.cryptography_backend()) - except Exception as e: - raise JWKError(e) - return - - raise JWKError("Unable to parse an RSA_JWK from key: %s" % key) - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "RSA": - raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get("kty")) - - e = base64_to_long(jwk_dict.get("e", 256)) - n = base64_to_long(jwk_dict.get("n")) - public = rsa.RSAPublicNumbers(e, n) - - if "d" not in jwk_dict: - return public.public_key(self.cryptography_backend()) - else: - # This is a private key. - d = base64_to_long(jwk_dict.get("d")) - - extra_params = ["p", "q", "dp", "dq", "qi"] - - if any(k in jwk_dict for k in extra_params): - # Precomputed private key parameters are available. - if not all(k in jwk_dict for k in extra_params): - # These values must be present when 'p' is according to - # Section 6.3.2 of RFC7518, so if they are not we raise - # an error. - raise JWKError("Precomputed private key parameters are incomplete.") - - p = base64_to_long(jwk_dict["p"]) - q = base64_to_long(jwk_dict["q"]) - dp = base64_to_long(jwk_dict["dp"]) - dq = base64_to_long(jwk_dict["dq"]) - qi = base64_to_long(jwk_dict["qi"]) - else: - # The precomputed private key parameters are not available, - # so we use cryptography's API to fill them in. - p, q = rsa.rsa_recover_prime_factors(n, e, d) - dp = rsa.rsa_crt_dmp1(d, p) - dq = rsa.rsa_crt_dmq1(d, q) - qi = rsa.rsa_crt_iqmp(p, q) - - private = rsa.RSAPrivateNumbers(p, q, d, dp, dq, qi, public) - - return private.private_key(self.cryptography_backend()) - - def _process_cert(self, key): - key = load_pem_x509_certificate(key, self.cryptography_backend()) - self.prepared_key = key.public_key() - - def sign(self, msg): - try: - signature = self.prepared_key.sign(msg, padding.PKCS1v15(), self.hash_alg()) - except Exception as e: - raise JWKError(e) - return signature - - def verify(self, msg, sig): - if not self.is_public(): - warnings.warn("Attempting to verify a message with a private key. " "This is not recommended.") - - try: - self.public_key().prepared_key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg()) - return True - except InvalidSignature: - return False - - def is_public(self): - return hasattr(self.prepared_key, "public_bytes") - - def public_key(self): - if self.is_public(): - return self - return self.__class__(self.prepared_key.public_key(), self._algorithm) - - def to_pem(self, pem_format="PKCS8"): - if self.is_public(): - if pem_format == "PKCS8": - fmt = serialization.PublicFormat.SubjectPublicKeyInfo - elif pem_format == "PKCS1": - fmt = serialization.PublicFormat.PKCS1 - else: - raise ValueError("Invalid format specified: %r" % pem_format) - pem = self.prepared_key.public_bytes(encoding=serialization.Encoding.PEM, format=fmt) - return pem - - if pem_format == "PKCS8": - fmt = serialization.PrivateFormat.PKCS8 - elif pem_format == "PKCS1": - fmt = serialization.PrivateFormat.TraditionalOpenSSL - else: - raise ValueError("Invalid format specified: %r" % pem_format) - - return self.prepared_key.private_bytes( - encoding=serialization.Encoding.PEM, format=fmt, encryption_algorithm=serialization.NoEncryption() - ) - - def to_dict(self): - if not self.is_public(): - public_key = self.prepared_key.public_key() - else: - public_key = self.prepared_key - - data = { - "alg": self._algorithm, - "kty": "RSA", - "n": long_to_base64(public_key.public_numbers().n).decode("ASCII"), - "e": long_to_base64(public_key.public_numbers().e).decode("ASCII"), - } - - if not self.is_public(): - data.update( - { - "d": long_to_base64(self.prepared_key.private_numbers().d).decode("ASCII"), - "p": long_to_base64(self.prepared_key.private_numbers().p).decode("ASCII"), - "q": long_to_base64(self.prepared_key.private_numbers().q).decode("ASCII"), - "dp": long_to_base64(self.prepared_key.private_numbers().dmp1).decode("ASCII"), - "dq": long_to_base64(self.prepared_key.private_numbers().dmq1).decode("ASCII"), - "qi": long_to_base64(self.prepared_key.private_numbers().iqmp).decode("ASCII"), - } - ) - - return data - - def wrap_key(self, key_data): - try: - wrapped_key = self.prepared_key.encrypt(key_data, self.padding) - except Exception as e: - raise JWEError(e) - - return wrapped_key - - def unwrap_key(self, wrapped_key): - try: - unwrapped_key = self.prepared_key.decrypt(wrapped_key, self.padding) - return unwrapped_key - except Exception as e: - raise JWEError(e) - - -class CryptographyAESKey(Key): - KEY_128 = (ALGORITHMS.A128GCM, ALGORITHMS.A128GCMKW, ALGORITHMS.A128KW, ALGORITHMS.A128CBC) - KEY_192 = (ALGORITHMS.A192GCM, ALGORITHMS.A192GCMKW, ALGORITHMS.A192KW, ALGORITHMS.A192CBC) - KEY_256 = ( - ALGORITHMS.A256GCM, - ALGORITHMS.A256GCMKW, - ALGORITHMS.A256KW, - ALGORITHMS.A128CBC_HS256, - ALGORITHMS.A256CBC, - ) - KEY_384 = (ALGORITHMS.A192CBC_HS384,) - KEY_512 = (ALGORITHMS.A256CBC_HS512,) - - AES_KW_ALGS = (ALGORITHMS.A128KW, ALGORITHMS.A192KW, ALGORITHMS.A256KW) - - MODES = { - ALGORITHMS.A128GCM: modes.GCM, - ALGORITHMS.A192GCM: modes.GCM, - ALGORITHMS.A256GCM: modes.GCM, - ALGORITHMS.A128CBC_HS256: modes.CBC, - ALGORITHMS.A192CBC_HS384: modes.CBC, - ALGORITHMS.A256CBC_HS512: modes.CBC, - ALGORITHMS.A128CBC: modes.CBC, - ALGORITHMS.A192CBC: modes.CBC, - ALGORITHMS.A256CBC: modes.CBC, - ALGORITHMS.A128GCMKW: modes.GCM, - ALGORITHMS.A192GCMKW: modes.GCM, - ALGORITHMS.A256GCMKW: modes.GCM, - ALGORITHMS.A128KW: None, - ALGORITHMS.A192KW: None, - ALGORITHMS.A256KW: None, - } - - def __init__(self, key, algorithm): - if algorithm not in ALGORITHMS.AES: - raise JWKError("%s is not a valid AES algorithm" % algorithm) - if algorithm not in ALGORITHMS.SUPPORTED.union(ALGORITHMS.AES_PSEUDO): - raise JWKError("%s is not a supported algorithm" % algorithm) - - self._algorithm = algorithm - self._mode = self.MODES.get(self._algorithm) - - if algorithm in self.KEY_128 and len(key) != 16: - raise JWKError(f"Key must be 128 bit for alg {algorithm}") - elif algorithm in self.KEY_192 and len(key) != 24: - raise JWKError(f"Key must be 192 bit for alg {algorithm}") - elif algorithm in self.KEY_256 and len(key) != 32: - raise JWKError(f"Key must be 256 bit for alg {algorithm}") - elif algorithm in self.KEY_384 and len(key) != 48: - raise JWKError(f"Key must be 384 bit for alg {algorithm}") - elif algorithm in self.KEY_512 and len(key) != 64: - raise JWKError(f"Key must be 512 bit for alg {algorithm}") - - self._key = key - - def to_dict(self): - data = {"alg": self._algorithm, "kty": "oct", "k": base64url_encode(self._key)} - return data - - def encrypt(self, plain_text, aad=None): - plain_text = ensure_binary(plain_text) - try: - iv = get_random_bytes(algorithms.AES.block_size // 8) - mode = self._mode(iv) - if mode.name == "GCM": - cipher = aead.AESGCM(self._key) - cipher_text_and_tag = cipher.encrypt(iv, plain_text, aad) - cipher_text = cipher_text_and_tag[: len(cipher_text_and_tag) - 16] - auth_tag = cipher_text_and_tag[-16:] - else: - cipher = Cipher(algorithms.AES(self._key), mode, backend=default_backend()) - encryptor = cipher.encryptor() - padder = PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(plain_text) - padded_data += padder.finalize() - cipher_text = encryptor.update(padded_data) + encryptor.finalize() - auth_tag = None - return iv, cipher_text, auth_tag - except Exception as e: - raise JWEError(e) - - def decrypt(self, cipher_text, iv=None, aad=None, tag=None): - cipher_text = ensure_binary(cipher_text) - try: - iv = ensure_binary(iv) - mode = self._mode(iv) - if mode.name == "GCM": - if tag is None: - raise ValueError("tag cannot be None") - cipher = aead.AESGCM(self._key) - cipher_text_and_tag = cipher_text + tag - try: - plain_text = cipher.decrypt(iv, cipher_text_and_tag, aad) - except InvalidTag: - raise JWEError("Invalid JWE Auth Tag") - else: - cipher = Cipher(algorithms.AES(self._key), mode, backend=default_backend()) - decryptor = cipher.decryptor() - padded_plain_text = decryptor.update(cipher_text) - padded_plain_text += decryptor.finalize() - unpadder = PKCS7(algorithms.AES.block_size).unpadder() - plain_text = unpadder.update(padded_plain_text) - plain_text += unpadder.finalize() - - return plain_text - except Exception as e: - raise JWEError(e) - - def wrap_key(self, key_data): - key_data = ensure_binary(key_data) - cipher_text = aes_key_wrap(self._key, key_data, default_backend()) - return cipher_text # IV, cipher text, auth tag - - def unwrap_key(self, wrapped_key): - wrapped_key = ensure_binary(wrapped_key) - try: - plain_text = aes_key_unwrap(self._key, wrapped_key, default_backend()) - except InvalidUnwrap as cause: - raise JWEError(cause) - return plain_text - - -class CryptographyHMACKey(Key): - """ - Performs signing and verification operations using HMAC - and the specified hash function. - """ - - ALG_MAP = {ALGORITHMS.HS256: hashes.SHA256(), ALGORITHMS.HS384: hashes.SHA384(), ALGORITHMS.HS512: hashes.SHA512()} - - def __init__(self, key, algorithm): - if algorithm not in ALGORITHMS.HMAC: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - self._algorithm = algorithm - self._hash_alg = self.ALG_MAP.get(algorithm) - - if isinstance(key, dict): - self.prepared_key = self._process_jwk(key) - return - - if not isinstance(key, str) and not isinstance(key, bytes): - raise JWKError("Expecting a string- or bytes-formatted key.") - - if isinstance(key, str): - key = key.encode("utf-8") - - invalid_strings = [ - b"-----BEGIN PUBLIC KEY-----", - b"-----BEGIN RSA PUBLIC KEY-----", - b"-----BEGIN CERTIFICATE-----", - b"ssh-rsa", - ] - - if any(string_value in key for string_value in invalid_strings): - raise JWKError( - "The specified key is an asymmetric key or x509 certificate and" - " should not be used as an HMAC secret." - ) - - self.prepared_key = key - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "oct": - raise JWKError("Incorrect key type. Expected: 'oct', Received: %s" % jwk_dict.get("kty")) - - k = jwk_dict.get("k") - k = k.encode("utf-8") - k = bytes(k) - k = base64url_decode(k) - - return k - - def to_dict(self): - return { - "alg": self._algorithm, - "kty": "oct", - "k": base64url_encode(self.prepared_key).decode("ASCII"), - } - - def sign(self, msg): - msg = ensure_binary(msg) - h = hmac.HMAC(self.prepared_key, self._hash_alg, backend=default_backend()) - h.update(msg) - signature = h.finalize() - return signature - - def verify(self, msg, sig): - msg = ensure_binary(msg) - sig = ensure_binary(sig) - h = hmac.HMAC(self.prepared_key, self._hash_alg, backend=default_backend()) - h.update(msg) - try: - h.verify(sig) - verified = True - except InvalidSignature: - verified = False - return verified diff --git a/.venv/Lib/site-packages/jose/backends/ecdsa_backend.py b/.venv/Lib/site-packages/jose/backends/ecdsa_backend.py deleted file mode 100644 index 756c7ea..0000000 --- a/.venv/Lib/site-packages/jose/backends/ecdsa_backend.py +++ /dev/null @@ -1,150 +0,0 @@ -import hashlib - -import ecdsa - -from jose.backends.base import Key -from jose.constants import ALGORITHMS -from jose.exceptions import JWKError -from jose.utils import base64_to_long, long_to_base64 - - -class ECDSAECKey(Key): - """ - Performs signing and verification operations using - ECDSA and the specified hash function - - This class requires the ecdsa package to be installed. - - This is based off of the implementation in PyJWT 0.3.2 - """ - - SHA256 = hashlib.sha256 - SHA384 = hashlib.sha384 - SHA512 = hashlib.sha512 - - CURVE_MAP = { - SHA256: ecdsa.curves.NIST256p, - SHA384: ecdsa.curves.NIST384p, - SHA512: ecdsa.curves.NIST521p, - } - CURVE_NAMES = ( - (ecdsa.curves.NIST256p, "P-256"), - (ecdsa.curves.NIST384p, "P-384"), - (ecdsa.curves.NIST521p, "P-521"), - ) - - def __init__(self, key, algorithm): - if algorithm not in ALGORITHMS.EC: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - - self.hash_alg = { - ALGORITHMS.ES256: self.SHA256, - ALGORITHMS.ES384: self.SHA384, - ALGORITHMS.ES512: self.SHA512, - }.get(algorithm) - self._algorithm = algorithm - - self.curve = self.CURVE_MAP.get(self.hash_alg) - - if isinstance(key, (ecdsa.SigningKey, ecdsa.VerifyingKey)): - self.prepared_key = key - return - - if isinstance(key, dict): - self.prepared_key = self._process_jwk(key) - return - - if isinstance(key, str): - key = key.encode("utf-8") - - if isinstance(key, bytes): - # Attempt to load key. We don't know if it's - # a Signing Key or a Verifying Key, so we try - # the Verifying Key first. - try: - key = ecdsa.VerifyingKey.from_pem(key) - except ecdsa.der.UnexpectedDER: - key = ecdsa.SigningKey.from_pem(key) - except Exception as e: - raise JWKError(e) - - self.prepared_key = key - return - - raise JWKError("Unable to parse an ECKey from key: %s" % key) - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "EC": - raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get("kty")) - - if not all(k in jwk_dict for k in ["x", "y", "crv"]): - raise JWKError("Mandatory parameters are missing") - - if "d" in jwk_dict: - # We are dealing with a private key; the secret exponent is enough - # to create an ecdsa key. - d = base64_to_long(jwk_dict.get("d")) - return ecdsa.keys.SigningKey.from_secret_exponent(d, self.curve) - else: - x = base64_to_long(jwk_dict.get("x")) - y = base64_to_long(jwk_dict.get("y")) - - if not ecdsa.ecdsa.point_is_valid(self.curve.generator, x, y): - raise JWKError(f"Point: {x}, {y} is not a valid point") - - point = ecdsa.ellipticcurve.Point(self.curve.curve, x, y, self.curve.order) - return ecdsa.keys.VerifyingKey.from_public_point(point, self.curve) - - def sign(self, msg): - return self.prepared_key.sign( - msg, hashfunc=self.hash_alg, sigencode=ecdsa.util.sigencode_string, allow_truncate=False - ) - - def verify(self, msg, sig): - try: - return self.prepared_key.verify( - sig, msg, hashfunc=self.hash_alg, sigdecode=ecdsa.util.sigdecode_string, allow_truncate=False - ) - except Exception: - return False - - def is_public(self): - return isinstance(self.prepared_key, ecdsa.VerifyingKey) - - def public_key(self): - if self.is_public(): - return self - return self.__class__(self.prepared_key.get_verifying_key(), self._algorithm) - - def to_pem(self): - return self.prepared_key.to_pem() - - def to_dict(self): - if not self.is_public(): - public_key = self.prepared_key.get_verifying_key() - else: - public_key = self.prepared_key - crv = None - for key, value in self.CURVE_NAMES: - if key == self.prepared_key.curve: - crv = value - if not crv: - raise KeyError(f"Can't match {self.prepared_key.curve}") - - # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of - # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve - # points must be encoded as octed-strings of this length. - key_size = self.prepared_key.curve.baselen - - data = { - "alg": self._algorithm, - "kty": "EC", - "crv": crv, - "x": long_to_base64(public_key.pubkey.point.x(), size=key_size).decode("ASCII"), - "y": long_to_base64(public_key.pubkey.point.y(), size=key_size).decode("ASCII"), - } - - if not self.is_public(): - data["d"] = long_to_base64(self.prepared_key.privkey.secret_multiplier, size=key_size).decode("ASCII") - - return data diff --git a/.venv/Lib/site-packages/jose/backends/native.py b/.venv/Lib/site-packages/jose/backends/native.py deleted file mode 100644 index eb3a6ae..0000000 --- a/.venv/Lib/site-packages/jose/backends/native.py +++ /dev/null @@ -1,76 +0,0 @@ -import hashlib -import hmac -import os - -from jose.backends.base import Key -from jose.constants import ALGORITHMS -from jose.exceptions import JWKError -from jose.utils import base64url_decode, base64url_encode - - -def get_random_bytes(num_bytes): - return bytes(os.urandom(num_bytes)) - - -class HMACKey(Key): - """ - Performs signing and verification operations using HMAC - and the specified hash function. - """ - - HASHES = {ALGORITHMS.HS256: hashlib.sha256, ALGORITHMS.HS384: hashlib.sha384, ALGORITHMS.HS512: hashlib.sha512} - - def __init__(self, key, algorithm): - if algorithm not in ALGORITHMS.HMAC: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - self._algorithm = algorithm - self._hash_alg = self.HASHES.get(algorithm) - - if isinstance(key, dict): - self.prepared_key = self._process_jwk(key) - return - - if not isinstance(key, str) and not isinstance(key, bytes): - raise JWKError("Expecting a string- or bytes-formatted key.") - - if isinstance(key, str): - key = key.encode("utf-8") - - invalid_strings = [ - b"-----BEGIN PUBLIC KEY-----", - b"-----BEGIN RSA PUBLIC KEY-----", - b"-----BEGIN CERTIFICATE-----", - b"ssh-rsa", - ] - - if any(string_value in key for string_value in invalid_strings): - raise JWKError( - "The specified key is an asymmetric key or x509 certificate and" - " should not be used as an HMAC secret." - ) - - self.prepared_key = key - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "oct": - raise JWKError("Incorrect key type. Expected: 'oct', Received: %s" % jwk_dict.get("kty")) - - k = jwk_dict.get("k") - k = k.encode("utf-8") - k = bytes(k) - k = base64url_decode(k) - - return k - - def sign(self, msg): - return hmac.new(self.prepared_key, msg, self._hash_alg).digest() - - def verify(self, msg, sig): - return hmac.compare_digest(sig, self.sign(msg)) - - def to_dict(self): - return { - "alg": self._algorithm, - "kty": "oct", - "k": base64url_encode(self.prepared_key).decode("ASCII"), - } diff --git a/.venv/Lib/site-packages/jose/backends/rsa_backend.py b/.venv/Lib/site-packages/jose/backends/rsa_backend.py deleted file mode 100644 index 4e8ccf1..0000000 --- a/.venv/Lib/site-packages/jose/backends/rsa_backend.py +++ /dev/null @@ -1,284 +0,0 @@ -import binascii -import warnings - -import rsa as pyrsa -import rsa.pem as pyrsa_pem -from pyasn1.error import PyAsn1Error -from rsa import DecryptionError - -from jose.backends._asn1 import ( - rsa_private_key_pkcs1_to_pkcs8, - rsa_private_key_pkcs8_to_pkcs1, - rsa_public_key_pkcs1_to_pkcs8, -) -from jose.backends.base import Key -from jose.constants import ALGORITHMS -from jose.exceptions import JWEError, JWKError -from jose.utils import base64_to_long, long_to_base64 - -ALGORITHMS.SUPPORTED.remove(ALGORITHMS.RSA_OAEP) # RSA OAEP not supported - -LEGACY_INVALID_PKCS8_RSA_HEADER = binascii.unhexlify( - "30" # sequence - "8204BD" # DER-encoded sequence contents length of 1213 bytes -- INCORRECT STATIC LENGTH - "020100" # integer: 0 -- Version - "30" # sequence - "0D" # DER-encoded sequence contents length of 13 bytes -- PrivateKeyAlgorithmIdentifier - "06092A864886F70D010101" # OID -- rsaEncryption - "0500" # NULL -- parameters -) -ASN1_SEQUENCE_ID = binascii.unhexlify("30") -RSA_ENCRYPTION_ASN1_OID = "1.2.840.113549.1.1.1" - -# Functions gcd and rsa_recover_prime_factors were copied from cryptography 1.9 -# to enable pure python rsa module to be in compliance with section 6.3.1 of RFC7518 -# which requires only private exponent (d) for private key. - - -def _gcd(a, b): - """Calculate the Greatest Common Divisor of a and b. - - Unless b==0, the result will have the same sign as b (so that when - b is divided by it, the result comes out positive). - """ - while b: - a, b = b, (a % b) - return a - - -# Controls the number of iterations rsa_recover_prime_factors will perform -# to obtain the prime factors. Each iteration increments by 2 so the actual -# maximum attempts is half this number. -_MAX_RECOVERY_ATTEMPTS = 1000 - - -def _rsa_recover_prime_factors(n, e, d): - """ - Compute factors p and q from the private exponent d. We assume that n has - no more than two factors. This function is adapted from code in PyCrypto. - """ - # See 8.2.2(i) in Handbook of Applied Cryptography. - ktot = d * e - 1 - # The quantity d*e-1 is a multiple of phi(n), even, - # and can be represented as t*2^s. - t = ktot - while t % 2 == 0: - t = t // 2 - # Cycle through all multiplicative inverses in Zn. - # The algorithm is non-deterministic, but there is a 50% chance - # any candidate a leads to successful factoring. - # See "Digitalized Signatures and Public Key Functions as Intractable - # as Factorization", M. Rabin, 1979 - spotted = False - a = 2 - while not spotted and a < _MAX_RECOVERY_ATTEMPTS: - k = t - # Cycle through all values a^{t*2^i}=a^k - while k < ktot: - cand = pow(a, k, n) - # Check if a^k is a non-trivial root of unity (mod n) - if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: - # We have found a number such that (cand-1)(cand+1)=0 (mod n). - # Either of the terms divides n. - p = _gcd(cand + 1, n) - spotted = True - break - k *= 2 - # This value was not any good... let's try another! - a += 2 - if not spotted: - raise ValueError("Unable to compute factors p and q from exponent d.") - # Found ! - q, r = divmod(n, p) - assert r == 0 - p, q = sorted((p, q), reverse=True) - return (p, q) - - -def pem_to_spki(pem, fmt="PKCS8"): - key = RSAKey(pem, ALGORITHMS.RS256) - return key.to_pem(fmt) - - -def _legacy_private_key_pkcs8_to_pkcs1(pkcs8_key): - """Legacy RSA private key PKCS8-to-PKCS1 conversion. - - .. warning:: - - This is incorrect parsing and only works because the legacy PKCS1-to-PKCS8 - encoding was also incorrect. - """ - # Only allow this processing if the prefix matches - # AND the following byte indicates an ASN1 sequence, - # as we would expect with the legacy encoding. - if not pkcs8_key.startswith(LEGACY_INVALID_PKCS8_RSA_HEADER + ASN1_SEQUENCE_ID): - raise ValueError("Invalid private key encoding") - - return pkcs8_key[len(LEGACY_INVALID_PKCS8_RSA_HEADER) :] - - -class RSAKey(Key): - SHA256 = "SHA-256" - SHA384 = "SHA-384" - SHA512 = "SHA-512" - - def __init__(self, key, algorithm): - if algorithm not in ALGORITHMS.RSA: - raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm) - - if algorithm in ALGORITHMS.RSA_KW and algorithm != ALGORITHMS.RSA1_5: - raise JWKError("alg: %s is not supported by the RSA backend" % algorithm) - - self.hash_alg = { - ALGORITHMS.RS256: self.SHA256, - ALGORITHMS.RS384: self.SHA384, - ALGORITHMS.RS512: self.SHA512, - }.get(algorithm) - self._algorithm = algorithm - - if isinstance(key, dict): - self._prepared_key = self._process_jwk(key) - return - - if isinstance(key, (pyrsa.PublicKey, pyrsa.PrivateKey)): - self._prepared_key = key - return - - if isinstance(key, str): - key = key.encode("utf-8") - - if isinstance(key, bytes): - try: - self._prepared_key = pyrsa.PublicKey.load_pkcs1(key) - except ValueError: - try: - self._prepared_key = pyrsa.PublicKey.load_pkcs1_openssl_pem(key) - except ValueError: - try: - self._prepared_key = pyrsa.PrivateKey.load_pkcs1(key) - except ValueError: - try: - der = pyrsa_pem.load_pem(key, b"PRIVATE KEY") - try: - pkcs1_key = rsa_private_key_pkcs8_to_pkcs1(der) - except PyAsn1Error: - # If the key was encoded using the old, invalid, - # encoding then pyasn1 will throw an error attempting - # to parse the key. - pkcs1_key = _legacy_private_key_pkcs8_to_pkcs1(der) - self._prepared_key = pyrsa.PrivateKey.load_pkcs1(pkcs1_key, format="DER") - except ValueError as e: - raise JWKError(e) - return - raise JWKError("Unable to parse an RSA_JWK from key: %s" % key) - - def _process_jwk(self, jwk_dict): - if not jwk_dict.get("kty") == "RSA": - raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get("kty")) - - e = base64_to_long(jwk_dict.get("e")) - n = base64_to_long(jwk_dict.get("n")) - - if "d" not in jwk_dict: - return pyrsa.PublicKey(e=e, n=n) - else: - d = base64_to_long(jwk_dict.get("d")) - extra_params = ["p", "q", "dp", "dq", "qi"] - - if any(k in jwk_dict for k in extra_params): - # Precomputed private key parameters are available. - if not all(k in jwk_dict for k in extra_params): - # These values must be present when 'p' is according to - # Section 6.3.2 of RFC7518, so if they are not we raise - # an error. - raise JWKError("Precomputed private key parameters are incomplete.") - - p = base64_to_long(jwk_dict["p"]) - q = base64_to_long(jwk_dict["q"]) - return pyrsa.PrivateKey(e=e, n=n, d=d, p=p, q=q) - else: - p, q = _rsa_recover_prime_factors(n, e, d) - return pyrsa.PrivateKey(n=n, e=e, d=d, p=p, q=q) - - def sign(self, msg): - return pyrsa.sign(msg, self._prepared_key, self.hash_alg) - - def verify(self, msg, sig): - if not self.is_public(): - warnings.warn("Attempting to verify a message with a private key. " "This is not recommended.") - try: - pyrsa.verify(msg, sig, self._prepared_key) - return True - except pyrsa.pkcs1.VerificationError: - return False - - def is_public(self): - return isinstance(self._prepared_key, pyrsa.PublicKey) - - def public_key(self): - if isinstance(self._prepared_key, pyrsa.PublicKey): - return self - return self.__class__(pyrsa.PublicKey(n=self._prepared_key.n, e=self._prepared_key.e), self._algorithm) - - def to_pem(self, pem_format="PKCS8"): - - if isinstance(self._prepared_key, pyrsa.PrivateKey): - der = self._prepared_key.save_pkcs1(format="DER") - if pem_format == "PKCS8": - pkcs8_der = rsa_private_key_pkcs1_to_pkcs8(der) - pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker="PRIVATE KEY") - elif pem_format == "PKCS1": - pem = pyrsa_pem.save_pem(der, pem_marker="RSA PRIVATE KEY") - else: - raise ValueError(f"Invalid pem format specified: {pem_format!r}") - else: - if pem_format == "PKCS8": - pkcs1_der = self._prepared_key.save_pkcs1(format="DER") - pkcs8_der = rsa_public_key_pkcs1_to_pkcs8(pkcs1_der) - pem = pyrsa_pem.save_pem(pkcs8_der, pem_marker="PUBLIC KEY") - elif pem_format == "PKCS1": - der = self._prepared_key.save_pkcs1(format="DER") - pem = pyrsa_pem.save_pem(der, pem_marker="RSA PUBLIC KEY") - else: - raise ValueError(f"Invalid pem format specified: {pem_format!r}") - return pem - - def to_dict(self): - if not self.is_public(): - public_key = self.public_key()._prepared_key - else: - public_key = self._prepared_key - - data = { - "alg": self._algorithm, - "kty": "RSA", - "n": long_to_base64(public_key.n).decode("ASCII"), - "e": long_to_base64(public_key.e).decode("ASCII"), - } - - if not self.is_public(): - data.update( - { - "d": long_to_base64(self._prepared_key.d).decode("ASCII"), - "p": long_to_base64(self._prepared_key.p).decode("ASCII"), - "q": long_to_base64(self._prepared_key.q).decode("ASCII"), - "dp": long_to_base64(self._prepared_key.exp1).decode("ASCII"), - "dq": long_to_base64(self._prepared_key.exp2).decode("ASCII"), - "qi": long_to_base64(self._prepared_key.coef).decode("ASCII"), - } - ) - - return data - - def wrap_key(self, key_data): - if not self.is_public(): - warnings.warn("Attempting to encrypt a message with a private key." " This is not recommended.") - wrapped_key = pyrsa.encrypt(key_data, self._prepared_key) - return wrapped_key - - def unwrap_key(self, wrapped_key): - try: - unwrapped_key = pyrsa.decrypt(wrapped_key, self._prepared_key) - except DecryptionError as e: - raise JWEError(e) - return unwrapped_key diff --git a/.venv/Lib/site-packages/jose/constants.py b/.venv/Lib/site-packages/jose/constants.py deleted file mode 100644 index ab4d74d..0000000 --- a/.venv/Lib/site-packages/jose/constants.py +++ /dev/null @@ -1,98 +0,0 @@ -import hashlib - - -class Algorithms: - # DS Algorithms - NONE = "none" - HS256 = "HS256" - HS384 = "HS384" - HS512 = "HS512" - RS256 = "RS256" - RS384 = "RS384" - RS512 = "RS512" - ES256 = "ES256" - ES384 = "ES384" - ES512 = "ES512" - - # Content Encryption Algorithms - A128CBC_HS256 = "A128CBC-HS256" - A192CBC_HS384 = "A192CBC-HS384" - A256CBC_HS512 = "A256CBC-HS512" - A128GCM = "A128GCM" - A192GCM = "A192GCM" - A256GCM = "A256GCM" - - # Pseudo algorithm for encryption - A128CBC = "A128CBC" - A192CBC = "A192CBC" - A256CBC = "A256CBC" - - # CEK Encryption Algorithms - DIR = "dir" - RSA1_5 = "RSA1_5" - RSA_OAEP = "RSA-OAEP" - RSA_OAEP_256 = "RSA-OAEP-256" - A128KW = "A128KW" - A192KW = "A192KW" - A256KW = "A256KW" - ECDH_ES = "ECDH-ES" - ECDH_ES_A128KW = "ECDH-ES+A128KW" - ECDH_ES_A192KW = "ECDH-ES+A192KW" - ECDH_ES_A256KW = "ECDH-ES+A256KW" - A128GCMKW = "A128GCMKW" - A192GCMKW = "A192GCMKW" - A256GCMKW = "A256GCMKW" - PBES2_HS256_A128KW = "PBES2-HS256+A128KW" - PBES2_HS384_A192KW = "PBES2-HS384+A192KW" - PBES2_HS512_A256KW = "PBES2-HS512+A256KW" - - # Compression Algorithms - DEF = "DEF" - - HMAC = {HS256, HS384, HS512} - RSA_DS = {RS256, RS384, RS512} - RSA_KW = {RSA1_5, RSA_OAEP, RSA_OAEP_256} - RSA = RSA_DS.union(RSA_KW) - EC_DS = {ES256, ES384, ES512} - EC_KW = {ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW} - EC = EC_DS.union(EC_KW) - AES_PSEUDO = {A128CBC, A192CBC, A256CBC, A128GCM, A192GCM, A256GCM} - AES_JWE_ENC = {A128CBC_HS256, A192CBC_HS384, A256CBC_HS512, A128GCM, A192GCM, A256GCM} - AES_ENC = AES_JWE_ENC.union(AES_PSEUDO) - AES_KW = {A128KW, A192KW, A256KW} - AEC_GCM_KW = {A128GCMKW, A192GCMKW, A256GCMKW} - AES = AES_ENC.union(AES_KW) - PBES2_KW = {PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW} - - HMAC_AUTH_TAG = {A128CBC_HS256, A192CBC_HS384, A256CBC_HS512} - GCM = {A128GCM, A192GCM, A256GCM} - - SUPPORTED = HMAC.union(RSA_DS).union(EC_DS).union([DIR]).union(AES_JWE_ENC).union(RSA_KW).union(AES_KW) - - ALL = SUPPORTED.union([NONE]).union(AEC_GCM_KW).union(EC_KW).union(PBES2_KW) - - HASHES = { - HS256: hashlib.sha256, - HS384: hashlib.sha384, - HS512: hashlib.sha512, - RS256: hashlib.sha256, - RS384: hashlib.sha384, - RS512: hashlib.sha512, - ES256: hashlib.sha256, - ES384: hashlib.sha384, - ES512: hashlib.sha512, - } - - KEYS = {} - - -ALGORITHMS = Algorithms() - - -class Zips: - DEF = "DEF" - NONE = None - SUPPORTED = {DEF, NONE} - - -ZIPS = Zips() diff --git a/.venv/Lib/site-packages/jose/exceptions.py b/.venv/Lib/site-packages/jose/exceptions.py deleted file mode 100644 index e8edc3b..0000000 --- a/.venv/Lib/site-packages/jose/exceptions.py +++ /dev/null @@ -1,59 +0,0 @@ -class JOSEError(Exception): - pass - - -class JWSError(JOSEError): - pass - - -class JWSSignatureError(JWSError): - pass - - -class JWSAlgorithmError(JWSError): - pass - - -class JWTError(JOSEError): - pass - - -class JWTClaimsError(JWTError): - pass - - -class ExpiredSignatureError(JWTError): - pass - - -class JWKError(JOSEError): - pass - - -class JWEError(JOSEError): - """Base error for all JWE errors""" - - pass - - -class JWEParseError(JWEError): - """Could not parse the JWE string provided""" - - pass - - -class JWEInvalidAuth(JWEError): - """ - The authentication tag did not match the protected sections of the - JWE string provided - """ - - pass - - -class JWEAlgorithmUnsupportedError(JWEError): - """ - The JWE algorithm is not supported by the backend - """ - - pass diff --git a/.venv/Lib/site-packages/jose/jwe.py b/.venv/Lib/site-packages/jose/jwe.py deleted file mode 100644 index 2c387ff..0000000 --- a/.venv/Lib/site-packages/jose/jwe.py +++ /dev/null @@ -1,607 +0,0 @@ -import binascii -import json -import zlib -from collections.abc import Mapping -from struct import pack - -from . import jwk -from .backends import get_random_bytes -from .constants import ALGORITHMS, ZIPS -from .exceptions import JWEError, JWEParseError -from .utils import base64url_decode, base64url_encode, ensure_binary - - -def encrypt(plaintext, key, encryption=ALGORITHMS.A256GCM, algorithm=ALGORITHMS.DIR, zip=None, cty=None, kid=None): - """Encrypts plaintext and returns a JWE cmpact serialization string. - - Args: - plaintext (bytes): A bytes object to encrypt - key (str or dict): The key(s) to use for encrypting the content. Can be - individual JWK or JWK set. - encryption (str, optional): The content encryption algorithm used to - perform authenticated encryption on the plaintext to produce the - ciphertext and the Authentication Tag. Defaults to A256GCM. - algorithm (str, optional): The cryptographic algorithm used - to encrypt or determine the value of the CEK. Defaults to dir. - zip (str, optional): The compression algorithm) applied to the - plaintext before encryption. Defaults to None. - cty (str, optional): The media type for the secured content. - See http://www.iana.org/assignments/media-types/media-types.xhtml - kid (str, optional): Key ID for the provided key - - Returns: - bytes: The string representation of the header, encrypted key, - initialization vector, ciphertext, and authentication tag. - - Raises: - JWEError: If there is an error signing the token. - - Examples: - >>> from jose import jwe - >>> jwe.encrypt('Hello, World!', 'asecret128bitkey', algorithm='dir', encryption='A128GCM') - 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..McILMB3dYsNJSuhcDzQshA.OfX9H_mcUpHDeRM4IA.CcnTWqaqxNsjT4eCaUABSg' - - """ - plaintext = ensure_binary(plaintext) # Make sure it's bytes - if algorithm not in ALGORITHMS.SUPPORTED: - raise JWEError("Algorithm %s not supported." % algorithm) - if encryption not in ALGORITHMS.SUPPORTED: - raise JWEError("Algorithm %s not supported." % encryption) - key = jwk.construct(key, algorithm) - encoded_header = _encoded_header(algorithm, encryption, zip, cty, kid) - - plaintext = _compress(zip, plaintext) - enc_cek, iv, cipher_text, auth_tag = _encrypt_and_auth(key, algorithm, encryption, zip, plaintext, encoded_header) - - jwe_string = _jwe_compact_serialize(encoded_header, enc_cek, iv, cipher_text, auth_tag) - return jwe_string - - -def decrypt(jwe_str, key): - """Decrypts a JWE compact serialized string and returns the plaintext. - - Args: - jwe_str (str): A JWE to be decrypt. - key (str or dict): A key to attempt to decrypt the payload with. Can be - individual JWK or JWK set. - - Returns: - bytes: The plaintext bytes, assuming the authentication tag is valid. - - Raises: - JWEError: If there is an exception verifying the token. - - Examples: - >>> from jose import jwe - >>> jwe.decrypt(jwe_string, 'asecret128bitkey') - 'Hello, World!' - """ - header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str) - - # Verify that the implementation understands and can process all - # fields that it is required to support, whether required by this - # specification, by the algorithms being used, or by the "crit" - # Header Parameter value, and that the values of those parameters - # are also understood and supported. - - try: - # Determine the Key Management Mode employed by the algorithm - # specified by the "alg" (algorithm) Header Parameter. - alg = header["alg"] - enc = header["enc"] - if alg not in ALGORITHMS.SUPPORTED: - raise JWEError("Algorithm %s not supported." % alg) - if enc not in ALGORITHMS.SUPPORTED: - raise JWEError("Algorithm %s not supported." % enc) - - except KeyError: - raise JWEParseError("alg and enc headers are required!") - - # Verify that the JWE uses a key known to the recipient. - key = jwk.construct(key, alg) - - # When Direct Key Agreement or Key Agreement with Key Wrapping are - # employed, use the key agreement algorithm to compute the value - # of the agreed upon key. When Direct Key Agreement is employed, - # let the CEK be the agreed upon key. When Key Agreement with Key - # Wrapping is employed, the agreed upon key will be used to - # decrypt the JWE Encrypted Key. - # - # When Key Wrapping, Key Encryption, or Key Agreement with Key - # Wrapping are employed, decrypt the JWE Encrypted Key to produce - # the CEK. The CEK MUST have a length equal to that required for - # the content encryption algorithm. Note that when there are - # multiple recipients, each recipient will only be able to decrypt - # JWE Encrypted Key values that were encrypted to a key in that - # recipient's possession. It is therefore normal to only be able - # to decrypt one of the per-recipient JWE Encrypted Key values to - # obtain the CEK value. Also, see Section 11.5 for security - # considerations on mitigating timing attacks. - if alg == ALGORITHMS.DIR: - # When Direct Key Agreement or Direct Encryption are employed, - # verify that the JWE Encrypted Key value is an empty octet - # sequence. - - # Record whether the CEK could be successfully determined for this - # recipient or not. - cek_valid = encrypted_key == b"" - - # When Direct Encryption is employed, let the CEK be the shared - # symmetric key. - cek_bytes = _get_key_bytes_from_key(key) - else: - try: - cek_bytes = key.unwrap_key(encrypted_key) - - # Record whether the CEK could be successfully determined for this - # recipient or not. - cek_valid = True - except NotImplementedError: - raise JWEError(f"alg {alg} is not implemented") - except Exception: - # Record whether the CEK could be successfully determined for this - # recipient or not. - cek_valid = False - - # To mitigate the attacks described in RFC 3218 [RFC3218], the - # recipient MUST NOT distinguish between format, padding, and length - # errors of encrypted keys. It is strongly recommended, in the event - # of receiving an improperly formatted key, that the recipient - # substitute a randomly generated CEK and proceed to the next step, to - # mitigate timing attacks. - cek_bytes = _get_random_cek_bytes_for_enc(enc) - - # Compute the Encoded Protected Header value BASE64URL(UTF8(JWE - # Protected Header)). If the JWE Protected Header is not present - # (which can only happen when using the JWE JSON Serialization and - # no "protected" member is present), let this value be the empty - # string. - protected_header = encoded_header - - # Let the Additional Authenticated Data encryption parameter be - # ASCII(Encoded Protected Header). However, if a JWE AAD value is - # present (which can only be the case when using the JWE JSON - # Serialization), instead let the Additional Authenticated Data - # encryption parameter be ASCII(Encoded Protected Header || '.' || - # BASE64URL(JWE AAD)). - aad = protected_header - - # Decrypt the JWE Ciphertext using the CEK, the JWE Initialization - # Vector, the Additional Authenticated Data value, and the JWE - # Authentication Tag (which is the Authentication Tag input to the - # calculation) using the specified content encryption algorithm, - # returning the decrypted plaintext and validating the JWE - # Authentication Tag in the manner specified for the algorithm, - # rejecting the input without emitting any decrypted output if the - # JWE Authentication Tag is incorrect. - try: - plain_text = _decrypt_and_auth(cek_bytes, enc, cipher_text, iv, aad, auth_tag) - except NotImplementedError: - raise JWEError(f"enc {enc} is not implemented") - except Exception as e: - raise JWEError(e) - - # If a "zip" parameter was included, uncompress the decrypted - # plaintext using the specified compression algorithm. - if plain_text is not None: - plain_text = _decompress(header.get("zip"), plain_text) - - return plain_text if cek_valid else None - - -def get_unverified_header(jwe_str): - """Returns the decoded headers without verification of any kind. - - Args: - jwe_str (str): A compact serialized JWE to decode the headers from. - - Returns: - dict: The dict representation of the JWE headers. - - Raises: - JWEError: If there is an exception decoding the JWE. - """ - header = _jwe_compact_deserialize(jwe_str)[0] - return header - - -def _decrypt_and_auth(cek_bytes, enc, cipher_text, iv, aad, auth_tag): - """ - Decrypt and verify the data - - Args: - cek_bytes (bytes): cek to derive encryption and possible auth key to - verify the auth tag - cipher_text (bytes): Encrypted data - iv (bytes): Initialization vector (iv) used to encrypt data - aad (bytes): Additional Authenticated Data used to verify the data - auth_tag (bytes): Authentication ntag to verify the data - - Returns: - (bytes): Decrypted data - """ - # Decrypt the JWE Ciphertext using the CEK, the JWE Initialization - # Vector, the Additional Authenticated Data value, and the JWE - # Authentication Tag (which is the Authentication Tag input to the - # calculation) using the specified content encryption algorithm, - # returning the decrypted plaintext - # and validating the JWE - # Authentication Tag in the manner specified for the algorithm, - if enc in ALGORITHMS.HMAC_AUTH_TAG: - encryption_key, mac_key, key_len = _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc) - auth_tag_check = _auth_tag(cipher_text, iv, aad, mac_key, key_len) - elif enc in ALGORITHMS.GCM: - encryption_key = jwk.construct(cek_bytes, enc) - auth_tag_check = auth_tag # GCM check auth on decrypt - else: - raise NotImplementedError(f"enc {enc} is not implemented!") - - plaintext = encryption_key.decrypt(cipher_text, iv, aad, auth_tag) - if auth_tag != auth_tag_check: - raise JWEError("Invalid JWE Auth Tag") - - return plaintext - - -def _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc): - derived_key_len = len(cek_bytes) // 2 - mac_key_bytes = cek_bytes[0:derived_key_len] - mac_key = _get_hmac_key(enc, mac_key_bytes) - encryption_key_bytes = cek_bytes[-derived_key_len:] - encryption_alg, _ = enc.split("-") - encryption_key = jwk.construct(encryption_key_bytes, encryption_alg) - return encryption_key, mac_key, derived_key_len - - -def _jwe_compact_deserialize(jwe_bytes): - """ - Deserialize and verify the header and segments are appropriate. - - Args: - jwe_bytes (bytes): The compact serialized JWE - Returns: - (dict, bytes, bytes, bytes, bytes, bytes) - """ - - # Base64url decode the encoded representations of the JWE - # Protected Header, the JWE Encrypted Key, the JWE Initialization - # Vector, the JWE Ciphertext, the JWE Authentication Tag, and the - # JWE AAD, following the restriction that no line breaks, - # whitespace, or other additional characters have been used. - jwe_bytes = ensure_binary(jwe_bytes) - try: - header_segment, encrypted_key_segment, iv_segment, cipher_text_segment, auth_tag_segment = jwe_bytes.split( - b".", 4 - ) - header_data = base64url_decode(header_segment) - except ValueError: - raise JWEParseError("Not enough segments") - except (TypeError, binascii.Error): - raise JWEParseError("Invalid header") - - # Verify that the octet sequence resulting from decoding the - # encoded JWE Protected Header is a UTF-8-encoded representation - # of a completely valid JSON object conforming to RFC 7159 - # [RFC7159]; let the JWE Protected Header be this JSON object. - # - # If using the JWE Compact Serialization, let the JOSE Header be - # the JWE Protected Header. Otherwise, when using the JWE JSON - # Serialization, let the JOSE Header be the union of the members - # of the JWE Protected Header, the JWE Shared Unprotected Header - # and the corresponding JWE Per-Recipient Unprotected Header, all - # of which must be completely valid JSON objects. During this - # step, verify that the resulting JOSE Header does not contain - # duplicate Header Parameter names. When using the JWE JSON - # Serialization, this restriction includes that the same Header - # Parameter name also MUST NOT occur in distinct JSON object - # values that together comprise the JOSE Header. - - try: - header = json.loads(header_data) - except ValueError as e: - raise JWEParseError(f"Invalid header string: {e}") - - if not isinstance(header, Mapping): - raise JWEParseError("Invalid header string: must be a json object") - - try: - encrypted_key = base64url_decode(encrypted_key_segment) - except (TypeError, binascii.Error): - raise JWEParseError("Invalid encrypted key") - - try: - iv = base64url_decode(iv_segment) - except (TypeError, binascii.Error): - raise JWEParseError("Invalid IV") - - try: - ciphertext = base64url_decode(cipher_text_segment) - except (TypeError, binascii.Error): - raise JWEParseError("Invalid cyphertext") - - try: - auth_tag = base64url_decode(auth_tag_segment) - except (TypeError, binascii.Error): - raise JWEParseError("Invalid auth tag") - - return header, header_segment, encrypted_key, iv, ciphertext, auth_tag - - -def _encoded_header(alg, enc, zip, cty, kid): - """ - Generate an appropriate JOSE header based on the values provided - Args: - alg (str): Key wrap/negotiation algorithm - enc (str): Encryption algorithm - zip (str): Compression method - cty (str): Content type of the encrypted data - kid (str): ID for the key used for the operation - - Returns: - bytes: JSON object of header based on input - """ - header = {"alg": alg, "enc": enc} - if zip: - header["zip"] = zip - if cty: - header["cty"] = cty - if kid: - header["kid"] = kid - json_header = json.dumps( - header, - separators=(",", ":"), - sort_keys=True, - ).encode("utf-8") - return base64url_encode(json_header) - - -def _big_endian(int_val): - return pack("!Q", int_val) - - -def _encrypt_and_auth(key, alg, enc, zip, plaintext, aad): - """ - Generate a content encryption key (cek) and initialization - vector (iv) based on enc and alg, compress the plaintext based on zip, - encrypt the compressed plaintext using the cek and iv based on enc - - Args: - key (Key): The key provided for encryption - alg (str): The algorithm use for key wrap/negotiation - enc (str): The encryption algorithm with which to encrypt the plaintext - zip (str): The compression algorithm with which to compress the plaintext - plaintext (bytes): The data to encrypt - aad (str): Additional authentication data utilized for generating an - auth tag - - Returns: - (bytes, bytes, bytes, bytes): A tuple of the following data - (key wrapped cek, iv, cipher text, auth tag) - """ - try: - cek_bytes, kw_cek = _get_cek(enc, alg, key) - except NotImplementedError: - raise JWEError(f"alg {alg} is not implemented") - - if enc in ALGORITHMS.HMAC_AUTH_TAG: - encryption_key, mac_key, key_len = _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc) - iv, ciphertext, tag = encryption_key.encrypt(plaintext, aad) - auth_tag = _auth_tag(ciphertext, iv, aad, mac_key, key_len) - elif enc in ALGORITHMS.GCM: - encryption_key = jwk.construct(cek_bytes, enc) - iv, ciphertext, auth_tag = encryption_key.encrypt(plaintext, aad) - else: - raise NotImplementedError(f"enc {enc} is not implemented!") - - return kw_cek, iv, ciphertext, auth_tag - - -def _get_hmac_key(enc, mac_key_bytes): - """ - Get an HMACKey for the provided encryption algorithm and key bytes - - Args: - enc (str): Encryption algorithm - mac_key_bytes (bytes): vytes for the HMAC key - - Returns: - (HMACKey): The key to perform HMAC actions - """ - _, hash_alg = enc.split("-") - mac_key = jwk.construct(mac_key_bytes, hash_alg) - return mac_key - - -def _compress(zip, plaintext): - """ - Compress the plaintext based on the algorithm supplied - - Args: - zip (str): Compression Algorithm - plaintext (bytes): plaintext to compress - - Returns: - (bytes): Compressed plaintext - """ - if zip not in ZIPS.SUPPORTED: - raise NotImplementedError("ZIP {} is not supported!") - if zip is None: - compressed = plaintext - elif zip == ZIPS.DEF: - compressed = zlib.compress(plaintext) - else: - raise NotImplementedError("ZIP {} is not implemented!") - return compressed - - -def _decompress(zip, compressed): - """ - Decompress the plaintext based on the algorithm supplied - - Args: - zip (str): Compression Algorithm - plaintext (bytes): plaintext to decompress - - Returns: - (bytes): Compressed plaintext - """ - if zip not in ZIPS.SUPPORTED: - raise NotImplementedError("ZIP {} is not supported!") - if zip is None: - decompressed = compressed - elif zip == ZIPS.DEF: - decompressed = zlib.decompress(compressed) - else: - raise NotImplementedError("ZIP {} is not implemented!") - return decompressed - - -def _get_cek(enc, alg, key): - """ - Get the content encryption key - - Args: - enc (str): Encryption algorithm - alg (str): kwy wrap/negotiation algorithm - key (Key): Key provided to encryption method - - Return: - (bytes, bytes): Tuple of (cek bytes and wrapped cek) - """ - if alg == ALGORITHMS.DIR: - cek, wrapped_cek = _get_direct_key_wrap_cek(key) - else: - cek, wrapped_cek = _get_key_wrap_cek(enc, key) - - return cek, wrapped_cek - - -def _get_direct_key_wrap_cek(key): - """ - Get the cek and wrapped cek from the encryption key direct - - Args: - key (Key): Key provided to encryption method - - Return: - (Key, bytes): Tuple of (cek Key object and wrapped cek) - """ - # Get the JWK data to determine how to derive the cek - jwk_data = key.to_dict() - if jwk_data["kty"] == "oct": - # Get the last half of an octal key as the cek - cek_bytes = _get_key_bytes_from_key(key) - wrapped_cek = b"" - else: - raise NotImplementedError("JWK type {} not supported!".format(jwk_data["kty"])) - return cek_bytes, wrapped_cek - - -def _get_key_bytes_from_key(key): - """ - Get the raw key bytes from a Key object - - Args: - key (Key): Key from which to extract the raw key bytes - Returns: - (bytes) key data - """ - jwk_data = key.to_dict() - encoded_key = jwk_data["k"] - cek_bytes = base64url_decode(encoded_key) - return cek_bytes - - -def _get_key_wrap_cek(enc, key): - """_get_rsa_key_wrap_cek - Get the content encryption key for RSA key wrap - - Args: - enc (str): Encryption algorithm - key (Key): Key provided to encryption method - - Returns: - (Key, bytes): Tuple of (cek Key object and wrapped cek) - """ - cek_bytes = _get_random_cek_bytes_for_enc(enc) - wrapped_cek = key.wrap_key(cek_bytes) - return cek_bytes, wrapped_cek - - -def _get_random_cek_bytes_for_enc(enc): - """ - Get the random cek bytes based on the encryptionn algorithm - - Args: - enc (str): Encryption algorithm - - Returns: - (bytes) random bytes for cek key - """ - if enc == ALGORITHMS.A128GCM: - num_bits = 128 - elif enc == ALGORITHMS.A192GCM: - num_bits = 192 - elif enc in (ALGORITHMS.A128CBC_HS256, ALGORITHMS.A256GCM): - num_bits = 256 - elif enc == ALGORITHMS.A192CBC_HS384: - num_bits = 384 - elif enc == ALGORITHMS.A256CBC_HS512: - num_bits = 512 - else: - raise NotImplementedError(f"{enc} not supported") - cek_bytes = get_random_bytes(num_bits // 8) - return cek_bytes - - -def _auth_tag(ciphertext, iv, aad, mac_key, tag_length): - """ - Get ann auth tag from the provided data - - Args: - ciphertext (bytes): Encrypted value - iv (bytes): Initialization vector - aad (bytes): Additional Authenticated Data - mac_key (bytes): Key to use in generating the MAC - tag_length (int): How log the tag should be - - Returns: - (bytes) Auth tag - """ - al = _big_endian(len(aad) * 8) - auth_tag_input = aad + iv + ciphertext + al - signature = mac_key.sign(auth_tag_input) - auth_tag = signature[0:tag_length] - return auth_tag - - -def _jwe_compact_serialize(encoded_header, encrypted_cek, iv, cipher_text, auth_tag): - """ - Generate a compact serialized JWE - - Args: - encoded_header (bytes): Base64 URL Encoded JWE header JSON - encrypted_cek (bytes): Encrypted content encryption key (cek) - iv (bytes): Initialization vector (IV) - cipher_text (bytes): Cipher text - auth_tag (bytes): JWE Auth Tag - - Returns: - (str): JWE compact serialized string - """ - cipher_text = ensure_binary(cipher_text) - encoded_encrypted_cek = base64url_encode(encrypted_cek) - encoded_iv = base64url_encode(iv) - encoded_cipher_text = base64url_encode(cipher_text) - encoded_auth_tag = base64url_encode(auth_tag) - return ( - encoded_header - + b"." - + encoded_encrypted_cek - + b"." - + encoded_iv - + b"." - + encoded_cipher_text - + b"." - + encoded_auth_tag - ) diff --git a/.venv/Lib/site-packages/jose/jwk.py b/.venv/Lib/site-packages/jose/jwk.py deleted file mode 100644 index 7afc054..0000000 --- a/.venv/Lib/site-packages/jose/jwk.py +++ /dev/null @@ -1,79 +0,0 @@ -from jose.backends.base import Key -from jose.constants import ALGORITHMS -from jose.exceptions import JWKError - -try: - from jose.backends import RSAKey # noqa: F401 -except ImportError: - pass - -try: - from jose.backends import ECKey # noqa: F401 -except ImportError: - pass - -try: - from jose.backends import AESKey # noqa: F401 -except ImportError: - pass - -try: - from jose.backends import DIRKey # noqa: F401 -except ImportError: - pass - -try: - from jose.backends import HMACKey # noqa: F401 -except ImportError: - pass - - -def get_key(algorithm): - if algorithm in ALGORITHMS.KEYS: - return ALGORITHMS.KEYS[algorithm] - elif algorithm in ALGORITHMS.HMAC: # noqa: F811 - return HMACKey - elif algorithm in ALGORITHMS.RSA: - from jose.backends import RSAKey # noqa: F811 - - return RSAKey - elif algorithm in ALGORITHMS.EC: - from jose.backends import ECKey # noqa: F811 - - return ECKey - elif algorithm in ALGORITHMS.AES: - from jose.backends import AESKey # noqa: F811 - - return AESKey - elif algorithm == ALGORITHMS.DIR: - from jose.backends import DIRKey # noqa: F811 - - return DIRKey - return None - - -def register_key(algorithm, key_class): - if not issubclass(key_class, Key): - raise TypeError("Key class is not a subclass of jwk.Key") - ALGORITHMS.KEYS[algorithm] = key_class - ALGORITHMS.SUPPORTED.add(algorithm) - return True - - -def construct(key_data, algorithm=None): - """ - Construct a Key object for the given algorithm with the given - key_data. - """ - - # Allow for pulling the algorithm off of the passed in jwk. - if not algorithm and isinstance(key_data, dict): - algorithm = key_data.get("alg", None) - - if not algorithm: - raise JWKError("Unable to find an algorithm for key: %s" % key_data) - - key_class = get_key(algorithm) - if not key_class: - raise JWKError("Unable to find an algorithm for key: %s" % key_data) - return key_class(key_data, algorithm) diff --git a/.venv/Lib/site-packages/jose/jws.py b/.venv/Lib/site-packages/jose/jws.py deleted file mode 100644 index bfaf6bd..0000000 --- a/.venv/Lib/site-packages/jose/jws.py +++ /dev/null @@ -1,266 +0,0 @@ -import binascii -import json -from collections.abc import Iterable, Mapping - -from jose import jwk -from jose.backends.base import Key -from jose.constants import ALGORITHMS -from jose.exceptions import JWSError, JWSSignatureError -from jose.utils import base64url_decode, base64url_encode - - -def sign(payload, key, headers=None, algorithm=ALGORITHMS.HS256): - """Signs a claims set and returns a JWS string. - - Args: - payload (str or dict): A string to sign - key (str or dict): The key to use for signing the claim set. Can be - individual JWK or JWK set. - headers (dict, optional): A set of headers that will be added to - the default headers. Any headers that are added as additional - headers will override the default headers. - algorithm (str, optional): The algorithm to use for signing the - the claims. Defaults to HS256. - - Returns: - str: The string representation of the header, claims, and signature. - - Raises: - JWSError: If there is an error signing the token. - - Examples: - - >>> jws.sign({'a': 'b'}, 'secret', algorithm='HS256') - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' - - """ - - if algorithm not in ALGORITHMS.SUPPORTED: - raise JWSError("Algorithm %s not supported." % algorithm) - - encoded_header = _encode_header(algorithm, additional_headers=headers) - encoded_payload = _encode_payload(payload) - signed_output = _sign_header_and_claims(encoded_header, encoded_payload, algorithm, key) - - return signed_output - - -def verify(token, key, algorithms, verify=True): - """Verifies a JWS string's signature. - - Args: - token (str): A signed JWS to be verified. - key (str or dict): A key to attempt to verify the payload with. Can be - individual JWK or JWK set. - algorithms (str or list): Valid algorithms that should be used to verify the JWS. - - Returns: - str: The str representation of the payload, assuming the signature is valid. - - Raises: - JWSError: If there is an exception verifying a token. - - Examples: - - >>> token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' - >>> jws.verify(token, 'secret', algorithms='HS256') - - """ - - header, payload, signing_input, signature = _load(token) - - if verify: - _verify_signature(signing_input, header, signature, key, algorithms) - - return payload - - -def get_unverified_header(token): - """Returns the decoded headers without verification of any kind. - - Args: - token (str): A signed JWS to decode the headers from. - - Returns: - dict: The dict representation of the token headers. - - Raises: - JWSError: If there is an exception decoding the token. - """ - header, claims, signing_input, signature = _load(token) - return header - - -def get_unverified_headers(token): - """Returns the decoded headers without verification of any kind. - - This is simply a wrapper of get_unverified_header() for backwards - compatibility. - - Args: - token (str): A signed JWS to decode the headers from. - - Returns: - dict: The dict representation of the token headers. - - Raises: - JWSError: If there is an exception decoding the token. - """ - return get_unverified_header(token) - - -def get_unverified_claims(token): - """Returns the decoded claims without verification of any kind. - - Args: - token (str): A signed JWS to decode the headers from. - - Returns: - str: The str representation of the token claims. - - Raises: - JWSError: If there is an exception decoding the token. - """ - header, claims, signing_input, signature = _load(token) - return claims - - -def _encode_header(algorithm, additional_headers=None): - header = {"typ": "JWT", "alg": algorithm} - - if additional_headers: - header.update(additional_headers) - - json_header = json.dumps( - header, - separators=(",", ":"), - sort_keys=True, - ).encode("utf-8") - - return base64url_encode(json_header) - - -def _encode_payload(payload): - if isinstance(payload, Mapping): - try: - payload = json.dumps( - payload, - separators=(",", ":"), - ).encode("utf-8") - except ValueError: - pass - - return base64url_encode(payload) - - -def _sign_header_and_claims(encoded_header, encoded_claims, algorithm, key): - signing_input = b".".join([encoded_header, encoded_claims]) - try: - if not isinstance(key, Key): - key = jwk.construct(key, algorithm) - signature = key.sign(signing_input) - except Exception as e: - raise JWSError(e) - - encoded_signature = base64url_encode(signature) - - encoded_string = b".".join([encoded_header, encoded_claims, encoded_signature]) - - return encoded_string.decode("utf-8") - - -def _load(jwt): - if isinstance(jwt, str): - jwt = jwt.encode("utf-8") - try: - signing_input, crypto_segment = jwt.rsplit(b".", 1) - header_segment, claims_segment = signing_input.split(b".", 1) - header_data = base64url_decode(header_segment) - except ValueError: - raise JWSError("Not enough segments") - except (TypeError, binascii.Error): - raise JWSError("Invalid header padding") - - try: - header = json.loads(header_data.decode("utf-8")) - except ValueError as e: - raise JWSError("Invalid header string: %s" % e) - - if not isinstance(header, Mapping): - raise JWSError("Invalid header string: must be a json object") - - try: - payload = base64url_decode(claims_segment) - except (TypeError, binascii.Error): - raise JWSError("Invalid payload padding") - - try: - signature = base64url_decode(crypto_segment) - except (TypeError, binascii.Error): - raise JWSError("Invalid crypto padding") - - return (header, payload, signing_input, signature) - - -def _sig_matches_keys(keys, signing_input, signature, alg): - for key in keys: - if not isinstance(key, Key): - key = jwk.construct(key, alg) - try: - if key.verify(signing_input, signature): - return True - except Exception: - pass - return False - - -def _get_keys(key): - - if isinstance(key, Key): - return (key,) - - try: - key = json.loads(key, parse_int=str, parse_float=str) - except Exception: - pass - - if isinstance(key, Mapping): - if "keys" in key: - # JWK Set per RFC 7517 - return key["keys"] - elif "kty" in key: - # Individual JWK per RFC 7517 - return (key,) - else: - # Some other mapping. Firebase uses just dict of kid, cert pairs - values = key.values() - if values: - return values - return (key,) - - # Iterable but not text or mapping => list- or tuple-like - elif isinstance(key, Iterable) and not (isinstance(key, str) or isinstance(key, bytes)): - return key - - # Scalar value, wrap in tuple. - else: - return (key,) - - -def _verify_signature(signing_input, header, signature, key="", algorithms=None): - - alg = header.get("alg") - if not alg: - raise JWSError("No algorithm was specified in the JWS header.") - - if algorithms is not None and alg not in algorithms: - raise JWSError("The specified alg value is not allowed") - - keys = _get_keys(key) - try: - if not _sig_matches_keys(keys, signing_input, signature, alg): - raise JWSSignatureError() - except JWSSignatureError: - raise JWSError("Signature verification failed.") - except JWSError: - raise JWSError("Invalid or unsupported algorithm: %s" % alg) diff --git a/.venv/Lib/site-packages/jose/jwt.py b/.venv/Lib/site-packages/jose/jwt.py deleted file mode 100644 index 3f2142e..0000000 --- a/.venv/Lib/site-packages/jose/jwt.py +++ /dev/null @@ -1,496 +0,0 @@ -import json -from calendar import timegm -from collections.abc import Mapping -from datetime import datetime, timedelta - -from jose import jws - -from .constants import ALGORITHMS -from .exceptions import ExpiredSignatureError, JWSError, JWTClaimsError, JWTError -from .utils import calculate_at_hash, timedelta_total_seconds - - -def encode(claims, key, algorithm=ALGORITHMS.HS256, headers=None, access_token=None): - """Encodes a claims set and returns a JWT string. - - JWTs are JWS signed objects with a few reserved claims. - - Args: - claims (dict): A claims set to sign - key (str or dict): The key to use for signing the claim set. Can be - individual JWK or JWK set. - algorithm (str, optional): The algorithm to use for signing the - the claims. Defaults to HS256. - headers (dict, optional): A set of headers that will be added to - the default headers. Any headers that are added as additional - headers will override the default headers. - access_token (str, optional): If present, the 'at_hash' claim will - be calculated and added to the claims present in the 'claims' - parameter. - - Returns: - str: The string representation of the header, claims, and signature. - - Raises: - JWTError: If there is an error encoding the claims. - - Examples: - - >>> jwt.encode({'a': 'b'}, 'secret', algorithm='HS256') - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' - - """ - - for time_claim in ["exp", "iat", "nbf"]: - - # Convert datetime to a intDate value in known time-format claims - if isinstance(claims.get(time_claim), datetime): - claims[time_claim] = timegm(claims[time_claim].utctimetuple()) - - if access_token: - claims["at_hash"] = calculate_at_hash(access_token, ALGORITHMS.HASHES[algorithm]) - - return jws.sign(claims, key, headers=headers, algorithm=algorithm) - - -def decode(token, key, algorithms=None, options=None, audience=None, issuer=None, subject=None, access_token=None): - """Verifies a JWT string's signature and validates reserved claims. - - Args: - token (str): A signed JWS to be verified. - key (str or dict): A key to attempt to verify the payload with. Can be - individual JWK or JWK set. - algorithms (str or list): Valid algorithms that should be used to verify the JWS. - audience (str): The intended audience of the token. If the "aud" claim is - included in the claim set, then the audience must be included and must equal - the provided claim. - issuer (str or iterable): Acceptable value(s) for the issuer of the token. - If the "iss" claim is included in the claim set, then the issuer must be - given and the claim in the token must be among the acceptable values. - subject (str): The subject of the token. If the "sub" claim is - included in the claim set, then the subject must be included and must equal - the provided claim. - access_token (str): An access token string. If the "at_hash" claim is included in the - claim set, then the access_token must be included, and it must match - the "at_hash" claim. - options (dict): A dictionary of options for skipping validation steps. - - defaults = { - 'verify_signature': True, - 'verify_aud': True, - 'verify_iat': True, - 'verify_exp': True, - 'verify_nbf': True, - 'verify_iss': True, - 'verify_sub': True, - 'verify_jti': True, - 'verify_at_hash': True, - 'require_aud': False, - 'require_iat': False, - 'require_exp': False, - 'require_nbf': False, - 'require_iss': False, - 'require_sub': False, - 'require_jti': False, - 'require_at_hash': False, - 'leeway': 0, - } - - Returns: - dict: The dict representation of the claims set, assuming the signature is valid - and all requested data validation passes. - - Raises: - JWTError: If the signature is invalid in any way. - ExpiredSignatureError: If the signature has expired. - JWTClaimsError: If any claim is invalid in any way. - - Examples: - - >>> payload = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhIjoiYiJ9.jiMyrsmD8AoHWeQgmxZ5yq8z0lXS67_QGs52AzC8Ru8' - >>> jwt.decode(payload, 'secret', algorithms='HS256') - - """ - - defaults = { - "verify_signature": True, - "verify_aud": True, - "verify_iat": True, - "verify_exp": True, - "verify_nbf": True, - "verify_iss": True, - "verify_sub": True, - "verify_jti": True, - "verify_at_hash": True, - "require_aud": False, - "require_iat": False, - "require_exp": False, - "require_nbf": False, - "require_iss": False, - "require_sub": False, - "require_jti": False, - "require_at_hash": False, - "leeway": 0, - } - - if options: - defaults.update(options) - - verify_signature = defaults.get("verify_signature", True) - - try: - payload = jws.verify(token, key, algorithms, verify=verify_signature) - except JWSError as e: - raise JWTError(e) - - # Needed for at_hash verification - algorithm = jws.get_unverified_header(token)["alg"] - - try: - claims = json.loads(payload.decode("utf-8")) - except ValueError as e: - raise JWTError("Invalid payload string: %s" % e) - - if not isinstance(claims, Mapping): - raise JWTError("Invalid payload string: must be a json object") - - _validate_claims( - claims, - audience=audience, - issuer=issuer, - subject=subject, - algorithm=algorithm, - access_token=access_token, - options=defaults, - ) - - return claims - - -def get_unverified_header(token): - """Returns the decoded headers without verification of any kind. - - Args: - token (str): A signed JWT to decode the headers from. - - Returns: - dict: The dict representation of the token headers. - - Raises: - JWTError: If there is an exception decoding the token. - """ - try: - headers = jws.get_unverified_headers(token) - except Exception: - raise JWTError("Error decoding token headers.") - - return headers - - -def get_unverified_headers(token): - """Returns the decoded headers without verification of any kind. - - This is simply a wrapper of get_unverified_header() for backwards - compatibility. - - Args: - token (str): A signed JWT to decode the headers from. - - Returns: - dict: The dict representation of the token headers. - - Raises: - JWTError: If there is an exception decoding the token. - """ - return get_unverified_header(token) - - -def get_unverified_claims(token): - """Returns the decoded claims without verification of any kind. - - Args: - token (str): A signed JWT to decode the headers from. - - Returns: - dict: The dict representation of the token claims. - - Raises: - JWTError: If there is an exception decoding the token. - """ - try: - claims = jws.get_unverified_claims(token) - except Exception: - raise JWTError("Error decoding token claims.") - - try: - claims = json.loads(claims.decode("utf-8")) - except ValueError as e: - raise JWTError("Invalid claims string: %s" % e) - - if not isinstance(claims, Mapping): - raise JWTError("Invalid claims string: must be a json object") - - return claims - - -def _validate_iat(claims): - """Validates that the 'iat' claim is valid. - - The "iat" (issued at) claim identifies the time at which the JWT was - issued. This claim can be used to determine the age of the JWT. Its - value MUST be a number containing a NumericDate value. Use of this - claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - """ - - if "iat" not in claims: - return - - try: - int(claims["iat"]) - except ValueError: - raise JWTClaimsError("Issued At claim (iat) must be an integer.") - - -def _validate_nbf(claims, leeway=0): - """Validates that the 'nbf' claim is valid. - - The "nbf" (not before) claim identifies the time before which the JWT - MUST NOT be accepted for processing. The processing of the "nbf" - claim requires that the current date/time MUST be after or equal to - the not-before date/time listed in the "nbf" claim. Implementers MAY - provide for some small leeway, usually no more than a few minutes, to - account for clock skew. Its value MUST be a number containing a - NumericDate value. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - leeway (int): The number of seconds of skew that is allowed. - """ - - if "nbf" not in claims: - return - - try: - nbf = int(claims["nbf"]) - except ValueError: - raise JWTClaimsError("Not Before claim (nbf) must be an integer.") - - now = timegm(datetime.utcnow().utctimetuple()) - - if nbf > (now + leeway): - raise JWTClaimsError("The token is not yet valid (nbf)") - - -def _validate_exp(claims, leeway=0): - """Validates that the 'exp' claim is valid. - - The "exp" (expiration time) claim identifies the expiration time on - or after which the JWT MUST NOT be accepted for processing. The - processing of the "exp" claim requires that the current date/time - MUST be before the expiration date/time listed in the "exp" claim. - Implementers MAY provide for some small leeway, usually no more than - a few minutes, to account for clock skew. Its value MUST be a number - containing a NumericDate value. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - leeway (int): The number of seconds of skew that is allowed. - """ - - if "exp" not in claims: - return - - try: - exp = int(claims["exp"]) - except ValueError: - raise JWTClaimsError("Expiration Time claim (exp) must be an integer.") - - now = timegm(datetime.utcnow().utctimetuple()) - - if exp < (now - leeway): - raise ExpiredSignatureError("Signature has expired.") - - -def _validate_aud(claims, audience=None): - """Validates that the 'aud' claim is valid. - - The "aud" (audience) claim identifies the recipients that the JWT is - intended for. Each principal intended to process the JWT MUST - identify itself with a value in the audience claim. If the principal - processing the claim does not identify itself with a value in the - "aud" claim when this claim is present, then the JWT MUST be - rejected. In the general case, the "aud" value is an array of case- - sensitive strings, each containing a StringOrURI value. In the - special case when the JWT has one audience, the "aud" value MAY be a - single case-sensitive string containing a StringOrURI value. The - interpretation of audience values is generally application specific. - Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - audience (str): The audience that is verifying the token. - """ - - if "aud" not in claims: - # if audience: - # raise JWTError('Audience claim expected, but not in claims') - return - - audience_claims = claims["aud"] - if isinstance(audience_claims, str): - audience_claims = [audience_claims] - if not isinstance(audience_claims, list): - raise JWTClaimsError("Invalid claim format in token") - if any(not isinstance(c, str) for c in audience_claims): - raise JWTClaimsError("Invalid claim format in token") - if audience not in audience_claims: - raise JWTClaimsError("Invalid audience") - - -def _validate_iss(claims, issuer=None): - """Validates that the 'iss' claim is valid. - - The "iss" (issuer) claim identifies the principal that issued the - JWT. The processing of this claim is generally application specific. - The "iss" value is a case-sensitive string containing a StringOrURI - value. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - issuer (str or iterable): Acceptable value(s) for the issuer that - signed the token. - """ - - if issuer is not None: - if isinstance(issuer, str): - issuer = (issuer,) - if claims.get("iss") not in issuer: - raise JWTClaimsError("Invalid issuer") - - -def _validate_sub(claims, subject=None): - """Validates that the 'sub' claim is valid. - - The "sub" (subject) claim identifies the principal that is the - subject of the JWT. The claims in a JWT are normally statements - about the subject. The subject value MUST either be scoped to be - locally unique in the context of the issuer or be globally unique. - The processing of this claim is generally application specific. The - "sub" value is a case-sensitive string containing a StringOrURI - value. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - subject (str): The subject of the token. - """ - - if "sub" not in claims: - return - - if not isinstance(claims["sub"], str): - raise JWTClaimsError("Subject must be a string.") - - if subject is not None: - if claims.get("sub") != subject: - raise JWTClaimsError("Invalid subject") - - -def _validate_jti(claims): - """Validates that the 'jti' claim is valid. - - The "jti" (JWT ID) claim provides a unique identifier for the JWT. - The identifier value MUST be assigned in a manner that ensures that - there is a negligible probability that the same value will be - accidentally assigned to a different data object; if the application - uses multiple issuers, collisions MUST be prevented among values - produced by different issuers as well. The "jti" claim can be used - to prevent the JWT from being replayed. The "jti" value is a case- - sensitive string. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - """ - if "jti" not in claims: - return - - if not isinstance(claims["jti"], str): - raise JWTClaimsError("JWT ID must be a string.") - - -def _validate_at_hash(claims, access_token, algorithm): - """ - Validates that the 'at_hash' is valid. - - Its value is the base64url encoding of the left-most half of the hash - of the octets of the ASCII representation of the access_token value, - where the hash algorithm used is the hash algorithm used in the alg - Header Parameter of the ID Token's JOSE Header. For instance, if the - alg is RS256, hash the access_token value with SHA-256, then take the - left-most 128 bits and base64url encode them. The at_hash value is a - case sensitive string. Use of this claim is OPTIONAL. - - Args: - claims (dict): The claims dictionary to validate. - access_token (str): The access token returned by the OpenID Provider. - algorithm (str): The algorithm used to sign the JWT, as specified by - the token headers. - """ - if "at_hash" not in claims: - return - - if not access_token: - msg = "No access_token provided to compare against at_hash claim." - raise JWTClaimsError(msg) - - try: - expected_hash = calculate_at_hash(access_token, ALGORITHMS.HASHES[algorithm]) - except (TypeError, ValueError): - msg = "Unable to calculate at_hash to verify against token claims." - raise JWTClaimsError(msg) - - if claims["at_hash"] != expected_hash: - raise JWTClaimsError("at_hash claim does not match access_token.") - - -def _validate_claims(claims, audience=None, issuer=None, subject=None, algorithm=None, access_token=None, options=None): - - leeway = options.get("leeway", 0) - - if isinstance(leeway, timedelta): - leeway = timedelta_total_seconds(leeway) - required_claims = [e[len("require_") :] for e in options.keys() if e.startswith("require_") and options[e]] - for require_claim in required_claims: - if require_claim not in claims: - raise JWTError('missing required key "%s" among claims' % require_claim) - else: - options["verify_" + require_claim] = True # override verify when required - - if not isinstance(audience, ((str,), type(None))): - raise JWTError("audience must be a string or None") - - if options.get("verify_iat"): - _validate_iat(claims) - - if options.get("verify_nbf"): - _validate_nbf(claims, leeway=leeway) - - if options.get("verify_exp"): - _validate_exp(claims, leeway=leeway) - - if options.get("verify_aud"): - _validate_aud(claims, audience=audience) - - if options.get("verify_iss"): - _validate_iss(claims, issuer=issuer) - - if options.get("verify_sub"): - _validate_sub(claims, subject=subject) - - if options.get("verify_jti"): - _validate_jti(claims) - - if options.get("verify_at_hash"): - _validate_at_hash(claims, access_token, algorithm) diff --git a/.venv/Lib/site-packages/jose/utils.py b/.venv/Lib/site-packages/jose/utils.py deleted file mode 100644 index fcef885..0000000 --- a/.venv/Lib/site-packages/jose/utils.py +++ /dev/null @@ -1,108 +0,0 @@ -import base64 -import struct - -# Piggyback of the backends implementation of the function that converts a long -# to a bytes stream. Some plumbing is necessary to have the signatures match. -try: - from cryptography.utils import int_to_bytes as _long_to_bytes - - def long_to_bytes(n, blocksize=0): - return _long_to_bytes(n, blocksize or None) - - -except ImportError: - from ecdsa.ecdsa import int_to_string as _long_to_bytes - - def long_to_bytes(n, blocksize=0): - ret = _long_to_bytes(n) - if blocksize == 0: - return ret - else: - assert len(ret) <= blocksize - padding = blocksize - len(ret) - return b"\x00" * padding + ret - - -def long_to_base64(data, size=0): - return base64.urlsafe_b64encode(long_to_bytes(data, size)).strip(b"=") - - -def int_arr_to_long(arr): - return int("".join(["%02x" % byte for byte in arr]), 16) - - -def base64_to_long(data): - if isinstance(data, str): - data = data.encode("ascii") - - # urlsafe_b64decode will happily convert b64encoded data - _d = base64.urlsafe_b64decode(bytes(data) + b"==") - return int_arr_to_long(struct.unpack("%sB" % len(_d), _d)) - - -def calculate_at_hash(access_token, hash_alg): - """Helper method for calculating an access token - hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken - - Its value is the base64url encoding of the left-most half of the hash of the octets - of the ASCII representation of the access_token value, where the hash algorithm - used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE - Header. For instance, if the alg is RS256, hash the access_token value with SHA-256, - then take the left-most 128 bits and base64url encode them. The at_hash value is a - case sensitive string. - - Args: - access_token (str): An access token string. - hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256 - - """ - hash_digest = hash_alg(access_token.encode("utf-8")).digest() - cut_at = int(len(hash_digest) / 2) - truncated = hash_digest[:cut_at] - at_hash = base64url_encode(truncated) - return at_hash.decode("utf-8") - - -def base64url_decode(input): - """Helper method to base64url_decode a string. - - Args: - input (str): A base64url_encoded string to decode. - - """ - rem = len(input) % 4 - - if rem > 0: - input += b"=" * (4 - rem) - - return base64.urlsafe_b64decode(input) - - -def base64url_encode(input): - """Helper method to base64url_encode a string. - - Args: - input (str): A base64url_encoded string to encode. - - """ - return base64.urlsafe_b64encode(input).replace(b"=", b"") - - -def timedelta_total_seconds(delta): - """Helper method to determine the total number of seconds - from a timedelta. - - Args: - delta (timedelta): A timedelta to convert to seconds. - """ - return delta.days * 24 * 60 * 60 + delta.seconds - - -def ensure_binary(s): - """Coerce **s** to bytes.""" - - if isinstance(s, bytes): - return s - if isinstance(s, str): - return s.encode("utf-8", "strict") - raise TypeError(f"not expecting type '{type(s)}'") diff --git a/.venv/Lib/site-packages/multipart/__init__.py b/.venv/Lib/site-packages/multipart/__init__.py deleted file mode 100644 index dc13f13..0000000 --- a/.venv/Lib/site-packages/multipart/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# This is the canonical package information. -__author__ = "Andrew Dunham" -__license__ = "Apache" -__copyright__ = "Copyright (c) 2012-2013, Andrew Dunham" -__version__ = "0.0.9" - -from .multipart import FormParser, MultipartParser, OctetStreamParser, QuerystringParser, create_form_parser, parse_form - -__all__ = ( - "FormParser", - "MultipartParser", - "OctetStreamParser", - "QuerystringParser", - "create_form_parser", - "parse_form", -) diff --git a/.venv/Lib/site-packages/multipart/decoders.py b/.venv/Lib/site-packages/multipart/decoders.py deleted file mode 100644 index 417650c..0000000 --- a/.venv/Lib/site-packages/multipart/decoders.py +++ /dev/null @@ -1,171 +0,0 @@ -import base64 -import binascii - -from .exceptions import DecodeError - - -class Base64Decoder: - """This object provides an interface to decode a stream of Base64 data. It - is instantiated with an "underlying object", and whenever a write() - operation is performed, it will decode the incoming data as Base64, and - call write() on the underlying object. This is primarily used for decoding - form data encoded as Base64, but can be used for other purposes:: - - from multipart.decoders import Base64Decoder - fd = open("notb64.txt", "wb") - decoder = Base64Decoder(fd) - try: - decoder.write("Zm9vYmFy") # "foobar" in Base64 - decoder.finalize() - finally: - decoder.close() - - # The contents of "notb64.txt" should be "foobar". - - This object will also pass all finalize() and close() calls to the - underlying object, if the underlying object supports them. - - Note that this class maintains a cache of base64 chunks, so that a write of - arbitrary size can be performed. You must call :meth:`finalize` on this - object after all writes are completed to ensure that all data is flushed - to the underlying object. - - :param underlying: the underlying object to pass writes to - """ - - def __init__(self, underlying): - self.cache = bytearray() - self.underlying = underlying - - def write(self, data): - """Takes any input data provided, decodes it as base64, and passes it - on to the underlying object. If the data provided is invalid base64 - data, then this method will raise - a :class:`multipart.exceptions.DecodeError` - - :param data: base64 data to decode - """ - - # Prepend any cache info to our data. - if len(self.cache) > 0: - data = self.cache + data - - # Slice off a string that's a multiple of 4. - decode_len = (len(data) // 4) * 4 - val = data[:decode_len] - - # Decode and write, if we have any. - if len(val) > 0: - try: - decoded = base64.b64decode(val) - except binascii.Error: - raise DecodeError("There was an error raised while decoding base64-encoded data.") - - self.underlying.write(decoded) - - # Get the remaining bytes and save in our cache. - remaining_len = len(data) % 4 - if remaining_len > 0: - self.cache = data[-remaining_len:] - else: - self.cache = b"" - - # Return the length of the data to indicate no error. - return len(data) - - def close(self): - """Close this decoder. If the underlying object has a `close()` - method, this function will call it. - """ - if hasattr(self.underlying, "close"): - self.underlying.close() - - def finalize(self): - """Finalize this object. This should be called when no more data - should be written to the stream. This function can raise a - :class:`multipart.exceptions.DecodeError` if there is some remaining - data in the cache. - - If the underlying object has a `finalize()` method, this function will - call it. - """ - if len(self.cache) > 0: - raise DecodeError( - "There are %d bytes remaining in the Base64Decoder cache when finalize() is called" % len(self.cache) - ) - - if hasattr(self.underlying, "finalize"): - self.underlying.finalize() - - def __repr__(self): - return f"{self.__class__.__name__}(underlying={self.underlying!r})" - - -class QuotedPrintableDecoder: - """This object provides an interface to decode a stream of quoted-printable - data. It is instantiated with an "underlying object", in the same manner - as the :class:`multipart.decoders.Base64Decoder` class. This class behaves - in exactly the same way, including maintaining a cache of quoted-printable - chunks. - - :param underlying: the underlying object to pass writes to - """ - - def __init__(self, underlying): - self.cache = b"" - self.underlying = underlying - - def write(self, data): - """Takes any input data provided, decodes it as quoted-printable, and - passes it on to the underlying object. - - :param data: quoted-printable data to decode - """ - # Prepend any cache info to our data. - if len(self.cache) > 0: - data = self.cache + data - - # If the last 2 characters have an '=' sign in it, then we won't be - # able to decode the encoded value and we'll need to save it for the - # next decoding step. - if data[-2:].find(b"=") != -1: - enc, rest = data[:-2], data[-2:] - else: - enc = data - rest = b"" - - # Encode and write, if we have data. - if len(enc) > 0: - self.underlying.write(binascii.a2b_qp(enc)) - - # Save remaining in cache. - self.cache = rest - return len(data) - - def close(self): - """Close this decoder. If the underlying object has a `close()` - method, this function will call it. - """ - if hasattr(self.underlying, "close"): - self.underlying.close() - - def finalize(self): - """Finalize this object. This should be called when no more data - should be written to the stream. This function will not raise any - exceptions, but it may write more data to the underlying object if - there is data remaining in the cache. - - If the underlying object has a `finalize()` method, this function will - call it. - """ - # If we have a cache, write and then remove it. - if len(self.cache) > 0: - self.underlying.write(binascii.a2b_qp(self.cache)) - self.cache = b"" - - # Finalize our underlying stream. - if hasattr(self.underlying, "finalize"): - self.underlying.finalize() - - def __repr__(self): - return f"{self.__class__.__name__}(underlying={self.underlying!r})" diff --git a/.venv/Lib/site-packages/multipart/exceptions.py b/.venv/Lib/site-packages/multipart/exceptions.py deleted file mode 100644 index cc3671f..0000000 --- a/.venv/Lib/site-packages/multipart/exceptions.py +++ /dev/null @@ -1,34 +0,0 @@ -class FormParserError(ValueError): - """Base error class for our form parser.""" - - -class ParseError(FormParserError): - """This exception (or a subclass) is raised when there is an error while - parsing something. - """ - - #: This is the offset in the input data chunk (*NOT* the overall stream) in - #: which the parse error occurred. It will be -1 if not specified. - offset = -1 - - -class MultipartParseError(ParseError): - """This is a specific error that is raised when the MultipartParser detects - an error while parsing. - """ - - -class QuerystringParseError(ParseError): - """This is a specific error that is raised when the QuerystringParser - detects an error while parsing. - """ - - -class DecodeError(ParseError): - """This exception is raised when there is a decoding error - for example - with the Base64Decoder or QuotedPrintableDecoder. - """ - - -class FileError(FormParserError, OSError): - """Exception class for problems with the File class.""" diff --git a/.venv/Lib/site-packages/multipart/multipart.py b/.venv/Lib/site-packages/multipart/multipart.py deleted file mode 100644 index 221bb71..0000000 --- a/.venv/Lib/site-packages/multipart/multipart.py +++ /dev/null @@ -1,1911 +0,0 @@ -from __future__ import annotations - -import logging -import os -import shutil -import sys -import tempfile -from email.message import Message -from enum import IntEnum -from io import BytesIO -from numbers import Number -from typing import TYPE_CHECKING - -from .decoders import Base64Decoder, QuotedPrintableDecoder -from .exceptions import FileError, FormParserError, MultipartParseError, QuerystringParseError - -if TYPE_CHECKING: # pragma: no cover - from typing import Callable, TypedDict - - class QuerystringCallbacks(TypedDict, total=False): - on_field_start: Callable[[], None] - on_field_name: Callable[[bytes, int, int], None] - on_field_data: Callable[[bytes, int, int], None] - on_field_end: Callable[[], None] - on_end: Callable[[], None] - - class OctetStreamCallbacks(TypedDict, total=False): - on_start: Callable[[], None] - on_data: Callable[[bytes, int, int], None] - on_end: Callable[[], None] - - class MultipartCallbacks(TypedDict, total=False): - on_part_begin: Callable[[], None] - on_part_data: Callable[[bytes, int, int], None] - on_part_end: Callable[[], None] - on_headers_begin: Callable[[], None] - on_header_field: Callable[[bytes, int, int], None] - on_header_value: Callable[[bytes, int, int], None] - on_header_end: Callable[[], None] - on_headers_finished: Callable[[], None] - on_end: Callable[[], None] - - class FormParserConfig(TypedDict, total=False): - UPLOAD_DIR: str | None - UPLOAD_KEEP_FILENAME: bool - UPLOAD_KEEP_EXTENSIONS: bool - UPLOAD_ERROR_ON_BAD_CTE: bool - MAX_MEMORY_FILE_SIZE: int - MAX_BODY_SIZE: float - - class FileConfig(TypedDict, total=False): - UPLOAD_DIR: str | None - UPLOAD_DELETE_TMP: bool - UPLOAD_KEEP_FILENAME: bool - UPLOAD_KEEP_EXTENSIONS: bool - MAX_MEMORY_FILE_SIZE: int - - -# Unique missing object. -_missing = object() - - -class QuerystringState(IntEnum): - """Querystring parser states. - - These are used to keep track of the state of the parser, and are used to determine - what to do when new data is encountered. - """ - - BEFORE_FIELD = 0 - FIELD_NAME = 1 - FIELD_DATA = 2 - - -class MultipartState(IntEnum): - """Multipart parser states. - - These are used to keep track of the state of the parser, and are used to determine - what to do when new data is encountered. - """ - - START = 0 - START_BOUNDARY = 1 - HEADER_FIELD_START = 2 - HEADER_FIELD = 3 - HEADER_VALUE_START = 4 - HEADER_VALUE = 5 - HEADER_VALUE_ALMOST_DONE = 6 - HEADERS_ALMOST_DONE = 7 - PART_DATA_START = 8 - PART_DATA = 9 - PART_DATA_END = 10 - END = 11 - - -# Flags for the multipart parser. -FLAG_PART_BOUNDARY = 1 -FLAG_LAST_BOUNDARY = 2 - -# Get constants. Since iterating over a str on Python 2 gives you a 1-length -# string, but iterating over a bytes object on Python 3 gives you an integer, -# we need to save these constants. -CR = b"\r"[0] -LF = b"\n"[0] -COLON = b":"[0] -SPACE = b" "[0] -HYPHEN = b"-"[0] -AMPERSAND = b"&"[0] -SEMICOLON = b";"[0] -LOWER_A = b"a"[0] -LOWER_Z = b"z"[0] -NULL = b"\x00"[0] - - -# Lower-casing a character is different, because of the difference between -# str on Py2, and bytes on Py3. Same with getting the ordinal value of a byte, -# and joining a list of bytes together. -# These functions abstract that. -def lower_char(c): - return c | 0x20 - - -def ord_char(c): - return c - - -def join_bytes(b): - return bytes(list(b)) - - -def parse_options_header(value: str | bytes) -> tuple[bytes, dict[bytes, bytes]]: - """ - Parses a Content-Type header into a value in the following format: - (content_type, {parameters}) - """ - # Uses email.message.Message to parse the header as described in PEP 594. - # Ref: https://peps.python.org/pep-0594/#cgi - if not value: - return (b"", {}) - - # If we are passed bytes, we assume that it conforms to WSGI, encoding in latin-1. - if isinstance(value, bytes): # pragma: no cover - value = value.decode("latin-1") - - # For types - assert isinstance(value, str), "Value should be a string by now" - - # If we have no options, return the string as-is. - if ";" not in value: - return (value.lower().strip().encode("latin-1"), {}) - - # Split at the first semicolon, to get our value and then options. - # ctype, rest = value.split(b';', 1) - message = Message() - message["content-type"] = value - params = message.get_params() - # If there were no parameters, this would have already returned above - assert params, "At least the content type value should be present" - ctype = params.pop(0)[0].encode("latin-1") - options = {} - for param in params: - key, value = param - # If the value returned from get_params() is a 3-tuple, the last - # element corresponds to the value. - # See: https://docs.python.org/3/library/email.compat32-message.html - if isinstance(value, tuple): - value = value[-1] - # If the value is a filename, we need to fix a bug on IE6 that sends - # the full file path instead of the filename. - if key == "filename": - if value[1:3] == ":\\" or value[:2] == "\\\\": - value = value.split("\\")[-1] - options[key.encode("latin-1")] = value.encode("latin-1") - return ctype, options - - -class Field: - """A Field object represents a (parsed) form field. It represents a single - field with a corresponding name and value. - - The name that a :class:`Field` will be instantiated with is the same name - that would be found in the following HTML:: - - - - This class defines two methods, :meth:`on_data` and :meth:`on_end`, that - will be called when data is written to the Field, and when the Field is - finalized, respectively. - - :param name: the name of the form field - """ - - def __init__(self, name: str): - self._name = name - self._value: list[bytes] = [] - - # We cache the joined version of _value for speed. - self._cache = _missing - - @classmethod - def from_value(cls, name: str, value: bytes | None) -> Field: - """Create an instance of a :class:`Field`, and set the corresponding - value - either None or an actual value. This method will also - finalize the Field itself. - - :param name: the name of the form field - :param value: the value of the form field - either a bytestring or - None - """ - - f = cls(name) - if value is None: - f.set_none() - else: - f.write(value) - f.finalize() - return f - - def write(self, data: bytes) -> int: - """Write some data into the form field. - - :param data: a bytestring - """ - return self.on_data(data) - - def on_data(self, data: bytes) -> int: - """This method is a callback that will be called whenever data is - written to the Field. - - :param data: a bytestring - """ - self._value.append(data) - self._cache = _missing - return len(data) - - def on_end(self) -> None: - """This method is called whenever the Field is finalized.""" - if self._cache is _missing: - self._cache = b"".join(self._value) - - def finalize(self) -> None: - """Finalize the form field.""" - self.on_end() - - def close(self) -> None: - """Close the Field object. This will free any underlying cache.""" - # Free our value array. - if self._cache is _missing: - self._cache = b"".join(self._value) - - del self._value - - def set_none(self) -> None: - """Some fields in a querystring can possibly have a value of None - for - example, the string "foo&bar=&baz=asdf" will have a field with the - name "foo" and value None, one with name "bar" and value "", and one - with name "baz" and value "asdf". Since the write() interface doesn't - support writing None, this function will set the field value to None. - """ - self._cache = None - - @property - def field_name(self) -> str: - """This property returns the name of the field.""" - return self._name - - @property - def value(self): - """This property returns the value of the form field.""" - if self._cache is _missing: - self._cache = b"".join(self._value) - - return self._cache - - def __eq__(self, other: object) -> bool: - if isinstance(other, Field): - return self.field_name == other.field_name and self.value == other.value - else: - return NotImplemented - - def __repr__(self) -> str: - if len(self.value) > 97: - # We get the repr, and then insert three dots before the final - # quote. - v = repr(self.value[:97])[:-1] + "...'" - else: - v = repr(self.value) - - return "{}(field_name={!r}, value={})".format(self.__class__.__name__, self.field_name, v) - - -class File: - """This class represents an uploaded file. It handles writing file data to - either an in-memory file or a temporary file on-disk, if the optional - threshold is passed. - - There are some options that can be passed to the File to change behavior - of the class. Valid options are as follows: - - .. list-table:: - :widths: 15 5 5 30 - :header-rows: 1 - - * - Name - - Type - - Default - - Description - * - UPLOAD_DIR - - `str` - - None - - The directory to store uploaded files in. If this is None, a - temporary file will be created in the system's standard location. - * - UPLOAD_DELETE_TMP - - `bool` - - True - - Delete automatically created TMP file - * - UPLOAD_KEEP_FILENAME - - `bool` - - False - - Whether or not to keep the filename of the uploaded file. If True, - then the filename will be converted to a safe representation (e.g. - by removing any invalid path segments), and then saved with the - same name). Otherwise, a temporary name will be used. - * - UPLOAD_KEEP_EXTENSIONS - - `bool` - - False - - Whether or not to keep the uploaded file's extension. If False, the - file will be saved with the default temporary extension (usually - ".tmp"). Otherwise, the file's extension will be maintained. Note - that this will properly combine with the UPLOAD_KEEP_FILENAME - setting. - * - MAX_MEMORY_FILE_SIZE - - `int` - - 1 MiB - - The maximum number of bytes of a File to keep in memory. By - default, the contents of a File are kept into memory until a certain - limit is reached, after which the contents of the File are written - to a temporary file. This behavior can be disabled by setting this - value to an appropriately large value (or, for example, infinity, - such as `float('inf')`. - - :param file_name: The name of the file that this :class:`File` represents - - :param field_name: The field name that uploaded this file. Note that this - can be None, if, for example, the file was uploaded - with Content-Type application/octet-stream - - :param config: The configuration for this File. See above for valid - configuration keys and their corresponding values. - """ - - def __init__(self, file_name: bytes | None, field_name: bytes | None = None, config: FileConfig = {}): - # Save configuration, set other variables default. - self.logger = logging.getLogger(__name__) - self._config = config - self._in_memory = True - self._bytes_written = 0 - self._fileobj = BytesIO() - - # Save the provided field/file name. - self._field_name = field_name - self._file_name = file_name - - # Our actual file name is None by default, since, depending on our - # config, we may not actually use the provided name. - self._actual_file_name = None - - # Split the extension from the filename. - if file_name is not None: - base, ext = os.path.splitext(file_name) - self._file_base = base - self._ext = ext - - @property - def field_name(self) -> bytes | None: - """The form field associated with this file. May be None if there isn't - one, for example when we have an application/octet-stream upload. - """ - return self._field_name - - @property - def file_name(self) -> bytes | None: - """The file name given in the upload request.""" - return self._file_name - - @property - def actual_file_name(self): - """The file name that this file is saved as. Will be None if it's not - currently saved on disk. - """ - return self._actual_file_name - - @property - def file_object(self): - """The file object that we're currently writing to. Note that this - will either be an instance of a :class:`io.BytesIO`, or a regular file - object. - """ - return self._fileobj - - @property - def size(self): - """The total size of this file, counted as the number of bytes that - currently have been written to the file. - """ - return self._bytes_written - - @property - def in_memory(self) -> bool: - """A boolean representing whether or not this file object is currently - stored in-memory or on-disk. - """ - return self._in_memory - - def flush_to_disk(self) -> None: - """If the file is already on-disk, do nothing. Otherwise, copy from - the in-memory buffer to a disk file, and then reassign our internal - file object to this new disk file. - - Note that if you attempt to flush a file that is already on-disk, a - warning will be logged to this module's logger. - """ - if not self._in_memory: - self.logger.warning("Trying to flush to disk when we're not in memory") - return - - # Go back to the start of our file. - self._fileobj.seek(0) - - # Open a new file. - new_file = self._get_disk_file() - - # Copy the file objects. - shutil.copyfileobj(self._fileobj, new_file) - - # Seek to the new position in our new file. - new_file.seek(self._bytes_written) - - # Reassign the fileobject. - old_fileobj = self._fileobj - self._fileobj = new_file - - # We're no longer in memory. - self._in_memory = False - - # Close the old file object. - old_fileobj.close() - - def _get_disk_file(self): - """This function is responsible for getting a file object on-disk for us.""" - self.logger.info("Opening a file on disk") - - file_dir = self._config.get("UPLOAD_DIR") - keep_filename = self._config.get("UPLOAD_KEEP_FILENAME", False) - keep_extensions = self._config.get("UPLOAD_KEEP_EXTENSIONS", False) - delete_tmp = self._config.get("UPLOAD_DELETE_TMP", True) - - # If we have a directory and are to keep the filename... - if file_dir is not None and keep_filename: - self.logger.info("Saving with filename in: %r", file_dir) - - # Build our filename. - # TODO: what happens if we don't have a filename? - fname = self._file_base - if keep_extensions: - fname = fname + self._ext - - path = os.path.join(file_dir, fname) - try: - self.logger.info("Opening file: %r", path) - tmp_file = open(path, "w+b") - except OSError: - tmp_file = None - - self.logger.exception("Error opening temporary file") - raise FileError("Error opening temporary file: %r" % path) - else: - # Build options array. - # Note that on Python 3, tempfile doesn't support byte names. We - # encode our paths using the default filesystem encoding. - options = {} - if keep_extensions: - ext = self._ext - if isinstance(ext, bytes): - ext = ext.decode(sys.getfilesystemencoding()) - - options["suffix"] = ext - if file_dir is not None: - d = file_dir - if isinstance(d, bytes): - d = d.decode(sys.getfilesystemencoding()) - - options["dir"] = d - options["delete"] = delete_tmp - - # Create a temporary (named) file with the appropriate settings. - self.logger.info("Creating a temporary file with options: %r", options) - try: - tmp_file = tempfile.NamedTemporaryFile(**options) - except OSError: - self.logger.exception("Error creating named temporary file") - raise FileError("Error creating named temporary file") - - fname = tmp_file.name - - # Encode filename as bytes. - if isinstance(fname, str): - fname = fname.encode(sys.getfilesystemencoding()) - - self._actual_file_name = fname - return tmp_file - - def write(self, data: bytes): - """Write some data to the File. - - :param data: a bytestring - """ - return self.on_data(data) - - def on_data(self, data: bytes): - """This method is a callback that will be called whenever data is - written to the File. - - :param data: a bytestring - """ - pos = self._fileobj.tell() - bwritten = self._fileobj.write(data) - # true file objects write returns None - if bwritten is None: - bwritten = self._fileobj.tell() - pos - - # If the bytes written isn't the same as the length, just return. - if bwritten != len(data): - self.logger.warning("bwritten != len(data) (%d != %d)", bwritten, len(data)) - return bwritten - - # Keep track of how many bytes we've written. - self._bytes_written += bwritten - - # If we're in-memory and are over our limit, we create a file. - if ( - self._in_memory - and self._config.get("MAX_MEMORY_FILE_SIZE") is not None - and (self._bytes_written > self._config.get("MAX_MEMORY_FILE_SIZE")) - ): - self.logger.info("Flushing to disk") - self.flush_to_disk() - - # Return the number of bytes written. - return bwritten - - def on_end(self) -> None: - """This method is called whenever the Field is finalized.""" - # Flush the underlying file object - self._fileobj.flush() - - def finalize(self) -> None: - """Finalize the form file. This will not close the underlying file, - but simply signal that we are finished writing to the File. - """ - self.on_end() - - def close(self) -> None: - """Close the File object. This will actually close the underlying - file object (whether it's a :class:`io.BytesIO` or an actual file - object). - """ - self._fileobj.close() - - def __repr__(self) -> str: - return "{}(file_name={!r}, field_name={!r})".format(self.__class__.__name__, self.file_name, self.field_name) - - -class BaseParser: - """This class is the base class for all parsers. It contains the logic for - calling and adding callbacks. - - A callback can be one of two different forms. "Notification callbacks" are - callbacks that are called when something happens - for example, when a new - part of a multipart message is encountered by the parser. "Data callbacks" - are called when we get some sort of data - for example, part of the body of - a multipart chunk. Notification callbacks are called with no parameters, - whereas data callbacks are called with three, as follows:: - - data_callback(data, start, end) - - The "data" parameter is a bytestring (i.e. "foo" on Python 2, or b"foo" on - Python 3). "start" and "end" are integer indexes into the "data" string - that represent the data of interest. Thus, in a data callback, the slice - `data[start:end]` represents the data that the callback is "interested in". - The callback is not passed a copy of the data, since copying severely hurts - performance. - """ - - def __init__(self): - self.logger = logging.getLogger(__name__) - - def callback(self, name: str, data=None, start=None, end=None): - """This function calls a provided callback with some data. If the - callback is not set, will do nothing. - - :param name: The name of the callback to call (as a string). - - :param data: Data to pass to the callback. If None, then it is - assumed that the callback is a notification callback, - and no parameters are given. - - :param end: An integer that is passed to the data callback. - - :param start: An integer that is passed to the data callback. - """ - name = "on_" + name - func = self.callbacks.get(name) - if func is None: - return - - # Depending on whether we're given a buffer... - if data is not None: - # Don't do anything if we have start == end. - if start is not None and start == end: - return - - self.logger.debug("Calling %s with data[%d:%d]", name, start, end) - func(data, start, end) - else: - self.logger.debug("Calling %s with no data", name) - func() - - def set_callback(self, name: str, new_func): - """Update the function for a callback. Removes from the callbacks dict - if new_func is None. - - :param name: The name of the callback to call (as a string). - - :param new_func: The new function for the callback. If None, then the - callback will be removed (with no error if it does not - exist). - """ - if new_func is None: - self.callbacks.pop("on_" + name, None) - else: - self.callbacks["on_" + name] = new_func - - def close(self): - pass # pragma: no cover - - def finalize(self): - pass # pragma: no cover - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - -class OctetStreamParser(BaseParser): - """This parser parses an octet-stream request body and calls callbacks when - incoming data is received. Callbacks are as follows: - - .. list-table:: - :widths: 15 10 30 - :header-rows: 1 - - * - Callback Name - - Parameters - - Description - * - on_start - - None - - Called when the first data is parsed. - * - on_data - - data, start, end - - Called for each data chunk that is parsed. - * - on_end - - None - - Called when the parser is finished parsing all data. - - :param callbacks: A dictionary of callbacks. See the documentation for - :class:`BaseParser`. - - :param max_size: The maximum size of body to parse. Defaults to infinity - - i.e. unbounded. - """ - - def __init__(self, callbacks: OctetStreamCallbacks = {}, max_size=float("inf")): - super().__init__() - self.callbacks = callbacks - self._started = False - - if not isinstance(max_size, Number) or max_size < 1: - raise ValueError("max_size must be a positive number, not %r" % max_size) - self.max_size = max_size - self._current_size = 0 - - def write(self, data: bytes): - """Write some data to the parser, which will perform size verification, - and then pass the data to the underlying callback. - - :param data: a bytestring - """ - if not self._started: - self.callback("start") - self._started = True - - # Truncate data length. - data_len = len(data) - if (self._current_size + data_len) > self.max_size: - # We truncate the length of data that we are to process. - new_size = int(self.max_size - self._current_size) - self.logger.warning( - "Current size is %d (max %d), so truncating data length from %d to %d", - self._current_size, - self.max_size, - data_len, - new_size, - ) - data_len = new_size - - # Increment size, then callback, in case there's an exception. - self._current_size += data_len - self.callback("data", data, 0, data_len) - return data_len - - def finalize(self) -> None: - """Finalize this parser, which signals to that we are finished parsing, - and sends the on_end callback. - """ - self.callback("end") - - def __repr__(self) -> str: - return "%s()" % self.__class__.__name__ - - -class QuerystringParser(BaseParser): - """This is a streaming querystring parser. It will consume data, and call - the callbacks given when it has data. - - .. list-table:: - :widths: 15 10 30 - :header-rows: 1 - - * - Callback Name - - Parameters - - Description - * - on_field_start - - None - - Called when a new field is encountered. - * - on_field_name - - data, start, end - - Called when a portion of a field's name is encountered. - * - on_field_data - - data, start, end - - Called when a portion of a field's data is encountered. - * - on_field_end - - None - - Called when the end of a field is encountered. - * - on_end - - None - - Called when the parser is finished parsing all data. - - :param callbacks: A dictionary of callbacks. See the documentation for - :class:`BaseParser`. - - :param strict_parsing: Whether or not to parse the body strictly. Defaults - to False. If this is set to True, then the behavior - of the parser changes as the following: if a field - has a value with an equal sign (e.g. "foo=bar", or - "foo="), it is always included. If a field has no - equals sign (e.g. "...&name&..."), it will be - treated as an error if 'strict_parsing' is True, - otherwise included. If an error is encountered, - then a - :class:`multipart.exceptions.QuerystringParseError` - will be raised. - - :param max_size: The maximum size of body to parse. Defaults to infinity - - i.e. unbounded. - """ - - state: QuerystringState - - def __init__(self, callbacks: QuerystringCallbacks = {}, strict_parsing: bool = False, max_size=float("inf")): - super().__init__() - self.state = QuerystringState.BEFORE_FIELD - self._found_sep = False - - self.callbacks = callbacks - - # Max-size stuff - if not isinstance(max_size, Number) or max_size < 1: - raise ValueError("max_size must be a positive number, not %r" % max_size) - self.max_size = max_size - self._current_size = 0 - - # Should parsing be strict? - self.strict_parsing = strict_parsing - - def write(self, data: bytes) -> int: - """Write some data to the parser, which will perform size verification, - parse into either a field name or value, and then pass the - corresponding data to the underlying callback. If an error is - encountered while parsing, a QuerystringParseError will be raised. The - "offset" attribute of the raised exception will be set to the offset in - the input data chunk (NOT the overall stream) that caused the error. - - :param data: a bytestring - """ - # Handle sizing. - data_len = len(data) - if (self._current_size + data_len) > self.max_size: - # We truncate the length of data that we are to process. - new_size = int(self.max_size - self._current_size) - self.logger.warning( - "Current size is %d (max %d), so truncating data length from %d to %d", - self._current_size, - self.max_size, - data_len, - new_size, - ) - data_len = new_size - - l = 0 - try: - l = self._internal_write(data, data_len) - finally: - self._current_size += l - - return l - - def _internal_write(self, data: bytes, length: int) -> int: - state = self.state - strict_parsing = self.strict_parsing - found_sep = self._found_sep - - i = 0 - while i < length: - ch = data[i] - - # Depending on our state... - if state == QuerystringState.BEFORE_FIELD: - # If the 'found_sep' flag is set, we've already encountered - # and skipped a single separator. If so, we check our strict - # parsing flag and decide what to do. Otherwise, we haven't - # yet reached a separator, and thus, if we do, we need to skip - # it as it will be the boundary between fields that's supposed - # to be there. - if ch == AMPERSAND or ch == SEMICOLON: - if found_sep: - # If we're parsing strictly, we disallow blank chunks. - if strict_parsing: - e = QuerystringParseError("Skipping duplicate ampersand/semicolon at %d" % i) - e.offset = i - raise e - else: - self.logger.debug("Skipping duplicate ampersand/semicolon at %d", i) - else: - # This case is when we're skipping the (first) - # separator between fields, so we just set our flag - # and continue on. - found_sep = True - else: - # Emit a field-start event, and go to that state. Also, - # reset the "found_sep" flag, for the next time we get to - # this state. - self.callback("field_start") - i -= 1 - state = QuerystringState.FIELD_NAME - found_sep = False - - elif state == QuerystringState.FIELD_NAME: - # Try and find a separator - we ensure that, if we do, we only - # look for the equal sign before it. - sep_pos = data.find(b"&", i) - if sep_pos == -1: - sep_pos = data.find(b";", i) - - # See if we can find an equals sign in the remaining data. If - # so, we can immediately emit the field name and jump to the - # data state. - if sep_pos != -1: - equals_pos = data.find(b"=", i, sep_pos) - else: - equals_pos = data.find(b"=", i) - - if equals_pos != -1: - # Emit this name. - self.callback("field_name", data, i, equals_pos) - - # Jump i to this position. Note that it will then have 1 - # added to it below, which means the next iteration of this - # loop will inspect the character after the equals sign. - i = equals_pos - state = QuerystringState.FIELD_DATA - else: - # No equals sign found. - if not strict_parsing: - # See also comments in the QuerystringState.FIELD_DATA case below. - # If we found the separator, we emit the name and just - # end - there's no data callback at all (not even with - # a blank value). - if sep_pos != -1: - self.callback("field_name", data, i, sep_pos) - self.callback("field_end") - - i = sep_pos - 1 - state = QuerystringState.BEFORE_FIELD - else: - # Otherwise, no separator in this block, so the - # rest of this chunk must be a name. - self.callback("field_name", data, i, length) - i = length - - else: - # We're parsing strictly. If we find a separator, - # this is an error - we require an equals sign. - if sep_pos != -1: - e = QuerystringParseError( - "When strict_parsing is True, we require an " - "equals sign in all field chunks. Did not " - "find one in the chunk that starts at %d" % (i,) - ) - e.offset = i - raise e - - # No separator in the rest of this chunk, so it's just - # a field name. - self.callback("field_name", data, i, length) - i = length - - elif state == QuerystringState.FIELD_DATA: - # Try finding either an ampersand or a semicolon after this - # position. - sep_pos = data.find(b"&", i) - if sep_pos == -1: - sep_pos = data.find(b";", i) - - # If we found it, callback this bit as data and then go back - # to expecting to find a field. - if sep_pos != -1: - self.callback("field_data", data, i, sep_pos) - self.callback("field_end") - - # Note that we go to the separator, which brings us to the - # "before field" state. This allows us to properly emit - # "field_start" events only when we actually have data for - # a field of some sort. - i = sep_pos - 1 - state = QuerystringState.BEFORE_FIELD - - # Otherwise, emit the rest as data and finish. - else: - self.callback("field_data", data, i, length) - i = length - - else: # pragma: no cover (error case) - msg = "Reached an unknown state %d at %d" % (state, i) - self.logger.warning(msg) - e = QuerystringParseError(msg) - e.offset = i - raise e - - i += 1 - - self.state = state - self._found_sep = found_sep - return len(data) - - def finalize(self) -> None: - """Finalize this parser, which signals to that we are finished parsing, - if we're still in the middle of a field, an on_field_end callback, and - then the on_end callback. - """ - # If we're currently in the middle of a field, we finish it. - if self.state == QuerystringState.FIELD_DATA: - self.callback("field_end") - self.callback("end") - - def __repr__(self) -> str: - return "{}(strict_parsing={!r}, max_size={!r})".format( - self.__class__.__name__, self.strict_parsing, self.max_size - ) - - -class MultipartParser(BaseParser): - """This class is a streaming multipart/form-data parser. - - .. list-table:: - :widths: 15 10 30 - :header-rows: 1 - - * - Callback Name - - Parameters - - Description - * - on_part_begin - - None - - Called when a new part of the multipart message is encountered. - * - on_part_data - - data, start, end - - Called when a portion of a part's data is encountered. - * - on_part_end - - None - - Called when the end of a part is reached. - * - on_header_begin - - None - - Called when we've found a new header in a part of a multipart - message - * - on_header_field - - data, start, end - - Called each time an additional portion of a header is read (i.e. the - part of the header that is before the colon; the "Foo" in - "Foo: Bar"). - * - on_header_value - - data, start, end - - Called when we get data for a header. - * - on_header_end - - None - - Called when the current header is finished - i.e. we've reached the - newline at the end of the header. - * - on_headers_finished - - None - - Called when all headers are finished, and before the part data - starts. - * - on_end - - None - - Called when the parser is finished parsing all data. - - - :param boundary: The multipart boundary. This is required, and must match - what is given in the HTTP request - usually in the - Content-Type header. - - :param callbacks: A dictionary of callbacks. See the documentation for - :class:`BaseParser`. - - :param max_size: The maximum size of body to parse. Defaults to infinity - - i.e. unbounded. - """ - - def __init__(self, boundary: bytes | str, callbacks: MultipartCallbacks = {}, max_size=float("inf")): - # Initialize parser state. - super().__init__() - self.state = MultipartState.START - self.index = self.flags = 0 - - self.callbacks = callbacks - - if not isinstance(max_size, Number) or max_size < 1: - raise ValueError("max_size must be a positive number, not %r" % max_size) - self.max_size = max_size - self._current_size = 0 - - # Setup marks. These are used to track the state of data received. - self.marks = {} - - # TODO: Actually use this rather than the dumb version we currently use - # # Precompute the skip table for the Boyer-Moore-Horspool algorithm. - # skip = [len(boundary) for x in range(256)] - # for i in range(len(boundary) - 1): - # skip[ord_char(boundary[i])] = len(boundary) - i - 1 - # - # # We use a tuple since it's a constant, and marginally faster. - # self.skip = tuple(skip) - - # Save our boundary. - if isinstance(boundary, str): # pragma: no cover - boundary = boundary.encode("latin-1") - self.boundary = b"\r\n--" + boundary - - # Get a set of characters that belong to our boundary. - self.boundary_chars = frozenset(self.boundary) - - # We also create a lookbehind list. - # Note: the +8 is since we can have, at maximum, "\r\n--" + boundary + - # "--\r\n" at the final boundary, and the length of '\r\n--' and - # '--\r\n' is 8 bytes. - self.lookbehind = [NULL for x in range(len(boundary) + 8)] - - def write(self, data: bytes) -> int: - """Write some data to the parser, which will perform size verification, - and then parse the data into the appropriate location (e.g. header, - data, etc.), and pass this on to the underlying callback. If an error - is encountered, a MultipartParseError will be raised. The "offset" - attribute on the raised exception will be set to the offset of the byte - in the input chunk that caused the error. - - :param data: a bytestring - """ - # Handle sizing. - data_len = len(data) - if (self._current_size + data_len) > self.max_size: - # We truncate the length of data that we are to process. - new_size = int(self.max_size - self._current_size) - self.logger.warning( - "Current size is %d (max %d), so truncating data length from %d to %d", - self._current_size, - self.max_size, - data_len, - new_size, - ) - data_len = new_size - - l = 0 - try: - l = self._internal_write(data, data_len) - finally: - self._current_size += l - - return l - - def _internal_write(self, data: bytes, length: int) -> int: - # Get values from locals. - boundary = self.boundary - - # Get our state, flags and index. These are persisted between calls to - # this function. - state = self.state - index = self.index - flags = self.flags - - # Our index defaults to 0. - i = 0 - - # Set a mark. - def set_mark(name): - self.marks[name] = i - - # Remove a mark. - def delete_mark(name, reset=False): - self.marks.pop(name, None) - - # Helper function that makes calling a callback with data easier. The - # 'remaining' parameter will callback from the marked value until the - # end of the buffer, and reset the mark, instead of deleting it. This - # is used at the end of the function to call our callbacks with any - # remaining data in this chunk. - def data_callback(name, remaining=False): - marked_index = self.marks.get(name) - if marked_index is None: - return - - # If we're getting remaining data, we ignore the current i value - # and just call with the remaining data. - if remaining: - self.callback(name, data, marked_index, length) - self.marks[name] = 0 - - # Otherwise, we call it from the mark to the current byte we're - # processing. - else: - self.callback(name, data, marked_index, i) - self.marks.pop(name, None) - - # For each byte... - while i < length: - c = data[i] - - if state == MultipartState.START: - # Skip leading newlines - if c == CR or c == LF: - i += 1 - self.logger.debug("Skipping leading CR/LF at %d", i) - continue - - # index is used as in index into our boundary. Set to 0. - index = 0 - - # Move to the next state, but decrement i so that we re-process - # this character. - state = MultipartState.START_BOUNDARY - i -= 1 - - elif state == MultipartState.START_BOUNDARY: - # Check to ensure that the last 2 characters in our boundary - # are CRLF. - if index == len(boundary) - 2: - if c != CR: - # Error! - msg = "Did not find CR at end of boundary (%d)" % (i,) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - index += 1 - - elif index == len(boundary) - 2 + 1: - if c != LF: - msg = "Did not find LF at end of boundary (%d)" % (i,) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - # The index is now used for indexing into our boundary. - index = 0 - - # Callback for the start of a part. - self.callback("part_begin") - - # Move to the next character and state. - state = MultipartState.HEADER_FIELD_START - - else: - # Check to ensure our boundary matches - if c != boundary[index + 2]: - msg = "Did not find boundary character %r at index " "%d" % (c, index + 2) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - # Increment index into boundary and continue. - index += 1 - - elif state == MultipartState.HEADER_FIELD_START: - # Mark the start of a header field here, reset the index, and - # continue parsing our header field. - index = 0 - - # Set a mark of our header field. - set_mark("header_field") - - # Move to parsing header fields. - state = MultipartState.HEADER_FIELD - i -= 1 - - elif state == MultipartState.HEADER_FIELD: - # If we've reached a CR at the beginning of a header, it means - # that we've reached the second of 2 newlines, and so there are - # no more headers to parse. - if c == CR: - delete_mark("header_field") - state = MultipartState.HEADERS_ALMOST_DONE - i += 1 - continue - - # Increment our index in the header. - index += 1 - - # Do nothing if we encounter a hyphen. - if c == HYPHEN: - pass - - # If we've reached a colon, we're done with this header. - elif c == COLON: - # A 0-length header is an error. - if index == 1: - msg = "Found 0-length header at %d" % (i,) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - # Call our callback with the header field. - data_callback("header_field") - - # Move to parsing the header value. - state = MultipartState.HEADER_VALUE_START - - else: - # Lower-case this character, and ensure that it is in fact - # a valid letter. If not, it's an error. - cl = lower_char(c) - if cl < LOWER_A or cl > LOWER_Z: - msg = "Found non-alphanumeric character %r in " "header at %d" % (c, i) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - elif state == MultipartState.HEADER_VALUE_START: - # Skip leading spaces. - if c == SPACE: - i += 1 - continue - - # Mark the start of the header value. - set_mark("header_value") - - # Move to the header-value state, reprocessing this character. - state = MultipartState.HEADER_VALUE - i -= 1 - - elif state == MultipartState.HEADER_VALUE: - # If we've got a CR, we're nearly done our headers. Otherwise, - # we do nothing and just move past this character. - if c == CR: - data_callback("header_value") - self.callback("header_end") - state = MultipartState.HEADER_VALUE_ALMOST_DONE - - elif state == MultipartState.HEADER_VALUE_ALMOST_DONE: - # The last character should be a LF. If not, it's an error. - if c != LF: - msg = "Did not find LF character at end of header " "(found %r)" % (c,) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - # Move back to the start of another header. Note that if that - # state detects ANOTHER newline, it'll trigger the end of our - # headers. - state = MultipartState.HEADER_FIELD_START - - elif state == MultipartState.HEADERS_ALMOST_DONE: - # We're almost done our headers. This is reached when we parse - # a CR at the beginning of a header, so our next character - # should be a LF, or it's an error. - if c != LF: - msg = f"Did not find LF at end of headers (found {c!r})" - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - self.callback("headers_finished") - state = MultipartState.PART_DATA_START - - elif state == MultipartState.PART_DATA_START: - # Mark the start of our part data. - set_mark("part_data") - - # Start processing part data, including this character. - state = MultipartState.PART_DATA - i -= 1 - - elif state == MultipartState.PART_DATA: - # We're processing our part data right now. During this, we - # need to efficiently search for our boundary, since any data - # on any number of lines can be a part of the current data. - # We use the Boyer-Moore-Horspool algorithm to efficiently - # search through the remainder of the buffer looking for our - # boundary. - - # Save the current value of our index. We use this in case we - # find part of a boundary, but it doesn't match fully. - prev_index = index - - # Set up variables. - boundary_length = len(boundary) - boundary_end = boundary_length - 1 - data_length = length - boundary_chars = self.boundary_chars - - # If our index is 0, we're starting a new part, so start our - # search. - if index == 0: - # Search forward until we either hit the end of our buffer, - # or reach a character that's in our boundary. - i += boundary_end - while i < data_length - 1 and data[i] not in boundary_chars: - i += boundary_length - - # Reset i back the length of our boundary, which is the - # earliest possible location that could be our match (i.e. - # if we've just broken out of our loop since we saw the - # last character in our boundary) - i -= boundary_end - c = data[i] - - # Now, we have a couple of cases here. If our index is before - # the end of the boundary... - if index < boundary_length: - # If the character matches... - if boundary[index] == c: - # If we found a match for our boundary, we send the - # existing data. - if index == 0: - data_callback("part_data") - - # The current character matches, so continue! - index += 1 - else: - index = 0 - - # Our index is equal to the length of our boundary! - elif index == boundary_length: - # First we increment it. - index += 1 - - # Now, if we've reached a newline, we need to set this as - # the potential end of our boundary. - if c == CR: - flags |= FLAG_PART_BOUNDARY - - # Otherwise, if this is a hyphen, we might be at the last - # of all boundaries. - elif c == HYPHEN: - flags |= FLAG_LAST_BOUNDARY - - # Otherwise, we reset our index, since this isn't either a - # newline or a hyphen. - else: - index = 0 - - # Our index is right after the part boundary, which should be - # a LF. - elif index == boundary_length + 1: - # If we're at a part boundary (i.e. we've seen a CR - # character already)... - if flags & FLAG_PART_BOUNDARY: - # We need a LF character next. - if c == LF: - # Unset the part boundary flag. - flags &= ~FLAG_PART_BOUNDARY - - # Callback indicating that we've reached the end of - # a part, and are starting a new one. - self.callback("part_end") - self.callback("part_begin") - - # Move to parsing new headers. - index = 0 - state = MultipartState.HEADER_FIELD_START - i += 1 - continue - - # We didn't find an LF character, so no match. Reset - # our index and clear our flag. - index = 0 - flags &= ~FLAG_PART_BOUNDARY - - # Otherwise, if we're at the last boundary (i.e. we've - # seen a hyphen already)... - elif flags & FLAG_LAST_BOUNDARY: - # We need a second hyphen here. - if c == HYPHEN: - # Callback to end the current part, and then the - # message. - self.callback("part_end") - self.callback("end") - state = MultipartState.END - else: - # No match, so reset index. - index = 0 - - # If we have an index, we need to keep this byte for later, in - # case we can't match the full boundary. - if index > 0: - self.lookbehind[index - 1] = c - - # Otherwise, our index is 0. If the previous index is not, it - # means we reset something, and we need to take the data we - # thought was part of our boundary and send it along as actual - # data. - elif prev_index > 0: - # Callback to write the saved data. - lb_data = join_bytes(self.lookbehind) - self.callback("part_data", lb_data, 0, prev_index) - - # Overwrite our previous index. - prev_index = 0 - - # Re-set our mark for part data. - set_mark("part_data") - - # Re-consider the current character, since this could be - # the start of the boundary itself. - i -= 1 - - elif state == MultipartState.END: - # Do nothing and just consume a byte in the end state. - if c not in (CR, LF): - self.logger.warning("Consuming a byte '0x%x' in the end state", c) - - else: # pragma: no cover (error case) - # We got into a strange state somehow! Just stop processing. - msg = "Reached an unknown state %d at %d" % (state, i) - self.logger.warning(msg) - e = MultipartParseError(msg) - e.offset = i - raise e - - # Move to the next byte. - i += 1 - - # We call our callbacks with any remaining data. Note that we pass - # the 'remaining' flag, which sets the mark back to 0 instead of - # deleting it, if it's found. This is because, if the mark is found - # at this point, we assume that there's data for one of these things - # that has been parsed, but not yet emitted. And, as such, it implies - # that we haven't yet reached the end of this 'thing'. So, by setting - # the mark to 0, we cause any data callbacks that take place in future - # calls to this function to start from the beginning of that buffer. - data_callback("header_field", True) - data_callback("header_value", True) - data_callback("part_data", True) - - # Save values to locals. - self.state = state - self.index = index - self.flags = flags - - # Return our data length to indicate no errors, and that we processed - # all of it. - return length - - def finalize(self) -> None: - """Finalize this parser, which signals to that we are finished parsing. - - Note: It does not currently, but in the future, it will verify that we - are in the final state of the parser (i.e. the end of the multipart - message is well-formed), and, if not, throw an error. - """ - # TODO: verify that we're in the state MultipartState.END, otherwise throw an - # error or otherwise state that we're not finished parsing. - pass - - def __repr__(self): - return f"{self.__class__.__name__}(boundary={self.boundary!r})" - - -class FormParser: - """This class is the all-in-one form parser. Given all the information - necessary to parse a form, it will instantiate the correct parser, create - the proper :class:`Field` and :class:`File` classes to store the data that - is parsed, and call the two given callbacks with each field and file as - they become available. - - :param content_type: The Content-Type of the incoming request. This is - used to select the appropriate parser. - - :param on_field: The callback to call when a field has been parsed and is - ready for usage. See above for parameters. - - :param on_file: The callback to call when a file has been parsed and is - ready for usage. See above for parameters. - - :param on_end: An optional callback to call when all fields and files in a - request has been parsed. Can be None. - - :param boundary: If the request is a multipart/form-data request, this - should be the boundary of the request, as given in the - Content-Type header, as a bytestring. - - :param file_name: If the request is of type application/octet-stream, then - the body of the request will not contain any information - about the uploaded file. In such cases, you can provide - the file name of the uploaded file manually. - - :param FileClass: The class to use for uploaded files. Defaults to - :class:`File`, but you can provide your own class if you - wish to customize behaviour. The class will be - instantiated as FileClass(file_name, field_name), and it - must provide the following functions:: - file_instance.write(data) - file_instance.finalize() - file_instance.close() - - :param FieldClass: The class to use for uploaded fields. Defaults to - :class:`Field`, but you can provide your own class if - you wish to customize behaviour. The class will be - instantiated as FieldClass(field_name), and it must - provide the following functions:: - field_instance.write(data) - field_instance.finalize() - field_instance.close() - - :param config: Configuration to use for this FormParser. The default - values are taken from the DEFAULT_CONFIG value, and then - any keys present in this dictionary will overwrite the - default values. - - """ - - #: This is the default configuration for our form parser. - #: Note: all file sizes should be in bytes. - DEFAULT_CONFIG: FormParserConfig = { - "MAX_BODY_SIZE": float("inf"), - "MAX_MEMORY_FILE_SIZE": 1 * 1024 * 1024, - "UPLOAD_DIR": None, - "UPLOAD_KEEP_FILENAME": False, - "UPLOAD_KEEP_EXTENSIONS": False, - # Error on invalid Content-Transfer-Encoding? - "UPLOAD_ERROR_ON_BAD_CTE": False, - } - - def __init__( - self, - content_type, - on_field, - on_file, - on_end=None, - boundary=None, - file_name=None, - FileClass=File, - FieldClass=Field, - config: FormParserConfig = {}, - ): - self.logger = logging.getLogger(__name__) - - # Save variables. - self.content_type = content_type - self.boundary = boundary - self.bytes_received = 0 - self.parser = None - - # Save callbacks. - self.on_field = on_field - self.on_file = on_file - self.on_end = on_end - - # Save classes. - self.FileClass = File - self.FieldClass = Field - - # Set configuration options. - self.config = self.DEFAULT_CONFIG.copy() - self.config.update(config) - - # Depending on the Content-Type, we instantiate the correct parser. - if content_type == "application/octet-stream": - # Work around the lack of 'nonlocal' in Py2 - class vars: - f = None - - def on_start() -> None: - vars.f = FileClass(file_name, None, config=self.config) - - def on_data(data: bytes, start: int, end: int) -> None: - vars.f.write(data[start:end]) - - def on_end() -> None: - # Finalize the file itself. - vars.f.finalize() - - # Call our callback. - on_file(vars.f) - - # Call the on-end callback. - if self.on_end is not None: - self.on_end() - - # Instantiate an octet-stream parser - parser = OctetStreamParser( - callbacks={"on_start": on_start, "on_data": on_data, "on_end": on_end}, - max_size=self.config["MAX_BODY_SIZE"], - ) - - elif content_type == "application/x-www-form-urlencoded" or content_type == "application/x-url-encoded": - name_buffer: list[bytes] = [] - - class vars: - f = None - - def on_field_start() -> None: - pass - - def on_field_name(data: bytes, start: int, end: int) -> None: - name_buffer.append(data[start:end]) - - def on_field_data(data: bytes, start: int, end: int) -> None: - if vars.f is None: - vars.f = FieldClass(b"".join(name_buffer)) - del name_buffer[:] - vars.f.write(data[start:end]) - - def on_field_end() -> None: - # Finalize and call callback. - if vars.f is None: - # If we get here, it's because there was no field data. - # We create a field, set it to None, and then continue. - vars.f = FieldClass(b"".join(name_buffer)) - del name_buffer[:] - vars.f.set_none() - - vars.f.finalize() - on_field(vars.f) - vars.f = None - - def on_end() -> None: - if self.on_end is not None: - self.on_end() - - # Instantiate parser. - parser = QuerystringParser( - callbacks={ - "on_field_start": on_field_start, - "on_field_name": on_field_name, - "on_field_data": on_field_data, - "on_field_end": on_field_end, - "on_end": on_end, - }, - max_size=self.config["MAX_BODY_SIZE"], - ) - - elif content_type == "multipart/form-data": - if boundary is None: - self.logger.error("No boundary given") - raise FormParserError("No boundary given") - - header_name: list[bytes] = [] - header_value: list[bytes] = [] - headers = {} - - # No 'nonlocal' on Python 2 :-( - class vars: - f = None - writer = None - is_file = False - - def on_part_begin(): - pass - - def on_part_data(data: bytes, start: int, end: int): - bytes_processed = vars.writer.write(data[start:end]) - # TODO: check for error here. - return bytes_processed - - def on_part_end() -> None: - vars.f.finalize() - if vars.is_file: - on_file(vars.f) - else: - on_field(vars.f) - - def on_header_field(data: bytes, start: int, end: int): - header_name.append(data[start:end]) - - def on_header_value(data: bytes, start: int, end: int): - header_value.append(data[start:end]) - - def on_header_end(): - headers[b"".join(header_name)] = b"".join(header_value) - del header_name[:] - del header_value[:] - - def on_headers_finished() -> None: - # Reset the 'is file' flag. - vars.is_file = False - - # Parse the content-disposition header. - # TODO: handle mixed case - content_disp = headers.get(b"Content-Disposition") - disp, options = parse_options_header(content_disp) - - # Get the field and filename. - field_name = options.get(b"name") - file_name = options.get(b"filename") - # TODO: check for errors - - # Create the proper class. - if file_name is None: - vars.f = FieldClass(field_name) - else: - vars.f = FileClass(file_name, field_name, config=self.config) - vars.is_file = True - - # Parse the given Content-Transfer-Encoding to determine what - # we need to do with the incoming data. - # TODO: check that we properly handle 8bit / 7bit encoding. - transfer_encoding = headers.get(b"Content-Transfer-Encoding", b"7bit") - - if transfer_encoding == b"binary" or transfer_encoding == b"8bit" or transfer_encoding == b"7bit": - vars.writer = vars.f - - elif transfer_encoding == b"base64": - vars.writer = Base64Decoder(vars.f) - - elif transfer_encoding == b"quoted-printable": - vars.writer = QuotedPrintableDecoder(vars.f) - - else: - self.logger.warning("Unknown Content-Transfer-Encoding: %r", transfer_encoding) - if self.config["UPLOAD_ERROR_ON_BAD_CTE"]: - raise FormParserError('Unknown Content-Transfer-Encoding "{}"'.format(transfer_encoding)) - else: - # If we aren't erroring, then we just treat this as an - # unencoded Content-Transfer-Encoding. - vars.writer = vars.f - - def on_end() -> None: - vars.writer.finalize() - if self.on_end is not None: - self.on_end() - - # Instantiate a multipart parser. - parser = MultipartParser( - boundary, - callbacks={ - "on_part_begin": on_part_begin, - "on_part_data": on_part_data, - "on_part_end": on_part_end, - "on_header_field": on_header_field, - "on_header_value": on_header_value, - "on_header_end": on_header_end, - "on_headers_finished": on_headers_finished, - "on_end": on_end, - }, - max_size=self.config["MAX_BODY_SIZE"], - ) - - else: - self.logger.warning("Unknown Content-Type: %r", content_type) - raise FormParserError("Unknown Content-Type: {}".format(content_type)) - - self.parser = parser - - def write(self, data: bytes): - """Write some data. The parser will forward this to the appropriate - underlying parser. - - :param data: a bytestring - """ - self.bytes_received += len(data) - # TODO: check the parser's return value for errors? - return self.parser.write(data) - - def finalize(self) -> None: - """Finalize the parser.""" - if self.parser is not None and hasattr(self.parser, "finalize"): - self.parser.finalize() - - def close(self) -> None: - """Close the parser.""" - if self.parser is not None and hasattr(self.parser, "close"): - self.parser.close() - - def __repr__(self) -> str: - return "{}(content_type={!r}, parser={!r})".format(self.__class__.__name__, self.content_type, self.parser) - - -def create_form_parser(headers, on_field, on_file, trust_x_headers=False, config={}): - """This function is a helper function to aid in creating a FormParser - instances. Given a dictionary-like headers object, it will determine - the correct information needed, instantiate a FormParser with the - appropriate values and given callbacks, and then return the corresponding - parser. - - :param headers: A dictionary-like object of HTTP headers. The only - required header is Content-Type. - - :param on_field: Callback to call with each parsed field. - - :param on_file: Callback to call with each parsed file. - - :param trust_x_headers: Whether or not to trust information received from - certain X-Headers - for example, the file name from - X-File-Name. - - :param config: Configuration variables to pass to the FormParser. - """ - content_type = headers.get("Content-Type") - if content_type is None: - logging.getLogger(__name__).warning("No Content-Type header given") - raise ValueError("No Content-Type header given!") - - # Boundaries are optional (the FormParser will raise if one is needed - # but not given). - content_type, params = parse_options_header(content_type) - boundary = params.get(b"boundary") - - # We need content_type to be a string, not a bytes object. - content_type = content_type.decode("latin-1") - - # File names are optional. - file_name = headers.get("X-File-Name") - - # Instantiate a form parser. - form_parser = FormParser(content_type, on_field, on_file, boundary=boundary, file_name=file_name, config=config) - - # Return our parser. - return form_parser - - -def parse_form(headers, input_stream, on_field, on_file, chunk_size=1048576, **kwargs): - """This function is useful if you just want to parse a request body, - without too much work. Pass it a dictionary-like object of the request's - headers, and a file-like object for the input stream, along with two - callbacks that will get called whenever a field or file is parsed. - - :param headers: A dictionary-like object of HTTP headers. The only - required header is Content-Type. - - :param input_stream: A file-like object that represents the request body. - The read() method must return bytestrings. - - :param on_field: Callback to call with each parsed field. - - :param on_file: Callback to call with each parsed file. - - :param chunk_size: The maximum size to read from the input stream and write - to the parser at one time. Defaults to 1 MiB. - """ - - # Create our form parser. - parser = create_form_parser(headers, on_field, on_file) - - # Read chunks of 100KiB and write to the parser, but never read more than - # the given Content-Length, if any. - content_length = headers.get("Content-Length") - if content_length is not None: - content_length = int(content_length) - else: - content_length = float("inf") - bytes_read = 0 - - while True: - # Read only up to the Content-Length given. - max_readable = min(content_length - bytes_read, 1048576) - buff = input_stream.read(max_readable) - - # Write to the parser and update our length. - parser.write(buff) - bytes_read += len(buff) - - # If we get a buffer that's smaller than the size requested, or if we - # have read up to our content length, we're done. - if len(buff) != max_readable or bytes_read == content_length: - break - - # Tell our parser that we're done writing data. - parser.finalize() diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/INSTALLER b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/LICENSE b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/LICENSE deleted file mode 100644 index 48c0f72..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/LICENSE +++ /dev/null @@ -1,116 +0,0 @@ -.. -*- restructuredtext -*- - -===================== -Copyrights & Licenses -===================== - -Credits -======= -Passlib is primarily developed by Eli Collins. - -Special thanks to Darin Gordon for testing and -feedback on the :mod:`passlib.totp` module. - -License for Passlib -=================== -Passlib is (c) `Assurance Technologies `_, -and is released under the `BSD license `_:: - - Passlib - Copyright (c) 2008-2020 Assurance Technologies, LLC. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Assurance Technologies, nor the names of the - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Licenses for incorporated software -================================== -Passlib contains some code derived from the following sources: - -MD5-Crypt ---------- -The source file ``passlib/handlers/md5_crypt.py`` contains code derived from the original -`FreeBSD md5-crypt implementation `_, -which is available under the following license:: - - "THE BEER-WARE LICENSE" (Revision 42): - wrote this file. As long as you retain this notice you - can do whatever you want with this stuff. If we meet some day, and you think - this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - - converted to python May 2008 - by Eli Collins - -DES ---- -The source file ``passlib/crypto/des.py`` contains code derived from -`UnixCrypt.java `_, -a pure-java implementation of the historic unix-crypt password hash algorithm. -It is available under the following license:: - - UnixCrypt.java 0.9 96/11/25 - Copyright (c) 1996 Aki Yoshida. All rights reserved. - Permission to use, copy, modify and distribute this software - for non-commercial or commercial purposes and without fee is - hereby granted provided that this copyright notice appears in - all copies. - - modified April 2001 - by Iris Van den Broeke, Daniel Deville - - modified Aug 2005 - by Greg Wilkins (gregw) - - converted to python Jun 2009 - by Eli Collins - -jBCrypt -------- -The source file ``passlib/crypto/_blowfish/base.py`` contains code derived -from `jBcrypt 0.2 `_, a Java -implementation of the BCrypt password hash algorithm. It is available under -a BSD/ISC license:: - - Copyright (c) 2006 Damien Miller - - Permission to use, copy, modify, and 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 TORTUOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Wordsets --------- -The EFF wordsets in ``passlib/_data/wordsets`` are (c) 2016 the Electronic Freedom Foundation. -They were downloaded from ``_, -and are released under the `Creative Commons License `_. diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/METADATA b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/METADATA deleted file mode 100644 index 0665d8a..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/METADATA +++ /dev/null @@ -1,40 +0,0 @@ -Metadata-Version: 2.1 -Name: passlib -Version: 1.7.4 -Summary: comprehensive password hashing framework supporting over 30 schemes -Home-page: https://passlib.readthedocs.io -Author: Eli Collins -Author-email: elic@assurancetechnologies.com -License: BSD -Download-URL: https://pypi.python.org/packages/source/p/passlib/passlib-1.7.4.tar.gz -Keywords: password secret hash security -Provides-Extra: argon2 -Requires-Dist: argon2-cffi (>=18.2.0) ; extra == 'argon2' -Provides-Extra: bcrypt -Requires-Dist: bcrypt (>=3.1.0) ; extra == 'bcrypt' -Provides-Extra: build_docs -Requires-Dist: sphinx (>=1.6) ; extra == 'build_docs' -Requires-Dist: sphinxcontrib-fulltoc (>=1.2.0) ; extra == 'build_docs' -Requires-Dist: cloud-sptheme (>=1.10.1) ; extra == 'build_docs' -Provides-Extra: totp -Requires-Dist: cryptography ; extra == 'totp' - -Passlib is a password hashing library for Python 2 & 3, which provides -cross-platform implementations of over 30 password hashing algorithms, as well -as a framework for managing existing password hashes. It's designed to be useful -for a wide range of tasks, from verifying a hash found in /etc/shadow, to -providing full-strength password hashing for multi-user applications. - -* See the `documentation `_ - for details, installation instructions, and examples. - -* See the `homepage `_ - for the latest news and more information. - -* See the `changelog `_ - for a description of what's new in Passlib. - -All releases are signed with the gpg key -`4D8592DF4CE1ED31 `_. - - diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/RECORD b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/RECORD deleted file mode 100644 index 2af39ff..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/RECORD +++ /dev/null @@ -1,202 +0,0 @@ -passlib-1.7.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -passlib-1.7.4.dist-info/LICENSE,sha256=qVuo8a-I_41fDQwzUZ9DC3-diZK2nUvDaawEI6egWok,4954 -passlib-1.7.4.dist-info/METADATA,sha256=l-uRq14ie328RCoVsayT7AfMHaJqv34ICbpQtKG00jM,1688 -passlib-1.7.4.dist-info/RECORD,, -passlib-1.7.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -passlib-1.7.4.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110 -passlib-1.7.4.dist-info/top_level.txt,sha256=BA9xbJpLdaTxqvYbKigYnMQkzp8-UQr6S4m3lBTkxzw,8 -passlib-1.7.4.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 -passlib/__init__.py,sha256=nSZrPEtlMQSKZqxERmYcWCDBD6pJ1P_DL5TdeSjIReU,87 -passlib/__pycache__/__init__.cpython-311.pyc,, -passlib/__pycache__/apache.cpython-311.pyc,, -passlib/__pycache__/apps.cpython-311.pyc,, -passlib/__pycache__/context.cpython-311.pyc,, -passlib/__pycache__/exc.cpython-311.pyc,, -passlib/__pycache__/hash.cpython-311.pyc,, -passlib/__pycache__/hosts.cpython-311.pyc,, -passlib/__pycache__/ifc.cpython-311.pyc,, -passlib/__pycache__/pwd.cpython-311.pyc,, -passlib/__pycache__/registry.cpython-311.pyc,, -passlib/__pycache__/totp.cpython-311.pyc,, -passlib/__pycache__/win32.cpython-311.pyc,, -passlib/_data/wordsets/bip39.txt,sha256=JM5Cwv1KlcG4a77pvOHhzyVb0AIuGbq2vVka_Wi379s,13117 -passlib/_data/wordsets/eff_long.txt,sha256=bVV_BpOVj7XmULaLW-5YXrgs9NoyllUFx4npJHQ7xSI,62144 -passlib/_data/wordsets/eff_prefixed.txt,sha256=eqV6TT7PZYFymZK62VdbrN6_fCg3ivKuxqUPEa7DJvU,10778 -passlib/_data/wordsets/eff_short.txt,sha256=NuzKSeT6IMqEsXbDLy6cgvmPRGWFGQ51-YealcCCR78,7180 -passlib/apache.py,sha256=TsHUCur5W8tK3Rsb9jYeeBCc7Ua_hP9e2tSxzoUVzwc,46661 -passlib/apps.py,sha256=AYqni3QIelR7HCiPj_hv2Mcr8bsfdcUkh07DwQqZxWs,8067 -passlib/context.py,sha256=aJeTjA-h7ke3KObvEM8aSJzKdN3wrOyu0hTt-MTbJt0,109195 -passlib/crypto/__init__.py,sha256=St6CGqhrfz3L5Da3aZvRK69le_FcLLE3gA2dEByOmC0,84 -passlib/crypto/__pycache__/__init__.cpython-311.pyc,, -passlib/crypto/__pycache__/_md4.cpython-311.pyc,, -passlib/crypto/__pycache__/des.cpython-311.pyc,, -passlib/crypto/__pycache__/digest.cpython-311.pyc,, -passlib/crypto/_blowfish/__init__.py,sha256=iZb7ft1vxBjCW7lpDtWwTxuMicgvi673M5F_1PKdVkg,6426 -passlib/crypto/_blowfish/__pycache__/__init__.cpython-311.pyc,, -passlib/crypto/_blowfish/__pycache__/_gen_files.cpython-311.pyc,, -passlib/crypto/_blowfish/__pycache__/base.cpython-311.pyc,, -passlib/crypto/_blowfish/__pycache__/unrolled.cpython-311.pyc,, -passlib/crypto/_blowfish/_gen_files.py,sha256=fUrNGWA5NX9CyvoJbNhJv7PJmptbp1uSR9iaWzKkb1I,6176 -passlib/crypto/_blowfish/base.py,sha256=_zF7x6XSbqCl2HH5Eya8KIhhJVbDYuYAWKfxbjOQZWg,20390 -passlib/crypto/_blowfish/unrolled.py,sha256=FOMhVo_jnGS3bMafXfjEffDPSP5vMogFvupnVKAa1lg,37153 -passlib/crypto/_md4.py,sha256=_5RXBX_gowtN0x05PnN0EF_csO4Q_NA5whm6e_vJx08,6905 -passlib/crypto/des.py,sha256=1EsvVd34Z82BYmGb8JIzfVWvTMN70fWhJGmIfmNrBAU,51878 -passlib/crypto/digest.py,sha256=WsfpcC8IM-gvZh56m6v8bjzG4nsNAsaoSv2LNY1_5go,36158 -passlib/crypto/scrypt/__init__.py,sha256=bXmeIerN6DKJSw8XsQEYcsUKCfRpXGb190e-gdHbbqU,9630 -passlib/crypto/scrypt/__pycache__/__init__.cpython-311.pyc,, -passlib/crypto/scrypt/__pycache__/_builtin.cpython-311.pyc,, -passlib/crypto/scrypt/__pycache__/_gen_files.cpython-311.pyc,, -passlib/crypto/scrypt/__pycache__/_salsa.cpython-311.pyc,, -passlib/crypto/scrypt/_builtin.py,sha256=82RZc_4LQv2JCL06bX70hCICBaK30Uy7PGzmZtiOjA0,8910 -passlib/crypto/scrypt/_gen_files.py,sha256=vRhjlIKqwvcILCo20sVf8dXr15tW636t5oojAZFssJE,4683 -passlib/crypto/scrypt/_salsa.py,sha256=b87_YEP3jJSmlU2BHSx-NKiJ4e_1eK-RlC4pWA4y71I,5719 -passlib/exc.py,sha256=MIjUTBLcOai52paDLM1nFh6lMTLBLPAn1PTdbCm-9Fo,14481 -passlib/ext/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 -passlib/ext/__pycache__/__init__.cpython-311.pyc,, -passlib/ext/django/__init__.py,sha256=RvooHmuUwjLXuSuUJr-9URnY1CRVCzU2xdh-jW-mrN0,228 -passlib/ext/django/__pycache__/__init__.cpython-311.pyc,, -passlib/ext/django/__pycache__/models.cpython-311.pyc,, -passlib/ext/django/__pycache__/utils.cpython-311.pyc,, -passlib/ext/django/models.py,sha256=-XpQRLGG2kTuLWNoh-EhKOaeEV5aIfzavw8qTQ-p1fM,1314 -passlib/ext/django/utils.py,sha256=ObpILR1seOZyecYhuQ1G_R9_N6DMuS4kWZve_giRLiw,49409 -passlib/handlers/__init__.py,sha256=sIPjJgOGHpOIstAwDeHTfxKR8wLVqP4zSa4mvBhAZ_8,86 -passlib/handlers/__pycache__/__init__.cpython-311.pyc,, -passlib/handlers/__pycache__/argon2.cpython-311.pyc,, -passlib/handlers/__pycache__/bcrypt.cpython-311.pyc,, -passlib/handlers/__pycache__/cisco.cpython-311.pyc,, -passlib/handlers/__pycache__/des_crypt.cpython-311.pyc,, -passlib/handlers/__pycache__/digests.cpython-311.pyc,, -passlib/handlers/__pycache__/django.cpython-311.pyc,, -passlib/handlers/__pycache__/fshp.cpython-311.pyc,, -passlib/handlers/__pycache__/ldap_digests.cpython-311.pyc,, -passlib/handlers/__pycache__/md5_crypt.cpython-311.pyc,, -passlib/handlers/__pycache__/misc.cpython-311.pyc,, -passlib/handlers/__pycache__/mssql.cpython-311.pyc,, -passlib/handlers/__pycache__/mysql.cpython-311.pyc,, -passlib/handlers/__pycache__/oracle.cpython-311.pyc,, -passlib/handlers/__pycache__/pbkdf2.cpython-311.pyc,, -passlib/handlers/__pycache__/phpass.cpython-311.pyc,, -passlib/handlers/__pycache__/postgres.cpython-311.pyc,, -passlib/handlers/__pycache__/roundup.cpython-311.pyc,, -passlib/handlers/__pycache__/scram.cpython-311.pyc,, -passlib/handlers/__pycache__/scrypt.cpython-311.pyc,, -passlib/handlers/__pycache__/sha1_crypt.cpython-311.pyc,, -passlib/handlers/__pycache__/sha2_crypt.cpython-311.pyc,, -passlib/handlers/__pycache__/sun_md5_crypt.cpython-311.pyc,, -passlib/handlers/__pycache__/windows.cpython-311.pyc,, -passlib/handlers/argon2.py,sha256=XrMPknuG-16IAwrd7WUuTKdIkKOD-3UPlHHZOjXZe68,38934 -passlib/handlers/bcrypt.py,sha256=LF33HnoxOhjtr7aFtrKgU5SB4mtw3xGx7C4tqecosrk,53582 -passlib/handlers/cisco.py,sha256=Yz0KhmqVVAV_szNnuZq40WgYg6eomBRoAJBbRrSUkGg,16284 -passlib/handlers/des_crypt.py,sha256=W3srE5kIaRQdhfIObz237sm0vPgqR4p_9ZkSd-9UNPo,22367 -passlib/handlers/digests.py,sha256=AeuVSxas2793ILXX0s6xm1lA1u4RPpE9G8wZSaq0Bs4,6327 -passlib/handlers/django.py,sha256=MmoLua6kZWVItsjRrnDgfktzuEpqlvwlPaexvti6I9M,20185 -passlib/handlers/fshp.py,sha256=78sMdnAkW5YHCPC13bLptdElLFrWzZF7rm3bwUWHATo,7799 -passlib/handlers/ldap_digests.py,sha256=jgxtxcERep4xXgVVfKfSVe5JEE45b5tt87NKGvK9_Zk,13049 -passlib/handlers/md5_crypt.py,sha256=jLt3IP-l0HFfU1u2VEtGI1WBYVNjTqhjvwovfFREiwg,13740 -passlib/handlers/misc.py,sha256=o1tWKAdTp3EnCYJOERpdkQnRwrQfWWKeiJXSQurbVMo,10109 -passlib/handlers/mssql.py,sha256=BECU0VaVtc-RzhGx7E_LVu2moZpEI5GASChFJnzDVxA,8482 -passlib/handlers/mysql.py,sha256=8h83lpTHs5q8zflXP0TyMavrELgtlvgUbcLtFUHnbDY,4796 -passlib/handlers/oracle.py,sha256=WDCqJEo2rDihcuUs4Ka48JBpSm4_JnNqXIVsCGUrkO8,6691 -passlib/handlers/pbkdf2.py,sha256=jVqdo1MSD3_7B5m-osqUTBTwTXnhedLan9lQaM-gysU,19010 -passlib/handlers/phpass.py,sha256=0c7maDUNGxIuyiG_O2hK6MJbxcTu7V2vx67xOq8d7ps,4785 -passlib/handlers/postgres.py,sha256=y9AzGpxjK-z1HLHElRQtLzCMqqtvBwd_xxJraHdGpN4,2274 -passlib/handlers/roundup.py,sha256=lvYArKerC702_MHZXMi3F-iZ9Y2jH10h2UXKDpgqoO8,1178 -passlib/handlers/scram.py,sha256=wBsoBg0qLW8HA5Nsgcnd1bM7ZDYEFbapAGoP0_44N58,22539 -passlib/handlers/scrypt.py,sha256=OYfF2Jjltydr5BswyZ-uFgl4yEjQZowGdIZpEyB7s5Q,14146 -passlib/handlers/sha1_crypt.py,sha256=DZOdKExzlucHCfpgszG1cFdareTqpGUGORNIEn4FJCs,5873 -passlib/handlers/sha2_crypt.py,sha256=kTZm-jmRVnKRhquetVBbiDWi9eY87NTJvUYkjGEm7MY,21800 -passlib/handlers/sun_md5_crypt.py,sha256=uWhoKxBITVwPlh9MIQ3WjVrYjlRMgLrBjLR1Ui2kmZw,13933 -passlib/handlers/windows.py,sha256=nviGebFjOiJO_cDJRo7RiccEhlN2UM7nAQL0pTso9MQ,12384 -passlib/hash.py,sha256=9lVasGFiXDGcL8VOWuEwAjzlATQbmEYF30wOIVotP-U,3750 -passlib/hosts.py,sha256=odRo2WnSfjMuktSIwfR50rzxbKGfzUwZ2CUkvcxvJoA,3302 -passlib/ifc.py,sha256=kL2svtkF99VQDOim_6TE6OGhmSf2EyHrzp0v_UQksqA,14196 -passlib/pwd.py,sha256=VeU_PVkZSvwXPI6AQA96cjqIKyuTvXtUoCK7eI5ab7w,28690 -passlib/registry.py,sha256=5qLDF72XHGSQVoEVqhvEngfZsO2fxVsBpWntX_D0YRs,20301 -passlib/tests/__init__.py,sha256=JIK29mBP8OKz3ChmaEbyr9vvml3weGe7YHMTHzBJcr0,20 -passlib/tests/__main__.py,sha256=iKv9ZuQe5jBzp4Gyp_G3wXhQBxSTJguMx1BCCVVZL6Y,82 -passlib/tests/__pycache__/__init__.cpython-311.pyc,, -passlib/tests/__pycache__/__main__.cpython-311.pyc,, -passlib/tests/__pycache__/_test_bad_register.cpython-311.pyc,, -passlib/tests/__pycache__/backports.cpython-311.pyc,, -passlib/tests/__pycache__/test_apache.cpython-311.pyc,, -passlib/tests/__pycache__/test_apps.cpython-311.pyc,, -passlib/tests/__pycache__/test_context.cpython-311.pyc,, -passlib/tests/__pycache__/test_context_deprecated.cpython-311.pyc,, -passlib/tests/__pycache__/test_crypto_builtin_md4.cpython-311.pyc,, -passlib/tests/__pycache__/test_crypto_des.cpython-311.pyc,, -passlib/tests/__pycache__/test_crypto_digest.cpython-311.pyc,, -passlib/tests/__pycache__/test_crypto_scrypt.cpython-311.pyc,, -passlib/tests/__pycache__/test_ext_django.cpython-311.pyc,, -passlib/tests/__pycache__/test_ext_django_source.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_argon2.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_bcrypt.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_cisco.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_django.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_pbkdf2.cpython-311.pyc,, -passlib/tests/__pycache__/test_handlers_scrypt.cpython-311.pyc,, -passlib/tests/__pycache__/test_hosts.cpython-311.pyc,, -passlib/tests/__pycache__/test_pwd.cpython-311.pyc,, -passlib/tests/__pycache__/test_registry.cpython-311.pyc,, -passlib/tests/__pycache__/test_totp.cpython-311.pyc,, -passlib/tests/__pycache__/test_utils.cpython-311.pyc,, -passlib/tests/__pycache__/test_utils_handlers.cpython-311.pyc,, -passlib/tests/__pycache__/test_utils_md4.cpython-311.pyc,, -passlib/tests/__pycache__/test_utils_pbkdf2.cpython-311.pyc,, -passlib/tests/__pycache__/test_win32.cpython-311.pyc,, -passlib/tests/__pycache__/tox_support.cpython-311.pyc,, -passlib/tests/__pycache__/utils.cpython-311.pyc,, -passlib/tests/_test_bad_register.py,sha256=yws8uO2HsUWg8GRQPlxKvE5HniP84QSQW6ncCPiZDpw,541 -passlib/tests/backports.py,sha256=QTi9tD9DO_RlawkInpPDsFaol--5hsMI-cFvwLIE9B0,2593 -passlib/tests/sample1.cfg,sha256=lJsayArbi6FElINzcTQ1VbgTTGY5LKpMdbCJvK_6H8s,243 -passlib/tests/sample1b.cfg,sha256=2ZQnnpumQsEJpKFsTOHuv_ULhQY5PhQPnsa2rSZmTEU,252 -passlib/tests/sample1c.cfg,sha256=u-BGMklAN05efndzADJfFV9gP1Jbns1gDdwC__VfW-8,490 -passlib/tests/sample_config_1s.cfg,sha256=mMgYjX_UvxVVLFTfZ4m-vxVo31MbSNrZA0R7VY6DzTk,238 -passlib/tests/test_apache.py,sha256=_XhDKgV1nON4ddQQU3GdUfSXrwY_x2OoJQ6l7w2Gzbw,29432 -passlib/tests/test_apps.py,sha256=6MrGeFenjSACzbAtp6jf3PNHoITv_v5DbT_7nhrR-KA,5281 -passlib/tests/test_context.py,sha256=Vsl2hhouEi3yn4_J7J10E09OotLneRHzkAY_jS16F08,74546 -passlib/tests/test_context_deprecated.py,sha256=cVXqcPx_Xqlsh6QF2az34RY23wP3pv8SOBbJFQn65Jg,29282 -passlib/tests/test_crypto_builtin_md4.py,sha256=5PWKh1HoQKC4gI4BcgVDh89xw7lix0R1n9Jn0Y8t8mQ,5660 -passlib/tests/test_crypto_des.py,sha256=0xWgS74G6ygl7gIvF6uhjcoThVTt1TqIH4ZUeqXbVmA,8874 -passlib/tests/test_crypto_digest.py,sha256=b15XIFLDUsjsaxPEQUJkb-csM65IRz_9glwZz7qwN7U,20478 -passlib/tests/test_crypto_scrypt.py,sha256=xJDU3e4bt9N1X0fA9zBLBxESk3PsTR89qJeEWNX2Em4,26646 -passlib/tests/test_ext_django.py,sha256=QUKoa6rLn3hbCVNk7_0z9JW5aOFmyLbBwj0PiWhQJ7s,41364 -passlib/tests/test_ext_django_source.py,sha256=AW-PQRQeLz2cOpKGPeKPLSESC4o-ATbu3-Zd45Coi3k,11034 -passlib/tests/test_handlers.py,sha256=WxYhRTthTzDj-FIP2vS_mH0nlpjgrWOp2C-h3mN6DzE,68622 -passlib/tests/test_handlers_argon2.py,sha256=bSNARahGKPZTawLq-qhVdcuvprCDTNXGWPhSh8aRyaY,22837 -passlib/tests/test_handlers_bcrypt.py,sha256=izOVd0WthIi90YKkvskrW5DZPMMCvO2qtwRkefvgkdY,29549 -passlib/tests/test_handlers_cisco.py,sha256=TLvuGQZygEZbjA01t1hfGfBvx3THnv6ZwbNQCKUhsuI,20471 -passlib/tests/test_handlers_django.py,sha256=ADphUgbG9PwoXQPFbEAPeIDfqjK6DENl_wizP52wYSE,15538 -passlib/tests/test_handlers_pbkdf2.py,sha256=vDM9ipts9EYoauheNHtOOYq0Nl8-9ltTML4gnw2EB2g,18788 -passlib/tests/test_handlers_scrypt.py,sha256=wHsbgoV5xhY4SQtgWFCuit3lygkNvd0AQKZ0lmp72do,4188 -passlib/tests/test_hosts.py,sha256=n0gCywmbsw8q8p4WLp-AlQrQuPfe-29fYwUfWwXi4Co,3906 -passlib/tests/test_pwd.py,sha256=Si9qFDXwkbjTsJ9wQTYe-QhlprVoMQ2E79-eX11FPBk,7190 -passlib/tests/test_registry.py,sha256=9BgXvMhHKQQHBGdgV4WyDDZUboUh0tbHYdgPYr1upSo,9246 -passlib/tests/test_totp.py,sha256=T1o3B97SltvC1OKweXQpX1bBGf6KYQnMl8jcpBSg5DU,65746 -passlib/tests/test_utils.py,sha256=yMWrrnsMIg8b8guyzRK8lDJ243rul6ANhrIgImGlyVI,46118 -passlib/tests/test_utils_handlers.py,sha256=rVSuaNqRUb4Q520nVD4C5smzVs-LdFqQjFZMDRTz-zU,32134 -passlib/tests/test_utils_md4.py,sha256=CfQor3ZfV2JO_8x2RxY5Tl5ZsS0hDvIje46cLvLN5Ew,1474 -passlib/tests/test_utils_pbkdf2.py,sha256=gIhycQf4NUNd5yjUrtKfRm3eqqpklS9W2B7-8INp4Cg,12193 -passlib/tests/test_win32.py,sha256=BXVpHSm71ePXmmbBPTN4H38lUgGqG6-iZasbj_l1mVg,1920 -passlib/tests/tox_support.py,sha256=PDaO1ftDtOFzd299EXm0X5HWRzg37VsBiHsdiMOu5FA,2473 -passlib/tests/utils.py,sha256=mNbhjFNG16dmU13ChMyqOSY39OiR2d8LRUBi41dAMko,147541 -passlib/totp.py,sha256=Wryr57req8NFJnw1fI_eycCaTwmSY8WA7Z3OFjAwHOE,73033 -passlib/utils/__init__.py,sha256=VHkQHu7DcdVKyDjhPuyRG_2-25aI4Zwat3wr6K-rAlo,42925 -passlib/utils/__pycache__/__init__.cpython-311.pyc,, -passlib/utils/__pycache__/binary.cpython-311.pyc,, -passlib/utils/__pycache__/decor.cpython-311.pyc,, -passlib/utils/__pycache__/des.cpython-311.pyc,, -passlib/utils/__pycache__/handlers.cpython-311.pyc,, -passlib/utils/__pycache__/md4.cpython-311.pyc,, -passlib/utils/__pycache__/pbkdf2.cpython-311.pyc,, -passlib/utils/binary.py,sha256=dZe2ZjuGr0g6iQseO-ThkQ5XM6KnQFISGQr68vUOOhM,31422 -passlib/utils/compat/__init__.py,sha256=xuPP5PsmLJh_I5NrlaYa012zmWrdzfrYbL_oHqc4tCk,14235 -passlib/utils/compat/__pycache__/__init__.cpython-311.pyc,, -passlib/utils/compat/__pycache__/_ordered_dict.cpython-311.pyc,, -passlib/utils/compat/_ordered_dict.py,sha256=1nga6blaxokrrDdY3UrQgRXYdifZHCDgPYie1aCJkuI,8368 -passlib/utils/decor.py,sha256=svc2C-_DKfiCMmOBNhn_DK7IeS_WYNg26asjhx76LUA,7651 -passlib/utils/des.py,sha256=jFuvhUA3aaiR1xWX4NpXYm5XgcdewRT5Uas-7jLoSTE,2163 -passlib/utils/handlers.py,sha256=E3oRL908uudK_ZLZWeX5DoPxJL8uCfCGpmAkyfJoWQ8,105286 -passlib/utils/md4.py,sha256=pyxEpUe_t8E0u2ZDWOzYIJa0oXgTQBO7DQ8SMKGX8ag,1218 -passlib/utils/pbkdf2.py,sha256=foDGTAKeZywBAVlLZIRf4bX6fC3bzsoC1i_DtcdXr2I,6832 -passlib/win32.py,sha256=E6Ca-4Ki5ZlCSzd86N1CXjh-xQoJYjW-74-kJ6VsHUU,2591 diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/REQUESTED b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/WHEEL b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/WHEEL deleted file mode 100644 index 6d38aa0..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.35.1) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/top_level.txt b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/top_level.txt deleted file mode 100644 index 419829d..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -passlib diff --git a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/zip-safe b/.venv/Lib/site-packages/passlib-1.7.4.dist-info/zip-safe deleted file mode 100644 index 8b13789..0000000 --- a/.venv/Lib/site-packages/passlib-1.7.4.dist-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.venv/Lib/site-packages/passlib/__init__.py b/.venv/Lib/site-packages/passlib/__init__.py deleted file mode 100644 index 963bfcc..0000000 --- a/.venv/Lib/site-packages/passlib/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""passlib - suite of password hashing & generation routines""" - -__version__ = '1.7.4' diff --git a/.venv/Lib/site-packages/passlib/_data/wordsets/bip39.txt b/.venv/Lib/site-packages/passlib/_data/wordsets/bip39.txt deleted file mode 100644 index e29842e..0000000 --- a/.venv/Lib/site-packages/passlib/_data/wordsets/bip39.txt +++ /dev/null @@ -1,2049 +0,0 @@ -abandon -ability -able -about -above -absent -absorb -abstract -absurd -abuse -access -accident -account -accuse -achieve -acid -acoustic -acquire -across -act -action -actor -actress -actual -adapt -add -addict -address -adjust -admit -adult -advance -advice -aerobic -affair -afford -afraid -again -age -agent -agree -ahead -aim -air -airport -aisle -alarm -album -alcohol -alert -alien -all -alley -allow -almost -alone -alpha -already -also -alter -always -amateur -amazing -among -amount -amused -analyst -anchor -ancient -anger -angle -angry -animal -ankle -announce -annual -another -answer -antenna -antique -anxiety -any -apart -apology -appear -apple -approve -april -arch -arctic -area -arena -argue -arm -armed -armor -army -around -arrange -arrest -arrive -arrow -art -artefact -artist -artwork -ask -aspect -assault -asset -assist -assume -asthma -athlete -atom -attack -attend -attitude -attract -auction -audit -august -aunt -author -auto -autumn -average -avocado -avoid -awake -aware -away -awesome -awful -awkward -axis -baby -bachelor -bacon -badge -bag -balance -balcony -ball -bamboo -banana -banner -bar -barely -bargain -barrel -base -basic -basket -battle -beach -bean -beauty -because -become -beef -before -begin -behave -behind -believe -below -belt -bench -benefit -best -betray -better -between -beyond -bicycle -bid -bike -bind -biology -bird -birth -bitter -black -blade -blame -blanket -blast -bleak -bless -blind -blood -blossom -blouse -blue -blur -blush -board -boat -body -boil -bomb -bone -bonus -book -boost -border -boring -borrow -boss -bottom -bounce -box -boy -bracket -brain -brand -brass -brave -bread -breeze -brick -bridge -brief -bright -bring -brisk -broccoli -broken -bronze -broom -brother -brown -brush -bubble -buddy -budget -buffalo -build -bulb -bulk -bullet -bundle -bunker -burden -burger -burst -bus -business -busy -butter -buyer -buzz -cabbage -cabin -cable -cactus -cage -cake -call -calm -camera -camp -can -canal -cancel -candy -cannon -canoe -canvas -canyon -capable -capital -captain -car -carbon -card -cargo -carpet -carry -cart -case -cash -casino -castle -casual -cat -catalog -catch -category -cattle -caught -cause -caution -cave -ceiling -celery -cement -census -century -cereal -certain -chair -chalk -champion -change -chaos -chapter -charge -chase -chat -cheap -check -cheese -chef -cherry -chest -chicken -chief -child -chimney -choice -choose -chronic -chuckle -chunk -churn -cigar -cinnamon -circle -citizen -city -civil -claim -clap -clarify -claw -clay -clean -clerk -clever -click -client -cliff -climb -clinic -clip -clock -clog -close -cloth -cloud -clown -club -clump -cluster -clutch -coach -coast -coconut -code -coffee -coil -coin -collect -color -column -combine -come -comfort -comic -common -company -concert -conduct -confirm -congress -connect -consider -control -convince -cook -cool -copper -copy -coral -core -corn -correct -cost -cotton -couch -country -couple -course -cousin -cover -coyote -crack -cradle -craft -cram -crane -crash -crater -crawl -crazy -cream -credit -creek -crew -cricket -crime -crisp -critic -crop -cross -crouch -crowd -crucial -cruel -cruise -crumble -crunch -crush -cry -crystal -cube -culture -cup -cupboard -curious -current -curtain -curve -cushion -custom -cute -cycle -dad -damage -damp -dance -danger -daring -dash -daughter -dawn -day -deal -debate -debris -decade -december -decide -decline -decorate -decrease -deer -defense -define -defy -degree -delay -deliver -demand -demise -denial -dentist -deny -depart -depend -deposit -depth -deputy -derive -describe -desert -design -desk -despair -destroy -detail -detect -develop -device -devote -diagram -dial -diamond -diary -dice -diesel -diet -differ -digital -dignity -dilemma -dinner -dinosaur -direct -dirt -disagree -discover -disease -dish -dismiss -disorder -display -distance -divert -divide -divorce -dizzy -doctor -document -dog -doll -dolphin -domain -donate -donkey -donor -door -dose -double -dove -draft -dragon -drama -drastic -draw -dream -dress -drift -drill -drink -drip -drive -drop -drum -dry -duck -dumb -dune -during -dust -dutch -duty -dwarf -dynamic -eager -eagle -early -earn -earth -easily -east -easy -echo -ecology -economy -edge -edit -educate -effort -egg -eight -either -elbow -elder -electric -elegant -element -elephant -elevator -elite -else -embark -embody -embrace -emerge -emotion -employ -empower -empty -enable -enact -end -endless -endorse -enemy -energy -enforce -engage -engine -enhance -enjoy -enlist -enough -enrich -enroll -ensure -enter -entire -entry -envelope -episode -equal -equip -era -erase -erode -erosion -error -erupt -escape -essay -essence -estate -eternal -ethics -evidence -evil -evoke -evolve -exact -example -excess -exchange -excite -exclude -excuse -execute -exercise -exhaust -exhibit -exile -exist -exit -exotic -expand -expect -expire -explain -expose -express -extend -extra -eye -eyebrow -fabric -face -faculty -fade -faint -faith -fall -false -fame -family -famous -fan -fancy -fantasy -farm -fashion -fat -fatal -father -fatigue -fault -favorite -feature -february -federal -fee -feed -feel -female -fence -festival -fetch -fever -few -fiber -fiction -field -figure -file -film -filter -final -find -fine -finger -finish -fire -firm -first -fiscal -fish -fit -fitness -fix -flag -flame -flash -flat -flavor -flee -flight -flip -float -flock -floor -flower -fluid -flush -fly -foam -focus -fog -foil -fold -follow -food -foot -force -forest -forget -fork -fortune -forum -forward -fossil -foster -found -fox -fragile -frame -frequent -fresh -friend -fringe -frog -front -frost -frown -frozen -fruit -fuel -fun -funny -furnace -fury -future -gadget -gain -galaxy -gallery -game -gap -garage -garbage -garden -garlic -garment -gas -gasp -gate -gather -gauge -gaze -general -genius -genre -gentle -genuine -gesture -ghost -giant -gift -giggle -ginger -giraffe -girl -give -glad -glance -glare -glass -glide -glimpse -globe -gloom -glory -glove -glow -glue -goat -goddess -gold -good -goose -gorilla -gospel -gossip -govern -gown -grab -grace -grain -grant -grape -grass -gravity -great -green -grid -grief -grit -grocery -group -grow -grunt -guard -guess -guide -guilt -guitar -gun -gym -habit -hair -half -hammer -hamster -hand -happy -harbor -hard -harsh -harvest -hat -have -hawk -hazard -head -health -heart -heavy -hedgehog -height -hello -helmet -help -hen -hero -hidden -high -hill -hint -hip -hire -history -hobby -hockey -hold -hole -holiday -hollow -home -honey -hood -hope -horn -horror -horse -hospital -host -hotel -hour -hover -hub -huge -human -humble -humor -hundred -hungry -hunt -hurdle -hurry -hurt -husband -hybrid -ice -icon -idea -identify -idle -ignore -ill -illegal -illness -image -imitate -immense -immune -impact -impose -improve -impulse -inch -include -income -increase -index -indicate -indoor -industry -infant -inflict -inform -inhale -inherit -initial -inject -injury -inmate -inner -innocent -input -inquiry -insane -insect -inside -inspire -install -intact -interest -into -invest -invite -involve -iron -island -isolate -issue -item -ivory -jacket -jaguar -jar -jazz -jealous -jeans -jelly -jewel -job -join -joke -journey -joy -judge -juice -jump -jungle -junior -junk -just -kangaroo -keen -keep -ketchup -key -kick -kid -kidney -kind -kingdom -kiss -kit -kitchen -kite -kitten -kiwi -knee -knife -knock -know -lab -label -labor -ladder -lady -lake -lamp -language -laptop -large -later -latin -laugh -laundry -lava -law -lawn -lawsuit -layer -lazy -leader -leaf -learn -leave -lecture -left -leg -legal -legend -leisure -lemon -lend -length -lens -leopard -lesson -letter -level -liar -liberty -library -license -life -lift -light -like -limb -limit -link -lion -liquid -list -little -live -lizard -load -loan -lobster -local -lock -logic -lonely -long -loop -lottery -loud -lounge -love -loyal -lucky -luggage -lumber -lunar -lunch -luxury -lyrics -machine -mad -magic -magnet -maid -mail -main -major -make -mammal -man -manage -mandate -mango -mansion -manual -maple -marble -march -margin -marine -market -marriage -mask -mass -master -match -material -math -matrix -matter -maximum -maze -meadow -mean -measure -meat -mechanic -medal -media -melody -melt -member -memory -mention -menu -mercy -merge -merit -merry -mesh -message -metal -method -middle -midnight -milk -million -mimic -mind -minimum -minor -minute -miracle -mirror -misery -miss -mistake -mix -mixed -mixture -mobile -model -modify -mom -moment -monitor -monkey -monster -month -moon -moral -more -morning -mosquito -mother -motion -motor -mountain -mouse -move -movie -much -muffin -mule -multiply -muscle -museum -mushroom -music -must -mutual -myself -mystery -myth -naive -name -napkin -narrow -nasty -nation -nature -near -neck -need -negative -neglect -neither -nephew -nerve -nest -net -network -neutral -never -news -next -nice -night -noble -noise -nominee -noodle -normal -north -nose -notable -note -nothing -notice -novel -now -nuclear -number -nurse -nut -oak -obey -object -oblige -obscure -observe -obtain -obvious -occur -ocean -october -odor -off -offer -office -often -oil -okay -old -olive -olympic -omit -once -one -onion -online -only -open -opera -opinion -oppose -option -orange -orbit -orchard -order -ordinary -organ -orient -original -orphan -ostrich -other -outdoor -outer -output -outside -oval -oven -over -own -owner -oxygen -oyster -ozone -pact -paddle -page -pair -palace -palm -panda -panel -panic -panther -paper -parade -parent -park -parrot -party -pass -patch -path -patient -patrol -pattern -pause -pave -payment -peace -peanut -pear -peasant -pelican -pen -penalty -pencil -people -pepper -perfect -permit -person -pet -phone -photo -phrase -physical -piano -picnic -picture -piece -pig -pigeon -pill -pilot -pink -pioneer -pipe -pistol -pitch -pizza -place -planet -plastic -plate -play -please -pledge -pluck -plug -plunge -poem -poet -point -polar -pole -police -pond -pony -pool -popular -portion -position -possible -post -potato -pottery -poverty -powder -power -practice -praise -predict -prefer -prepare -present -pretty -prevent -price -pride -primary -print -priority -prison -private -prize -problem -process -produce -profit -program -project -promote -proof -property -prosper -protect -proud -provide -public -pudding -pull -pulp -pulse -pumpkin -punch -pupil -puppy -purchase -purity -purpose -purse -push -put -puzzle -pyramid -quality -quantum -quarter -question -quick -quit -quiz -quote -rabbit -raccoon -race -rack -radar -radio -rail -rain -raise -rally -ramp -ranch -random -range -rapid -rare -rate -rather -raven -raw -razor -ready -real -reason -rebel -rebuild -recall -receive -recipe -record -recycle -reduce -reflect -reform -refuse -region -regret -regular -reject -relax -release -relief -rely -remain -remember -remind -remove -render -renew -rent -reopen -repair -repeat -replace -report -require -rescue -resemble -resist -resource -response -result -retire -retreat -return -reunion -reveal -review -reward -rhythm -rib -ribbon -rice -rich -ride -ridge -rifle -right -rigid -ring -riot -ripple -risk -ritual -rival -river -road -roast -robot -robust -rocket -romance -roof -rookie -room -rose -rotate -rough -round -route -royal -rubber -rude -rug -rule -run -runway -rural -sad -saddle -sadness -safe -sail -salad -salmon -salon -salt -salute -same -sample -sand -satisfy -satoshi -sauce -sausage -save -say -scale -scan -scare -scatter -scene -scheme -school -science -scissors -scorpion -scout -scrap -screen -script -scrub -sea -search -season -seat -second -secret -section -security -seed -seek -segment -select -sell -seminar -senior -sense -sentence -series -service -session -settle -setup -seven -shadow -shaft -shallow -share -shed -shell -sheriff -shield -shift -shine -ship -shiver -shock -shoe -shoot -shop -short -shoulder -shove -shrimp -shrug -shuffle -shy -sibling -sick -side -siege -sight -sign -silent -silk -silly -silver -similar -simple -since -sing -siren -sister -situate -six -size -skate -sketch -ski -skill -skin -skirt -skull -slab -slam -sleep -slender -slice -slide -slight -slim -slogan -slot -slow -slush -small -smart -smile -smoke -smooth -snack -snake -snap -sniff -snow -soap -soccer -social -sock -soda -soft -solar -soldier -solid -solution -solve -someone -song -soon -sorry -sort -soul -sound -soup -source -south -space -spare -spatial -spawn -speak -special -speed -spell -spend -sphere -spice -spider -spike -spin -spirit -split -spoil -sponsor -spoon -sport -spot -spray -spread -spring -spy -square -squeeze -squirrel -stable -stadium -staff -stage -stairs -stamp -stand -start -state -stay -steak -steel -stem -step -stereo -stick -still -sting -stock -stomach -stone -stool -story -stove -strategy -street -strike -strong -struggle -student -stuff -stumble -style -subject -submit -subway -success -such -sudden -suffer -sugar -suggest -suit -summer -sun -sunny -sunset -super -supply -supreme -sure -surface -surge -surprise -surround -survey -suspect -sustain -swallow -swamp -swap -swarm -swear -sweet -swift -swim -swing -switch -sword -symbol -symptom -syrup -system -table -tackle -tag -tail -talent -talk -tank -tape -target -task -taste -tattoo -taxi -teach -team -tell -ten -tenant -tennis -tent -term -test -text -thank -that -theme -then -theory -there -they -thing -this -thought -three -thrive -throw -thumb -thunder -ticket -tide -tiger -tilt -timber -time -tiny -tip -tired -tissue -title -toast -tobacco -today -toddler -toe -together -toilet -token -tomato -tomorrow -tone -tongue -tonight -tool -tooth -top -topic -topple -torch -tornado -tortoise -toss -total -tourist -toward -tower -town -toy -track -trade -traffic -tragic -train -transfer -trap -trash -travel -tray -treat -tree -trend -trial -tribe -trick -trigger -trim -trip -trophy -trouble -truck -true -truly -trumpet -trust -truth -try -tube -tuition -tumble -tuna -tunnel -turkey -turn -turtle -twelve -twenty -twice -twin -twist -two -type -typical -ugly -umbrella -unable -unaware -uncle -uncover -under -undo -unfair -unfold -unhappy -uniform -unique -unit -universe -unknown -unlock -until -unusual -unveil -update -upgrade -uphold -upon -upper -upset -urban -urge -usage -use -used -useful -useless -usual -utility -vacant -vacuum -vague -valid -valley -valve -van -vanish -vapor -various -vast -vault -vehicle -velvet -vendor -venture -venue -verb -verify -version -very -vessel -veteran -viable -vibrant -vicious -victory -video -view -village -vintage -violin -virtual -virus -visa -visit -visual -vital -vivid -vocal -voice -void -volcano -volume -vote -voyage -wage -wagon -wait -walk -wall -walnut -want -warfare -warm -warrior -wash -wasp -waste -water -wave -way -wealth -weapon -wear -weasel -weather -web -wedding -weekend -weird -welcome -west -wet -whale -what -wheat -wheel -when -where -whip -whisper -wide -width -wife -wild -will -win -window -wine -wing -wink -winner -winter -wire -wisdom -wise -wish -witness -wolf -woman -wonder -wood -wool -word -work -world -worry -worth -wrap -wreck -wrestle -wrist -write -wrong -yard -year -yellow -you -young -youth -zebra -zero -zone -zoo - diff --git a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_long.txt b/.venv/Lib/site-packages/passlib/_data/wordsets/eff_long.txt deleted file mode 100644 index caf71f5..0000000 --- a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_long.txt +++ /dev/null @@ -1,7776 +0,0 @@ -abacus -abdomen -abdominal -abide -abiding -ability -ablaze -able -abnormal -abrasion -abrasive -abreast -abridge -abroad -abruptly -absence -absentee -absently -absinthe -absolute -absolve -abstain -abstract -absurd -accent -acclaim -acclimate -accompany -account -accuracy -accurate -accustom -acetone -achiness -aching -acid -acorn -acquaint -acquire -acre -acrobat -acronym -acting -action -activate -activator -active -activism -activist -activity -actress -acts -acutely -acuteness -aeration -aerobics -aerosol -aerospace -afar -affair -affected -affecting -affection -affidavit -affiliate -affirm -affix -afflicted -affluent -afford -affront -aflame -afloat -aflutter -afoot -afraid -afterglow -afterlife -aftermath -aftermost -afternoon -aged -ageless -agency -agenda -agent -aggregate -aghast -agile -agility -aging -agnostic -agonize -agonizing -agony -agreeable -agreeably -agreed -agreeing -agreement -aground -ahead -ahoy -aide -aids -aim -ajar -alabaster -alarm -albatross -album -alfalfa -algebra -algorithm -alias -alibi -alienable -alienate -aliens -alike -alive -alkaline -alkalize -almanac -almighty -almost -aloe -aloft -aloha -alone -alongside -aloof -alphabet -alright -although -altitude -alto -aluminum -alumni -always -amaretto -amaze -amazingly -amber -ambiance -ambiguity -ambiguous -ambition -ambitious -ambulance -ambush -amendable -amendment -amends -amenity -amiable -amicably -amid -amigo -amino -amiss -ammonia -ammonium -amnesty -amniotic -among -amount -amperage -ample -amplifier -amplify -amply -amuck -amulet -amusable -amused -amusement -amuser -amusing -anaconda -anaerobic -anagram -anatomist -anatomy -anchor -anchovy -ancient -android -anemia -anemic -aneurism -anew -angelfish -angelic -anger -angled -angler -angles -angling -angrily -angriness -anguished -angular -animal -animate -animating -animation -animator -anime -animosity -ankle -annex -annotate -announcer -annoying -annually -annuity -anointer -another -answering -antacid -antarctic -anteater -antelope -antennae -anthem -anthill -anthology -antibody -antics -antidote -antihero -antiquely -antiques -antiquity -antirust -antitoxic -antitrust -antiviral -antivirus -antler -antonym -antsy -anvil -anybody -anyhow -anymore -anyone -anyplace -anything -anytime -anyway -anywhere -aorta -apache -apostle -appealing -appear -appease -appeasing -appendage -appendix -appetite -appetizer -applaud -applause -apple -appliance -applicant -applied -apply -appointee -appraisal -appraiser -apprehend -approach -approval -approve -apricot -april -apron -aptitude -aptly -aqua -aqueduct -arbitrary -arbitrate -ardently -area -arena -arguable -arguably -argue -arise -armadillo -armband -armchair -armed -armful -armhole -arming -armless -armoire -armored -armory -armrest -army -aroma -arose -around -arousal -arrange -array -arrest -arrival -arrive -arrogance -arrogant -arson -art -ascend -ascension -ascent -ascertain -ashamed -ashen -ashes -ashy -aside -askew -asleep -asparagus -aspect -aspirate -aspire -aspirin -astonish -astound -astride -astrology -astronaut -astronomy -astute -atlantic -atlas -atom -atonable -atop -atrium -atrocious -atrophy -attach -attain -attempt -attendant -attendee -attention -attentive -attest -attic -attire -attitude -attractor -attribute -atypical -auction -audacious -audacity -audible -audibly -audience -audio -audition -augmented -august -authentic -author -autism -autistic -autograph -automaker -automated -automatic -autopilot -available -avalanche -avatar -avenge -avenging -avenue -average -aversion -avert -aviation -aviator -avid -avoid -await -awaken -award -aware -awhile -awkward -awning -awoke -awry -axis -babble -babbling -babied -baboon -backache -backboard -backboned -backdrop -backed -backer -backfield -backfire -backhand -backing -backlands -backlash -backless -backlight -backlit -backlog -backpack -backpedal -backrest -backroom -backshift -backside -backslid -backspace -backspin -backstab -backstage -backtalk -backtrack -backup -backward -backwash -backwater -backyard -bacon -bacteria -bacterium -badass -badge -badland -badly -badness -baffle -baffling -bagel -bagful -baggage -bagged -baggie -bagginess -bagging -baggy -bagpipe -baguette -baked -bakery -bakeshop -baking -balance -balancing -balcony -balmy -balsamic -bamboo -banana -banish -banister -banjo -bankable -bankbook -banked -banker -banking -banknote -bankroll -banner -bannister -banshee -banter -barbecue -barbed -barbell -barber -barcode -barge -bargraph -barista -baritone -barley -barmaid -barman -barn -barometer -barrack -barracuda -barrel -barrette -barricade -barrier -barstool -bartender -barterer -bash -basically -basics -basil -basin -basis -basket -batboy -batch -bath -baton -bats -battalion -battered -battering -battery -batting -battle -bauble -bazooka -blabber -bladder -blade -blah -blame -blaming -blanching -blandness -blank -blaspheme -blasphemy -blast -blatancy -blatantly -blazer -blazing -bleach -bleak -bleep -blemish -blend -bless -blighted -blimp -bling -blinked -blinker -blinking -blinks -blip -blissful -blitz -blizzard -bloated -bloating -blob -blog -bloomers -blooming -blooper -blot -blouse -blubber -bluff -bluish -blunderer -blunt -blurb -blurred -blurry -blurt -blush -blustery -boaster -boastful -boasting -boat -bobbed -bobbing -bobble -bobcat -bobsled -bobtail -bodacious -body -bogged -boggle -bogus -boil -bok -bolster -bolt -bonanza -bonded -bonding -bondless -boned -bonehead -boneless -bonelike -boney -bonfire -bonnet -bonsai -bonus -bony -boogeyman -boogieman -book -boondocks -booted -booth -bootie -booting -bootlace -bootleg -boots -boozy -borax -boring -borough -borrower -borrowing -boss -botanical -botanist -botany -botch -both -bottle -bottling -bottom -bounce -bouncing -bouncy -bounding -boundless -bountiful -bovine -boxcar -boxer -boxing -boxlike -boxy -breach -breath -breeches -breeching -breeder -breeding -breeze -breezy -brethren -brewery -brewing -briar -bribe -brick -bride -bridged -brigade -bright -brilliant -brim -bring -brink -brisket -briskly -briskness -bristle -brittle -broadband -broadcast -broaden -broadly -broadness -broadside -broadways -broiler -broiling -broken -broker -bronchial -bronco -bronze -bronzing -brook -broom -brought -browbeat -brownnose -browse -browsing -bruising -brunch -brunette -brunt -brush -brussels -brute -brutishly -bubble -bubbling -bubbly -buccaneer -bucked -bucket -buckle -buckshot -buckskin -bucktooth -buckwheat -buddhism -buddhist -budding -buddy -budget -buffalo -buffed -buffer -buffing -buffoon -buggy -bulb -bulge -bulginess -bulgur -bulk -bulldog -bulldozer -bullfight -bullfrog -bullhorn -bullion -bullish -bullpen -bullring -bullseye -bullwhip -bully -bunch -bundle -bungee -bunion -bunkbed -bunkhouse -bunkmate -bunny -bunt -busboy -bush -busily -busload -bust -busybody -buzz -cabana -cabbage -cabbie -cabdriver -cable -caboose -cache -cackle -cacti -cactus -caddie -caddy -cadet -cadillac -cadmium -cage -cahoots -cake -calamari -calamity -calcium -calculate -calculus -caliber -calibrate -calm -caloric -calorie -calzone -camcorder -cameo -camera -camisole -camper -campfire -camping -campsite -campus -canal -canary -cancel -candied -candle -candy -cane -canine -canister -cannabis -canned -canning -cannon -cannot -canola -canon -canopener -canopy -canteen -canyon -capable -capably -capacity -cape -capillary -capital -capitol -capped -capricorn -capsize -capsule -caption -captivate -captive -captivity -capture -caramel -carat -caravan -carbon -cardboard -carded -cardiac -cardigan -cardinal -cardstock -carefully -caregiver -careless -caress -caretaker -cargo -caring -carless -carload -carmaker -carnage -carnation -carnival -carnivore -carol -carpenter -carpentry -carpool -carport -carried -carrot -carrousel -carry -cartel -cartload -carton -cartoon -cartridge -cartwheel -carve -carving -carwash -cascade -case -cash -casing -casino -casket -cassette -casually -casualty -catacomb -catalog -catalyst -catalyze -catapult -cataract -catatonic -catcall -catchable -catcher -catching -catchy -caterer -catering -catfight -catfish -cathedral -cathouse -catlike -catnap -catnip -catsup -cattail -cattishly -cattle -catty -catwalk -caucasian -caucus -causal -causation -cause -causing -cauterize -caution -cautious -cavalier -cavalry -caviar -cavity -cedar -celery -celestial -celibacy -celibate -celtic -cement -census -ceramics -ceremony -certainly -certainty -certified -certify -cesarean -cesspool -chafe -chaffing -chain -chair -chalice -challenge -chamber -chamomile -champion -chance -change -channel -chant -chaos -chaperone -chaplain -chapped -chaps -chapter -character -charbroil -charcoal -charger -charging -chariot -charity -charm -charred -charter -charting -chase -chasing -chaste -chastise -chastity -chatroom -chatter -chatting -chatty -cheating -cheddar -cheek -cheer -cheese -cheesy -chef -chemicals -chemist -chemo -cherisher -cherub -chess -chest -chevron -chevy -chewable -chewer -chewing -chewy -chief -chihuahua -childcare -childhood -childish -childless -childlike -chili -chill -chimp -chip -chirping -chirpy -chitchat -chivalry -chive -chloride -chlorine -choice -chokehold -choking -chomp -chooser -choosing -choosy -chop -chosen -chowder -chowtime -chrome -chubby -chuck -chug -chummy -chump -chunk -churn -chute -cider -cilantro -cinch -cinema -cinnamon -circle -circling -circular -circulate -circus -citable -citadel -citation -citizen -citric -citrus -city -civic -civil -clad -claim -clambake -clammy -clamor -clamp -clamshell -clang -clanking -clapped -clapper -clapping -clarify -clarinet -clarity -clash -clasp -class -clatter -clause -clavicle -claw -clay -clean -clear -cleat -cleaver -cleft -clench -clergyman -clerical -clerk -clever -clicker -client -climate -climatic -cling -clinic -clinking -clip -clique -cloak -clobber -clock -clone -cloning -closable -closure -clothes -clothing -cloud -clover -clubbed -clubbing -clubhouse -clump -clumsily -clumsy -clunky -clustered -clutch -clutter -coach -coagulant -coastal -coaster -coasting -coastland -coastline -coat -coauthor -cobalt -cobbler -cobweb -cocoa -coconut -cod -coeditor -coerce -coexist -coffee -cofounder -cognition -cognitive -cogwheel -coherence -coherent -cohesive -coil -coke -cola -cold -coleslaw -coliseum -collage -collapse -collar -collected -collector -collide -collie -collision -colonial -colonist -colonize -colony -colossal -colt -coma -come -comfort -comfy -comic -coming -comma -commence -commend -comment -commerce -commode -commodity -commodore -common -commotion -commute -commuting -compacted -compacter -compactly -compactor -companion -company -compare -compel -compile -comply -component -composed -composer -composite -compost -composure -compound -compress -comprised -computer -computing -comrade -concave -conceal -conceded -concept -concerned -concert -conch -concierge -concise -conclude -concrete -concur -condense -condiment -condition -condone -conducive -conductor -conduit -cone -confess -confetti -confidant -confident -confider -confiding -configure -confined -confining -confirm -conflict -conform -confound -confront -confused -confusing -confusion -congenial -congested -congrats -congress -conical -conjoined -conjure -conjuror -connected -connector -consensus -consent -console -consoling -consonant -constable -constant -constrain -constrict -construct -consult -consumer -consuming -contact -container -contempt -contend -contented -contently -contents -contest -context -contort -contour -contrite -control -contusion -convene -convent -copartner -cope -copied -copier -copilot -coping -copious -copper -copy -coral -cork -cornball -cornbread -corncob -cornea -corned -corner -cornfield -cornflake -cornhusk -cornmeal -cornstalk -corny -coronary -coroner -corporal -corporate -corral -correct -corridor -corrode -corroding -corrosive -corsage -corset -cortex -cosigner -cosmetics -cosmic -cosmos -cosponsor -cost -cottage -cotton -couch -cough -could -countable -countdown -counting -countless -country -county -courier -covenant -cover -coveted -coveting -coyness -cozily -coziness -cozy -crabbing -crabgrass -crablike -crabmeat -cradle -cradling -crafter -craftily -craftsman -craftwork -crafty -cramp -cranberry -crane -cranial -cranium -crank -crate -crave -craving -crawfish -crawlers -crawling -crayfish -crayon -crazed -crazily -craziness -crazy -creamed -creamer -creamlike -crease -creasing -creatable -create -creation -creative -creature -credible -credibly -credit -creed -creme -creole -crepe -crept -crescent -crested -cresting -crestless -crevice -crewless -crewman -crewmate -crib -cricket -cried -crier -crimp -crimson -cringe -cringing -crinkle -crinkly -crisped -crisping -crisply -crispness -crispy -criteria -critter -croak -crock -crook -croon -crop -cross -crouch -crouton -crowbar -crowd -crown -crucial -crudely -crudeness -cruelly -cruelness -cruelty -crumb -crummiest -crummy -crumpet -crumpled -cruncher -crunching -crunchy -crusader -crushable -crushed -crusher -crushing -crust -crux -crying -cryptic -crystal -cubbyhole -cube -cubical -cubicle -cucumber -cuddle -cuddly -cufflink -culinary -culminate -culpable -culprit -cultivate -cultural -culture -cupbearer -cupcake -cupid -cupped -cupping -curable -curator -curdle -cure -curfew -curing -curled -curler -curliness -curling -curly -curry -curse -cursive -cursor -curtain -curtly -curtsy -curvature -curve -curvy -cushy -cusp -cussed -custard -custodian -custody -customary -customer -customize -customs -cut -cycle -cyclic -cycling -cyclist -cylinder -cymbal -cytoplasm -cytoplast -dab -dad -daffodil -dagger -daily -daintily -dainty -dairy -daisy -dallying -dance -dancing -dandelion -dander -dandruff -dandy -danger -dangle -dangling -daredevil -dares -daringly -darkened -darkening -darkish -darkness -darkroom -darling -darn -dart -darwinism -dash -dastardly -data -datebook -dating -daughter -daunting -dawdler -dawn -daybed -daybreak -daycare -daydream -daylight -daylong -dayroom -daytime -dazzler -dazzling -deacon -deafening -deafness -dealer -dealing -dealmaker -dealt -dean -debatable -debate -debating -debit -debrief -debtless -debtor -debug -debunk -decade -decaf -decal -decathlon -decay -deceased -deceit -deceiver -deceiving -december -decency -decent -deception -deceptive -decibel -decidable -decimal -decimeter -decipher -deck -declared -decline -decode -decompose -decorated -decorator -decoy -decrease -decree -dedicate -dedicator -deduce -deduct -deed -deem -deepen -deeply -deepness -deface -defacing -defame -default -defeat -defection -defective -defendant -defender -defense -defensive -deferral -deferred -defiance -defiant -defile -defiling -define -definite -deflate -deflation -deflator -deflected -deflector -defog -deforest -defraud -defrost -deftly -defuse -defy -degraded -degrading -degrease -degree -dehydrate -deity -dejected -delay -delegate -delegator -delete -deletion -delicacy -delicate -delicious -delighted -delirious -delirium -deliverer -delivery -delouse -delta -deluge -delusion -deluxe -demanding -demeaning -demeanor -demise -democracy -democrat -demote -demotion -demystify -denatured -deniable -denial -denim -denote -dense -density -dental -dentist -denture -deny -deodorant -deodorize -departed -departure -depict -deplete -depletion -deplored -deploy -deport -depose -depraved -depravity -deprecate -depress -deprive -depth -deputize -deputy -derail -deranged -derby -derived -desecrate -deserve -deserving -designate -designed -designer -designing -deskbound -desktop -deskwork -desolate -despair -despise -despite -destiny -destitute -destruct -detached -detail -detection -detective -detector -detention -detergent -detest -detonate -detonator -detoxify -detract -deuce -devalue -deviancy -deviant -deviate -deviation -deviator -device -devious -devotedly -devotee -devotion -devourer -devouring -devoutly -dexterity -dexterous -diabetes -diabetic -diabolic -diagnoses -diagnosis -diagram -dial -diameter -diaper -diaphragm -diary -dice -dicing -dictate -dictation -dictator -difficult -diffused -diffuser -diffusion -diffusive -dig -dilation -diligence -diligent -dill -dilute -dime -diminish -dimly -dimmed -dimmer -dimness -dimple -diner -dingbat -dinghy -dinginess -dingo -dingy -dining -dinner -diocese -dioxide -diploma -dipped -dipper -dipping -directed -direction -directive -directly -directory -direness -dirtiness -disabled -disagree -disallow -disarm -disarray -disaster -disband -disbelief -disburse -discard -discern -discharge -disclose -discolor -discount -discourse -discover -discuss -disdain -disengage -disfigure -disgrace -dish -disinfect -disjoin -disk -dislike -disliking -dislocate -dislodge -disloyal -dismantle -dismay -dismiss -dismount -disobey -disorder -disown -disparate -disparity -dispatch -dispense -dispersal -dispersed -disperser -displace -display -displease -disposal -dispose -disprove -dispute -disregard -disrupt -dissuade -distance -distant -distaste -distill -distinct -distort -distract -distress -district -distrust -ditch -ditto -ditzy -dividable -divided -dividend -dividers -dividing -divinely -diving -divinity -divisible -divisibly -division -divisive -divorcee -dizziness -dizzy -doable -docile -dock -doctrine -document -dodge -dodgy -doily -doing -dole -dollar -dollhouse -dollop -dolly -dolphin -domain -domelike -domestic -dominion -dominoes -donated -donation -donator -donor -donut -doodle -doorbell -doorframe -doorknob -doorman -doormat -doornail -doorpost -doorstep -doorstop -doorway -doozy -dork -dormitory -dorsal -dosage -dose -dotted -doubling -douche -dove -down -dowry -doze -drab -dragging -dragonfly -dragonish -dragster -drainable -drainage -drained -drainer -drainpipe -dramatic -dramatize -drank -drapery -drastic -draw -dreaded -dreadful -dreadlock -dreamboat -dreamily -dreamland -dreamless -dreamlike -dreamt -dreamy -drearily -dreary -drench -dress -drew -dribble -dried -drier -drift -driller -drilling -drinkable -drinking -dripping -drippy -drivable -driven -driver -driveway -driving -drizzle -drizzly -drone -drool -droop -drop-down -dropbox -dropkick -droplet -dropout -dropper -drove -drown -drowsily -drudge -drum -dry -dubbed -dubiously -duchess -duckbill -ducking -duckling -ducktail -ducky -duct -dude -duffel -dugout -duh -duke -duller -dullness -duly -dumping -dumpling -dumpster -duo -dupe -duplex -duplicate -duplicity -durable -durably -duration -duress -during -dusk -dust -dutiful -duty -duvet -dwarf -dweeb -dwelled -dweller -dwelling -dwindle -dwindling -dynamic -dynamite -dynasty -dyslexia -dyslexic -each -eagle -earache -eardrum -earflap -earful -earlobe -early -earmark -earmuff -earphone -earpiece -earplugs -earring -earshot -earthen -earthlike -earthling -earthly -earthworm -earthy -earwig -easeful -easel -easiest -easily -easiness -easing -eastbound -eastcoast -easter -eastward -eatable -eaten -eatery -eating -eats -ebay -ebony -ebook -ecard -eccentric -echo -eclair -eclipse -ecologist -ecology -economic -economist -economy -ecosphere -ecosystem -edge -edginess -edging -edgy -edition -editor -educated -education -educator -eel -effective -effects -efficient -effort -eggbeater -egging -eggnog -eggplant -eggshell -egomaniac -egotism -egotistic -either -eject -elaborate -elastic -elated -elbow -eldercare -elderly -eldest -electable -election -elective -elephant -elevate -elevating -elevation -elevator -eleven -elf -eligible -eligibly -eliminate -elite -elitism -elixir -elk -ellipse -elliptic -elm -elongated -elope -eloquence -eloquent -elsewhere -elude -elusive -elves -email -embargo -embark -embassy -embattled -embellish -ember -embezzle -emblaze -emblem -embody -embolism -emboss -embroider -emcee -emerald -emergency -emission -emit -emote -emoticon -emotion -empathic -empathy -emperor -emphases -emphasis -emphasize -emphatic -empirical -employed -employee -employer -emporium -empower -emptier -emptiness -empty -emu -enable -enactment -enamel -enchanted -enchilada -encircle -enclose -enclosure -encode -encore -encounter -encourage -encroach -encrust -encrypt -endanger -endeared -endearing -ended -ending -endless -endnote -endocrine -endorphin -endorse -endowment -endpoint -endurable -endurance -enduring -energetic -energize -energy -enforced -enforcer -engaged -engaging -engine -engorge -engraved -engraver -engraving -engross -engulf -enhance -enigmatic -enjoyable -enjoyably -enjoyer -enjoying -enjoyment -enlarged -enlarging -enlighten -enlisted -enquirer -enrage -enrich -enroll -enslave -ensnare -ensure -entail -entangled -entering -entertain -enticing -entire -entitle -entity -entomb -entourage -entrap -entree -entrench -entrust -entryway -entwine -enunciate -envelope -enviable -enviably -envious -envision -envoy -envy -enzyme -epic -epidemic -epidermal -epidermis -epidural -epilepsy -epileptic -epilogue -epiphany -episode -equal -equate -equation -equator -equinox -equipment -equity -equivocal -eradicate -erasable -erased -eraser -erasure -ergonomic -errand -errant -erratic -error -erupt -escalate -escalator -escapable -escapade -escapist -escargot -eskimo -esophagus -espionage -espresso -esquire -essay -essence -essential -establish -estate -esteemed -estimate -estimator -estranged -estrogen -etching -eternal -eternity -ethanol -ether -ethically -ethics -euphemism -evacuate -evacuee -evade -evaluate -evaluator -evaporate -evasion -evasive -even -everglade -evergreen -everybody -everyday -everyone -evict -evidence -evident -evil -evoke -evolution -evolve -exact -exalted -example -excavate -excavator -exceeding -exception -excess -exchange -excitable -exciting -exclaim -exclude -excluding -exclusion -exclusive -excretion -excretory -excursion -excusable -excusably -excuse -exemplary -exemplify -exemption -exerciser -exert -exes -exfoliate -exhale -exhaust -exhume -exile -existing -exit -exodus -exonerate -exorcism -exorcist -expand -expanse -expansion -expansive -expectant -expedited -expediter -expel -expend -expenses -expensive -expert -expire -expiring -explain -expletive -explicit -explode -exploit -explore -exploring -exponent -exporter -exposable -expose -exposure -express -expulsion -exquisite -extended -extending -extent -extenuate -exterior -external -extinct -extortion -extradite -extras -extrovert -extrude -extruding -exuberant -fable -fabric -fabulous -facebook -facecloth -facedown -faceless -facelift -faceplate -faceted -facial -facility -facing -facsimile -faction -factoid -factor -factsheet -factual -faculty -fade -fading -failing -falcon -fall -false -falsify -fame -familiar -family -famine -famished -fanatic -fancied -fanciness -fancy -fanfare -fang -fanning -fantasize -fantastic -fantasy -fascism -fastball -faster -fasting -fastness -faucet -favorable -favorably -favored -favoring -favorite -fax -feast -federal -fedora -feeble -feed -feel -feisty -feline -felt-tip -feminine -feminism -feminist -feminize -femur -fence -fencing -fender -ferment -fernlike -ferocious -ferocity -ferret -ferris -ferry -fervor -fester -festival -festive -festivity -fetal -fetch -fever -fiber -fiction -fiddle -fiddling -fidelity -fidgeting -fidgety -fifteen -fifth -fiftieth -fifty -figment -figure -figurine -filing -filled -filler -filling -film -filter -filth -filtrate -finale -finalist -finalize -finally -finance -financial -finch -fineness -finer -finicky -finished -finisher -finishing -finite -finless -finlike -fiscally -fit -five -flaccid -flagman -flagpole -flagship -flagstick -flagstone -flail -flakily -flaky -flame -flammable -flanked -flanking -flannels -flap -flaring -flashback -flashbulb -flashcard -flashily -flashing -flashy -flask -flatbed -flatfoot -flatly -flatness -flatten -flattered -flatterer -flattery -flattop -flatware -flatworm -flavored -flavorful -flavoring -flaxseed -fled -fleshed -fleshy -flick -flier -flight -flinch -fling -flint -flip -flirt -float -flock -flogging -flop -floral -florist -floss -flounder -flyable -flyaway -flyer -flying -flyover -flypaper -foam -foe -fog -foil -folic -folk -follicle -follow -fondling -fondly -fondness -fondue -font -food -fool -footage -football -footbath -footboard -footer -footgear -foothill -foothold -footing -footless -footman -footnote -footpad -footpath -footprint -footrest -footsie -footsore -footwear -footwork -fossil -foster -founder -founding -fountain -fox -foyer -fraction -fracture -fragile -fragility -fragment -fragrance -fragrant -frail -frame -framing -frantic -fraternal -frayed -fraying -frays -freckled -freckles -freebase -freebee -freebie -freedom -freefall -freehand -freeing -freeload -freely -freemason -freeness -freestyle -freeware -freeway -freewill -freezable -freezing -freight -french -frenzied -frenzy -frequency -frequent -fresh -fretful -fretted -friction -friday -fridge -fried -friend -frighten -frightful -frigidity -frigidly -frill -fringe -frisbee -frisk -fritter -frivolous -frolic -from -front -frostbite -frosted -frostily -frosting -frostlike -frosty -froth -frown -frozen -fructose -frugality -frugally -fruit -frustrate -frying -gab -gaffe -gag -gainfully -gaining -gains -gala -gallantly -galleria -gallery -galley -gallon -gallows -gallstone -galore -galvanize -gambling -game -gaming -gamma -gander -gangly -gangrene -gangway -gap -garage -garbage -garden -gargle -garland -garlic -garment -garnet -garnish -garter -gas -gatherer -gathering -gating -gauging -gauntlet -gauze -gave -gawk -gazing -gear -gecko -geek -geiger -gem -gender -generic -generous -genetics -genre -gentile -gentleman -gently -gents -geography -geologic -geologist -geology -geometric -geometry -geranium -gerbil -geriatric -germicide -germinate -germless -germproof -gestate -gestation -gesture -getaway -getting -getup -giant -gibberish -giblet -giddily -giddiness -giddy -gift -gigabyte -gigahertz -gigantic -giggle -giggling -giggly -gigolo -gilled -gills -gimmick -girdle -giveaway -given -giver -giving -gizmo -gizzard -glacial -glacier -glade -gladiator -gladly -glamorous -glamour -glance -glancing -glandular -glare -glaring -glass -glaucoma -glazing -gleaming -gleeful -glider -gliding -glimmer -glimpse -glisten -glitch -glitter -glitzy -gloater -gloating -gloomily -gloomy -glorified -glorifier -glorify -glorious -glory -gloss -glove -glowing -glowworm -glucose -glue -gluten -glutinous -glutton -gnarly -gnat -goal -goatskin -goes -goggles -going -goldfish -goldmine -goldsmith -golf -goliath -gonad -gondola -gone -gong -good -gooey -goofball -goofiness -goofy -google -goon -gopher -gore -gorged -gorgeous -gory -gosling -gossip -gothic -gotten -gout -gown -grab -graceful -graceless -gracious -gradation -graded -grader -gradient -grading -gradually -graduate -graffiti -grafted -grafting -grain -granddad -grandkid -grandly -grandma -grandpa -grandson -granite -granny -granola -grant -granular -grape -graph -grapple -grappling -grasp -grass -gratified -gratify -grating -gratitude -gratuity -gravel -graveness -graves -graveyard -gravitate -gravity -gravy -gray -grazing -greasily -greedily -greedless -greedy -green -greeter -greeting -grew -greyhound -grid -grief -grievance -grieving -grievous -grill -grimace -grimacing -grime -griminess -grimy -grinch -grinning -grip -gristle -grit -groggily -groggy -groin -groom -groove -grooving -groovy -grope -ground -grouped -grout -grove -grower -growing -growl -grub -grudge -grudging -grueling -gruffly -grumble -grumbling -grumbly -grumpily -grunge -grunt -guacamole -guidable -guidance -guide -guiding -guileless -guise -gulf -gullible -gully -gulp -gumball -gumdrop -gumminess -gumming -gummy -gurgle -gurgling -guru -gush -gusto -gusty -gutless -guts -gutter -guy -guzzler -gyration -habitable -habitant -habitat -habitual -hacked -hacker -hacking -hacksaw -had -haggler -haiku -half -halogen -halt -halved -halves -hamburger -hamlet -hammock -hamper -hamster -hamstring -handbag -handball -handbook -handbrake -handcart -handclap -handclasp -handcraft -handcuff -handed -handful -handgrip -handgun -handheld -handiness -handiwork -handlebar -handled -handler -handling -handmade -handoff -handpick -handprint -handrail -handsaw -handset -handsfree -handshake -handstand -handwash -handwork -handwoven -handwrite -handyman -hangnail -hangout -hangover -hangup -hankering -hankie -hanky -haphazard -happening -happier -happiest -happily -happiness -happy -harbor -hardcopy -hardcore -hardcover -harddisk -hardened -hardener -hardening -hardhat -hardhead -hardiness -hardly -hardness -hardship -hardware -hardwired -hardwood -hardy -harmful -harmless -harmonica -harmonics -harmonize -harmony -harness -harpist -harsh -harvest -hash -hassle -haste -hastily -hastiness -hasty -hatbox -hatchback -hatchery -hatchet -hatching -hatchling -hate -hatless -hatred -haunt -haven -hazard -hazelnut -hazily -haziness -hazing -hazy -headache -headband -headboard -headcount -headdress -headed -header -headfirst -headgear -heading -headlamp -headless -headlock -headphone -headpiece -headrest -headroom -headscarf -headset -headsman -headstand -headstone -headway -headwear -heap -heat -heave -heavily -heaviness -heaving -hedge -hedging -heftiness -hefty -helium -helmet -helper -helpful -helping -helpless -helpline -hemlock -hemstitch -hence -henchman -henna -herald -herbal -herbicide -herbs -heritage -hermit -heroics -heroism -herring -herself -hertz -hesitancy -hesitant -hesitate -hexagon -hexagram -hubcap -huddle -huddling -huff -hug -hula -hulk -hull -human -humble -humbling -humbly -humid -humiliate -humility -humming -hummus -humongous -humorist -humorless -humorous -humpback -humped -humvee -hunchback -hundredth -hunger -hungrily -hungry -hunk -hunter -hunting -huntress -huntsman -hurdle -hurled -hurler -hurling -hurray -hurricane -hurried -hurry -hurt -husband -hush -husked -huskiness -hut -hybrid -hydrant -hydrated -hydration -hydrogen -hydroxide -hyperlink -hypertext -hyphen -hypnoses -hypnosis -hypnotic -hypnotism -hypnotist -hypnotize -hypocrisy -hypocrite -ibuprofen -ice -iciness -icing -icky -icon -icy -idealism -idealist -idealize -ideally -idealness -identical -identify -identity -ideology -idiocy -idiom -idly -igloo -ignition -ignore -iguana -illicitly -illusion -illusive -image -imaginary -imagines -imaging -imbecile -imitate -imitation -immature -immerse -immersion -imminent -immobile -immodest -immorally -immortal -immovable -immovably -immunity -immunize -impaired -impale -impart -impatient -impeach -impeding -impending -imperfect -imperial -impish -implant -implement -implicate -implicit -implode -implosion -implosive -imply -impolite -important -importer -impose -imposing -impotence -impotency -impotent -impound -imprecise -imprint -imprison -impromptu -improper -improve -improving -improvise -imprudent -impulse -impulsive -impure -impurity -iodine -iodize -ion -ipad -iphone -ipod -irate -irk -iron -irregular -irrigate -irritable -irritably -irritant -irritate -islamic -islamist -isolated -isolating -isolation -isotope -issue -issuing -italicize -italics -item -itinerary -itunes -ivory -ivy -jab -jackal -jacket -jackknife -jackpot -jailbird -jailbreak -jailer -jailhouse -jalapeno -jam -janitor -january -jargon -jarring -jasmine -jaundice -jaunt -java -jawed -jawless -jawline -jaws -jaybird -jaywalker -jazz -jeep -jeeringly -jellied -jelly -jersey -jester -jet -jiffy -jigsaw -jimmy -jingle -jingling -jinx -jitters -jittery -job -jockey -jockstrap -jogger -jogging -john -joining -jokester -jokingly -jolliness -jolly -jolt -jot -jovial -joyfully -joylessly -joyous -joyride -joystick -jubilance -jubilant -judge -judgingly -judicial -judiciary -judo -juggle -juggling -jugular -juice -juiciness -juicy -jujitsu -jukebox -july -jumble -jumbo -jump -junction -juncture -june -junior -juniper -junkie -junkman -junkyard -jurist -juror -jury -justice -justifier -justify -justly -justness -juvenile -kabob -kangaroo -karaoke -karate -karma -kebab -keenly -keenness -keep -keg -kelp -kennel -kept -kerchief -kerosene -kettle -kick -kiln -kilobyte -kilogram -kilometer -kilowatt -kilt -kimono -kindle -kindling -kindly -kindness -kindred -kinetic -kinfolk -king -kinship -kinsman -kinswoman -kissable -kisser -kissing -kitchen -kite -kitten -kitty -kiwi -kleenex -knapsack -knee -knelt -knickers -knoll -koala -kooky -kosher -krypton -kudos -kung -labored -laborer -laboring -laborious -labrador -ladder -ladies -ladle -ladybug -ladylike -lagged -lagging -lagoon -lair -lake -lance -landed -landfall -landfill -landing -landlady -landless -landline -landlord -landmark -landmass -landmine -landowner -landscape -landside -landslide -language -lankiness -lanky -lantern -lapdog -lapel -lapped -lapping -laptop -lard -large -lark -lash -lasso -last -latch -late -lather -latitude -latrine -latter -latticed -launch -launder -laundry -laurel -lavender -lavish -laxative -lazily -laziness -lazy -lecturer -left -legacy -legal -legend -legged -leggings -legible -legibly -legislate -lego -legroom -legume -legwarmer -legwork -lemon -lend -length -lens -lent -leotard -lesser -letdown -lethargic -lethargy -letter -lettuce -level -leverage -levers -levitate -levitator -liability -liable -liberty -librarian -library -licking -licorice -lid -life -lifter -lifting -liftoff -ligament -likely -likeness -likewise -liking -lilac -lilly -lily -limb -limeade -limelight -limes -limit -limping -limpness -line -lingo -linguini -linguist -lining -linked -linoleum -linseed -lint -lion -lip -liquefy -liqueur -liquid -lisp -list -litigate -litigator -litmus -litter -little -livable -lived -lively -liver -livestock -lividly -living -lizard -lubricant -lubricate -lucid -luckily -luckiness -luckless -lucrative -ludicrous -lugged -lukewarm -lullaby -lumber -luminance -luminous -lumpiness -lumping -lumpish -lunacy -lunar -lunchbox -luncheon -lunchroom -lunchtime -lung -lurch -lure -luridness -lurk -lushly -lushness -luster -lustfully -lustily -lustiness -lustrous -lusty -luxurious -luxury -lying -lyrically -lyricism -lyricist -lyrics -macarena -macaroni -macaw -mace -machine -machinist -magazine -magenta -maggot -magical -magician -magma -magnesium -magnetic -magnetism -magnetize -magnifier -magnify -magnitude -magnolia -mahogany -maimed -majestic -majesty -majorette -majority -makeover -maker -makeshift -making -malformed -malt -mama -mammal -mammary -mammogram -manager -managing -manatee -mandarin -mandate -mandatory -mandolin -manger -mangle -mango -mangy -manhandle -manhole -manhood -manhunt -manicotti -manicure -manifesto -manila -mankind -manlike -manliness -manly -manmade -manned -mannish -manor -manpower -mantis -mantra -manual -many -map -marathon -marauding -marbled -marbles -marbling -march -mardi -margarine -margarita -margin -marigold -marina -marine -marital -maritime -marlin -marmalade -maroon -married -marrow -marry -marshland -marshy -marsupial -marvelous -marxism -mascot -masculine -mashed -mashing -massager -masses -massive -mastiff -matador -matchbook -matchbox -matcher -matching -matchless -material -maternal -maternity -math -mating -matriarch -matrimony -matrix -matron -matted -matter -maturely -maturing -maturity -mauve -maverick -maximize -maximum -maybe -mayday -mayflower -moaner -moaning -mobile -mobility -mobilize -mobster -mocha -mocker -mockup -modified -modify -modular -modulator -module -moisten -moistness -moisture -molar -molasses -mold -molecular -molecule -molehill -mollusk -mom -monastery -monday -monetary -monetize -moneybags -moneyless -moneywise -mongoose -mongrel -monitor -monkhood -monogamy -monogram -monologue -monopoly -monorail -monotone -monotype -monoxide -monsieur -monsoon -monstrous -monthly -monument -moocher -moodiness -moody -mooing -moonbeam -mooned -moonlight -moonlike -moonlit -moonrise -moonscape -moonshine -moonstone -moonwalk -mop -morale -morality -morally -morbidity -morbidly -morphine -morphing -morse -mortality -mortally -mortician -mortified -mortify -mortuary -mosaic -mossy -most -mothball -mothproof -motion -motivate -motivator -motive -motocross -motor -motto -mountable -mountain -mounted -mounting -mourner -mournful -mouse -mousiness -moustache -mousy -mouth -movable -move -movie -moving -mower -mowing -much -muck -mud -mug -mulberry -mulch -mule -mulled -mullets -multiple -multiply -multitask -multitude -mumble -mumbling -mumbo -mummified -mummify -mummy -mumps -munchkin -mundane -municipal -muppet -mural -murkiness -murky -murmuring -muscular -museum -mushily -mushiness -mushroom -mushy -music -musket -muskiness -musky -mustang -mustard -muster -mustiness -musty -mutable -mutate -mutation -mute -mutilated -mutilator -mutiny -mutt -mutual -muzzle -myself -myspace -mystified -mystify -myth -nacho -nag -nail -name -naming -nanny -nanometer -nape -napkin -napped -napping -nappy -narrow -nastily -nastiness -national -native -nativity -natural -nature -naturist -nautical -navigate -navigator -navy -nearby -nearest -nearly -nearness -neatly -neatness -nebula -nebulizer -nectar -negate -negation -negative -neglector -negligee -negligent -negotiate -nemeses -nemesis -neon -nephew -nerd -nervous -nervy -nest -net -neurology -neuron -neurosis -neurotic -neuter -neutron -never -next -nibble -nickname -nicotine -niece -nifty -nimble -nimbly -nineteen -ninetieth -ninja -nintendo -ninth -nuclear -nuclei -nucleus -nugget -nullify -number -numbing -numbly -numbness -numeral -numerate -numerator -numeric -numerous -nuptials -nursery -nursing -nurture -nutcase -nutlike -nutmeg -nutrient -nutshell -nuttiness -nutty -nuzzle -nylon -oaf -oak -oasis -oat -obedience -obedient -obituary -object -obligate -obliged -oblivion -oblivious -oblong -obnoxious -oboe -obscure -obscurity -observant -observer -observing -obsessed -obsession -obsessive -obsolete -obstacle -obstinate -obstruct -obtain -obtrusive -obtuse -obvious -occultist -occupancy -occupant -occupier -occupy -ocean -ocelot -octagon -octane -october -octopus -ogle -oil -oink -ointment -okay -old -olive -olympics -omega -omen -ominous -omission -omit -omnivore -onboard -oncoming -ongoing -onion -online -onlooker -only -onscreen -onset -onshore -onslaught -onstage -onto -onward -onyx -oops -ooze -oozy -opacity -opal -open -operable -operate -operating -operation -operative -operator -opium -opossum -opponent -oppose -opposing -opposite -oppressed -oppressor -opt -opulently -osmosis -other -otter -ouch -ought -ounce -outage -outback -outbid -outboard -outbound -outbreak -outburst -outcast -outclass -outcome -outdated -outdoors -outer -outfield -outfit -outflank -outgoing -outgrow -outhouse -outing -outlast -outlet -outline -outlook -outlying -outmatch -outmost -outnumber -outplayed -outpost -outpour -output -outrage -outrank -outreach -outright -outscore -outsell -outshine -outshoot -outsider -outskirts -outsmart -outsource -outspoken -outtakes -outthink -outward -outweigh -outwit -oval -ovary -oven -overact -overall -overarch -overbid -overbill -overbite -overblown -overboard -overbook -overbuilt -overcast -overcoat -overcome -overcook -overcrowd -overdraft -overdrawn -overdress -overdrive -overdue -overeager -overeater -overexert -overfed -overfeed -overfill -overflow -overfull -overgrown -overhand -overhang -overhaul -overhead -overhear -overheat -overhung -overjoyed -overkill -overlabor -overlaid -overlap -overlay -overload -overlook -overlord -overlying -overnight -overpass -overpay -overplant -overplay -overpower -overprice -overrate -overreach -overreact -override -overripe -overrule -overrun -overshoot -overshot -oversight -oversized -oversleep -oversold -overspend -overstate -overstay -overstep -overstock -overstuff -oversweet -overtake -overthrow -overtime -overtly -overtone -overture -overturn -overuse -overvalue -overview -overwrite -owl -oxford -oxidant -oxidation -oxidize -oxidizing -oxygen -oxymoron -oyster -ozone -paced -pacemaker -pacific -pacifier -pacifism -pacifist -pacify -padded -padding -paddle -paddling -padlock -pagan -pager -paging -pajamas -palace -palatable -palm -palpable -palpitate -paltry -pampered -pamperer -pampers -pamphlet -panama -pancake -pancreas -panda -pandemic -pang -panhandle -panic -panning -panorama -panoramic -panther -pantomime -pantry -pants -pantyhose -paparazzi -papaya -paper -paprika -papyrus -parabola -parachute -parade -paradox -paragraph -parakeet -paralegal -paralyses -paralysis -paralyze -paramedic -parameter -paramount -parasail -parasite -parasitic -parcel -parched -parchment -pardon -parish -parka -parking -parkway -parlor -parmesan -parole -parrot -parsley -parsnip -partake -parted -parting -partition -partly -partner -partridge -party -passable -passably -passage -passcode -passenger -passerby -passing -passion -passive -passivism -passover -passport -password -pasta -pasted -pastel -pastime -pastor -pastrami -pasture -pasty -patchwork -patchy -paternal -paternity -path -patience -patient -patio -patriarch -patriot -patrol -patronage -patronize -pauper -pavement -paver -pavestone -pavilion -paving -pawing -payable -payback -paycheck -payday -payee -payer -paying -payment -payphone -payroll -pebble -pebbly -pecan -pectin -peculiar -peddling -pediatric -pedicure -pedigree -pedometer -pegboard -pelican -pellet -pelt -pelvis -penalize -penalty -pencil -pendant -pending -penholder -penknife -pennant -penniless -penny -penpal -pension -pentagon -pentagram -pep -perceive -percent -perch -percolate -perennial -perfected -perfectly -perfume -periscope -perish -perjurer -perjury -perkiness -perky -perm -peroxide -perpetual -perplexed -persecute -persevere -persuaded -persuader -pesky -peso -pessimism -pessimist -pester -pesticide -petal -petite -petition -petri -petroleum -petted -petticoat -pettiness -petty -petunia -phantom -phobia -phoenix -phonebook -phoney -phonics -phoniness -phony -phosphate -photo -phrase -phrasing -placard -placate -placidly -plank -planner -plant -plasma -plaster -plastic -plated -platform -plating -platinum -platonic -platter -platypus -plausible -plausibly -playable -playback -player -playful -playgroup -playhouse -playing -playlist -playmaker -playmate -playoff -playpen -playroom -playset -plaything -playtime -plaza -pleading -pleat -pledge -plentiful -plenty -plethora -plexiglas -pliable -plod -plop -plot -plow -ploy -pluck -plug -plunder -plunging -plural -plus -plutonium -plywood -poach -pod -poem -poet -pogo -pointed -pointer -pointing -pointless -pointy -poise -poison -poker -poking -polar -police -policy -polio -polish -politely -polka -polo -polyester -polygon -polygraph -polymer -poncho -pond -pony -popcorn -pope -poplar -popper -poppy -popsicle -populace -popular -populate -porcupine -pork -porous -porridge -portable -portal -portfolio -porthole -portion -portly -portside -poser -posh -posing -possible -possibly -possum -postage -postal -postbox -postcard -posted -poster -posting -postnasal -posture -postwar -pouch -pounce -pouncing -pound -pouring -pout -powdered -powdering -powdery -power -powwow -pox -praising -prance -prancing -pranker -prankish -prankster -prayer -praying -preacher -preaching -preachy -preamble -precinct -precise -precision -precook -precut -predator -predefine -predict -preface -prefix -preflight -preformed -pregame -pregnancy -pregnant -preheated -prelaunch -prelaw -prelude -premiere -premises -premium -prenatal -preoccupy -preorder -prepaid -prepay -preplan -preppy -preschool -prescribe -preseason -preset -preshow -president -presoak -press -presume -presuming -preteen -pretended -pretender -pretense -pretext -pretty -pretzel -prevail -prevalent -prevent -preview -previous -prewar -prewashed -prideful -pried -primal -primarily -primary -primate -primer -primp -princess -print -prior -prism -prison -prissy -pristine -privacy -private -privatize -prize -proactive -probable -probably -probation -probe -probing -probiotic -problem -procedure -process -proclaim -procreate -procurer -prodigal -prodigy -produce -product -profane -profanity -professed -professor -profile -profound -profusely -progeny -prognosis -program -progress -projector -prologue -prolonged -promenade -prominent -promoter -promotion -prompter -promptly -prone -prong -pronounce -pronto -proofing -proofread -proofs -propeller -properly -property -proponent -proposal -propose -props -prorate -protector -protegee -proton -prototype -protozoan -protract -protrude -proud -provable -proved -proven -provided -provider -providing -province -proving -provoke -provoking -provolone -prowess -prowler -prowling -proximity -proxy -prozac -prude -prudishly -prune -pruning -pry -psychic -public -publisher -pucker -pueblo -pug -pull -pulmonary -pulp -pulsate -pulse -pulverize -puma -pumice -pummel -punch -punctual -punctuate -punctured -pungent -punisher -punk -pupil -puppet -puppy -purchase -pureblood -purebred -purely -pureness -purgatory -purge -purging -purifier -purify -purist -puritan -purity -purple -purplish -purposely -purr -purse -pursuable -pursuant -pursuit -purveyor -pushcart -pushchair -pusher -pushiness -pushing -pushover -pushpin -pushup -pushy -putdown -putt -puzzle -puzzling -pyramid -pyromania -python -quack -quadrant -quail -quaintly -quake -quaking -qualified -qualifier -qualify -quality -qualm -quantum -quarrel -quarry -quartered -quarterly -quarters -quartet -quench -query -quicken -quickly -quickness -quicksand -quickstep -quiet -quill -quilt -quintet -quintuple -quirk -quit -quiver -quizzical -quotable -quotation -quote -rabid -race -racing -racism -rack -racoon -radar -radial -radiance -radiantly -radiated -radiation -radiator -radio -radish -raffle -raft -rage -ragged -raging -ragweed -raider -railcar -railing -railroad -railway -raisin -rake -raking -rally -ramble -rambling -ramp -ramrod -ranch -rancidity -random -ranged -ranger -ranging -ranked -ranking -ransack -ranting -rants -rare -rarity -rascal -rash -rasping -ravage -raven -ravine -raving -ravioli -ravishing -reabsorb -reach -reacquire -reaction -reactive -reactor -reaffirm -ream -reanalyze -reappear -reapply -reappoint -reapprove -rearrange -rearview -reason -reassign -reassure -reattach -reawake -rebalance -rebate -rebel -rebirth -reboot -reborn -rebound -rebuff -rebuild -rebuilt -reburial -rebuttal -recall -recant -recapture -recast -recede -recent -recess -recharger -recipient -recital -recite -reckless -reclaim -recliner -reclining -recluse -reclusive -recognize -recoil -recollect -recolor -reconcile -reconfirm -reconvene -recopy -record -recount -recoup -recovery -recreate -rectal -rectangle -rectified -rectify -recycled -recycler -recycling -reemerge -reenact -reenter -reentry -reexamine -referable -referee -reference -refill -refinance -refined -refinery -refining -refinish -reflected -reflector -reflex -reflux -refocus -refold -reforest -reformat -reformed -reformer -reformist -refract -refrain -refreeze -refresh -refried -refueling -refund -refurbish -refurnish -refusal -refuse -refusing -refutable -refute -regain -regalia -regally -reggae -regime -region -register -registrar -registry -regress -regretful -regroup -regular -regulate -regulator -rehab -reheat -rehire -rehydrate -reimburse -reissue -reiterate -rejoice -rejoicing -rejoin -rekindle -relapse -relapsing -relatable -related -relation -relative -relax -relay -relearn -release -relenting -reliable -reliably -reliance -reliant -relic -relieve -relieving -relight -relish -relive -reload -relocate -relock -reluctant -rely -remake -remark -remarry -rematch -remedial -remedy -remember -reminder -remindful -remission -remix -remnant -remodeler -remold -remorse -remote -removable -removal -removed -remover -removing -rename -renderer -rendering -rendition -renegade -renewable -renewably -renewal -renewed -renounce -renovate -renovator -rentable -rental -rented -renter -reoccupy -reoccur -reopen -reorder -repackage -repacking -repaint -repair -repave -repaying -repayment -repeal -repeated -repeater -repent -rephrase -replace -replay -replica -reply -reporter -repose -repossess -repost -repressed -reprimand -reprint -reprise -reproach -reprocess -reproduce -reprogram -reps -reptile -reptilian -repugnant -repulsion -repulsive -repurpose -reputable -reputably -request -require -requisite -reroute -rerun -resale -resample -rescuer -reseal -research -reselect -reseller -resemble -resend -resent -reset -reshape -reshoot -reshuffle -residence -residency -resident -residual -residue -resigned -resilient -resistant -resisting -resize -resolute -resolved -resonant -resonate -resort -resource -respect -resubmit -result -resume -resupply -resurface -resurrect -retail -retainer -retaining -retake -retaliate -retention -rethink -retinal -retired -retiree -retiring -retold -retool -retorted -retouch -retrace -retract -retrain -retread -retreat -retrial -retrieval -retriever -retry -return -retying -retype -reunion -reunite -reusable -reuse -reveal -reveler -revenge -revenue -reverb -revered -reverence -reverend -reversal -reverse -reversing -reversion -revert -revisable -revise -revision -revisit -revivable -revival -reviver -reviving -revocable -revoke -revolt -revolver -revolving -reward -rewash -rewind -rewire -reword -rework -rewrap -rewrite -rhyme -ribbon -ribcage -rice -riches -richly -richness -rickety -ricotta -riddance -ridden -ride -riding -rifling -rift -rigging -rigid -rigor -rimless -rimmed -rind -rink -rinse -rinsing -riot -ripcord -ripeness -ripening -ripping -ripple -rippling -riptide -rise -rising -risk -risotto -ritalin -ritzy -rival -riverbank -riverbed -riverboat -riverside -riveter -riveting -roamer -roaming -roast -robbing -robe -robin -robotics -robust -rockband -rocker -rocket -rockfish -rockiness -rocking -rocklike -rockslide -rockstar -rocky -rogue -roman -romp -rope -roping -roster -rosy -rotten -rotting -rotunda -roulette -rounding -roundish -roundness -roundup -roundworm -routine -routing -rover -roving -royal -rubbed -rubber -rubbing -rubble -rubdown -ruby -ruckus -rudder -rug -ruined -rule -rumble -rumbling -rummage -rumor -runaround -rundown -runner -running -runny -runt -runway -rupture -rural -ruse -rush -rust -rut -sabbath -sabotage -sacrament -sacred -sacrifice -sadden -saddlebag -saddled -saddling -sadly -sadness -safari -safeguard -safehouse -safely -safeness -saffron -saga -sage -sagging -saggy -said -saint -sake -salad -salami -salaried -salary -saline -salon -saloon -salsa -salt -salutary -salute -salvage -salvaging -salvation -same -sample -sampling -sanction -sanctity -sanctuary -sandal -sandbag -sandbank -sandbar -sandblast -sandbox -sanded -sandfish -sanding -sandlot -sandpaper -sandpit -sandstone -sandstorm -sandworm -sandy -sanitary -sanitizer -sank -santa -sapling -sappiness -sappy -sarcasm -sarcastic -sardine -sash -sasquatch -sassy -satchel -satiable -satin -satirical -satisfied -satisfy -saturate -saturday -sauciness -saucy -sauna -savage -savanna -saved -savings -savior -savor -saxophone -say -scabbed -scabby -scalded -scalding -scale -scaling -scallion -scallop -scalping -scam -scandal -scanner -scanning -scant -scapegoat -scarce -scarcity -scarecrow -scared -scarf -scarily -scariness -scarring -scary -scavenger -scenic -schedule -schematic -scheme -scheming -schilling -schnapps -scholar -science -scientist -scion -scoff -scolding -scone -scoop -scooter -scope -scorch -scorebook -scorecard -scored -scoreless -scorer -scoring -scorn -scorpion -scotch -scoundrel -scoured -scouring -scouting -scouts -scowling -scrabble -scraggly -scrambled -scrambler -scrap -scratch -scrawny -screen -scribble -scribe -scribing -scrimmage -script -scroll -scrooge -scrounger -scrubbed -scrubber -scruffy -scrunch -scrutiny -scuba -scuff -sculptor -sculpture -scurvy -scuttle -secluded -secluding -seclusion -second -secrecy -secret -sectional -sector -secular -securely -security -sedan -sedate -sedation -sedative -sediment -seduce -seducing -segment -seismic -seizing -seldom -selected -selection -selective -selector -self -seltzer -semantic -semester -semicolon -semifinal -seminar -semisoft -semisweet -senate -senator -send -senior -senorita -sensation -sensitive -sensitize -sensually -sensuous -sepia -september -septic -septum -sequel -sequence -sequester -series -sermon -serotonin -serpent -serrated -serve -service -serving -sesame -sessions -setback -setting -settle -settling -setup -sevenfold -seventeen -seventh -seventy -severity -shabby -shack -shaded -shadily -shadiness -shading -shadow -shady -shaft -shakable -shakily -shakiness -shaking -shaky -shale -shallot -shallow -shame -shampoo -shamrock -shank -shanty -shape -shaping -share -sharpener -sharper -sharpie -sharply -sharpness -shawl -sheath -shed -sheep -sheet -shelf -shell -shelter -shelve -shelving -sherry -shield -shifter -shifting -shiftless -shifty -shimmer -shimmy -shindig -shine -shingle -shininess -shining -shiny -ship -shirt -shivering -shock -shone -shoplift -shopper -shopping -shoptalk -shore -shortage -shortcake -shortcut -shorten -shorter -shorthand -shortlist -shortly -shortness -shorts -shortwave -shorty -shout -shove -showbiz -showcase -showdown -shower -showgirl -showing -showman -shown -showoff -showpiece -showplace -showroom -showy -shrank -shrapnel -shredder -shredding -shrewdly -shriek -shrill -shrimp -shrine -shrink -shrivel -shrouded -shrubbery -shrubs -shrug -shrunk -shucking -shudder -shuffle -shuffling -shun -shush -shut -shy -siamese -siberian -sibling -siding -sierra -siesta -sift -sighing -silenced -silencer -silent -silica -silicon -silk -silliness -silly -silo -silt -silver -similarly -simile -simmering -simple -simplify -simply -sincere -sincerity -singer -singing -single -singular -sinister -sinless -sinner -sinuous -sip -siren -sister -sitcom -sitter -sitting -situated -situation -sixfold -sixteen -sixth -sixties -sixtieth -sixtyfold -sizable -sizably -size -sizing -sizzle -sizzling -skater -skating -skedaddle -skeletal -skeleton -skeptic -sketch -skewed -skewer -skid -skied -skier -skies -skiing -skilled -skillet -skillful -skimmed -skimmer -skimming -skimpily -skincare -skinhead -skinless -skinning -skinny -skintight -skipper -skipping -skirmish -skirt -skittle -skydiver -skylight -skyline -skype -skyrocket -skyward -slab -slacked -slacker -slacking -slackness -slacks -slain -slam -slander -slang -slapping -slapstick -slashed -slashing -slate -slather -slaw -sled -sleek -sleep -sleet -sleeve -slept -sliceable -sliced -slicer -slicing -slick -slider -slideshow -sliding -slighted -slighting -slightly -slimness -slimy -slinging -slingshot -slinky -slip -slit -sliver -slobbery -slogan -sloped -sloping -sloppily -sloppy -slot -slouching -slouchy -sludge -slug -slum -slurp -slush -sly -small -smartly -smartness -smasher -smashing -smashup -smell -smelting -smile -smilingly -smirk -smite -smith -smitten -smock -smog -smoked -smokeless -smokiness -smoking -smoky -smolder -smooth -smother -smudge -smudgy -smuggler -smuggling -smugly -smugness -snack -snagged -snaking -snap -snare -snarl -snazzy -sneak -sneer -sneeze -sneezing -snide -sniff -snippet -snipping -snitch -snooper -snooze -snore -snoring -snorkel -snort -snout -snowbird -snowboard -snowbound -snowcap -snowdrift -snowdrop -snowfall -snowfield -snowflake -snowiness -snowless -snowman -snowplow -snowshoe -snowstorm -snowsuit -snowy -snub -snuff -snuggle -snugly -snugness -speak -spearfish -spearhead -spearman -spearmint -species -specimen -specked -speckled -specks -spectacle -spectator -spectrum -speculate -speech -speed -spellbind -speller -spelling -spendable -spender -spending -spent -spew -sphere -spherical -sphinx -spider -spied -spiffy -spill -spilt -spinach -spinal -spindle -spinner -spinning -spinout -spinster -spiny -spiral -spirited -spiritism -spirits -spiritual -splashed -splashing -splashy -splatter -spleen -splendid -splendor -splice -splicing -splinter -splotchy -splurge -spoilage -spoiled -spoiler -spoiling -spoils -spoken -spokesman -sponge -spongy -sponsor -spoof -spookily -spooky -spool -spoon -spore -sporting -sports -sporty -spotless -spotlight -spotted -spotter -spotting -spotty -spousal -spouse -spout -sprain -sprang -sprawl -spray -spree -sprig -spring -sprinkled -sprinkler -sprint -sprite -sprout -spruce -sprung -spry -spud -spur -sputter -spyglass -squabble -squad -squall -squander -squash -squatted -squatter -squatting -squeak -squealer -squealing -squeamish -squeegee -squeeze -squeezing -squid -squiggle -squiggly -squint -squire -squirt -squishier -squishy -stability -stabilize -stable -stack -stadium -staff -stage -staging -stagnant -stagnate -stainable -stained -staining -stainless -stalemate -staleness -stalling -stallion -stamina -stammer -stamp -stand -stank -staple -stapling -starboard -starch -stardom -stardust -starfish -stargazer -staring -stark -starless -starlet -starlight -starlit -starring -starry -starship -starter -starting -startle -startling -startup -starved -starving -stash -state -static -statistic -statue -stature -status -statute -statutory -staunch -stays -steadfast -steadier -steadily -steadying -steam -steed -steep -steerable -steering -steersman -stegosaur -stellar -stem -stench -stencil -step -stereo -sterile -sterility -sterilize -sterling -sternness -sternum -stew -stick -stiffen -stiffly -stiffness -stifle -stifling -stillness -stilt -stimulant -stimulate -stimuli -stimulus -stinger -stingily -stinging -stingray -stingy -stinking -stinky -stipend -stipulate -stir -stitch -stock -stoic -stoke -stole -stomp -stonewall -stoneware -stonework -stoning -stony -stood -stooge -stool -stoop -stoplight -stoppable -stoppage -stopped -stopper -stopping -stopwatch -storable -storage -storeroom -storewide -storm -stout -stove -stowaway -stowing -straddle -straggler -strained -strainer -straining -strangely -stranger -strangle -strategic -strategy -stratus -straw -stray -streak -stream -street -strength -strenuous -strep -stress -stretch -strewn -stricken -strict -stride -strife -strike -striking -strive -striving -strobe -strode -stroller -strongbox -strongly -strongman -struck -structure -strudel -struggle -strum -strung -strut -stubbed -stubble -stubbly -stubborn -stucco -stuck -student -studied -studio -study -stuffed -stuffing -stuffy -stumble -stumbling -stump -stung -stunned -stunner -stunning -stunt -stupor -sturdily -sturdy -styling -stylishly -stylist -stylized -stylus -suave -subarctic -subatomic -subdivide -subdued -subduing -subfloor -subgroup -subheader -subject -sublease -sublet -sublevel -sublime -submarine -submerge -submersed -submitter -subpanel -subpar -subplot -subprime -subscribe -subscript -subsector -subside -subsiding -subsidize -subsidy -subsoil -subsonic -substance -subsystem -subtext -subtitle -subtly -subtotal -subtract -subtype -suburb -subway -subwoofer -subzero -succulent -such -suction -sudden -sudoku -suds -sufferer -suffering -suffice -suffix -suffocate -suffrage -sugar -suggest -suing -suitable -suitably -suitcase -suitor -sulfate -sulfide -sulfite -sulfur -sulk -sullen -sulphate -sulphuric -sultry -superbowl -superglue -superhero -superior -superjet -superman -supermom -supernova -supervise -supper -supplier -supply -support -supremacy -supreme -surcharge -surely -sureness -surface -surfacing -surfboard -surfer -surgery -surgical -surging -surname -surpass -surplus -surprise -surreal -surrender -surrogate -surround -survey -survival -survive -surviving -survivor -sushi -suspect -suspend -suspense -sustained -sustainer -swab -swaddling -swagger -swampland -swan -swapping -swarm -sway -swear -sweat -sweep -swell -swept -swerve -swifter -swiftly -swiftness -swimmable -swimmer -swimming -swimsuit -swimwear -swinger -swinging -swipe -swirl -switch -swivel -swizzle -swooned -swoop -swoosh -swore -sworn -swung -sycamore -sympathy -symphonic -symphony -symptom -synapse -syndrome -synergy -synopses -synopsis -synthesis -synthetic -syrup -system -t-shirt -tabasco -tabby -tableful -tables -tablet -tableware -tabloid -tackiness -tacking -tackle -tackling -tacky -taco -tactful -tactical -tactics -tactile -tactless -tadpole -taekwondo -tag -tainted -take -taking -talcum -talisman -tall -talon -tamale -tameness -tamer -tamper -tank -tanned -tannery -tanning -tantrum -tapeless -tapered -tapering -tapestry -tapioca -tapping -taps -tarantula -target -tarmac -tarnish -tarot -tartar -tartly -tartness -task -tassel -taste -tastiness -tasting -tasty -tattered -tattle -tattling -tattoo -taunt -tavern -thank -that -thaw -theater -theatrics -thee -theft -theme -theology -theorize -thermal -thermos -thesaurus -these -thesis -thespian -thicken -thicket -thickness -thieving -thievish -thigh -thimble -thing -think -thinly -thinner -thinness -thinning -thirstily -thirsting -thirsty -thirteen -thirty -thong -thorn -those -thousand -thrash -thread -threaten -threefold -thrift -thrill -thrive -thriving -throat -throbbing -throng -throttle -throwaway -throwback -thrower -throwing -thud -thumb -thumping -thursday -thus -thwarting -thyself -tiara -tibia -tidal -tidbit -tidiness -tidings -tidy -tiger -tighten -tightly -tightness -tightrope -tightwad -tigress -tile -tiling -till -tilt -timid -timing -timothy -tinderbox -tinfoil -tingle -tingling -tingly -tinker -tinkling -tinsel -tinsmith -tint -tinwork -tiny -tipoff -tipped -tipper -tipping -tiptoeing -tiptop -tiring -tissue -trace -tracing -track -traction -tractor -trade -trading -tradition -traffic -tragedy -trailing -trailside -train -traitor -trance -tranquil -transfer -transform -translate -transpire -transport -transpose -trapdoor -trapeze -trapezoid -trapped -trapper -trapping -traps -trash -travel -traverse -travesty -tray -treachery -treading -treadmill -treason -treat -treble -tree -trekker -tremble -trembling -tremor -trench -trend -trespass -triage -trial -triangle -tribesman -tribunal -tribune -tributary -tribute -triceps -trickery -trickily -tricking -trickle -trickster -tricky -tricolor -tricycle -trident -tried -trifle -trifocals -trillion -trilogy -trimester -trimmer -trimming -trimness -trinity -trio -tripod -tripping -triumph -trivial -trodden -trolling -trombone -trophy -tropical -tropics -trouble -troubling -trough -trousers -trout -trowel -truce -truck -truffle -trump -trunks -trustable -trustee -trustful -trusting -trustless -truth -try -tubby -tubeless -tubular -tucking -tuesday -tug -tuition -tulip -tumble -tumbling -tummy -turban -turbine -turbofan -turbojet -turbulent -turf -turkey -turmoil -turret -turtle -tusk -tutor -tutu -tux -tweak -tweed -tweet -tweezers -twelve -twentieth -twenty -twerp -twice -twiddle -twiddling -twig -twilight -twine -twins -twirl -twistable -twisted -twister -twisting -twisty -twitch -twitter -tycoon -tying -tyke -udder -ultimate -ultimatum -ultra -umbilical -umbrella -umpire -unabashed -unable -unadorned -unadvised -unafraid -unaired -unaligned -unaltered -unarmored -unashamed -unaudited -unawake -unaware -unbaked -unbalance -unbeaten -unbend -unbent -unbiased -unbitten -unblended -unblessed -unblock -unbolted -unbounded -unboxed -unbraided -unbridle -unbroken -unbuckled -unbundle -unburned -unbutton -uncanny -uncapped -uncaring -uncertain -unchain -unchanged -uncharted -uncheck -uncivil -unclad -unclaimed -unclamped -unclasp -uncle -unclip -uncloak -unclog -unclothed -uncoated -uncoiled -uncolored -uncombed -uncommon -uncooked -uncork -uncorrupt -uncounted -uncouple -uncouth -uncover -uncross -uncrown -uncrushed -uncured -uncurious -uncurled -uncut -undamaged -undated -undaunted -undead -undecided -undefined -underage -underarm -undercoat -undercook -undercut -underdog -underdone -underfed -underfeed -underfoot -undergo -undergrad -underhand -underline -underling -undermine -undermost -underpaid -underpass -underpay -underrate -undertake -undertone -undertook -undertow -underuse -underwear -underwent -underwire -undesired -undiluted -undivided -undocked -undoing -undone -undrafted -undress -undrilled -undusted -undying -unearned -unearth -unease -uneasily -uneasy -uneatable -uneaten -unedited -unelected -unending -unengaged -unenvied -unequal -unethical -uneven -unexpired -unexposed -unfailing -unfair -unfasten -unfazed -unfeeling -unfiled -unfilled -unfitted -unfitting -unfixable -unfixed -unflawed -unfocused -unfold -unfounded -unframed -unfreeze -unfrosted -unfrozen -unfunded -unglazed -ungloved -unglue -ungodly -ungraded -ungreased -unguarded -unguided -unhappily -unhappy -unharmed -unhealthy -unheard -unhearing -unheated -unhelpful -unhidden -unhinge -unhitched -unholy -unhook -unicorn -unicycle -unified -unifier -uniformed -uniformly -unify -unimpeded -uninjured -uninstall -uninsured -uninvited -union -uniquely -unisexual -unison -unissued -unit -universal -universe -unjustly -unkempt -unkind -unknotted -unknowing -unknown -unlaced -unlatch -unlawful -unleaded -unlearned -unleash -unless -unleveled -unlighted -unlikable -unlimited -unlined -unlinked -unlisted -unlit -unlivable -unloaded -unloader -unlocked -unlocking -unlovable -unloved -unlovely -unloving -unluckily -unlucky -unmade -unmanaged -unmanned -unmapped -unmarked -unmasked -unmasking -unmatched -unmindful -unmixable -unmixed -unmolded -unmoral -unmovable -unmoved -unmoving -unnamable -unnamed -unnatural -unneeded -unnerve -unnerving -unnoticed -unopened -unopposed -unpack -unpadded -unpaid -unpainted -unpaired -unpaved -unpeeled -unpicked -unpiloted -unpinned -unplanned -unplanted -unpleased -unpledged -unplowed -unplug -unpopular -unproven -unquote -unranked -unrated -unraveled -unreached -unread -unreal -unreeling -unrefined -unrelated -unrented -unrest -unretired -unrevised -unrigged -unripe -unrivaled -unroasted -unrobed -unroll -unruffled -unruly -unrushed -unsaddle -unsafe -unsaid -unsalted -unsaved -unsavory -unscathed -unscented -unscrew -unsealed -unseated -unsecured -unseeing -unseemly -unseen -unselect -unselfish -unsent -unsettled -unshackle -unshaken -unshaved -unshaven -unsheathe -unshipped -unsightly -unsigned -unskilled -unsliced -unsmooth -unsnap -unsocial -unsoiled -unsold -unsolved -unsorted -unspoiled -unspoken -unstable -unstaffed -unstamped -unsteady -unsterile -unstirred -unstitch -unstopped -unstuck -unstuffed -unstylish -unsubtle -unsubtly -unsuited -unsure -unsworn -untagged -untainted -untaken -untamed -untangled -untapped -untaxed -unthawed -unthread -untidy -untie -until -untimed -untimely -untitled -untoasted -untold -untouched -untracked -untrained -untreated -untried -untrimmed -untrue -untruth -unturned -untwist -untying -unusable -unused -unusual -unvalued -unvaried -unvarying -unveiled -unveiling -unvented -unviable -unvisited -unvocal -unwanted -unwarlike -unwary -unwashed -unwatched -unweave -unwed -unwelcome -unwell -unwieldy -unwilling -unwind -unwired -unwitting -unwomanly -unworldly -unworn -unworried -unworthy -unwound -unwoven -unwrapped -unwritten -unzip -upbeat -upchuck -upcoming -upcountry -update -upfront -upgrade -upheaval -upheld -uphill -uphold -uplifted -uplifting -upload -upon -upper -upright -uprising -upriver -uproar -uproot -upscale -upside -upstage -upstairs -upstart -upstate -upstream -upstroke -upswing -uptake -uptight -uptown -upturned -upward -upwind -uranium -urban -urchin -urethane -urgency -urgent -urging -urologist -urology -usable -usage -useable -used -uselessly -user -usher -usual -utensil -utility -utilize -utmost -utopia -utter -vacancy -vacant -vacate -vacation -vagabond -vagrancy -vagrantly -vaguely -vagueness -valiant -valid -valium -valley -valuables -value -vanilla -vanish -vanity -vanquish -vantage -vaporizer -variable -variably -varied -variety -various -varmint -varnish -varsity -varying -vascular -vaseline -vastly -vastness -veal -vegan -veggie -vehicular -velcro -velocity -velvet -vendetta -vending -vendor -veneering -vengeful -venomous -ventricle -venture -venue -venus -verbalize -verbally -verbose -verdict -verify -verse -version -versus -vertebrae -vertical -vertigo -very -vessel -vest -veteran -veto -vexingly -viability -viable -vibes -vice -vicinity -victory -video -viewable -viewer -viewing -viewless -viewpoint -vigorous -village -villain -vindicate -vineyard -vintage -violate -violation -violator -violet -violin -viper -viral -virtual -virtuous -virus -visa -viscosity -viscous -viselike -visible -visibly -vision -visiting -visitor -visor -vista -vitality -vitalize -vitally -vitamins -vivacious -vividly -vividness -vixen -vocalist -vocalize -vocally -vocation -voice -voicing -void -volatile -volley -voltage -volumes -voter -voting -voucher -vowed -vowel -voyage -wackiness -wad -wafer -waffle -waged -wager -wages -waggle -wagon -wake -waking -walk -walmart -walnut -walrus -waltz -wand -wannabe -wanted -wanting -wasabi -washable -washbasin -washboard -washbowl -washcloth -washday -washed -washer -washhouse -washing -washout -washroom -washstand -washtub -wasp -wasting -watch -water -waviness -waving -wavy -whacking -whacky -wham -wharf -wheat -whenever -whiff -whimsical -whinny -whiny -whisking -whoever -whole -whomever -whoopee -whooping -whoops -why -wick -widely -widen -widget -widow -width -wieldable -wielder -wife -wifi -wikipedia -wildcard -wildcat -wilder -wildfire -wildfowl -wildland -wildlife -wildly -wildness -willed -willfully -willing -willow -willpower -wilt -wimp -wince -wincing -wind -wing -winking -winner -winnings -winter -wipe -wired -wireless -wiring -wiry -wisdom -wise -wish -wisplike -wispy -wistful -wizard -wobble -wobbling -wobbly -wok -wolf -wolverine -womanhood -womankind -womanless -womanlike -womanly -womb -woof -wooing -wool -woozy -word -work -worried -worrier -worrisome -worry -worsening -worshiper -worst -wound -woven -wow -wrangle -wrath -wreath -wreckage -wrecker -wrecking -wrench -wriggle -wriggly -wrinkle -wrinkly -wrist -writing -written -wrongdoer -wronged -wrongful -wrongly -wrongness -wrought -xbox -xerox -yahoo -yam -yanking -yapping -yard -yarn -yeah -yearbook -yearling -yearly -yearning -yeast -yelling -yelp -yen -yesterday -yiddish -yield -yin -yippee -yo-yo -yodel -yoga -yogurt -yonder -yoyo -yummy -zap -zealous -zebra -zen -zeppelin -zero -zestfully -zesty -zigzagged -zipfile -zipping -zippy -zips -zit -zodiac -zombie -zone -zoning -zookeeper -zoologist -zoology -zoom diff --git a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_prefixed.txt b/.venv/Lib/site-packages/passlib/_data/wordsets/eff_prefixed.txt deleted file mode 100644 index 9ac732f..0000000 --- a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_prefixed.txt +++ /dev/null @@ -1,1296 +0,0 @@ -aardvark -abandoned -abbreviate -abdomen -abhorrence -abiding -abnormal -abrasion -absorbing -abundant -abyss -academy -accountant -acetone -achiness -acid -acoustics -acquire -acrobat -actress -acuteness -aerosol -aesthetic -affidavit -afloat -afraid -aftershave -again -agency -aggressor -aghast -agitate -agnostic -agonizing -agreeing -aidless -aimlessly -ajar -alarmclock -albatross -alchemy -alfalfa -algae -aliens -alkaline -almanac -alongside -alphabet -already -also -altitude -aluminum -always -amazingly -ambulance -amendment -amiable -ammunition -amnesty -amoeba -amplifier -amuser -anagram -anchor -android -anesthesia -angelfish -animal -anklet -announcer -anonymous -answer -antelope -anxiety -anyplace -aorta -apartment -apnea -apostrophe -apple -apricot -aquamarine -arachnid -arbitrate -ardently -arena -argument -aristocrat -armchair -aromatic -arrowhead -arsonist -artichoke -asbestos -ascend -aseptic -ashamed -asinine -asleep -asocial -asparagus -astronaut -asymmetric -atlas -atmosphere -atom -atrocious -attic -atypical -auctioneer -auditorium -augmented -auspicious -automobile -auxiliary -avalanche -avenue -aviator -avocado -awareness -awhile -awkward -awning -awoke -axially -azalea -babbling -backpack -badass -bagpipe -bakery -balancing -bamboo -banana -barracuda -basket -bathrobe -bazooka -blade -blender -blimp -blouse -blurred -boatyard -bobcat -body -bogusness -bohemian -boiler -bonnet -boots -borough -bossiness -bottle -bouquet -boxlike -breath -briefcase -broom -brushes -bubblegum -buckle -buddhist -buffalo -bullfrog -bunny -busboy -buzzard -cabin -cactus -cadillac -cafeteria -cage -cahoots -cajoling -cakewalk -calculator -camera -canister -capsule -carrot -cashew -cathedral -caucasian -caviar -ceasefire -cedar -celery -cement -census -ceramics -cesspool -chalkboard -cheesecake -chimney -chlorine -chopsticks -chrome -chute -cilantro -cinnamon -circle -cityscape -civilian -clay -clergyman -clipboard -clock -clubhouse -coathanger -cobweb -coconut -codeword -coexistent -coffeecake -cognitive -cohabitate -collarbone -computer -confetti -copier -cornea -cosmetics -cotton -couch -coverless -coyote -coziness -crawfish -crewmember -crib -croissant -crumble -crystal -cubical -cucumber -cuddly -cufflink -cuisine -culprit -cup -curry -cushion -cuticle -cybernetic -cyclist -cylinder -cymbal -cynicism -cypress -cytoplasm -dachshund -daffodil -dagger -dairy -dalmatian -dandelion -dartboard -dastardly -datebook -daughter -dawn -daytime -dazzler -dealer -debris -decal -dedicate -deepness -defrost -degree -dehydrator -deliverer -democrat -dentist -deodorant -depot -deranged -desktop -detergent -device -dexterity -diamond -dibs -dictionary -diffuser -digit -dilated -dimple -dinnerware -dioxide -diploma -directory -dishcloth -ditto -dividers -dizziness -doctor -dodge -doll -dominoes -donut -doorstep -dorsal -double -downstairs -dozed -drainpipe -dresser -driftwood -droppings -drum -dryer -dubiously -duckling -duffel -dugout -dumpster -duplex -durable -dustpan -dutiful -duvet -dwarfism -dwelling -dwindling -dynamite -dyslexia -eagerness -earlobe -easel -eavesdrop -ebook -eccentric -echoless -eclipse -ecosystem -ecstasy -edged -editor -educator -eelworm -eerie -effects -eggnog -egomaniac -ejection -elastic -elbow -elderly -elephant -elfishly -eliminator -elk -elliptical -elongated -elsewhere -elusive -elves -emancipate -embroidery -emcee -emerald -emission -emoticon -emperor -emulate -enactment -enchilada -endorphin -energy -enforcer -engine -enhance -enigmatic -enjoyably -enlarged -enormous -enquirer -enrollment -ensemble -entryway -enunciate -envoy -enzyme -epidemic -equipment -erasable -ergonomic -erratic -eruption -escalator -eskimo -esophagus -espresso -essay -estrogen -etching -eternal -ethics -etiquette -eucalyptus -eulogy -euphemism -euthanize -evacuation -evergreen -evidence -evolution -exam -excerpt -exerciser -exfoliate -exhale -exist -exorcist -explode -exquisite -exterior -exuberant -fabric -factory -faded -failsafe -falcon -family -fanfare -fasten -faucet -favorite -feasibly -february -federal -feedback -feigned -feline -femur -fence -ferret -festival -fettuccine -feudalist -feverish -fiberglass -fictitious -fiddle -figurine -fillet -finalist -fiscally -fixture -flashlight -fleshiness -flight -florist -flypaper -foamless -focus -foggy -folksong -fondue -footpath -fossil -fountain -fox -fragment -freeway -fridge -frosting -fruit -fryingpan -gadget -gainfully -gallstone -gamekeeper -gangway -garlic -gaslight -gathering -gauntlet -gearbox -gecko -gem -generator -geographer -gerbil -gesture -getaway -geyser -ghoulishly -gibberish -giddiness -giftshop -gigabyte -gimmick -giraffe -giveaway -gizmo -glasses -gleeful -glisten -glove -glucose -glycerin -gnarly -gnomish -goatskin -goggles -goldfish -gong -gooey -gorgeous -gosling -gothic -gourmet -governor -grape -greyhound -grill -groundhog -grumbling -guacamole -guerrilla -guitar -gullible -gumdrop -gurgling -gusto -gutless -gymnast -gynecology -gyration -habitat -hacking -haggard -haiku -halogen -hamburger -handgun -happiness -hardhat -hastily -hatchling -haughty -hazelnut -headband -hedgehog -hefty -heinously -helmet -hemoglobin -henceforth -herbs -hesitation -hexagon -hubcap -huddling -huff -hugeness -hullabaloo -human -hunter -hurricane -hushing -hyacinth -hybrid -hydrant -hygienist -hypnotist -ibuprofen -icepack -icing -iconic -identical -idiocy -idly -igloo -ignition -iguana -illuminate -imaging -imbecile -imitator -immigrant -imprint -iodine -ionosphere -ipad -iphone -iridescent -irksome -iron -irrigation -island -isotope -issueless -italicize -itemizer -itinerary -itunes -ivory -jabbering -jackrabbit -jaguar -jailhouse -jalapeno -jamboree -janitor -jarring -jasmine -jaundice -jawbreaker -jaywalker -jazz -jealous -jeep -jelly -jeopardize -jersey -jetski -jezebel -jiffy -jigsaw -jingling -jobholder -jockstrap -jogging -john -joinable -jokingly -journal -jovial -joystick -jubilant -judiciary -juggle -juice -jujitsu -jukebox -jumpiness -junkyard -juror -justifying -juvenile -kabob -kamikaze -kangaroo -karate -kayak -keepsake -kennel -kerosene -ketchup -khaki -kickstand -kilogram -kimono -kingdom -kiosk -kissing -kite -kleenex -knapsack -kneecap -knickers -koala -krypton -laboratory -ladder -lakefront -lantern -laptop -laryngitis -lasagna -latch -laundry -lavender -laxative -lazybones -lecturer -leftover -leggings -leisure -lemon -length -leopard -leprechaun -lettuce -leukemia -levers -lewdness -liability -library -licorice -lifeboat -lightbulb -likewise -lilac -limousine -lint -lioness -lipstick -liquid -listless -litter -liverwurst -lizard -llama -luau -lubricant -lucidity -ludicrous -luggage -lukewarm -lullaby -lumberjack -lunchbox -luridness -luscious -luxurious -lyrics -macaroni -maestro -magazine -mahogany -maimed -majority -makeover -malformed -mammal -mango -mapmaker -marbles -massager -matchstick -maverick -maximum -mayonnaise -moaning -mobilize -moccasin -modify -moisture -molecule -momentum -monastery -moonshine -mortuary -mosquito -motorcycle -mousetrap -movie -mower -mozzarella -muckiness -mudflow -mugshot -mule -mummy -mundane -muppet -mural -mustard -mutation -myriad -myspace -myth -nail -namesake -nanosecond -napkin -narrator -nastiness -natives -nautically -navigate -nearest -nebula -nectar -nefarious -negotiator -neither -nemesis -neoliberal -nephew -nervously -nest -netting -neuron -nevermore -nextdoor -nicotine -niece -nimbleness -nintendo -nirvana -nuclear -nugget -nuisance -nullify -numbing -nuptials -nursery -nutcracker -nylon -oasis -oat -obediently -obituary -object -obliterate -obnoxious -observer -obtain -obvious -occupation -oceanic -octopus -ocular -office -oftentimes -oiliness -ointment -older -olympics -omissible -omnivorous -oncoming -onion -onlooker -onstage -onward -onyx -oomph -opaquely -opera -opium -opossum -opponent -optical -opulently -oscillator -osmosis -ostrich -otherwise -ought -outhouse -ovation -oven -owlish -oxford -oxidize -oxygen -oyster -ozone -pacemaker -padlock -pageant -pajamas -palm -pamphlet -pantyhose -paprika -parakeet -passport -patio -pauper -pavement -payphone -pebble -peculiarly -pedometer -pegboard -pelican -penguin -peony -pepperoni -peroxide -pesticide -petroleum -pewter -pharmacy -pheasant -phonebook -phrasing -physician -plank -pledge -plotted -plug -plywood -pneumonia -podiatrist -poetic -pogo -poison -poking -policeman -poncho -popcorn -porcupine -postcard -poultry -powerboat -prairie -pretzel -princess -propeller -prune -pry -pseudo -psychopath -publisher -pucker -pueblo -pulley -pumpkin -punchbowl -puppy -purse -pushup -putt -puzzle -pyramid -python -quarters -quesadilla -quilt -quote -racoon -radish -ragweed -railroad -rampantly -rancidity -rarity -raspberry -ravishing -rearrange -rebuilt -receipt -reentry -refinery -register -rehydrate -reimburse -rejoicing -rekindle -relic -remote -renovator -reopen -reporter -request -rerun -reservoir -retriever -reunion -revolver -rewrite -rhapsody -rhetoric -rhino -rhubarb -rhyme -ribbon -riches -ridden -rigidness -rimmed -riptide -riskily -ritzy -riverboat -roamer -robe -rocket -romancer -ropelike -rotisserie -roundtable -royal -rubber -rudderless -rugby -ruined -rulebook -rummage -running -rupture -rustproof -sabotage -sacrifice -saddlebag -saffron -sainthood -saltshaker -samurai -sandworm -sapphire -sardine -sassy -satchel -sauna -savage -saxophone -scarf -scenario -schoolbook -scientist -scooter -scrapbook -sculpture -scythe -secretary -sedative -segregator -seismology -selected -semicolon -senator -septum -sequence -serpent -sesame -settler -severely -shack -shelf -shirt -shovel -shrimp -shuttle -shyness -siamese -sibling -siesta -silicon -simmering -singles -sisterhood -sitcom -sixfold -sizable -skateboard -skeleton -skies -skulk -skylight -slapping -sled -slingshot -sloth -slumbering -smartphone -smelliness -smitten -smokestack -smudge -snapshot -sneezing -sniff -snowsuit -snugness -speakers -sphinx -spider -splashing -sponge -sprout -spur -spyglass -squirrel -statue -steamboat -stingray -stopwatch -strawberry -student -stylus -suave -subway -suction -suds -suffocate -sugar -suitcase -sulphur -superstore -surfer -sushi -swan -sweatshirt -swimwear -sword -sycamore -syllable -symphony -synagogue -syringes -systemize -tablespoon -taco -tadpole -taekwondo -tagalong -takeout -tallness -tamale -tanned -tapestry -tarantula -tastebud -tattoo -tavern -thaw -theater -thimble -thorn -throat -thumb -thwarting -tiara -tidbit -tiebreaker -tiger -timid -tinsel -tiptoeing -tirade -tissue -tractor -tree -tripod -trousers -trucks -tryout -tubeless -tuesday -tugboat -tulip -tumbleweed -tupperware -turtle -tusk -tutorial -tuxedo -tweezers -twins -tyrannical -ultrasound -umbrella -umpire -unarmored -unbuttoned -uncle -underwear -unevenness -unflavored -ungloved -unhinge -unicycle -unjustly -unknown -unlocking -unmarked -unnoticed -unopened -unpaved -unquenched -unroll -unscrewing -untied -unusual -unveiled -unwrinkled -unyielding -unzip -upbeat -upcountry -update -upfront -upgrade -upholstery -upkeep -upload -uppercut -upright -upstairs -uptown -upwind -uranium -urban -urchin -urethane -urgent -urologist -username -usher -utensil -utility -utmost -utopia -utterance -vacuum -vagrancy -valuables -vanquished -vaporizer -varied -vaseline -vegetable -vehicle -velcro -vendor -vertebrae -vestibule -veteran -vexingly -vicinity -videogame -viewfinder -vigilante -village -vinegar -violin -viperfish -virus -visor -vitamins -vivacious -vixen -vocalist -vogue -voicemail -volleyball -voucher -voyage -vulnerable -waffle -wagon -wakeup -walrus -wanderer -wasp -water -waving -wheat -whisper -wholesaler -wick -widow -wielder -wifeless -wikipedia -wildcat -windmill -wipeout -wired -wishbone -wizardry -wobbliness -wolverine -womb -woolworker -workbasket -wound -wrangle -wreckage -wristwatch -wrongdoing -xerox -xylophone -yacht -yahoo -yard -yearbook -yesterday -yiddish -yield -yo-yo -yodel -yogurt -yuppie -zealot -zebra -zeppelin -zestfully -zigzagged -zillion -zipping -zirconium -zodiac -zombie -zookeeper -zucchini diff --git a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_short.txt b/.venv/Lib/site-packages/passlib/_data/wordsets/eff_short.txt deleted file mode 100644 index 4c8baa4..0000000 --- a/.venv/Lib/site-packages/passlib/_data/wordsets/eff_short.txt +++ /dev/null @@ -1,1296 +0,0 @@ -acid -acorn -acre -acts -afar -affix -aged -agent -agile -aging -agony -ahead -aide -aids -aim -ajar -alarm -alias -alibi -alien -alike -alive -aloe -aloft -aloha -alone -amend -amino -ample -amuse -angel -anger -angle -ankle -apple -april -apron -aqua -area -arena -argue -arise -armed -armor -army -aroma -array -arson -art -ashen -ashes -atlas -atom -attic -audio -avert -avoid -awake -award -awoke -axis -bacon -badge -bagel -baggy -baked -baker -balmy -banjo -barge -barn -bash -basil -bask -batch -bath -baton -bats -blade -blank -blast -blaze -bleak -blend -bless -blimp -blink -bloat -blob -blog -blot -blunt -blurt -blush -boast -boat -body -boil -bok -bolt -boned -boney -bonus -bony -book -booth -boots -boss -botch -both -boxer -breed -bribe -brick -bride -brim -bring -brink -brisk -broad -broil -broke -brook -broom -brush -buck -bud -buggy -bulge -bulk -bully -bunch -bunny -bunt -bush -bust -busy -buzz -cable -cache -cadet -cage -cake -calm -cameo -canal -candy -cane -canon -cape -card -cargo -carol -carry -carve -case -cash -cause -cedar -chain -chair -chant -chaos -charm -chase -cheek -cheer -chef -chess -chest -chew -chief -chili -chill -chip -chomp -chop -chow -chuck -chump -chunk -churn -chute -cider -cinch -city -civic -civil -clad -claim -clamp -clap -clash -clasp -class -claw -clay -clean -clear -cleat -cleft -clerk -click -cling -clink -clip -cloak -clock -clone -cloth -cloud -clump -coach -coast -coat -cod -coil -coke -cola -cold -colt -coma -come -comic -comma -cone -cope -copy -coral -cork -cost -cot -couch -cough -cover -cozy -craft -cramp -crane -crank -crate -crave -crawl -crazy -creme -crepe -crept -crib -cried -crisp -crook -crop -cross -crowd -crown -crumb -crush -crust -cub -cult -cupid -cure -curl -curry -curse -curve -curvy -cushy -cut -cycle -dab -dad -daily -dairy -daisy -dance -dandy -darn -dart -dash -data -date -dawn -deaf -deal -dean -debit -debt -debug -decaf -decal -decay -deck -decor -decoy -deed -delay -denim -dense -dent -depth -derby -desk -dial -diary -dice -dig -dill -dime -dimly -diner -dingy -disco -dish -disk -ditch -ditzy -dizzy -dock -dodge -doing -doll -dome -donor -donut -dose -dot -dove -down -dowry -doze -drab -drama -drank -draw -dress -dried -drift -drill -drive -drone -droop -drove -drown -drum -dry -duck -duct -dude -dug -duke -duo -dusk -dust -duty -dwarf -dwell -eagle -early -earth -easel -east -eaten -eats -ebay -ebony -ebook -echo -edge -eel -eject -elbow -elder -elf -elk -elm -elope -elude -elves -email -emit -empty -emu -enter -entry -envoy -equal -erase -error -erupt -essay -etch -evade -even -evict -evil -evoke -exact -exit -fable -faced -fact -fade -fall -false -fancy -fang -fax -feast -feed -femur -fence -fend -ferry -fetal -fetch -fever -fiber -fifth -fifty -film -filth -final -finch -fit -five -flag -flaky -flame -flap -flask -fled -flick -fling -flint -flip -flirt -float -flock -flop -floss -flyer -foam -foe -fog -foil -folic -folk -food -fool -found -fox -foyer -frail -frame -fray -fresh -fried -frill -frisk -from -front -frost -froth -frown -froze -fruit -gag -gains -gala -game -gap -gas -gave -gear -gecko -geek -gem -genre -gift -gig -gills -given -giver -glad -glass -glide -gloss -glove -glow -glue -goal -going -golf -gong -good -gooey -goofy -gore -gown -grab -grain -grant -grape -graph -grasp -grass -grave -gravy -gray -green -greet -grew -grid -grief -grill -grip -grit -groom -grope -growl -grub -grunt -guide -gulf -gulp -gummy -guru -gush -gut -guy -habit -half -halo -halt -happy -harm -hash -hasty -hatch -hate -haven -hazel -hazy -heap -heat -heave -hedge -hefty -help -herbs -hers -hub -hug -hula -hull -human -humid -hump -hung -hunk -hunt -hurry -hurt -hush -hut -ice -icing -icon -icy -igloo -image -ion -iron -islam -issue -item -ivory -ivy -jab -jam -jaws -jazz -jeep -jelly -jet -jiffy -job -jog -jolly -jolt -jot -joy -judge -juice -juicy -july -jumbo -jump -junky -juror -jury -keep -keg -kept -kick -kilt -king -kite -kitty -kiwi -knee -knelt -koala -kung -ladle -lady -lair -lake -lance -land -lapel -large -lash -lasso -last -latch -late -lazy -left -legal -lemon -lend -lens -lent -level -lever -lid -life -lift -lilac -lily -limb -limes -line -lint -lion -lip -list -lived -liver -lunar -lunch -lung -lurch -lure -lurk -lying -lyric -mace -maker -malt -mama -mango -manor -many -map -march -mardi -marry -mash -match -mate -math -moan -mocha -moist -mold -mom -moody -mop -morse -most -motor -motto -mount -mouse -mousy -mouth -move -movie -mower -mud -mug -mulch -mule -mull -mumbo -mummy -mural -muse -music -musky -mute -nacho -nag -nail -name -nanny -nap -navy -near -neat -neon -nerd -nest -net -next -niece -ninth -nutty -oak -oasis -oat -ocean -oil -old -olive -omen -onion -only -ooze -opal -open -opera -opt -otter -ouch -ounce -outer -oval -oven -owl -ozone -pace -pagan -pager -palm -panda -panic -pants -panty -paper -park -party -pasta -patch -path -patio -payer -pecan -penny -pep -perch -perky -perm -pest -petal -petri -petty -photo -plank -plant -plaza -plead -plot -plow -pluck -plug -plus -poach -pod -poem -poet -pogo -point -poise -poker -polar -polio -polka -polo -pond -pony -poppy -pork -poser -pouch -pound -pout -power -prank -press -print -prior -prism -prize -probe -prong -proof -props -prude -prune -pry -pug -pull -pulp -pulse -puma -punch -punk -pupil -puppy -purr -purse -push -putt -quack -quake -query -quiet -quill -quilt -quit -quota -quote -rabid -race -rack -radar -radio -raft -rage -raid -rail -rake -rally -ramp -ranch -range -rank -rant -rash -raven -reach -react -ream -rebel -recap -relax -relay -relic -remix -repay -repel -reply -rerun -reset -rhyme -rice -rich -ride -rigid -rigor -rinse -riot -ripen -rise -risk -ritzy -rival -river -roast -robe -robin -rock -rogue -roman -romp -rope -rover -royal -ruby -rug -ruin -rule -runny -rush -rust -rut -sadly -sage -said -saint -salad -salon -salsa -salt -same -sandy -santa -satin -sauna -saved -savor -sax -say -scale -scam -scan -scare -scarf -scary -scoff -scold -scoop -scoot -scope -score -scorn -scout -scowl -scrap -scrub -scuba -scuff -sect -sedan -self -send -sepia -serve -set -seven -shack -shade -shady -shaft -shaky -sham -shape -share -sharp -shed -sheep -sheet -shelf -shell -shine -shiny -ship -shirt -shock -shop -shore -shout -shove -shown -showy -shred -shrug -shun -shush -shut -shy -sift -silk -silly -silo -sip -siren -sixth -size -skate -skew -skid -skier -skies -skip -skirt -skit -sky -slab -slack -slain -slam -slang -slash -slate -slaw -sled -sleek -sleep -sleet -slept -slice -slick -slimy -sling -slip -slit -slob -slot -slug -slum -slurp -slush -small -smash -smell -smile -smirk -smog -snack -snap -snare -snarl -sneak -sneer -sniff -snore -snort -snout -snowy -snub -snuff -speak -speed -spend -spent -spew -spied -spill -spiny -spoil -spoke -spoof -spool -spoon -sport -spot -spout -spray -spree -spur -squad -squat -squid -stack -staff -stage -stain -stall -stamp -stand -stank -stark -start -stash -state -stays -steam -steep -stem -step -stew -stick -sting -stir -stock -stole -stomp -stony -stood -stool -stoop -stop -storm -stout -stove -straw -stray -strut -stuck -stud -stuff -stump -stung -stunt -suds -sugar -sulk -surf -sushi -swab -swan -swarm -sway -swear -sweat -sweep -swell -swept -swim -swing -swipe -swirl -swoop -swore -syrup -tacky -taco -tag -take -tall -talon -tamer -tank -taper -taps -tarot -tart -task -taste -tasty -taunt -thank -thaw -theft -theme -thigh -thing -think -thong -thorn -those -throb -thud -thumb -thump -thus -tiara -tidal -tidy -tiger -tile -tilt -tint -tiny -trace -track -trade -train -trait -trap -trash -tray -treat -tree -trek -trend -trial -tribe -trick -trio -trout -truce -truck -trump -trunk -try -tug -tulip -tummy -turf -tusk -tutor -tutu -tux -tweak -tweet -twice -twine -twins -twirl -twist -uncle -uncut -undo -unify -union -unit -untie -upon -upper -urban -used -user -usher -utter -value -vapor -vegan -venue -verse -vest -veto -vice -video -view -viral -virus -visa -visor -vixen -vocal -voice -void -volt -voter -vowel -wad -wafer -wager -wages -wagon -wake -walk -wand -wasp -watch -water -wavy -wheat -whiff -whole -whoop -wick -widen -widow -width -wife -wifi -wilt -wimp -wind -wing -wink -wipe -wired -wiry -wise -wish -wispy -wok -wolf -womb -wool -woozy -word -work -worry -wound -woven -wrath -wreck -wrist -xerox -yahoo -yam -yard -year -yeast -yelp -yield -yo-yo -yodel -yoga -yoyo -yummy -zebra -zero -zesty -zippy -zone -zoom diff --git a/.venv/Lib/site-packages/passlib/apache.py b/.venv/Lib/site-packages/passlib/apache.py deleted file mode 100644 index a75f2cf..0000000 --- a/.venv/Lib/site-packages/passlib/apache.py +++ /dev/null @@ -1,1255 +0,0 @@ -"""passlib.apache - apache password support""" -# XXX: relocate this to passlib.ext.apache? -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -import os -from warnings import warn -# site -# pkg -from passlib import exc, registry -from passlib.context import CryptContext -from passlib.exc import ExpectedStringError -from passlib.hash import htdigest -from passlib.utils import render_bytes, to_bytes, is_ascii_codec -from passlib.utils.decor import deprecated_method -from passlib.utils.compat import join_bytes, unicode, BytesIO, PY3 -# local -__all__ = [ - 'HtpasswdFile', - 'HtdigestFile', -] - -#============================================================================= -# constants & support -#============================================================================= -_UNSET = object() - -_BCOLON = b":" -_BHASH = b"#" - -# byte values that aren't allowed in fields. -_INVALID_FIELD_CHARS = b":\n\r\t\x00" - -#: _CommonFile._source token types -_SKIPPED = "skipped" -_RECORD = "record" - -#============================================================================= -# common helpers -#============================================================================= -class _CommonFile(object): - """common framework for HtpasswdFile & HtdigestFile""" - #=================================================================== - # instance attrs - #=================================================================== - - # charset encoding used by file (defaults to utf-8) - encoding = None - - # whether users() and other public methods should return unicode or bytes? - # (defaults to False under PY2, True under PY3) - return_unicode = None - - # if bound to local file, these will be set. - _path = None # local file path - _mtime = None # mtime when last loaded, or 0 - - # if true, automatically save to local file after changes are made. - autosave = False - - # dict mapping key -> value for all records in database. - # (e.g. user => hash for Htpasswd) - _records = None - - #: list of tokens for recreating original file contents when saving. if present, - #: will be sequence of (_SKIPPED, b"whitespace/comments") and (_RECORD, ) tuples. - _source = None - - #=================================================================== - # alt constuctors - #=================================================================== - @classmethod - def from_string(cls, data, **kwds): - """create new object from raw string. - - :type data: unicode or bytes - :arg data: - database to load, as single string. - - :param \\*\\*kwds: - all other keywords are the same as in the class constructor - """ - if 'path' in kwds: - raise TypeError("'path' not accepted by from_string()") - self = cls(**kwds) - self.load_string(data) - return self - - @classmethod - def from_path(cls, path, **kwds): - """create new object from file, without binding object to file. - - :type path: str - :arg path: - local filepath to load from - - :param \\*\\*kwds: - all other keywords are the same as in the class constructor - """ - self = cls(**kwds) - self.load(path) - return self - - #=================================================================== - # init - #=================================================================== - def __init__(self, path=None, new=False, autoload=True, autosave=False, - encoding="utf-8", return_unicode=PY3, - ): - # set encoding - if not encoding: - warn("``encoding=None`` is deprecated as of Passlib 1.6, " - "and will cause a ValueError in Passlib 1.8, " - "use ``return_unicode=False`` instead.", - DeprecationWarning, stacklevel=2) - encoding = "utf-8" - return_unicode = False - elif not is_ascii_codec(encoding): - # htpasswd/htdigest files assumes 1-byte chars, and use ":" separator, - # so only ascii-compatible encodings are allowed. - raise ValueError("encoding must be 7-bit ascii compatible") - self.encoding = encoding - - # set other attrs - self.return_unicode = return_unicode - self.autosave = autosave - self._path = path - self._mtime = 0 - - # init db - if not autoload: - warn("``autoload=False`` is deprecated as of Passlib 1.6, " - "and will be removed in Passlib 1.8, use ``new=True`` instead", - DeprecationWarning, stacklevel=2) - new = True - if path and not new: - self.load() - else: - self._records = {} - self._source = [] - - def __repr__(self): - tail = '' - if self.autosave: - tail += ' autosave=True' - if self._path: - tail += ' path=%r' % self._path - if self.encoding != "utf-8": - tail += ' encoding=%r' % self.encoding - return "<%s 0x%0x%s>" % (self.__class__.__name__, id(self), tail) - - # NOTE: ``path`` is a property so that ``_mtime`` is wiped when it's set. - - @property - def path(self): - return self._path - - @path.setter - def path(self, value): - if value != self._path: - self._mtime = 0 - self._path = value - - @property - def mtime(self): - """modify time when last loaded (if bound to a local file)""" - return self._mtime - - #=================================================================== - # loading - #=================================================================== - def load_if_changed(self): - """Reload from ``self.path`` only if file has changed since last load""" - if not self._path: - raise RuntimeError("%r is not bound to a local file" % self) - if self._mtime and self._mtime == os.path.getmtime(self._path): - return False - self.load() - return True - - def load(self, path=None, force=True): - """Load state from local file. - If no path is specified, attempts to load from ``self.path``. - - :type path: str - :arg path: local file to load from - - :type force: bool - :param force: - if ``force=False``, only load from ``self.path`` if file - has changed since last load. - - .. deprecated:: 1.6 - This keyword will be removed in Passlib 1.8; - Applications should use :meth:`load_if_changed` instead. - """ - if path is not None: - with open(path, "rb") as fh: - self._mtime = 0 - self._load_lines(fh) - elif not force: - warn("%(name)s.load(force=False) is deprecated as of Passlib 1.6," - "and will be removed in Passlib 1.8; " - "use %(name)s.load_if_changed() instead." % - dict(name=self.__class__.__name__), - DeprecationWarning, stacklevel=2) - return self.load_if_changed() - elif self._path: - with open(self._path, "rb") as fh: - self._mtime = os.path.getmtime(self._path) - self._load_lines(fh) - else: - raise RuntimeError("%s().path is not set, an explicit path is required" % - self.__class__.__name__) - return True - - def load_string(self, data): - """Load state from unicode or bytes string, replacing current state""" - data = to_bytes(data, self.encoding, "data") - self._mtime = 0 - self._load_lines(BytesIO(data)) - - def _load_lines(self, lines): - """load from sequence of lists""" - parse = self._parse_record - records = {} - source = [] - skipped = b'' - for idx, line in enumerate(lines): - # NOTE: per htpasswd source (https://github.com/apache/httpd/blob/trunk/support/htpasswd.c), - # lines with only whitespace, or with "#" as first non-whitespace char, - # are left alone / ignored. - tmp = line.lstrip() - if not tmp or tmp.startswith(_BHASH): - skipped += line - continue - - # parse valid line - key, value = parse(line, idx+1) - - # NOTE: if multiple entries for a key, we use the first one, - # which seems to match htpasswd source - if key in records: - log.warning("username occurs multiple times in source file: %r" % key) - skipped += line - continue - - # flush buffer of skipped whitespace lines - if skipped: - source.append((_SKIPPED, skipped)) - skipped = b'' - - # store new user line - records[key] = value - source.append((_RECORD, key)) - - # don't bother preserving trailing whitespace, but do preserve trailing comments - if skipped.rstrip(): - source.append((_SKIPPED, skipped)) - - # NOTE: not replacing ._records until parsing succeeds, so loading is atomic. - self._records = records - self._source = source - - def _parse_record(self, record, lineno): # pragma: no cover - abstract method - """parse line of file into (key, value) pair""" - raise NotImplementedError("should be implemented in subclass") - - def _set_record(self, key, value): - """ - helper for setting record which takes care of inserting source line if needed; - - :returns: - bool if key already present - """ - records = self._records - existing = (key in records) - records[key] = value - if not existing: - self._source.append((_RECORD, key)) - return existing - - #=================================================================== - # saving - #=================================================================== - def _autosave(self): - """subclass helper to call save() after any changes""" - if self.autosave and self._path: - self.save() - - def save(self, path=None): - """Save current state to file. - If no path is specified, attempts to save to ``self.path``. - """ - if path is not None: - with open(path, "wb") as fh: - fh.writelines(self._iter_lines()) - elif self._path: - self.save(self._path) - self._mtime = os.path.getmtime(self._path) - else: - raise RuntimeError("%s().path is not set, cannot autosave" % - self.__class__.__name__) - - def to_string(self): - """Export current state as a string of bytes""" - return join_bytes(self._iter_lines()) - - # def clean(self): - # """ - # discard any comments or whitespace that were being preserved from the source file, - # and re-sort keys in alphabetical order - # """ - # self._source = [(_RECORD, key) for key in sorted(self._records)] - # self._autosave() - - def _iter_lines(self): - """iterator yielding lines of database""" - # NOTE: this relies on being an OrderedDict so that it outputs - # records in a deterministic order. - records = self._records - if __debug__: - pending = set(records) - for action, content in self._source: - if action == _SKIPPED: - # 'content' is whitespace/comments to write - yield content - else: - assert action == _RECORD - # 'content' is record key - if content not in records: - # record was deleted - # NOTE: doing it lazily like this so deleting & re-adding user - # preserves their original location in the file. - continue - yield self._render_record(content, records[content]) - if __debug__: - pending.remove(content) - if __debug__: - # sanity check that we actually wrote all the records - # (otherwise _source & _records are somehow out of sync) - assert not pending, "failed to write all records: missing=%r" % (pending,) - - def _render_record(self, key, value): # pragma: no cover - abstract method - """given key/value pair, encode as line of file""" - raise NotImplementedError("should be implemented in subclass") - - #=================================================================== - # field encoding - #=================================================================== - def _encode_user(self, user): - """user-specific wrapper for _encode_field()""" - return self._encode_field(user, "user") - - def _encode_realm(self, realm): # pragma: no cover - abstract method - """realm-specific wrapper for _encode_field()""" - return self._encode_field(realm, "realm") - - def _encode_field(self, value, param="field"): - """convert field to internal representation. - - internal representation is always bytes. byte strings are left as-is, - unicode strings encoding using file's default encoding (or ``utf-8`` - if no encoding has been specified). - - :raises UnicodeEncodeError: - if unicode value cannot be encoded using default encoding. - - :raises ValueError: - if resulting byte string contains a forbidden character, - or is too long (>255 bytes). - - :returns: - encoded identifer as bytes - """ - if isinstance(value, unicode): - value = value.encode(self.encoding) - elif not isinstance(value, bytes): - raise ExpectedStringError(value, param) - if len(value) > 255: - raise ValueError("%s must be at most 255 characters: %r" % - (param, value)) - if any(c in _INVALID_FIELD_CHARS for c in value): - raise ValueError("%s contains invalid characters: %r" % - (param, value,)) - return value - - def _decode_field(self, value): - """decode field from internal representation to format - returns by users() method, etc. - - :raises UnicodeDecodeError: - if unicode value cannot be decoded using default encoding. - (usually indicates wrong encoding set for file). - - :returns: - field as unicode or bytes, as appropriate. - """ - assert isinstance(value, bytes), "expected value to be bytes" - if self.return_unicode: - return value.decode(self.encoding) - else: - return value - - # FIXME: htpasswd doc says passwords limited to 255 chars under Windows & MPE, - # and that longer ones are truncated. this may be side-effect of those - # platforms supporting the 'plaintext' scheme. these classes don't currently - # check for this. - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# htpasswd context -# -# This section sets up a CryptContexts to mimic what schemes Apache -# (and the htpasswd tool) should support on the current system. -# -# Apache has long-time supported some basic builtin schemes (listed below), -# as well as the host's crypt() method -- though it's limited to being able -# to *verify* any scheme using that method, but can only generate "des_crypt" hashes. -# -# Apache 2.4 added builtin bcrypt support (even for platforms w/o native support). -# c.f. http://httpd.apache.org/docs/2.4/programs/htpasswd.html vs the 2.2 docs. -#============================================================================= - -#: set of default schemes that (if chosen) should be using bcrypt, -#: but can't due to lack of bcrypt. -_warn_no_bcrypt = set() - -def _init_default_schemes(): - - #: pick strongest one for host - host_best = None - for name in ["bcrypt", "sha256_crypt"]: - if registry.has_os_crypt_support(name): - host_best = name - break - - # check if we have a bcrypt backend -- otherwise issue warning - # XXX: would like to not spam this unless the user *requests* apache 24 - bcrypt = "bcrypt" if registry.has_backend("bcrypt") else None - _warn_no_bcrypt.clear() - if not bcrypt: - _warn_no_bcrypt.update(["portable_apache_24", "host_apache_24", - "linux_apache_24", "portable", "host"]) - - defaults = dict( - # strongest hash builtin to specific apache version - portable_apache_24=bcrypt or "apr_md5_crypt", - portable_apache_22="apr_md5_crypt", - - # strongest hash across current host & specific apache version - host_apache_24=bcrypt or host_best or "apr_md5_crypt", - host_apache_22=host_best or "apr_md5_crypt", - - # strongest hash on a linux host - linux_apache_24=bcrypt or "sha256_crypt", - linux_apache_22="sha256_crypt", - ) - - # set latest-apache version aliases - # XXX: could check for apache install, and pick correct host 22/24 default? - # could reuse _detect_htpasswd() helper in UTs - defaults.update( - portable=defaults['portable_apache_24'], - host=defaults['host_apache_24'], - ) - return defaults - -#: dict mapping default alias -> appropriate scheme -htpasswd_defaults = _init_default_schemes() - -def _init_htpasswd_context(): - - # start with schemes built into apache - schemes = [ - # builtin support added in apache 2.4 - # (https://bz.apache.org/bugzilla/show_bug.cgi?id=49288) - "bcrypt", - - # support not "builtin" to apache, instead it requires support through host's crypt(). - # adding them here to allow editing htpasswd under windows and then deploying under unix. - "sha256_crypt", - "sha512_crypt", - "des_crypt", - - # apache default as of 2.2.18, and still default in 2.4 - "apr_md5_crypt", - - # NOTE: apache says ONLY intended for transitioning htpasswd <-> ldap - "ldap_sha1", - - # NOTE: apache says ONLY supported on Windows, Netware, TPF - "plaintext" - ] - - # apache can verify anything supported by the native crypt(), - # though htpasswd tool can only generate a limited set of hashes. - # (this list may overlap w/ builtin apache schemes) - schemes.extend(registry.get_supported_os_crypt_schemes()) - - # hack to remove dups and sort into preferred order - preferred = schemes[:3] + ["apr_md5_crypt"] + schemes - schemes = sorted(set(schemes), key=preferred.index) - - # create context object - return CryptContext( - schemes=schemes, - - # NOTE: default will change to "portable" in passlib 2.0 - default=htpasswd_defaults['portable_apache_22'], - - # NOTE: bcrypt "2y" is required, "2b" isn't recognized by libapr (issue 95) - bcrypt__ident="2y", - ) - -#: CryptContext configured to match htpasswd -htpasswd_context = _init_htpasswd_context() - -#============================================================================= -# htpasswd editing -#============================================================================= - -class HtpasswdFile(_CommonFile): - """class for reading & writing Htpasswd files. - - The class constructor accepts the following arguments: - - :type path: filepath - :param path: - - Specifies path to htpasswd file, use to implicitly load from and save to. - - This class has two modes of operation: - - 1. It can be "bound" to a local file by passing a ``path`` to the class - constructor. In this case it will load the contents of the file when - created, and the :meth:`load` and :meth:`save` methods will automatically - load from and save to that file if they are called without arguments. - - 2. Alternately, it can exist as an independant object, in which case - :meth:`load` and :meth:`save` will require an explicit path to be - provided whenever they are called. As well, ``autosave`` behavior - will not be available. - - This feature is new in Passlib 1.6, and is the default if no - ``path`` value is provided to the constructor. - - This is also exposed as a readonly instance attribute. - - :type new: bool - :param new: - - Normally, if *path* is specified, :class:`HtpasswdFile` will - immediately load the contents of the file. However, when creating - a new htpasswd file, applications can set ``new=True`` so that - the existing file (if any) will not be loaded. - - .. versionadded:: 1.6 - This feature was previously enabled by setting ``autoload=False``. - That alias has been deprecated, and will be removed in Passlib 1.8 - - :type autosave: bool - :param autosave: - - Normally, any changes made to an :class:`HtpasswdFile` instance - will not be saved until :meth:`save` is explicitly called. However, - if ``autosave=True`` is specified, any changes made will be - saved to disk immediately (assuming *path* has been set). - - This is also exposed as a writeable instance attribute. - - :type encoding: str - :param encoding: - - Optionally specify character encoding used to read/write file - and hash passwords. Defaults to ``utf-8``, though ``latin-1`` - is the only other commonly encountered encoding. - - This is also exposed as a readonly instance attribute. - - :type default_scheme: str - :param default_scheme: - Optionally specify default scheme to use when encoding new passwords. - - This can be any of the schemes with builtin Apache support, - OR natively supported by the host OS's :func:`crypt.crypt` function. - - * Builtin schemes include ``"bcrypt"`` (apache 2.4+), ``"apr_md5_crypt"`, - and ``"des_crypt"``. - - * Schemes commonly supported by Unix hosts - include ``"bcrypt"``, ``"sha256_crypt"``, and ``"des_crypt"``. - - In order to not have to sort out what you should use, - passlib offers a number of aliases, that will resolve - to the most appropriate scheme based on your needs: - - * ``"portable"``, ``"portable_apache_24"`` -- pick scheme that's portable across hosts - running apache >= 2.4. **This will be the default as of Passlib 2.0**. - - * ``"portable_apache_22"`` -- pick scheme that's portable across hosts - running apache >= 2.4. **This is the default up to Passlib 1.9**. - - * ``"host"``, ``"host_apache_24"`` -- pick strongest scheme supported by - apache >= 2.4 and/or host OS. - - * ``"host_apache_22"`` -- pick strongest scheme supported by - apache >= 2.2 and/or host OS. - - .. versionadded:: 1.6 - This keyword was previously named ``default``. That alias - has been deprecated, and will be removed in Passlib 1.8. - - .. versionchanged:: 1.6.3 - - Added support for ``"bcrypt"``, ``"sha256_crypt"``, and ``"portable"`` alias. - - .. versionchanged:: 1.7 - - Added apache 2.4 semantics, and additional aliases. - - :type context: :class:`~passlib.context.CryptContext` - :param context: - :class:`!CryptContext` instance used to create - and verify the hashes found in the htpasswd file. - The default value is a pre-built context which supports all - of the hashes officially allowed in an htpasswd file. - - This is also exposed as a readonly instance attribute. - - .. warning:: - - This option may be used to add support for non-standard hash - formats to an htpasswd file. However, the resulting file - will probably not be usable by another application, - and particularly not by Apache. - - :param autoload: - Set to ``False`` to prevent the constructor from automatically - loaded the file from disk. - - .. deprecated:: 1.6 - This has been replaced by the *new* keyword. - Instead of setting ``autoload=False``, you should use - ``new=True``. Support for this keyword will be removed - in Passlib 1.8. - - :param default: - Change the default algorithm used to hash new passwords. - - .. deprecated:: 1.6 - This has been renamed to *default_scheme* for clarity. - Support for this alias will be removed in Passlib 1.8. - - Loading & Saving - ================ - .. automethod:: load - .. automethod:: load_if_changed - .. automethod:: load_string - .. automethod:: save - .. automethod:: to_string - - Inspection - ================ - .. automethod:: users - .. automethod:: check_password - .. automethod:: get_hash - - Modification - ================ - .. automethod:: set_password - .. automethod:: delete - - Alternate Constructors - ====================== - .. automethod:: from_string - - Attributes - ========== - .. attribute:: path - - Path to local file that will be used as the default - for all :meth:`load` and :meth:`save` operations. - May be written to, initialized by the *path* constructor keyword. - - .. attribute:: autosave - - Writeable flag indicating whether changes will be automatically - written to *path*. - - Errors - ====== - :raises ValueError: - All of the methods in this class will raise a :exc:`ValueError` if - any user name contains a forbidden character (one of ``:\\r\\n\\t\\x00``), - or is longer than 255 characters. - """ - #=================================================================== - # instance attrs - #=================================================================== - - # NOTE: _records map stores for the key, and for the value, - # both in bytes which use self.encoding - - #=================================================================== - # init & serialization - #=================================================================== - def __init__(self, path=None, default_scheme=None, context=htpasswd_context, - **kwds): - if 'default' in kwds: - warn("``default`` is deprecated as of Passlib 1.6, " - "and will be removed in Passlib 1.8, it has been renamed " - "to ``default_scheem``.", - DeprecationWarning, stacklevel=2) - default_scheme = kwds.pop("default") - if default_scheme: - if default_scheme in _warn_no_bcrypt: - warn("HtpasswdFile: no bcrypt backends available, " - "using fallback for default scheme %r" % default_scheme, - exc.PasslibSecurityWarning) - default_scheme = htpasswd_defaults.get(default_scheme, default_scheme) - context = context.copy(default=default_scheme) - self.context = context - super(HtpasswdFile, self).__init__(path, **kwds) - - def _parse_record(self, record, lineno): - # NOTE: should return (user, hash) tuple - result = record.rstrip().split(_BCOLON) - if len(result) != 2: - raise ValueError("malformed htpasswd file (error reading line %d)" - % lineno) - return result - - def _render_record(self, user, hash): - return render_bytes("%s:%s\n", user, hash) - - #=================================================================== - # public methods - #=================================================================== - - def users(self): - """ - Return list of all users in database - """ - return [self._decode_field(user) for user in self._records] - - ##def has_user(self, user): - ## "check whether entry is present for user" - ## return self._encode_user(user) in self._records - - ##def rename(self, old, new): - ## """rename user account""" - ## old = self._encode_user(old) - ## new = self._encode_user(new) - ## hash = self._records.pop(old) - ## self._records[new] = hash - ## self._autosave() - - def set_password(self, user, password): - """Set password for user; adds user if needed. - - :returns: - * ``True`` if existing user was updated. - * ``False`` if user account was added. - - .. versionchanged:: 1.6 - This method was previously called ``update``, it was renamed - to prevent ambiguity with the dictionary method. - The old alias is deprecated, and will be removed in Passlib 1.8. - """ - hash = self.context.hash(password) - return self.set_hash(user, hash) - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="set_password") - def update(self, user, password): - """set password for user""" - return self.set_password(user, password) - - def get_hash(self, user): - """Return hash stored for user, or ``None`` if user not found. - - .. versionchanged:: 1.6 - This method was previously named ``find``, it was renamed - for clarity. The old name is deprecated, and will be removed - in Passlib 1.8. - """ - try: - return self._records[self._encode_user(user)] - except KeyError: - return None - - def set_hash(self, user, hash): - """ - semi-private helper which allows writing a hash directly; - adds user if needed. - - .. warning:: - does not (currently) do any validation of the hash string - - .. versionadded:: 1.7 - """ - # assert self.context.identify(hash), "unrecognized hash format" - if PY3 and isinstance(hash, str): - hash = hash.encode(self.encoding) - user = self._encode_user(user) - existing = self._set_record(user, hash) - self._autosave() - return existing - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="get_hash") - def find(self, user): - """return hash for user""" - return self.get_hash(user) - - # XXX: rename to something more explicit, like delete_user()? - def delete(self, user): - """Delete user's entry. - - :returns: - * ``True`` if user deleted. - * ``False`` if user not found. - """ - try: - del self._records[self._encode_user(user)] - except KeyError: - return False - self._autosave() - return True - - def check_password(self, user, password): - """ - Verify password for specified user. - If algorithm marked as deprecated by CryptContext, will automatically be re-hashed. - - :returns: - * ``None`` if user not found. - * ``False`` if user found, but password does not match. - * ``True`` if user found and password matches. - - .. versionchanged:: 1.6 - This method was previously called ``verify``, it was renamed - to prevent ambiguity with the :class:`!CryptContext` method. - The old alias is deprecated, and will be removed in Passlib 1.8. - """ - user = self._encode_user(user) - hash = self._records.get(user) - if hash is None: - return None - if isinstance(password, unicode): - # NOTE: encoding password to match file, making the assumption - # that server will use same encoding to hash the password. - password = password.encode(self.encoding) - ok, new_hash = self.context.verify_and_update(password, hash) - if ok and new_hash is not None: - # rehash user's password if old hash was deprecated - assert user in self._records # otherwise would have to use ._set_record() - self._records[user] = new_hash - self._autosave() - return ok - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="check_password") - def verify(self, user, password): - """verify password for user""" - return self.check_password(user, password) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# htdigest editing -#============================================================================= -class HtdigestFile(_CommonFile): - """class for reading & writing Htdigest files. - - The class constructor accepts the following arguments: - - :type path: filepath - :param path: - - Specifies path to htdigest file, use to implicitly load from and save to. - - This class has two modes of operation: - - 1. It can be "bound" to a local file by passing a ``path`` to the class - constructor. In this case it will load the contents of the file when - created, and the :meth:`load` and :meth:`save` methods will automatically - load from and save to that file if they are called without arguments. - - 2. Alternately, it can exist as an independant object, in which case - :meth:`load` and :meth:`save` will require an explicit path to be - provided whenever they are called. As well, ``autosave`` behavior - will not be available. - - This feature is new in Passlib 1.6, and is the default if no - ``path`` value is provided to the constructor. - - This is also exposed as a readonly instance attribute. - - :type default_realm: str - :param default_realm: - - If ``default_realm`` is set, all the :class:`HtdigestFile` - methods that require a realm will use this value if one is not - provided explicitly. If unset, they will raise an error stating - that an explicit realm is required. - - This is also exposed as a writeable instance attribute. - - .. versionadded:: 1.6 - - :type new: bool - :param new: - - Normally, if *path* is specified, :class:`HtdigestFile` will - immediately load the contents of the file. However, when creating - a new htpasswd file, applications can set ``new=True`` so that - the existing file (if any) will not be loaded. - - .. versionadded:: 1.6 - This feature was previously enabled by setting ``autoload=False``. - That alias has been deprecated, and will be removed in Passlib 1.8 - - :type autosave: bool - :param autosave: - - Normally, any changes made to an :class:`HtdigestFile` instance - will not be saved until :meth:`save` is explicitly called. However, - if ``autosave=True`` is specified, any changes made will be - saved to disk immediately (assuming *path* has been set). - - This is also exposed as a writeable instance attribute. - - :type encoding: str - :param encoding: - - Optionally specify character encoding used to read/write file - and hash passwords. Defaults to ``utf-8``, though ``latin-1`` - is the only other commonly encountered encoding. - - This is also exposed as a readonly instance attribute. - - :param autoload: - Set to ``False`` to prevent the constructor from automatically - loaded the file from disk. - - .. deprecated:: 1.6 - This has been replaced by the *new* keyword. - Instead of setting ``autoload=False``, you should use - ``new=True``. Support for this keyword will be removed - in Passlib 1.8. - - Loading & Saving - ================ - .. automethod:: load - .. automethod:: load_if_changed - .. automethod:: load_string - .. automethod:: save - .. automethod:: to_string - - Inspection - ========== - .. automethod:: realms - .. automethod:: users - .. automethod:: check_password(user[, realm], password) - .. automethod:: get_hash - - Modification - ============ - .. automethod:: set_password(user[, realm], password) - .. automethod:: delete - .. automethod:: delete_realm - - Alternate Constructors - ====================== - .. automethod:: from_string - - Attributes - ========== - .. attribute:: default_realm - - The default realm that will be used if one is not provided - to methods that require it. By default this is ``None``, - in which case an explicit realm must be provided for every - method call. Can be written to. - - .. attribute:: path - - Path to local file that will be used as the default - for all :meth:`load` and :meth:`save` operations. - May be written to, initialized by the *path* constructor keyword. - - .. attribute:: autosave - - Writeable flag indicating whether changes will be automatically - written to *path*. - - Errors - ====== - :raises ValueError: - All of the methods in this class will raise a :exc:`ValueError` if - any user name or realm contains a forbidden character (one of ``:\\r\\n\\t\\x00``), - or is longer than 255 characters. - """ - #=================================================================== - # instance attrs - #=================================================================== - - # NOTE: _records map stores (,) for the key, - # and as the value, all as bytes. - - # NOTE: unlike htpasswd, this class doesn't use a CryptContext, - # as only one hash format is supported: htdigest. - - # optionally specify default realm that will be used if none - # is provided to a method call. otherwise realm is always required. - default_realm = None - - #=================================================================== - # init & serialization - #=================================================================== - def __init__(self, path=None, default_realm=None, **kwds): - self.default_realm = default_realm - super(HtdigestFile, self).__init__(path, **kwds) - - def _parse_record(self, record, lineno): - result = record.rstrip().split(_BCOLON) - if len(result) != 3: - raise ValueError("malformed htdigest file (error reading line %d)" - % lineno) - user, realm, hash = result - return (user, realm), hash - - def _render_record(self, key, hash): - user, realm = key - return render_bytes("%s:%s:%s\n", user, realm, hash) - - def _require_realm(self, realm): - if realm is None: - realm = self.default_realm - if realm is None: - raise TypeError("you must specify a realm explicitly, " - "or set the default_realm attribute") - return realm - - def _encode_realm(self, realm): - realm = self._require_realm(realm) - return self._encode_field(realm, "realm") - - def _encode_key(self, user, realm): - return self._encode_user(user), self._encode_realm(realm) - - #=================================================================== - # public methods - #=================================================================== - - def realms(self): - """Return list of all realms in database""" - realms = set(key[1] for key in self._records) - return [self._decode_field(realm) for realm in realms] - - def users(self, realm=None): - """Return list of all users in specified realm. - - * uses ``self.default_realm`` if no realm explicitly provided. - * returns empty list if realm not found. - """ - realm = self._encode_realm(realm) - return [self._decode_field(key[0]) for key in self._records - if key[1] == realm] - - ##def has_user(self, user, realm=None): - ## "check if user+realm combination exists" - ## return self._encode_key(user,realm) in self._records - - ##def rename_realm(self, old, new): - ## """rename all accounts in realm""" - ## old = self._encode_realm(old) - ## new = self._encode_realm(new) - ## keys = [key for key in self._records if key[1] == old] - ## for key in keys: - ## hash = self._records.pop(key) - ## self._set_record((key[0], new), hash) - ## self._autosave() - ## return len(keys) - - ##def rename(self, old, new, realm=None): - ## """rename user account""" - ## old = self._encode_user(old) - ## new = self._encode_user(new) - ## realm = self._encode_realm(realm) - ## hash = self._records.pop((old,realm)) - ## self._set_record((new, realm), hash) - ## self._autosave() - - def set_password(self, user, realm=None, password=_UNSET): - """Set password for user; adds user & realm if needed. - - If ``self.default_realm`` has been set, this may be called - with the syntax ``set_password(user, password)``, - otherwise it must be called with all three arguments: - ``set_password(user, realm, password)``. - - :returns: - * ``True`` if existing user was updated - * ``False`` if user account added. - """ - if password is _UNSET: - # called w/ two args - (user, password), use default realm - realm, password = None, realm - realm = self._require_realm(realm) - hash = htdigest.hash(password, user, realm, encoding=self.encoding) - return self.set_hash(user, realm, hash) - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="set_password") - def update(self, user, realm, password): - """set password for user""" - return self.set_password(user, realm, password) - - def get_hash(self, user, realm=None): - """Return :class:`~passlib.hash.htdigest` hash stored for user. - - * uses ``self.default_realm`` if no realm explicitly provided. - * returns ``None`` if user or realm not found. - - .. versionchanged:: 1.6 - This method was previously named ``find``, it was renamed - for clarity. The old name is deprecated, and will be removed - in Passlib 1.8. - """ - key = self._encode_key(user, realm) - hash = self._records.get(key) - if hash is None: - return None - if PY3: - hash = hash.decode(self.encoding) - return hash - - def set_hash(self, user, realm=None, hash=_UNSET): - """ - semi-private helper which allows writing a hash directly; - adds user & realm if needed. - - If ``self.default_realm`` has been set, this may be called - with the syntax ``set_hash(user, hash)``, - otherwise it must be called with all three arguments: - ``set_hash(user, realm, hash)``. - - .. warning:: - does not (currently) do any validation of the hash string - - .. versionadded:: 1.7 - """ - if hash is _UNSET: - # called w/ two args - (user, hash), use default realm - realm, hash = None, realm - # assert htdigest.identify(hash), "unrecognized hash format" - if PY3 and isinstance(hash, str): - hash = hash.encode(self.encoding) - key = self._encode_key(user, realm) - existing = self._set_record(key, hash) - self._autosave() - return existing - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="get_hash") - def find(self, user, realm): - """return hash for user""" - return self.get_hash(user, realm) - - # XXX: rename to something more explicit, like delete_user()? - def delete(self, user, realm=None): - """Delete user's entry for specified realm. - - if realm is not specified, uses ``self.default_realm``. - - :returns: - * ``True`` if user deleted, - * ``False`` if user not found in realm. - """ - key = self._encode_key(user, realm) - try: - del self._records[key] - except KeyError: - return False - self._autosave() - return True - - def delete_realm(self, realm): - """Delete all users for specified realm. - - if realm is not specified, uses ``self.default_realm``. - - :returns: number of users deleted (0 if realm not found) - """ - realm = self._encode_realm(realm) - records = self._records - keys = [key for key in records if key[1] == realm] - for key in keys: - del records[key] - self._autosave() - return len(keys) - - def check_password(self, user, realm=None, password=_UNSET): - """Verify password for specified user + realm. - - If ``self.default_realm`` has been set, this may be called - with the syntax ``check_password(user, password)``, - otherwise it must be called with all three arguments: - ``check_password(user, realm, password)``. - - :returns: - * ``None`` if user or realm not found. - * ``False`` if user found, but password does not match. - * ``True`` if user found and password matches. - - .. versionchanged:: 1.6 - This method was previously called ``verify``, it was renamed - to prevent ambiguity with the :class:`!CryptContext` method. - The old alias is deprecated, and will be removed in Passlib 1.8. - """ - if password is _UNSET: - # called w/ two args - (user, password), use default realm - realm, password = None, realm - user = self._encode_user(user) - realm = self._encode_realm(realm) - hash = self._records.get((user,realm)) - if hash is None: - return None - return htdigest.verify(password, hash, user, realm, - encoding=self.encoding) - - @deprecated_method(deprecated="1.6", removed="1.8", - replacement="check_password") - def verify(self, user, realm, password): - """verify password for user""" - return self.check_password(user, realm, password) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/apps.py b/.venv/Lib/site-packages/passlib/apps.py deleted file mode 100644 index 682bbff..0000000 --- a/.venv/Lib/site-packages/passlib/apps.py +++ /dev/null @@ -1,245 +0,0 @@ -"""passlib.apps""" -#============================================================================= -# imports -#============================================================================= -# core -import logging; log = logging.getLogger(__name__) -from itertools import chain -# site -# pkg -from passlib import hash -from passlib.context import LazyCryptContext -from passlib.utils import sys_bits -# local -__all__ = [ - 'custom_app_context', - 'django_context', - 'ldap_context', 'ldap_nocrypt_context', - 'mysql_context', 'mysql4_context', 'mysql3_context', - 'phpass_context', - 'phpbb3_context', - 'postgres_context', -] - -#============================================================================= -# master containing all identifiable hashes -#============================================================================= -def _load_master_config(): - from passlib.registry import list_crypt_handlers - - # get master list - schemes = list_crypt_handlers() - - # exclude the ones we know have ambiguous or greedy identify() methods. - excluded = [ - # frequently confused for eachother - 'bigcrypt', - 'crypt16', - - # no good identifiers - 'cisco_pix', - 'cisco_type7', - 'htdigest', - 'mysql323', - 'oracle10', - - # all have same size - 'lmhash', - 'msdcc', - 'msdcc2', - 'nthash', - - # plaintext handlers - 'plaintext', - 'ldap_plaintext', - - # disabled handlers - 'django_disabled', - 'unix_disabled', - 'unix_fallback', - ] - for name in excluded: - schemes.remove(name) - - # return config - return dict(schemes=schemes, default="sha256_crypt") -master_context = LazyCryptContext(onload=_load_master_config) - -#============================================================================= -# for quickly bootstrapping new custom applications -#============================================================================= -custom_app_context = LazyCryptContext( - # choose some reasonbly strong schemes - schemes=["sha512_crypt", "sha256_crypt"], - - # set some useful global options - default="sha256_crypt" if sys_bits < 64 else "sha512_crypt", - - # set a good starting point for rounds selection - sha512_crypt__min_rounds = 535000, - sha256_crypt__min_rounds = 535000, - - # if the admin user category is selected, make a much stronger hash, - admin__sha512_crypt__min_rounds = 1024000, - admin__sha256_crypt__min_rounds = 1024000, - ) - -#============================================================================= -# django -#============================================================================= - -#----------------------------------------------------------------------- -# 1.0 -#----------------------------------------------------------------------- - -_django10_schemes = [ - "django_salted_sha1", - "django_salted_md5", - "django_des_crypt", - "hex_md5", - "django_disabled", -] - -django10_context = LazyCryptContext( - schemes=_django10_schemes, - default="django_salted_sha1", - deprecated=["hex_md5"], -) - -#----------------------------------------------------------------------- -# 1.4 -#----------------------------------------------------------------------- - -_django14_schemes = [ - "django_pbkdf2_sha256", - "django_pbkdf2_sha1", - "django_bcrypt" -] + _django10_schemes - -django14_context = LazyCryptContext( - schemes=_django14_schemes, - deprecated=_django10_schemes, -) - -#----------------------------------------------------------------------- -# 1.6 -#----------------------------------------------------------------------- - -_django16_schemes = list(_django14_schemes) -_django16_schemes.insert(1, "django_bcrypt_sha256") -django16_context = LazyCryptContext( - schemes=_django16_schemes, - deprecated=_django10_schemes, -) - -#----------------------------------------------------------------------- -# 1.10 -#----------------------------------------------------------------------- - -_django_110_schemes = [ - "django_pbkdf2_sha256", - "django_pbkdf2_sha1", - "django_argon2", - "django_bcrypt", - "django_bcrypt_sha256", - "django_disabled", -] -django110_context = LazyCryptContext(schemes=_django_110_schemes) - -#----------------------------------------------------------------------- -# 2.1 -#----------------------------------------------------------------------- - -_django21_schemes = list(_django_110_schemes) -_django21_schemes.remove("django_bcrypt") -django21_context = LazyCryptContext(schemes=_django21_schemes) - -#----------------------------------------------------------------------- -# latest -#----------------------------------------------------------------------- - -# this will always point to latest version in passlib -django_context = django21_context - -#============================================================================= -# ldap -#============================================================================= - -#: standard ldap schemes -std_ldap_schemes = [ - "ldap_salted_sha512", - "ldap_salted_sha256", - "ldap_salted_sha1", - "ldap_salted_md5", - "ldap_sha1", - "ldap_md5", - "ldap_plaintext", -] - -# create context with all std ldap schemes EXCEPT crypt -ldap_nocrypt_context = LazyCryptContext(std_ldap_schemes) - -# create context with all possible std ldap + ldap crypt schemes -def _iter_ldap_crypt_schemes(): - from passlib.utils import unix_crypt_schemes - return ('ldap_' + name for name in unix_crypt_schemes) - -def _iter_ldap_schemes(): - """helper which iterates over supported std ldap schemes""" - return chain(std_ldap_schemes, _iter_ldap_crypt_schemes()) -ldap_context = LazyCryptContext(_iter_ldap_schemes()) - -### create context with all std ldap schemes + crypt schemes for localhost -##def _iter_host_ldap_schemes(): -## "helper which iterates over supported std ldap schemes" -## from passlib.handlers.ldap_digests import get_host_ldap_crypt_schemes -## return chain(std_ldap_schemes, get_host_ldap_crypt_schemes()) -##ldap_host_context = LazyCryptContext(_iter_host_ldap_schemes()) - -#============================================================================= -# mysql -#============================================================================= -mysql3_context = LazyCryptContext(["mysql323"]) -mysql4_context = LazyCryptContext(["mysql41", "mysql323"], deprecated="mysql323") -mysql_context = mysql4_context # tracks latest mysql version supported - -#============================================================================= -# postgres -#============================================================================= -postgres_context = LazyCryptContext(["postgres_md5"]) - -#============================================================================= -# phpass & variants -#============================================================================= -def _create_phpass_policy(**kwds): - """helper to choose default alg based on bcrypt availability""" - kwds['default'] = 'bcrypt' if hash.bcrypt.has_backend() else 'phpass' - return kwds - -phpass_context = LazyCryptContext( - schemes=["bcrypt", "phpass", "bsdi_crypt"], - onload=_create_phpass_policy, - ) - -phpbb3_context = LazyCryptContext(["phpass"], phpass__ident="H") - -# TODO: support the drupal phpass variants (see phpass homepage) - -#============================================================================= -# roundup -#============================================================================= - -_std_roundup_schemes = [ "ldap_hex_sha1", "ldap_hex_md5", "ldap_des_crypt", "roundup_plaintext" ] -roundup10_context = LazyCryptContext(_std_roundup_schemes) - -# NOTE: 'roundup15' really applies to roundup 1.4.17+ -roundup_context = roundup15_context = LazyCryptContext( - schemes=_std_roundup_schemes + [ "ldap_pbkdf2_sha1" ], - deprecated=_std_roundup_schemes, - default = "ldap_pbkdf2_sha1", - ldap_pbkdf2_sha1__default_rounds = 10000, - ) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/context.py b/.venv/Lib/site-packages/passlib/context.py deleted file mode 100644 index bc3cbf5..0000000 --- a/.venv/Lib/site-packages/passlib/context.py +++ /dev/null @@ -1,2637 +0,0 @@ -"""passlib.context - CryptContext implementation""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import re -import logging; log = logging.getLogger(__name__) -import threading -import time -from warnings import warn -# site -# pkg -from passlib import exc -from passlib.exc import ExpectedStringError, ExpectedTypeError, PasslibConfigWarning -from passlib.registry import get_crypt_handler, _validate_handler_name -from passlib.utils import (handlers as uh, to_bytes, - to_unicode, splitcomma, - as_bool, timer, rng, getrandstr, - ) -from passlib.utils.binary import BASE64_CHARS -from passlib.utils.compat import (iteritems, num_types, irange, - PY2, PY3, unicode, SafeConfigParser, - NativeStringIO, BytesIO, - unicode_or_bytes_types, native_string_types, - ) -from passlib.utils.decor import deprecated_method, memoized_property -# local -__all__ = [ - 'CryptContext', - 'LazyCryptContext', - 'CryptPolicy', -] - -#============================================================================= -# support -#============================================================================= - -# private object to detect unset params -_UNSET = object() - -def _coerce_vary_rounds(value): - """parse vary_rounds string to percent as [0,1) float, or integer""" - if value.endswith("%"): - # XXX: deprecate this in favor of raw float? - return float(value.rstrip("%"))*.01 - try: - return int(value) - except ValueError: - return float(value) - -# set of options which aren't allowed to be set via policy -_forbidden_scheme_options = set(["salt"]) - # 'salt' - not allowed since a fixed salt would defeat the purpose. - -# dict containing funcs used to coerce strings to correct type for scheme option keys. -# NOTE: this isn't really needed any longer, since Handler.using() handles the actual parsing. -# keeping this around for now, though, since it makes context.to_dict() output cleaner. -_coerce_scheme_options = dict( - min_rounds=int, - max_rounds=int, - default_rounds=int, - vary_rounds=_coerce_vary_rounds, - salt_size=int, -) - -def _is_handler_registered(handler): - """detect if handler is registered or a custom handler""" - return get_crypt_handler(handler.name, None) is handler - -@staticmethod -def _always_needs_update(hash, secret=None): - """ - dummy function patched into handler.needs_update() by _CryptConfig - when hash alg has been deprecated for context. - """ - return True - -#: list of keys allowed under wildcard "all" scheme w/o a security warning. -_global_settings = set(["truncate_error", "vary_rounds"]) - -#============================================================================= -# crypt policy -#============================================================================= -_preamble = ("The CryptPolicy class has been deprecated as of " - "Passlib 1.6, and will be removed in Passlib 1.8. ") - -class CryptPolicy(object): - """ - .. deprecated:: 1.6 - This class has been deprecated, and will be removed in Passlib 1.8. - All of its functionality has been rolled into :class:`CryptContext`. - - This class previously stored the configuration options for the - CryptContext class. In the interest of interface simplification, - all of this class' functionality has been rolled into the CryptContext - class itself. - The documentation for this class is now focused on documenting how to - migrate to the new api. Additionally, where possible, the deprecation - warnings issued by the CryptPolicy methods will list the replacement call - that should be used. - - Constructors - ============ - CryptPolicy objects can be constructed directly using any of - the keywords accepted by :class:`CryptContext`. Direct uses of the - :class:`!CryptPolicy` constructor should either pass the keywords - directly into the CryptContext constructor, or to :meth:`CryptContext.update` - if the policy object was being used to update an existing context object. - - In addition to passing in keywords directly, - CryptPolicy objects can be constructed by the following methods: - - .. automethod:: from_path - .. automethod:: from_string - .. automethod:: from_source - .. automethod:: from_sources - .. automethod:: replace - - Introspection - ============= - All of the informational methods provided by this class have been deprecated - by identical or similar methods in the :class:`CryptContext` class: - - .. automethod:: has_schemes - .. automethod:: schemes - .. automethod:: iter_handlers - .. automethod:: get_handler - .. automethod:: get_options - .. automethod:: handler_is_deprecated - .. automethod:: get_min_verify_time - - Exporting - ========= - .. automethod:: iter_config - .. automethod:: to_dict - .. automethod:: to_file - .. automethod:: to_string - - .. note:: - CryptPolicy are immutable. - Use the :meth:`replace` method to mutate existing instances. - - .. deprecated:: 1.6 - """ - #=================================================================== - # class methods - #=================================================================== - @classmethod - def from_path(cls, path, section="passlib", encoding="utf-8"): - """create a CryptPolicy instance from a local file. - - .. deprecated:: 1.6 - - Creating a new CryptContext from a file, which was previously done via - ``CryptContext(policy=CryptPolicy.from_path(path))``, can now be - done via ``CryptContext.from_path(path)``. - See :meth:`CryptContext.from_path` for details. - - Updating an existing CryptContext from a file, which was previously done - ``context.policy = CryptPolicy.from_path(path)``, can now be - done via ``context.load_path(path)``. - See :meth:`CryptContext.load_path` for details. - """ - warn(_preamble + - "Instead of ``CryptPolicy.from_path(path)``, " - "use ``CryptContext.from_path(path)`` " - " or ``context.load_path(path)`` for an existing CryptContext.", - DeprecationWarning, stacklevel=2) - return cls(_internal_context=CryptContext.from_path(path, section, - encoding)) - - @classmethod - def from_string(cls, source, section="passlib", encoding="utf-8"): - """create a CryptPolicy instance from a string. - - .. deprecated:: 1.6 - - Creating a new CryptContext from a string, which was previously done via - ``CryptContext(policy=CryptPolicy.from_string(data))``, can now be - done via ``CryptContext.from_string(data)``. - See :meth:`CryptContext.from_string` for details. - - Updating an existing CryptContext from a string, which was previously done - ``context.policy = CryptPolicy.from_string(data)``, can now be - done via ``context.load(data)``. - See :meth:`CryptContext.load` for details. - """ - warn(_preamble + - "Instead of ``CryptPolicy.from_string(source)``, " - "use ``CryptContext.from_string(source)`` or " - "``context.load(source)`` for an existing CryptContext.", - DeprecationWarning, stacklevel=2) - return cls(_internal_context=CryptContext.from_string(source, section, - encoding)) - - @classmethod - def from_source(cls, source, _warn=True): - """create a CryptPolicy instance from some source. - - this method autodetects the source type, and invokes - the appropriate constructor automatically. it attempts - to detect whether the source is a configuration string, a filepath, - a dictionary, or an existing CryptPolicy instance. - - .. deprecated:: 1.6 - - Create a new CryptContext, which could previously be done via - ``CryptContext(policy=CryptPolicy.from_source(source))``, should - now be done using an explicit method: the :class:`CryptContext` - constructor itself, :meth:`CryptContext.from_path`, - or :meth:`CryptContext.from_string`. - - Updating an existing CryptContext, which could previously be done via - ``context.policy = CryptPolicy.from_source(source)``, should - now be done using an explicit method: :meth:`CryptContext.update`, - or :meth:`CryptContext.load`. - """ - if _warn: - warn(_preamble + - "Instead of ``CryptPolicy.from_source()``, " - "use ``CryptContext.from_string(path)`` " - " or ``CryptContext.from_path(source)``, as appropriate.", - DeprecationWarning, stacklevel=2) - if isinstance(source, CryptPolicy): - return source - elif isinstance(source, dict): - return cls(_internal_context=CryptContext(**source)) - elif not isinstance(source, (bytes,unicode)): - raise TypeError("source must be CryptPolicy, dict, config string, " - "or file path: %r" % (type(source),)) - elif any(c in source for c in "\n\r\t") or not source.strip(" \t./;:"): - return cls(_internal_context=CryptContext.from_string(source)) - else: - return cls(_internal_context=CryptContext.from_path(source)) - - @classmethod - def from_sources(cls, sources, _warn=True): - """create a CryptPolicy instance by merging multiple sources. - - each source is interpreted as by :meth:`from_source`, - and the results are merged together. - - .. deprecated:: 1.6 - Instead of using this method to merge multiple policies together, - a :class:`CryptContext` instance should be created, and then - the multiple sources merged together via :meth:`CryptContext.load`. - """ - if _warn: - warn(_preamble + - "Instead of ``CryptPolicy.from_sources()``, " - "use the various CryptContext constructors " - " followed by ``context.update()``.", - DeprecationWarning, stacklevel=2) - if len(sources) == 0: - raise ValueError("no sources specified") - if len(sources) == 1: - return cls.from_source(sources[0], _warn=False) - kwds = {} - for source in sources: - kwds.update(cls.from_source(source, _warn=False)._context.to_dict(resolve=True)) - return cls(_internal_context=CryptContext(**kwds)) - - def replace(self, *args, **kwds): - """create a new CryptPolicy, optionally updating parts of the - existing configuration. - - .. deprecated:: 1.6 - Callers of this method should :meth:`CryptContext.update` or - :meth:`CryptContext.copy` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.replace()``, " - "use ``context.update()`` or ``context.copy()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().replace()``, " - "create a CryptContext instance and " - "use ``context.update()`` or ``context.copy()``.", - DeprecationWarning, stacklevel=2) - sources = [ self ] - if args: - sources.extend(args) - if kwds: - sources.append(kwds) - return CryptPolicy.from_sources(sources, _warn=False) - - #=================================================================== - # instance attrs - #=================================================================== - - # internal CryptContext we're wrapping to handle everything - # until this class is removed. - _context = None - - # flag indicating this is wrapper generated by the CryptContext.policy - # attribute, rather than one created independantly by the application. - _stub_policy = False - - #=================================================================== - # init - #=================================================================== - def __init__(self, *args, **kwds): - context = kwds.pop("_internal_context", None) - if context: - assert isinstance(context, CryptContext) - self._context = context - self._stub_policy = kwds.pop("_stub_policy", False) - assert not (args or kwds), "unexpected args: %r %r" % (args,kwds) - else: - if args: - if len(args) != 1: - raise TypeError("only one positional argument accepted") - if kwds: - raise TypeError("cannot specify positional arg and kwds") - kwds = args[0] - warn(_preamble + - "Instead of constructing a CryptPolicy instance, " - "create a CryptContext directly, or use ``context.update()`` " - "and ``context.load()`` to reconfigure existing CryptContext " - "instances.", - DeprecationWarning, stacklevel=2) - self._context = CryptContext(**kwds) - - #=================================================================== - # public interface for examining options - #=================================================================== - def has_schemes(self): - """return True if policy defines *any* schemes for use. - - .. deprecated:: 1.6 - applications should use ``bool(context.schemes())`` instead. - see :meth:`CryptContext.schemes`. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.has_schemes()``, " - "use ``bool(context.schemes())``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().has_schemes()``, " - "create a CryptContext instance and " - "use ``bool(context.schemes())``.", - DeprecationWarning, stacklevel=2) - return bool(self._context.schemes()) - - def iter_handlers(self): - """return iterator over handlers defined in policy. - - .. deprecated:: 1.6 - applications should use ``context.schemes(resolve=True))`` instead. - see :meth:`CryptContext.schemes`. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.iter_handlers()``, " - "use ``context.schemes(resolve=True)``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().iter_handlers()``, " - "create a CryptContext instance and " - "use ``context.schemes(resolve=True)``.", - DeprecationWarning, stacklevel=2) - return self._context.schemes(resolve=True, unconfigured=True) - - def schemes(self, resolve=False): - """return list of schemes defined in policy. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.schemes` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.schemes()``, " - "use ``context.schemes()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().schemes()``, " - "create a CryptContext instance and " - "use ``context.schemes()``.", - DeprecationWarning, stacklevel=2) - return list(self._context.schemes(resolve=resolve, unconfigured=True)) - - def get_handler(self, name=None, category=None, required=False): - """return handler as specified by name, or default handler. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.handler` instead, - though note that the ``required`` keyword has been removed, - and the new method will always act as if ``required=True``. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.get_handler()``, " - "use ``context.handler()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().get_handler()``, " - "create a CryptContext instance and " - "use ``context.handler()``.", - DeprecationWarning, stacklevel=2) - # CryptContext.handler() doesn't support required=False, - # so wrapping it in try/except - try: - return self._context.handler(name, category, unconfigured=True) - except KeyError: - if required: - raise - else: - return None - - def get_min_verify_time(self, category=None): - """get min_verify_time setting for policy. - - .. deprecated:: 1.6 - min_verify_time option will be removed entirely in passlib 1.8 - - .. versionchanged:: 1.7 - this method now always returns the value automatically - calculated by :meth:`CryptContext.min_verify_time`, - any value specified by policy is ignored. - """ - warn("get_min_verify_time() and min_verify_time option is deprecated and ignored, " - "and will be removed in Passlib 1.8", DeprecationWarning, - stacklevel=2) - return 0 - - def get_options(self, name, category=None): - """return dictionary of options specific to a given handler. - - .. deprecated:: 1.6 - this method has no direct replacement in the 1.6 api, as there - is not a clearly defined use-case. however, examining the output of - :meth:`CryptContext.to_dict` should serve as the closest alternative. - """ - # XXX: might make a public replacement, but need more study of the use cases. - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "``context.policy.get_options()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "``CryptPolicy().get_options()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - if hasattr(name, "name"): - name = name.name - return self._context._config._get_record_options_with_flag(name, category)[0] - - def handler_is_deprecated(self, name, category=None): - """check if handler has been deprecated by policy. - - .. deprecated:: 1.6 - this method has no direct replacement in the 1.6 api, as there - is not a clearly defined use-case. however, examining the output of - :meth:`CryptContext.to_dict` should serve as the closest alternative. - """ - # XXX: might make a public replacement, but need more study of the use cases. - if self._stub_policy: - warn(_preamble + - "``context.policy.handler_is_deprecated()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "``CryptPolicy().handler_is_deprecated()`` will no longer be available.", - DeprecationWarning, stacklevel=2) - if hasattr(name, "name"): - name = name.name - return self._context.handler(name, category).deprecated - - #=================================================================== - # serialization - #=================================================================== - - def iter_config(self, ini=False, resolve=False): - """iterate over key/value pairs representing the policy object. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_dict` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.iter_config()``, " - "use ``context.to_dict().items()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().iter_config()``, " - "create a CryptContext instance and " - "use ``context.to_dict().items()``.", - DeprecationWarning, stacklevel=2) - # hacked code that renders keys & values in manner that approximates - # old behavior. context.to_dict() is much cleaner. - context = self._context - if ini: - def render_key(key): - return context._render_config_key(key).replace("__", ".") - def render_value(value): - if isinstance(value, (list,tuple)): - value = ", ".join(value) - return value - resolve = False - else: - render_key = context._render_config_key - render_value = lambda value: value - return ( - (render_key(key), render_value(value)) - for key, value in context._config.iter_config(resolve) - ) - - def to_dict(self, resolve=False): - """export policy object as dictionary of options. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_dict` instead. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.to_dict()``, " - "use ``context.to_dict()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_dict()``, " - "create a CryptContext instance and " - "use ``context.to_dict()``.", - DeprecationWarning, stacklevel=2) - return self._context.to_dict(resolve) - - def to_file(self, stream, section="passlib"): # pragma: no cover -- deprecated & unused - """export policy to file. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_string` instead, - and then write the output to a file as desired. - """ - if self._stub_policy: - warn(_preamble + - "Instead of ``context.policy.to_file(stream)``, " - "use ``stream.write(context.to_string())``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_file(stream)``, " - "create a CryptContext instance and " - "use ``stream.write(context.to_string())``.", - DeprecationWarning, stacklevel=2) - out = self._context.to_string(section=section) - if PY2: - out = out.encode("utf-8") - stream.write(out) - - def to_string(self, section="passlib", encoding=None): - """export policy to file. - - .. deprecated:: 1.6 - applications should use :meth:`CryptContext.to_string` instead. - """ - if self._stub_policy: - warn(_preamble + # pragma: no cover -- deprecated & unused - "Instead of ``context.policy.to_string()``, " - "use ``context.to_string()``.", - DeprecationWarning, stacklevel=2) - else: - warn(_preamble + - "Instead of ``CryptPolicy().to_string()``, " - "create a CryptContext instance and " - "use ``context.to_string()``.", - DeprecationWarning, stacklevel=2) - out = self._context.to_string(section=section) - if encoding: - out = out.encode(encoding) - return out - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# _CryptConfig helper class -#============================================================================= -class _CryptConfig(object): - """parses, validates, and stores CryptContext config - - this is a helper used internally by CryptContext to handle - parsing, validation, and serialization of its config options. - split out from the main class, but not made public since - that just complicates interface too much (c.f. CryptPolicy) - - :arg source: config as dict mapping ``(cat,scheme,option) -> value`` - """ - #=================================================================== - # instance attrs - #=================================================================== - - # triple-nested dict which maps scheme -> category -> key -> value, - # storing all hash-specific options - _scheme_options = None - - # double-nested dict which maps key -> category -> value - # storing all CryptContext options - _context_options = None - - # tuple of handler objects - handlers = None - - # tuple of scheme objects in same order as handlers - schemes = None - - # tuple of categories in alphabetical order (not including None) - categories = None - - # set of all context keywords used by active schemes - context_kwds = None - - # dict mapping category -> default scheme - _default_schemes = None - - # dict mapping (scheme, category) -> custom handler - _records = None - - # dict mapping category -> list of custom handler instances for that category, - # in order of schemes(). populated on demand by _get_record_list() - _record_lists = None - - #=================================================================== - # constructor - #=================================================================== - def __init__(self, source): - self._init_scheme_list(source.get((None,None,"schemes"))) - self._init_options(source) - self._init_default_schemes() - self._init_records() - - def _init_scheme_list(self, data): - """initialize .handlers and .schemes attributes""" - handlers = [] - schemes = [] - if isinstance(data, native_string_types): - data = splitcomma(data) - for elem in data or (): - # resolve elem -> handler & scheme - if hasattr(elem, "name"): - handler = elem - scheme = handler.name - _validate_handler_name(scheme) - elif isinstance(elem, native_string_types): - handler = get_crypt_handler(elem) - scheme = handler.name - else: - raise TypeError("scheme must be name or CryptHandler, " - "not %r" % type(elem)) - - # check scheme name isn't already in use - if scheme in schemes: - raise KeyError("multiple handlers with same name: %r" % - (scheme,)) - - # add to handler list - handlers.append(handler) - schemes.append(scheme) - - self.handlers = tuple(handlers) - self.schemes = tuple(schemes) - - #=================================================================== - # lowlevel options - #=================================================================== - - #--------------------------------------------------------------- - # init lowlevel option storage - #--------------------------------------------------------------- - def _init_options(self, source): - """load config dict into internal representation, - and init .categories attr - """ - # prepare dicts & locals - norm_scheme_option = self._norm_scheme_option - norm_context_option = self._norm_context_option - self._scheme_options = scheme_options = {} - self._context_options = context_options = {} - categories = set() - - # load source config into internal storage - for (cat, scheme, key), value in iteritems(source): - categories.add(cat) - explicit_scheme = scheme - if not cat and not scheme and key in _global_settings: - # going forward, not using "__all__" format. instead... - # whitelisting set of keys which should be passed to (all) schemes, - # rather than passed to the CryptContext itself - scheme = "all" - if scheme: - # normalize scheme option - key, value = norm_scheme_option(key, value) - - # e.g. things like "min_rounds" should never be set cross-scheme - # this will be fatal under 2.0. - if scheme == "all" and key not in _global_settings: - warn("The '%s' option should be configured per-algorithm, and not set " - "globally in the context; This will be an error in Passlib 2.0" % - (key,), PasslibConfigWarning) - - # this scheme is going away in 2.0; - # but most keys deserve an extra warning since it impacts security. - if explicit_scheme == "all": - warn("The 'all' scheme is deprecated as of Passlib 1.7, " - "and will be removed in Passlib 2.0; Please configure " - "options on a per-algorithm basis.", DeprecationWarning) - - # store in scheme_options - # map structure: scheme_options[scheme][category][key] = value - try: - category_map = scheme_options[scheme] - except KeyError: - scheme_options[scheme] = {cat: {key: value}} - else: - try: - option_map = category_map[cat] - except KeyError: - category_map[cat] = {key: value} - else: - option_map[key] = value - else: - # normalize context option - if cat and key == "schemes": - raise KeyError("'schemes' context option is not allowed " - "per category") - key, value = norm_context_option(cat, key, value) - if key == "min_verify_time": # ignored in 1.7, to be removed in 1.8 - continue - - # store in context_options - # map structure: context_options[key][category] = value - try: - category_map = context_options[key] - except KeyError: - context_options[key] = {cat: value} - else: - category_map[cat] = value - - # store list of configured categories - categories.discard(None) - self.categories = tuple(sorted(categories)) - - def _norm_scheme_option(self, key, value): - # check for invalid options - if key in _forbidden_scheme_options: - raise KeyError("%r option not allowed in CryptContext " - "configuration" % (key,)) - # coerce strings for certain fields (e.g. min_rounds uses ints) - if isinstance(value, native_string_types): - func = _coerce_scheme_options.get(key) - if func: - value = func(value) - return key, value - - def _norm_context_option(self, cat, key, value): - schemes = self.schemes - if key == "default": - if hasattr(value, "name"): - value = value.name - elif not isinstance(value, native_string_types): - raise ExpectedTypeError(value, "str", "default") - if schemes and value not in schemes: - raise KeyError("default scheme not found in policy") - elif key == "deprecated": - if isinstance(value, native_string_types): - value = splitcomma(value) - elif not isinstance(value, (list,tuple)): - raise ExpectedTypeError(value, "str or seq", "deprecated") - if 'auto' in value: - # XXX: have any statements been made about when this is default? - # should do it in 1.8 at latest. - if len(value) > 1: - raise ValueError("cannot list other schemes if " - "``deprecated=['auto']`` is used") - elif schemes: - # make sure list of deprecated schemes is subset of configured schemes - for scheme in value: - if not isinstance(scheme, native_string_types): - raise ExpectedTypeError(value, "str", "deprecated element") - if scheme not in schemes: - raise KeyError("deprecated scheme not found " - "in policy: %r" % (scheme,)) - elif key == "min_verify_time": - warn("'min_verify_time' was deprecated in Passlib 1.6, is " - "ignored in 1.7, and will be removed in 1.8", - DeprecationWarning) - elif key == "harden_verify": - warn("'harden_verify' is deprecated & ignored as of Passlib 1.7.1, " - " and will be removed in 1.8", - DeprecationWarning) - elif key != "schemes": - raise KeyError("unknown CryptContext keyword: %r" % (key,)) - return key, value - - #--------------------------------------------------------------- - # reading context options - #--------------------------------------------------------------- - def get_context_optionmap(self, key, _default={}): - """return dict mapping category->value for specific context option. - - .. warning:: treat return value as readonly! - """ - return self._context_options.get(key, _default) - - def get_context_option_with_flag(self, category, key): - """return value of specific option, handling category inheritance. - also returns flag indicating whether value is category-specific. - """ - try: - category_map = self._context_options[key] - except KeyError: - return None, False - value = category_map.get(None) - if category: - try: - alt = category_map[category] - except KeyError: - pass - else: - if value is None or alt != value: - return alt, True - return value, False - - #--------------------------------------------------------------- - # reading scheme options - #--------------------------------------------------------------- - def _get_scheme_optionmap(self, scheme, category, default={}): - """return all options for (scheme,category) combination - - .. warning:: treat return value as readonly! - """ - try: - return self._scheme_options[scheme][category] - except KeyError: - return default - - def get_base_handler(self, scheme): - return self.handlers[self.schemes.index(scheme)] - - @staticmethod - def expand_settings(handler): - setting_kwds = handler.setting_kwds - if 'rounds' in handler.setting_kwds: - # XXX: historically this extras won't be listed in setting_kwds - setting_kwds += uh.HasRounds.using_rounds_kwds - return setting_kwds - - # NOTE: this is only used by _get_record_options_with_flag()... - def get_scheme_options_with_flag(self, scheme, category): - """return composite dict of all options set for scheme. - includes options inherited from 'all' and from default category. - result can be modified. - returns (kwds, has_cat_specific_options) - """ - # start out with copy of global options - get_optionmap = self._get_scheme_optionmap - kwds = get_optionmap("all", None).copy() - has_cat_options = False - - # add in category-specific global options - if category: - defkwds = kwds.copy() # <-- used to detect category-specific options - kwds.update(get_optionmap("all", category)) - - # filter out global settings not supported by handler - allowed_settings = self.expand_settings(self.get_base_handler(scheme)) - for key in set(kwds).difference(allowed_settings): - kwds.pop(key) - if category: - for key in set(defkwds).difference(allowed_settings): - defkwds.pop(key) - - # add in default options for scheme - other = get_optionmap(scheme, None) - kwds.update(other) - - # load category-specific options for scheme - if category: - defkwds.update(other) - kwds.update(get_optionmap(scheme, category)) - - # compare default category options to see if there's anything - # category-specific - if kwds != defkwds: - has_cat_options = True - - return kwds, has_cat_options - - #=================================================================== - # deprecated & default schemes - #=================================================================== - def _init_default_schemes(self): - """initialize maps containing default scheme for each category. - - have to do this after _init_options(), since the default scheme - is affected by the list of deprecated schemes. - """ - # init maps & locals - get_optionmap = self.get_context_optionmap - default_map = self._default_schemes = get_optionmap("default").copy() - dep_map = get_optionmap("deprecated") - schemes = self.schemes - if not schemes: - return - - # figure out default scheme - deps = dep_map.get(None) or () - default = default_map.get(None) - if not default: - for scheme in schemes: - if scheme not in deps: - default_map[None] = scheme - break - else: - raise ValueError("must have at least one non-deprecated scheme") - elif default in deps: - raise ValueError("default scheme cannot be deprecated") - - # figure out per-category default schemes, - for cat in self.categories: - cdeps = dep_map.get(cat, deps) - cdefault = default_map.get(cat, default) - if not cdefault: - for scheme in schemes: - if scheme not in cdeps: - default_map[cat] = scheme - break - else: - raise ValueError("must have at least one non-deprecated " - "scheme for %r category" % cat) - elif cdefault in cdeps: - raise ValueError("default scheme for %r category " - "cannot be deprecated" % cat) - - def default_scheme(self, category): - """return default scheme for specific category""" - defaults = self._default_schemes - try: - return defaults[category] - except KeyError: - pass - if not self.schemes: - raise KeyError("no hash schemes configured for this " - "CryptContext instance") - return defaults[None] - - def is_deprecated_with_flag(self, scheme, category): - """is scheme deprecated under particular category?""" - depmap = self.get_context_optionmap("deprecated") - def test(cat): - source = depmap.get(cat, depmap.get(None)) - if source is None: - return None - elif 'auto' in source: - return scheme != self.default_scheme(cat) - else: - return scheme in source - value = test(None) or False - if category: - alt = test(category) - if alt is not None and value != alt: - return alt, True - return value, False - - #=================================================================== - # CryptRecord objects - #=================================================================== - def _init_records(self): - # NOTE: this step handles final validation of settings, - # checking for violations against handler's internal invariants. - # this is why we create all the records now, - # so CryptContext throws error immediately rather than later. - self._record_lists = {} - records = self._records = {} - all_context_kwds = self.context_kwds = set() - get_options = self._get_record_options_with_flag - categories = (None,) + self.categories - for handler in self.handlers: - scheme = handler.name - all_context_kwds.update(handler.context_kwds) - for cat in categories: - kwds, has_cat_options = get_options(scheme, cat) - if cat is None or has_cat_options: - records[scheme, cat] = self._create_record(handler, cat, **kwds) - # NOTE: if handler has no category-specific opts, get_record() - # will automatically use the default category's record. - # NOTE: default records for specific category stored under the - # key (None,category); these are populated on-demand by get_record(). - - @staticmethod - def _create_record(handler, category=None, deprecated=False, **settings): - # create custom handler if needed. - try: - # XXX: relaxed=True is mostly here to retain backwards-compat behavior. - # could make this optional flag in future. - subcls = handler.using(relaxed=True, **settings) - except TypeError as err: - m = re.match(r".* unexpected keyword argument '(.*)'$", str(err)) - if m and m.group(1) in settings: - # translate into KeyError, for backwards compat. - # XXX: push this down to GenericHandler.using() implementation? - key = m.group(1) - raise KeyError("keyword not supported by %s handler: %r" % - (handler.name, key)) - raise - - # using private attrs to store some extra metadata in custom handler - assert subcls is not handler, "expected unique variant of handler" - ##subcls._Context__category = category - subcls._Context__orig_handler = handler - subcls.deprecated = deprecated # attr reserved for this purpose - return subcls - - def _get_record_options_with_flag(self, scheme, category): - """return composite dict of options for given scheme + category. - - this is currently a private method, though some variant - of its output may eventually be made public. - - given a scheme & category, it returns two things: - a set of all the keyword options to pass to :meth:`_create_record`, - and a bool flag indicating whether any of these options - were specific to the named category. if this flag is false, - the options are identical to the options for the default category. - - the options dict includes all the scheme-specific settings, - as well as optional *deprecated* keyword. - """ - # get scheme options - kwds, has_cat_options = self.get_scheme_options_with_flag(scheme, category) - - # throw in deprecated flag - value, not_inherited = self.is_deprecated_with_flag(scheme, category) - if value: - kwds['deprecated'] = True - if not_inherited: - has_cat_options = True - - return kwds, has_cat_options - - def get_record(self, scheme, category): - """return record for specific scheme & category (cached)""" - # NOTE: this is part of the critical path shared by - # all of CryptContext's PasswordHash methods, - # hence all the caching and error checking. - - # quick lookup in cache - try: - return self._records[scheme, category] - except KeyError: - pass - - # type check - if category is not None and not isinstance(category, native_string_types): - if PY2 and isinstance(category, unicode): - # for compatibility with unicode-centric py2 apps - return self.get_record(scheme, category.encode("utf-8")) - raise ExpectedTypeError(category, "str or None", "category") - if scheme is not None and not isinstance(scheme, native_string_types): - raise ExpectedTypeError(scheme, "str or None", "scheme") - - # if scheme=None, - # use record for category's default scheme, and cache result. - if not scheme: - default = self.default_scheme(category) - assert default - record = self._records[None, category] = self.get_record(default, - category) - return record - - # if no record for (scheme, category), - # use record for (scheme, None), and cache result. - if category: - try: - cache = self._records - record = cache[scheme, category] = cache[scheme, None] - return record - except KeyError: - pass - - # scheme not found in configuration for default category - raise KeyError("crypt algorithm not found in policy: %r" % (scheme,)) - - def _get_record_list(self, category=None): - """return list of records for category (cached) - - this is an internal helper used only by identify_record() - """ - # type check of category - handled by _get_record() - # quick lookup in cache - try: - return self._record_lists[category] - except KeyError: - pass - # cache miss - build list from scratch - value = self._record_lists[category] = [ - self.get_record(scheme, category) - for scheme in self.schemes - ] - return value - - def identify_record(self, hash, category, required=True): - """internal helper to identify appropriate custom handler for hash""" - # NOTE: this is part of the critical path shared by - # all of CryptContext's PasswordHash methods, - # hence all the caching and error checking. - # FIXME: if multiple hashes could match (e.g. lmhash vs nthash) - # this will only return first match. might want to do something - # about this in future, but for now only hashes with - # unique identifiers will work properly in a CryptContext. - # XXX: if all handlers have a unique prefix (e.g. all are MCF / LDAP), - # could use dict-lookup to speed up this search. - if not isinstance(hash, unicode_or_bytes_types): - raise ExpectedStringError(hash, "hash") - # type check of category - handled by _get_record_list() - for record in self._get_record_list(category): - if record.identify(hash): - return record - if not required: - return None - elif not self.schemes: - raise KeyError("no crypt algorithms supported") - else: - raise exc.UnknownHashError("hash could not be identified") - - @memoized_property - def disabled_record(self): - for record in self._get_record_list(None): - if record.is_disabled: - return record - raise RuntimeError("no disabled hasher present " - "(perhaps add 'unix_disabled' to list of schemes?)") - - #=================================================================== - # serialization - #=================================================================== - def iter_config(self, resolve=False): - """regenerate original config. - - this is an iterator which yields ``(cat,scheme,option),value`` items, - in the order they generally appear inside an INI file. - if interpreted as a dictionary, it should match the original - keywords passed to the CryptContext (aside from any canonization). - - it's mainly used as the internal backend for most of the public - serialization methods. - """ - # grab various bits of data - scheme_options = self._scheme_options - context_options = self._context_options - scheme_keys = sorted(scheme_options) - context_keys = sorted(context_options) - - # write loaded schemes (may differ from 'schemes' local var) - if 'schemes' in context_keys: - context_keys.remove("schemes") - value = self.handlers if resolve else self.schemes - if value: - yield (None, None, "schemes"), list(value) - - # then run through config for each user category - for cat in (None,) + self.categories: - - # write context options - for key in context_keys: - try: - value = context_options[key][cat] - except KeyError: - pass - else: - if isinstance(value, list): - value = list(value) - yield (cat, None, key), value - - # write per-scheme options for all schemes. - for scheme in scheme_keys: - try: - kwds = scheme_options[scheme][cat] - except KeyError: - pass - else: - for key in sorted(kwds): - yield (cat, scheme, key), kwds[key] - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# main CryptContext class -#============================================================================= -class CryptContext(object): - """Helper for hashing & verifying passwords using multiple algorithms. - - Instances of this class allow applications to choose a specific - set of hash algorithms which they wish to support, set limits and defaults - for the rounds and salt sizes those algorithms should use, flag - which algorithms should be deprecated, and automatically handle - migrating users to stronger hashes when they log in. - - Basic usage:: - - >>> ctx = CryptContext(schemes=[...]) - - See the Passlib online documentation for details and full documentation. - """ - # FIXME: altering the configuration of this object isn't threadsafe, - # but is generally only done during application init, so not a major - # issue (just yet). - - # XXX: would like some way to restrict the categories that are allowed, - # to restrict what the app OR the config can use. - - # XXX: add wrap/unwrap callback hooks so app can mutate hash format? - - # XXX: add method for detecting and warning user about schemes - # which don't have any good distinguishing marks? - # or greedy ones (unix_disabled, plaintext) which are not listed at the end? - - #=================================================================== - # instance attrs - #=================================================================== - - # _CryptConfig instance holding current parsed config - _config = None - - # copy of _config methods, stored in CryptContext instance for speed. - _get_record = None - _identify_record = None - - #=================================================================== - # secondary constructors - #=================================================================== - @classmethod - def _norm_source(cls, source): - """internal helper - accepts string, dict, or context""" - if isinstance(source, dict): - return cls(**source) - elif isinstance(source, cls): - return source - else: - self = cls() - self.load(source) - return self - - @classmethod - def from_string(cls, source, section="passlib", encoding="utf-8"): - """create new CryptContext instance from an INI-formatted string. - - :type source: unicode or bytes - :arg source: - string containing INI-formatted content. - - :type section: str - :param section: - option name of section to read from, defaults to ``"passlib"``. - - :type encoding: str - :arg encoding: - optional encoding used when source is bytes, defaults to ``"utf-8"``. - - :returns: - new :class:`CryptContext` instance, configured based on the - parameters in the *source* string. - - Usage example:: - - >>> from passlib.context import CryptContext - >>> context = CryptContext.from_string(''' - ... [passlib] - ... schemes = sha256_crypt, des_crypt - ... sha256_crypt__default_rounds = 30000 - ... ''') - - .. versionadded:: 1.6 - - .. seealso:: :meth:`to_string`, the inverse of this constructor. - """ - if not isinstance(source, unicode_or_bytes_types): - raise ExpectedTypeError(source, "unicode or bytes", "source") - self = cls(_autoload=False) - self.load(source, section=section, encoding=encoding) - return self - - @classmethod - def from_path(cls, path, section="passlib", encoding="utf-8"): - """create new CryptContext instance from an INI-formatted file. - - this functions exactly the same as :meth:`from_string`, - except that it loads from a local file. - - :type path: str - :arg path: - path to local file containing INI-formatted config. - - :type section: str - :param section: - option name of section to read from, defaults to ``"passlib"``. - - :type encoding: str - :arg encoding: - encoding used to load file, defaults to ``"utf-8"``. - - :returns: - new CryptContext instance, configured based on the parameters - stored in the file *path*. - - .. versionadded:: 1.6 - - .. seealso:: :meth:`from_string` for an equivalent usage example. - """ - self = cls(_autoload=False) - self.load_path(path, section=section, encoding=encoding) - return self - - def copy(self, **kwds): - """Return copy of existing CryptContext instance. - - This function returns a new CryptContext instance whose configuration - is exactly the same as the original, with the exception that any keywords - passed in will take precedence over the original settings. - As an example:: - - >>> from passlib.context import CryptContext - - >>> # given an existing context... - >>> ctx1 = CryptContext(["sha256_crypt", "md5_crypt"]) - - >>> # copy can be used to make a clone, and update - >>> # some of the settings at the same time... - >>> ctx2 = custom_app_context.copy(default="md5_crypt") - - >>> # and the original will be unaffected by the change - >>> ctx1.default_scheme() - "sha256_crypt" - >>> ctx2.default_scheme() - "md5_crypt" - - .. versionadded:: 1.6 - This method was previously named :meth:`!replace`. That alias - has been deprecated, and will be removed in Passlib 1.8. - - .. seealso:: :meth:`update` - """ - # XXX: it would be faster to store ref to self._config, - # but don't want to share config objects til sure - # can rely on them being immutable. - other = CryptContext(_autoload=False) - other.load(self) - if kwds: - other.load(kwds, update=True) - return other - - def using(self, **kwds): - """ - alias for :meth:`copy`, to match PasswordHash.using() - """ - return self.copy(**kwds) - - def replace(self, **kwds): - """deprecated alias of :meth:`copy`""" - warn("CryptContext().replace() has been deprecated in Passlib 1.6, " - "and will be removed in Passlib 1.8, " - "it has been renamed to CryptContext().copy()", - DeprecationWarning, stacklevel=2) - return self.copy(**kwds) - - #=================================================================== - # init - #=================================================================== - def __init__(self, schemes=None, - # keyword only... - policy=_UNSET, # <-- deprecated - _autoload=True, **kwds): - # XXX: add ability to make flag certain contexts as immutable, - # e.g. the builtin passlib ones? - # XXX: add a name or import path for the contexts, to help out repr? - if schemes is not None: - kwds['schemes'] = schemes - if policy is not _UNSET: - warn("The CryptContext ``policy`` keyword has been deprecated as of Passlib 1.6, " - "and will be removed in Passlib 1.8; please use " - "``CryptContext.from_string()` or " - "``CryptContext.from_path()`` instead.", - DeprecationWarning) - if policy is None: - self.load(kwds) - elif isinstance(policy, CryptPolicy): - self.load(policy._context) - self.update(kwds) - else: - raise TypeError("policy must be a CryptPolicy instance") - elif _autoload: - self.load(kwds) - else: - assert not kwds, "_autoload=False and kwds are mutually exclusive" - - # XXX: would this be useful? - ##def __str__(self): - ## if PY3: - ## return self.to_string() - ## else: - ## return self.to_string().encode("utf-8") - - def __repr__(self): - return "" % id(self) - - #=================================================================== - # deprecated policy object - #=================================================================== - def _get_policy(self): - # The CryptPolicy class has been deprecated, so to support any - # legacy accesses, we create a stub policy object so .policy attr - # will continue to work. - # - # the code waits until app accesses a specific policy object attribute - # before issuing deprecation warning, so developer gets method-specific - # suggestion for how to upgrade. - - # NOTE: making a copy of the context so the policy acts like a snapshot, - # to retain the pre-1.6 behavior. - return CryptPolicy(_internal_context=self.copy(), _stub_policy=True) - - def _set_policy(self, policy): - warn("The CryptPolicy class and the ``context.policy`` attribute have " - "been deprecated as of Passlib 1.6, and will be removed in " - "Passlib 1.8; please use the ``context.load()`` and " - "``context.update()`` methods instead.", - DeprecationWarning, stacklevel=2) - if isinstance(policy, CryptPolicy): - self.load(policy._context) - else: - raise TypeError("expected CryptPolicy instance") - - policy = property(_get_policy, _set_policy, - doc="[deprecated] returns CryptPolicy instance " - "tied to this CryptContext") - - #=================================================================== - # loading / updating configuration - #=================================================================== - @staticmethod - def _parse_ini_stream(stream, section, filename): - """helper read INI from stream, extract passlib section as dict""" - # NOTE: this expects a unicode stream under py3, - # and a utf-8 bytes stream under py2, - # allowing the resulting dict to always use native strings. - p = SafeConfigParser() - if PY3: - # python 3.2 deprecated readfp in favor of read_file - p.read_file(stream, filename) - else: - p.readfp(stream, filename) - # XXX: could change load() to accept list of items, - # and skip intermediate dict creation - return dict(p.items(section)) - - def load_path(self, path, update=False, section="passlib", encoding="utf-8"): - """Load new configuration into CryptContext from a local file. - - This function is a wrapper for :meth:`load` which - loads a configuration string from the local file *path*, - instead of an in-memory source. Its behavior and options - are otherwise identical to :meth:`!load` when provided with - an INI-formatted string. - - .. versionadded:: 1.6 - """ - def helper(stream): - kwds = self._parse_ini_stream(stream, section, path) - return self.load(kwds, update=update) - if PY3: - # decode to unicode, which load() expected under py3 - with open(path, "rt", encoding=encoding) as stream: - return helper(stream) - elif encoding in ["utf-8", "ascii"]: - # keep as utf-8 bytes, which load() expects under py2 - with open(path, "rb") as stream: - return helper(stream) - else: - # transcode to utf-8 bytes - with open(path, "rb") as fh: - tmp = fh.read().decode(encoding).encode("utf-8") - return helper(BytesIO(tmp)) - - def load(self, source, update=False, section="passlib", encoding="utf-8"): - """Load new configuration into CryptContext, replacing existing config. - - :arg source: - source of new configuration to load. - this value can be a number of different types: - - * a :class:`!dict` object, or compatible Mapping - - the key/value pairs will be interpreted the same - keywords for the :class:`CryptContext` class constructor. - - * a :class:`!unicode` or :class:`!bytes` string - - this will be interpreted as an INI-formatted file, - and appropriate key/value pairs will be loaded from - the specified *section*. - - * another :class:`!CryptContext` object. - - this will export a snapshot of its configuration - using :meth:`to_dict`. - - :type update: bool - :param update: - By default, :meth:`load` will replace the existing configuration - entirely. If ``update=True``, it will preserve any existing - configuration options that are not overridden by the new source, - much like the :meth:`update` method. - - :type section: str - :param section: - When parsing an INI-formatted string, :meth:`load` will look for - a section named ``"passlib"``. This option allows an alternate - section name to be used. Ignored when loading from a dictionary. - - :type encoding: str - :param encoding: - Encoding to use when **source** is bytes. - Defaults to ``"utf-8"``. Ignored when loading from a dictionary. - - .. deprecated:: 1.8 - - This keyword, and support for bytes input, will be dropped in Passlib 2.0 - - :raises TypeError: - * If the source cannot be identified. - * If an unknown / malformed keyword is encountered. - - :raises ValueError: - If an invalid keyword value is encountered. - - .. note:: - - If an error occurs during a :meth:`!load` call, the :class:`!CryptContext` - instance will be restored to the configuration it was in before - the :meth:`!load` call was made; this is to ensure it is - *never* left in an inconsistent state due to a load error. - - .. versionadded:: 1.6 - """ - #----------------------------------------------------------- - # autodetect source type, convert to dict - #----------------------------------------------------------- - parse_keys = True - if isinstance(source, unicode_or_bytes_types): - if PY3: - source = to_unicode(source, encoding, param="source") - else: - source = to_bytes(source, "utf-8", source_encoding=encoding, - param="source") - source = self._parse_ini_stream(NativeStringIO(source), section, - "") - elif isinstance(source, CryptContext): - # extract dict directly from config, so it can be merged later - source = dict(source._config.iter_config(resolve=True)) - parse_keys = False - elif not hasattr(source, "items"): - # mappings are left alone, otherwise throw an error. - raise ExpectedTypeError(source, "string or dict", "source") - - # XXX: add support for other iterable types, e.g. sequence of pairs? - - #----------------------------------------------------------- - # parse dict keys into (category, scheme, option) format, - # and merge with existing configuration if needed. - #----------------------------------------------------------- - if parse_keys: - parse = self._parse_config_key - source = dict((parse(key), value) - for key, value in iteritems(source)) - if update and self._config is not None: - # if updating, do nothing if source is empty, - if not source: - return - # otherwise overlay source on top of existing config - tmp = source - source = dict(self._config.iter_config(resolve=True)) - source.update(tmp) - - #----------------------------------------------------------- - # compile into _CryptConfig instance, and update state - #----------------------------------------------------------- - config = _CryptConfig(source) - self._config = config - self._reset_dummy_verify() - self._get_record = config.get_record - self._identify_record = config.identify_record - if config.context_kwds: - # (re-)enable method for this instance (in case ELSE clause below ran last load). - self.__dict__.pop("_strip_unused_context_kwds", None) - else: - # disable method for this instance, it's not needed. - self._strip_unused_context_kwds = None - - @staticmethod - def _parse_config_key(ckey): - """helper used to parse ``cat__scheme__option`` keys into a tuple""" - # split string into 1-3 parts - assert isinstance(ckey, native_string_types) - parts = ckey.replace(".", "__").split("__") - count = len(parts) - if count == 1: - cat, scheme, key = None, None, parts[0] - elif count == 2: - cat = None - scheme, key = parts - elif count == 3: - cat, scheme, key = parts - else: - raise TypeError("keys must have less than 3 separators: %r" % - (ckey,)) - # validate & normalize the parts - if cat == "default": - cat = None - elif not cat and cat is not None: - raise TypeError("empty category: %r" % ckey) - if scheme == "context": - scheme = None - elif not scheme and scheme is not None: - raise TypeError("empty scheme: %r" % ckey) - if not key: - raise TypeError("empty option: %r" % ckey) - return cat, scheme, key - - def update(self, *args, **kwds): - """Helper for quickly changing configuration. - - This acts much like the :meth:`!dict.update` method: - it updates the context's configuration, - replacing the original value(s) for the specified keys, - and preserving the rest. - It accepts any :ref:`keyword ` - accepted by the :class:`!CryptContext` constructor. - - .. versionadded:: 1.6 - - .. seealso:: :meth:`copy` - """ - if args: - if len(args) > 1: - raise TypeError("expected at most one positional argument") - if kwds: - raise TypeError("positional arg and keywords mutually exclusive") - self.load(args[0], update=True) - elif kwds: - self.load(kwds, update=True) - - # XXX: make this public? even just as flag to load? - # FIXME: this function suffered some bitrot in 1.6.1, - # will need to be updated before works again. - ##def _simplify(self): - ## "helper to remove redundant/unused options" - ## # don't do anything if no schemes are defined - ## if not self._schemes: - ## return - ## - ## def strip_items(target, filter): - ## keys = [key for key,value in iteritems(target) - ## if filter(key,value)] - ## for key in keys: - ## del target[key] - ## - ## # remove redundant default. - ## defaults = self._default_schemes - ## if defaults.get(None) == self._schemes[0]: - ## del defaults[None] - ## - ## # remove options for unused schemes. - ## scheme_options = self._scheme_options - ## schemes = self._schemes + ("all",) - ## strip_items(scheme_options, lambda k,v: k not in schemes) - ## - ## # remove rendundant cat defaults. - ## cur = self.default_scheme() - ## strip_items(defaults, lambda k,v: k and v==cur) - ## - ## # remove redundant category deprecations. - ## # TODO: this should work w/ 'auto', but needs closer inspection - ## deprecated = self._deprecated_schemes - ## cur = self._deprecated_schemes.get(None) - ## strip_items(deprecated, lambda k,v: k and v==cur) - ## - ## # remove redundant category options. - ## for scheme, config in iteritems(scheme_options): - ## if None in config: - ## cur = config[None] - ## strip_items(config, lambda k,v: k and v==cur) - ## - ## # XXX: anything else? - - #=================================================================== - # reading configuration - #=================================================================== - def schemes(self, resolve=False, category=None, unconfigured=False): - """return schemes loaded into this CryptContext instance. - - :type resolve: bool - :arg resolve: - if ``True``, will return a tuple of :class:`~passlib.ifc.PasswordHash` - objects instead of their names. - - :returns: - returns tuple of the schemes configured for this context - via the *schemes* option. - - .. versionadded:: 1.6 - This was previously available as ``CryptContext().policy.schemes()`` - - .. seealso:: the :ref:`schemes ` option for usage example. - """ - # XXX: should resolv return records rather than handlers? - # or deprecate resolve keyword completely? - # offering up a .hashers Mapping in v1.8 would be great. - # NOTE: supporting 'category' and 'unconfigured' kwds as of 1.7 - # just to pass through to .handler(), but not documenting them... - # may not need to put them to use. - schemes = self._config.schemes - if resolve: - return tuple(self.handler(scheme, category, unconfigured=unconfigured) - for scheme in schemes) - else: - return schemes - - def default_scheme(self, category=None, resolve=False, unconfigured=False): - """return name of scheme that :meth:`hash` will use by default. - - :type resolve: bool - :arg resolve: - if ``True``, will return a :class:`~passlib.ifc.PasswordHash` - object instead of the name. - - :type category: str or None - :param category: - Optional :ref:`user category `. - If specified, this will return the catgory-specific default scheme instead. - - :returns: - name of the default scheme. - - .. seealso:: the :ref:`default ` option for usage example. - - .. versionadded:: 1.6 - - .. versionchanged:: 1.7 - - This now returns a hasher configured with any CryptContext-specific - options (custom rounds settings, etc). Previously this returned - the base hasher from :mod:`passlib.hash`. - """ - # XXX: deprecate this in favor of .handler() or whatever it's replaced with? - # NOTE: supporting 'unconfigured' kwds as of 1.7 - # just to pass through to .handler(), but not documenting them... - # may not need to put them to use. - hasher = self.handler(None, category, unconfigured=unconfigured) - return hasher if resolve else hasher.name - - # XXX: need to decide if exposing this would be useful in any way - ##def categories(self): - ## """return user-categories with algorithm-specific options in this CryptContext. - ## - ## this will always return a tuple. - ## if no categories besides the default category have been configured, - ## the tuple will be empty. - ## """ - ## return self._config.categories - - # XXX: need to decide if exposing this would be useful to applications - # in any meaningful way that isn't already served by to_dict() - ##def options(self, scheme, category=None): - ## kwds, percat = self._config.get_options(scheme, category) - ## return kwds - - def handler(self, scheme=None, category=None, unconfigured=False): - """helper to resolve name of scheme -> :class:`~passlib.ifc.PasswordHash` object used by scheme. - - :arg scheme: - This should identify the scheme to lookup. - If omitted or set to ``None``, this will return the handler - for the default scheme. - - :arg category: - If a user category is specified, and no scheme is provided, - it will use the default for that category. - Otherwise this parameter is ignored. - - :param unconfigured: - - By default, this returns a handler object whose .hash() - and .needs_update() methods will honor the configured - provided by CryptContext. See ``unconfigured=True`` - to get the underlying handler from before any context-specific - configuration was applied. - - :raises KeyError: - If the scheme does not exist OR is not being used within this context. - - :returns: - :class:`~passlib.ifc.PasswordHash` object used to implement - the named scheme within this context (this will usually - be one of the objects from :mod:`passlib.hash`) - - .. versionadded:: 1.6 - This was previously available as ``CryptContext().policy.get_handler()`` - - .. versionchanged:: 1.7 - - This now returns a hasher configured with any CryptContext-specific - options (custom rounds settings, etc). Previously this returned - the base hasher from :mod:`passlib.hash`. - """ - try: - hasher = self._get_record(scheme, category) - if unconfigured: - return hasher._Context__orig_handler - else: - return hasher - except KeyError: - pass - if self._config.handlers: - raise KeyError("crypt algorithm not found in this " - "CryptContext instance: %r" % (scheme,)) - else: - raise KeyError("no crypt algorithms loaded in this " - "CryptContext instance") - - def _get_unregistered_handlers(self): - """check if any handlers in this context aren't in the global registry""" - return tuple(handler for handler in self._config.handlers - if not _is_handler_registered(handler)) - - @property - def context_kwds(self): - """ - return :class:`!set` containing union of all :ref:`contextual keywords ` - supported by the handlers in this context. - - .. versionadded:: 1.6.6 - """ - return self._config.context_kwds - - #=================================================================== - # exporting config - #=================================================================== - @staticmethod - def _render_config_key(key): - """convert 3-part config key to single string""" - cat, scheme, option = key - if cat: - return "%s__%s__%s" % (cat, scheme or "context", option) - elif scheme: - return "%s__%s" % (scheme, option) - else: - return option - - @staticmethod - def _render_ini_value(key, value): - """render value to string suitable for INI file""" - # convert lists to comma separated lists - # (mainly 'schemes' & 'deprecated') - if isinstance(value, (list,tuple)): - value = ", ".join(value) - - # convert numbers to strings - elif isinstance(value, num_types): - if isinstance(value, float) and key[2] == "vary_rounds": - value = ("%.2f" % value).rstrip("0") if value else "0" - else: - value = str(value) - - assert isinstance(value, native_string_types), \ - "expected string for key: %r %r" % (key, value) - - # escape any percent signs. - return value.replace("%", "%%") - - def to_dict(self, resolve=False): - """Return current configuration as a dictionary. - - :type resolve: bool - :arg resolve: - if ``True``, the ``schemes`` key will contain a list of - a :class:`~passlib.ifc.PasswordHash` objects instead of just - their names. - - This method dumps the current configuration of the CryptContext - instance. The key/value pairs should be in the format accepted - by the :class:`!CryptContext` class constructor, in fact - ``CryptContext(**myctx.to_dict())`` will create an exact copy of ``myctx``. - As an example:: - - >>> # you can dump the configuration of any crypt context... - >>> from passlib.apps import ldap_nocrypt_context - >>> ldap_nocrypt_context.to_dict() - {'schemes': ['ldap_salted_sha1', - 'ldap_salted_md5', - 'ldap_sha1', - 'ldap_md5', - 'ldap_plaintext']} - - .. versionadded:: 1.6 - This was previously available as ``CryptContext().policy.to_dict()`` - - .. seealso:: the :ref:`context-serialization-example` example in the tutorial. - """ - # XXX: should resolve default to conditional behavior - # based on presence of unregistered handlers? - render_key = self._render_config_key - return dict((render_key(key), value) - for key, value in self._config.iter_config(resolve)) - - def _write_to_parser(self, parser, section): - """helper to write to ConfigParser instance""" - render_key = self._render_config_key - render_value = self._render_ini_value - parser.add_section(section) - for k,v in self._config.iter_config(): - v = render_value(k, v) - k = render_key(k) - parser.set(section, k, v) - - def to_string(self, section="passlib"): - """serialize to INI format and return as unicode string. - - :param section: - name of INI section to output, defaults to ``"passlib"``. - - :returns: - CryptContext configuration, serialized to a INI unicode string. - - This function acts exactly like :meth:`to_dict`, except that it - serializes all the contents into a single human-readable string, - which can be hand edited, and/or stored in a file. The - output of this method is accepted by :meth:`from_string`, - :meth:`from_path`, and :meth:`load`. As an example:: - - >>> # you can dump the configuration of any crypt context... - >>> from passlib.apps import ldap_nocrypt_context - >>> print ldap_nocrypt_context.to_string() - [passlib] - schemes = ldap_salted_sha1, ldap_salted_md5, ldap_sha1, ldap_md5, ldap_plaintext - - .. versionadded:: 1.6 - This was previously available as ``CryptContext().policy.to_string()`` - - .. seealso:: the :ref:`context-serialization-example` example in the tutorial. - """ - parser = SafeConfigParser() - self._write_to_parser(parser, section) - buf = NativeStringIO() - parser.write(buf) - unregistered = self._get_unregistered_handlers() - if unregistered: - buf.write(( - "# NOTE: the %s handler(s) are not registered with Passlib,\n" - "# this string may not correctly reproduce the current configuration.\n\n" - ) % ", ".join(repr(handler.name) for handler in unregistered)) - out = buf.getvalue() - if not PY3: - out = out.decode("utf-8") - return out - - # XXX: is this useful enough to enable? - ##def write_to_path(self, path, section="passlib", update=False): - ## "write to INI file" - ## parser = ConfigParser() - ## if update and os.path.exists(path): - ## if not parser.read([path]): - ## raise EnvironmentError("failed to read existing file") - ## parser.remove_section(section) - ## self._write_to_parser(parser, section) - ## fh = file(path, "w") - ## parser.write(fh) - ## fh.close() - - #=================================================================== - # verify() hardening - # NOTE: this entire feature has been disabled. - # all contents of this section are NOOPs as of 1.7.1, - # and will be removed in 1.8. - #=================================================================== - - mvt_estimate_max_samples = 20 - mvt_estimate_min_samples = 10 - mvt_estimate_max_time = 2 - mvt_estimate_resolution = 0.01 - harden_verify = None - min_verify_time = 0 - - def reset_min_verify_time(self): - self._reset_dummy_verify() - - #=================================================================== - # password hash api - #=================================================================== - - # NOTE: all the following methods do is look up the appropriate - # custom handler for a given (scheme,category) combination, - # and hand off the real work to the handler itself, - # which is optimized for the specific (scheme,category) configuration. - # - # The custom handlers are cached inside the _CryptConfig - # instance stored in self._config, and are retrieved - # via get_record() and identify_record(). - # - # _get_record() and _identify_record() are references - # to _config methods of the same name, - # stored in CryptContext for speed. - - def _get_or_identify_record(self, hash, scheme=None, category=None): - """return record based on scheme, or failing that, by identifying hash""" - if scheme: - if not isinstance(hash, unicode_or_bytes_types): - raise ExpectedStringError(hash, "hash") - return self._get_record(scheme, category) - else: - # hash typecheck handled by identify_record() - return self._identify_record(hash, category) - - def _strip_unused_context_kwds(self, kwds, record): - """ - helper which removes any context keywords from **kwds** - that are known to be used by another scheme in this context, - but are NOT supported by handler specified by **record**. - - .. note:: - as optimization, load() will set this method to None on a per-instance basis - if there are no context kwds. - """ - if not kwds: - return - unused_kwds = self._config.context_kwds.difference(record.context_kwds) - for key in unused_kwds: - kwds.pop(key, None) - - def needs_update(self, hash, scheme=None, category=None, secret=None): - """Check if hash needs to be replaced for some reason, - in which case the secret should be re-hashed. - - This function is the core of CryptContext's support for hash migration: - This function takes in a hash string, and checks the scheme, - number of rounds, and other properties against the current policy. - It returns ``True`` if the hash is using a deprecated scheme, - or is otherwise outside of the bounds specified by the policy - (e.g. the number of rounds is lower than :ref:`min_rounds ` - configuration for that algorithm). - If so, the password should be re-hashed using :meth:`hash` - Otherwise, it will return ``False``. - - :type hash: unicode or bytes - :arg hash: - The hash string to examine. - - :type scheme: str or None - :param scheme: - - Optional scheme to use. Scheme must be one of the ones - configured for this context (see the - :ref:`schemes ` option). - If no scheme is specified, it will be identified - based on the value of *hash*. - - .. deprecated:: 1.7 - - Support for this keyword is deprecated, and will be removed in Passlib 2.0. - - :type category: str or None - :param category: - Optional :ref:`user category `. - If specified, this will cause any category-specific defaults to - be used when determining if the hash needs to be updated - (e.g. is below the minimum rounds). - - :type secret: unicode, bytes, or None - :param secret: - Optional secret associated with the provided ``hash``. - This is not required, or even currently used for anything... - it's for forward-compatibility with any future - update checks that might need this information. - If provided, Passlib assumes the secret has already been - verified successfully against the hash. - - .. versionadded:: 1.6 - - :returns: ``True`` if hash should be replaced, otherwise ``False``. - - :raises ValueError: - If the hash did not match any of the configured :meth:`schemes`. - - .. versionadded:: 1.6 - This method was previously named :meth:`hash_needs_update`. - - .. seealso:: the :ref:`context-migration-example` example in the tutorial. - """ - if scheme is not None: - # TODO: offer replacement alternative. - # ``context.handler(scheme).needs_update()`` would work, - # but may deprecate .handler() in passlib 1.8. - warn("CryptContext.needs_update(): 'scheme' keyword is deprecated as of " - "Passlib 1.7, and will be removed in Passlib 2.0", - DeprecationWarning) - record = self._get_or_identify_record(hash, scheme, category) - return record.deprecated or record.needs_update(hash, secret=secret) - - @deprecated_method(deprecated="1.6", removed="2.0", replacement="CryptContext.needs_update()") - def hash_needs_update(self, hash, scheme=None, category=None): - """Legacy alias for :meth:`needs_update`. - - .. deprecated:: 1.6 - This method was renamed to :meth:`!needs_update` in version 1.6. - This alias will be removed in version 2.0, and should only - be used for compatibility with Passlib 1.3 - 1.5. - """ - return self.needs_update(hash, scheme, category) - - @deprecated_method(deprecated="1.7", removed="2.0") - def genconfig(self, scheme=None, category=None, **settings): - """Generate a config string for specified scheme. - - .. deprecated:: 1.7 - - This method will be removed in version 2.0, and should only - be used for compatibility with Passlib 1.3 - 1.6. - """ - record = self._get_record(scheme, category) - strip_unused = self._strip_unused_context_kwds - if strip_unused: - strip_unused(settings, record) - return record.genconfig(**settings) - - @deprecated_method(deprecated="1.7", removed="2.0") - def genhash(self, secret, config, scheme=None, category=None, **kwds): - """Generate hash for the specified secret using another hash. - - .. deprecated:: 1.7 - - This method will be removed in version 2.0, and should only - be used for compatibility with Passlib 1.3 - 1.6. - """ - record = self._get_or_identify_record(config, scheme, category) - strip_unused = self._strip_unused_context_kwds - if strip_unused: - strip_unused(kwds, record) - return record.genhash(secret, config, **kwds) - - def identify(self, hash, category=None, resolve=False, required=False, - unconfigured=False): - """Attempt to identify which algorithm the hash belongs to. - - Note that this will only consider the algorithms - currently configured for this context - (see the :ref:`schemes ` option). - All registered algorithms will be checked, from first to last, - and whichever one positively identifies the hash first will be returned. - - :type hash: unicode or bytes - :arg hash: - The hash string to test. - - :type category: str or None - :param category: - Optional :ref:`user category `. - Ignored by this function, this parameter - is provided for symmetry with the other methods. - - :type resolve: bool - :param resolve: - If ``True``, returns the hash handler itself, - instead of the name of the hash. - - :type required: bool - :param required: - If ``True``, this will raise a ValueError if the hash - cannot be identified, instead of returning ``None``. - - :returns: - The handler which first identifies the hash, - or ``None`` if none of the algorithms identify the hash. - """ - record = self._identify_record(hash, category, required) - if record is None: - return None - elif resolve: - if unconfigured: - return record._Context__orig_handler - else: - return record - else: - return record.name - - def hash(self, secret, scheme=None, category=None, **kwds): - """run secret through selected algorithm, returning resulting hash. - - :type secret: unicode or bytes - :arg secret: - the password to hash. - - :type scheme: str or None - :param scheme: - - Optional scheme to use. Scheme must be one of the ones - configured for this context (see the - :ref:`schemes ` option). - If no scheme is specified, the configured default - will be used. - - .. deprecated:: 1.7 - - Support for this keyword is deprecated, and will be removed in Passlib 2.0. - - :type category: str or None - :param category: - Optional :ref:`user category `. - If specified, this will cause any category-specific defaults to - be used when hashing the password (e.g. different default scheme, - different default rounds values, etc). - - :param \\*\\*kwds: - All other keyword options are passed to the selected algorithm's - :meth:`PasswordHash.hash() ` method. - - :returns: - The secret as encoded by the specified algorithm and options. - The return value will always be a :class:`!str`. - - :raises TypeError, ValueError: - * If any of the arguments have an invalid type or value. - This includes any keywords passed to the underlying hash's - :meth:`PasswordHash.hash() ` method. - - .. seealso:: the :ref:`context-basic-example` example in the tutorial - """ - # XXX: could insert normalization to preferred unicode encoding here - if scheme is not None: - # TODO: offer replacement alternative. - # ``context.handler(scheme).hash()`` would work, - # but may deprecate .handler() in passlib 1.8. - warn("CryptContext.hash(): 'scheme' keyword is deprecated as of " - "Passlib 1.7, and will be removed in Passlib 2.0", - DeprecationWarning) - record = self._get_record(scheme, category) - strip_unused = self._strip_unused_context_kwds - if strip_unused: - strip_unused(kwds, record) - return record.hash(secret, **kwds) - - @deprecated_method(deprecated="1.7", removed="2.0", replacement="CryptContext.hash()") - def encrypt(self, *args, **kwds): - """ - Legacy alias for :meth:`hash`. - - .. deprecated:: 1.7 - This method was renamed to :meth:`!hash` in version 1.7. - This alias will be removed in version 2.0, and should only - be used for compatibility with Passlib 1.3 - 1.6. - """ - return self.hash(*args, **kwds) - - def verify(self, secret, hash, scheme=None, category=None, **kwds): - """verify secret against an existing hash. - - If no scheme is specified, this will attempt to identify - the scheme based on the contents of the provided hash - (limited to the schemes configured for this context). - It will then check whether the password verifies against the hash. - - :type secret: unicode or bytes - :arg secret: - the secret to verify - - :type hash: unicode or bytes - :arg hash: - hash string to compare to - - if ``None`` is passed in, this will be treated as "never verifying" - - :type scheme: str - :param scheme: - Optionally force context to use specific scheme. - This is usually not needed, as most hashes can be unambiguously - identified. Scheme must be one of the ones configured - for this context - (see the :ref:`schemes ` option). - - .. deprecated:: 1.7 - - Support for this keyword is deprecated, and will be removed in Passlib 2.0. - - :type category: str or None - :param category: - Optional :ref:`user category ` string. - This is mainly used when generating new hashes, it has little - effect when verifying; this keyword is mainly provided for symmetry. - - :param \\*\\*kwds: - All additional keywords are passed to the appropriate handler, - and should match its :attr:`~passlib.ifc.PasswordHash.context_kwds`. - - :returns: - ``True`` if the password matched the hash, else ``False``. - - :raises ValueError: - * if the hash did not match any of the configured :meth:`schemes`. - - * if any of the arguments have an invalid value (this includes - any keywords passed to the underlying hash's - :meth:`PasswordHash.verify() ` method). - - :raises TypeError: - * if any of the arguments have an invalid type (this includes - any keywords passed to the underlying hash's - :meth:`PasswordHash.verify() ` method). - - .. seealso:: the :ref:`context-basic-example` example in the tutorial - """ - # XXX: could insert normalization to preferred unicode encoding here - # XXX: what about supporting a setter() callback ala django 1.4 ? - if scheme is not None: - # TODO: offer replacement alternative. - # ``context.handler(scheme).verify()`` would work, - # but may deprecate .handler() in passlib 1.8. - warn("CryptContext.verify(): 'scheme' keyword is deprecated as of " - "Passlib 1.7, and will be removed in Passlib 2.0", - DeprecationWarning) - if hash is None: - # convenience feature -- let apps pass in hash=None when user - # isn't found / has no hash; useful because it invokes dummy_verify() - self.dummy_verify() - return False - record = self._get_or_identify_record(hash, scheme, category) - strip_unused = self._strip_unused_context_kwds - if strip_unused: - strip_unused(kwds, record) - return record.verify(secret, hash, **kwds) - - def verify_and_update(self, secret, hash, scheme=None, category=None, **kwds): - """verify password and re-hash the password if needed, all in a single call. - - This is a convenience method which takes care of all the following: - first it verifies the password (:meth:`~CryptContext.verify`), if this is successfull - it checks if the hash needs updating (:meth:`~CryptContext.needs_update`), and if so, - re-hashes the password (:meth:`~CryptContext.hash`), returning the replacement hash. - This series of steps is a very common task for applications - which wish to update deprecated hashes, and this call takes - care of all 3 steps efficiently. - - :type secret: unicode or bytes - :arg secret: - the secret to verify - - :type secret: unicode or bytes - :arg hash: - hash string to compare to. - - if ``None`` is passed in, this will be treated as "never verifying" - - :type scheme: str - :param scheme: - Optionally force context to use specific scheme. - This is usually not needed, as most hashes can be unambiguously - identified. Scheme must be one of the ones configured - for this context - (see the :ref:`schemes ` option). - - .. deprecated:: 1.7 - - Support for this keyword is deprecated, and will be removed in Passlib 2.0. - - :type category: str or None - :param category: - Optional :ref:`user category `. - If specified, this will cause any category-specific defaults to - be used if the password has to be re-hashed. - - :param \\*\\*kwds: - all additional keywords are passed to the appropriate handler, - and should match that hash's - :attr:`PasswordHash.context_kwds `. - - :returns: - This function returns a tuple containing two elements: - ``(verified, replacement_hash)``. The first is a boolean - flag indicating whether the password verified, - and the second an optional replacement hash. - The tuple will always match one of the following 3 cases: - - * ``(False, None)`` indicates the secret failed to verify. - * ``(True, None)`` indicates the secret verified correctly, - and the hash does not need updating. - * ``(True, str)`` indicates the secret verified correctly, - but the current hash needs to be updated. The :class:`!str` - will be the freshly generated hash, to replace the old one. - - :raises TypeError, ValueError: - For the same reasons as :meth:`verify`. - - .. seealso:: the :ref:`context-migration-example` example in the tutorial. - """ - # XXX: could insert normalization to preferred unicode encoding here. - if scheme is not None: - warn("CryptContext.verify(): 'scheme' keyword is deprecated as of " - "Passlib 1.7, and will be removed in Passlib 2.0", - DeprecationWarning) - if hash is None: - # convenience feature -- let apps pass in hash=None when user - # isn't found / has no hash; useful because it invokes dummy_verify() - self.dummy_verify() - return False, None - record = self._get_or_identify_record(hash, scheme, category) - strip_unused = self._strip_unused_context_kwds - if strip_unused and kwds: - clean_kwds = kwds.copy() - strip_unused(clean_kwds, record) - else: - clean_kwds = kwds - # XXX: if record is default scheme, could extend PasswordHash - # api to combine verify & needs_update to single call, - # potentially saving some round-trip parsing. - # but might make these codepaths more complex... - if not record.verify(secret, hash, **clean_kwds): - return False, None - elif record.deprecated or record.needs_update(hash, secret=secret): - # NOTE: we re-hash with default scheme, not current one. - return True, self.hash(secret, category=category, **kwds) - else: - return True, None - - #=================================================================== - # missing-user helper - #=================================================================== - - #: secret used for dummy_verify() - _dummy_secret = "too many secrets" - - @memoized_property - def _dummy_hash(self): - """ - precalculated hash for dummy_verify() to use - """ - return self.hash(self._dummy_secret) - - def _reset_dummy_verify(self): - """ - flush memoized values used by dummy_verify() - """ - type(self)._dummy_hash.clear_cache(self) - - def dummy_verify(self, elapsed=0): - """ - Helper that applications can call when user wasn't found, - in order to simulate time it would take to hash a password. - - Runs verify() against a dummy hash, to simulate verification - of a real account password. - - :param elapsed: - - .. deprecated:: 1.7.1 - - this option is ignored, and will be removed in passlib 1.8. - - .. versionadded:: 1.7 - """ - self.verify(self._dummy_secret, self._dummy_hash) - return False - - #=================================================================== - # disabled hash support - #=================================================================== - - def is_enabled(self, hash): - """ - test if hash represents a usuable password -- - i.e. does not represent an unusuable password such as ``"!"``, - which is recognized by the :class:`~passlib.hash.unix_disabled` hash. - - :raises ValueError: - if the hash is not recognized - (typically solved by adding ``unix_disabled`` to the list of schemes). - """ - return not self._identify_record(hash, None).is_disabled - - def disable(self, hash=None): - """ - return a string to disable logins for user, - usually by returning a non-verifying string such as ``"!"``. - - :param hash: - Callers can optionally provide the account's existing hash. - Some disabled handlers (such as :class:`!unix_disabled`) - will encode this into the returned value, - so that it can be recovered via :meth:`enable`. - - :raises RuntimeError: - if this function is called w/o a disabled hasher - (such as :class:`~passlib.hash.unix_disabled`) included - in the list of schemes. - - :returns: - hash string which will be recognized as valid by the context, - but is guaranteed to not validate against *any* password. - """ - record = self._config.disabled_record - assert record.is_disabled - return record.disable(hash) - - def enable(self, hash): - """ - inverse of :meth:`disable` -- - attempts to recover original hash which was converted - by a :meth:`!disable` call into a disabled hash -- - thus restoring the user's original password. - - :raises ValueError: - if original hash not present, or if the disabled handler doesn't - support encoding the original hash (e.g. ``django_disabled``) - - :returns: - the original hash. - """ - record = self._identify_record(hash, None) - if record.is_disabled: - # XXX: should we throw error if result can't be identified by context? - return record.enable(hash) - else: - # hash wasn't a disabled hash, so return unchanged - return hash - - #=================================================================== - # eoc - #=================================================================== - -class LazyCryptContext(CryptContext): - """CryptContext subclass which doesn't load handlers until needed. - - This is a subclass of CryptContext which takes in a set of arguments - exactly like CryptContext, but won't import any handlers - (or even parse its arguments) until - the first time one of its methods is accessed. - - :arg schemes: - The first positional argument can be a list of schemes, or omitted, - just like CryptContext. - - :param onload: - - If a callable is passed in via this keyword, - it will be invoked at lazy-load time - with the following signature: - ``onload(**kwds) -> kwds``; - where ``kwds`` is all the additional kwds passed to LazyCryptContext. - It should perform any additional deferred initialization, - and return the final dict of options to be passed to CryptContext. - - .. versionadded:: 1.6 - - :param create_policy: - - .. deprecated:: 1.6 - This option will be removed in Passlib 1.8, - applications should use ``onload`` instead. - - :param kwds: - - All additional keywords are passed to CryptContext; - or to the *onload* function (if provided). - - This is mainly used internally by modules such as :mod:`passlib.apps`, - which define a large number of contexts, but only a few of them will be needed - at any one time. Use of this class saves the memory needed to import - the specified handlers until the context instance is actually accessed. - As well, it allows constructing a context at *module-init* time, - but using :func:`!onload()` to provide dynamic configuration - at *application-run* time. - - .. note:: - This class is only useful if you're referencing handler objects by name, - and don't want them imported until runtime. If you want to have the config - validated before your application runs, or are passing in already-imported - handler instances, you should use :class:`CryptContext` instead. - - .. versionadded:: 1.4 - """ - _lazy_kwds = None - - # NOTE: the way this class works changed in 1.6. - # previously it just called _lazy_init() when ``.policy`` was - # first accessed. now that is done whenever any of the public - # attributes are accessed, and the class itself is changed - # to a regular CryptContext, to remove the overhead once it's unneeded. - - def __init__(self, schemes=None, **kwds): - if schemes is not None: - kwds['schemes'] = schemes - self._lazy_kwds = kwds - - def _lazy_init(self): - kwds = self._lazy_kwds - if 'create_policy' in kwds: - warn("The CryptPolicy class, and LazyCryptContext's " - "``create_policy`` keyword have been deprecated as of " - "Passlib 1.6, and will be removed in Passlib 1.8; " - "please use the ``onload`` keyword instead.", - DeprecationWarning) - create_policy = kwds.pop("create_policy") - result = create_policy(**kwds) - policy = CryptPolicy.from_source(result, _warn=False) - kwds = policy._context.to_dict() - elif 'onload' in kwds: - onload = kwds.pop("onload") - kwds = onload(**kwds) - del self._lazy_kwds - super(LazyCryptContext, self).__init__(**kwds) - self.__class__ = CryptContext - - def __getattribute__(self, attr): - if (not attr.startswith("_") or attr.startswith("__")) and \ - self._lazy_kwds is not None: - self._lazy_init() - return object.__getattribute__(self, attr) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/__init__.py b/.venv/Lib/site-packages/passlib/crypto/__init__.py deleted file mode 100644 index 89f5484..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""passlib.crypto -- package containing cryptographic primitives used by passlib""" diff --git a/.venv/Lib/site-packages/passlib/crypto/_blowfish/__init__.py b/.venv/Lib/site-packages/passlib/crypto/_blowfish/__init__.py deleted file mode 100644 index 1aa1c85..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/_blowfish/__init__.py +++ /dev/null @@ -1,169 +0,0 @@ -"""passlib.crypto._blowfish - pure-python eks-blowfish implementation for bcrypt - -This is a pure-python implementation of the EKS-Blowfish algorithm described by -Provos and Mazieres in `A Future-Adaptable Password Scheme -`_. - -This package contains two submodules: - -* ``_blowfish/base.py`` contains a class implementing the eks-blowfish algorithm - using easy-to-examine code. - -* ``_blowfish/unrolled.py`` contains a subclass which replaces some methods - of the original class with sped-up versions, mainly using unrolled loops - and local variables. this is the class which is actually used by - Passlib to perform BCrypt in pure python. - - This module is auto-generated by a script, ``_blowfish/_gen_files.py``. - -Status ------- -This implementation is usable, but is an order of magnitude too slow to be -usable with real security. For "ok" security, BCrypt hashes should have at -least 2**11 rounds (as of 2011). Assuming a desired response time <= 100ms, -this means a BCrypt implementation should get at least 20 rounds/ms in order -to be both usable *and* secure. On a 2 ghz cpu, this implementation gets -roughly 0.09 rounds/ms under CPython (220x too slow), and 1.9 rounds/ms -under PyPy (10x too slow). - -History -------- -While subsequently modified considerly for Passlib, this code was originally -based on `jBcrypt 0.2 `_, which was -released under the BSD license:: - - Copyright (c) 2006 Damien Miller - - Permission to use, copy, modify, and 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. - -""" -#============================================================================= -# imports -#============================================================================= -# core -from itertools import chain -import struct -# pkg -from passlib.utils import getrandbytes, rng -from passlib.utils.binary import bcrypt64 -from passlib.utils.compat import BytesIO, unicode, u, native_string_types -from passlib.crypto._blowfish.unrolled import BlowfishEngine -# local -__all__ = [ - 'BlowfishEngine', - 'raw_bcrypt', -] - -#============================================================================= -# bcrypt constants -#============================================================================= - -# bcrypt constant data "OrpheanBeholderScryDoubt" as 6 integers -BCRYPT_CDATA = [ - 0x4f727068, 0x65616e42, 0x65686f6c, - 0x64657253, 0x63727944, 0x6f756274 -] - -# struct used to encode ciphertext as digest (last output byte discarded) -digest_struct = struct.Struct(">6I") - -#============================================================================= -# base bcrypt helper -# -# interface designed only for use by passlib.handlers.bcrypt:BCrypt -# probably not suitable for other purposes -#============================================================================= -BNULL = b'\x00' - -def raw_bcrypt(password, ident, salt, log_rounds): - """perform central password hashing step in bcrypt scheme. - - :param password: the password to hash - :param ident: identifier w/ minor version (e.g. 2, 2a) - :param salt: the binary salt to use (encoded in bcrypt-base64) - :param log_rounds: the log2 of the number of rounds (as int) - :returns: bcrypt-base64 encoded checksum - """ - #=================================================================== - # parse inputs - #=================================================================== - - # parse ident - assert isinstance(ident, native_string_types) - add_null_padding = True - if ident == u('2a') or ident == u('2y') or ident == u('2b'): - pass - elif ident == u('2'): - add_null_padding = False - elif ident == u('2x'): - raise ValueError("crypt_blowfish's buggy '2x' hashes are not " - "currently supported") - else: - raise ValueError("unknown ident: %r" % (ident,)) - - # decode & validate salt - assert isinstance(salt, bytes) - salt = bcrypt64.decode_bytes(salt) - if len(salt) < 16: - raise ValueError("Missing salt bytes") - elif len(salt) > 16: - salt = salt[:16] - - # prepare password - assert isinstance(password, bytes) - if add_null_padding: - password += BNULL - - # validate rounds - if log_rounds < 4 or log_rounds > 31: - raise ValueError("Bad number of rounds") - - #=================================================================== - # - # run EKS-Blowfish algorithm - # - # This uses the "enhanced key schedule" step described by - # Provos and Mazieres in "A Future-Adaptable Password Scheme" - # http://www.openbsd.org/papers/bcrypt-paper.ps - # - #=================================================================== - - engine = BlowfishEngine() - - # convert password & salt into list of 18 32-bit integers (72 bytes total). - pass_words = engine.key_to_words(password) - salt_words = engine.key_to_words(salt) - - # truncate salt_words to original 16 byte salt, or loop won't wrap - # correctly when passed to .eks_salted_expand() - salt_words16 = salt_words[:4] - - # do EKS key schedule setup - engine.eks_salted_expand(pass_words, salt_words16) - - # apply password & salt keys to key schedule a bunch more times. - rounds = 1<> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) -""".strip() - -def render_encipher(write, indent=0): - for i in irange(0, 15, 2): - write(indent, """\ - # Feistel substitution on left word (round %(i)d) - r ^= %(left)s ^ p%(i1)d - - # Feistel substitution on right word (round %(i1)d) - l ^= %(right)s ^ p%(i2)d - """, i=i, i1=i+1, i2=i+2, - left=BFSTR, right=BFSTR.replace("l","r"), - ) - -def write_encipher_function(write, indent=0): - write(indent, """\ - def encipher(self, l, r): - \"""blowfish encipher a single 64-bit block encoded as two 32-bit ints\""" - - (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, - p10, p11, p12, p13, p14, p15, p16, p17) = self.P - S0, S1, S2, S3 = self.S - - l ^= p0 - - """) - render_encipher(write, indent+1) - - write(indent+1, """\ - - return r ^ p17, l - - """) - -def write_expand_function(write, indent=0): - write(indent, """\ - def expand(self, key_words): - \"""unrolled version of blowfish key expansion\""" - ##assert len(key_words) >= 18, "size of key_words must be >= 18" - - P, S = self.P, self.S - S0, S1, S2, S3 = S - - #============================================================= - # integrate key - #============================================================= - """) - for i in irange(18): - write(indent+1, """\ - p%(i)d = P[%(i)d] ^ key_words[%(i)d] - """, i=i) - write(indent+1, """\ - - #============================================================= - # update P - #============================================================= - - #------------------------------------------------ - # update P[0] and P[1] - #------------------------------------------------ - l, r = p0, 0 - - """) - - render_encipher(write, indent+1) - - write(indent+1, """\ - - p0, p1 = l, r = r ^ p17, l - - """) - - for i in irange(2, 18, 2): - write(indent+1, """\ - #------------------------------------------------ - # update P[%(i)d] and P[%(i1)d] - #------------------------------------------------ - l ^= p0 - - """, i=i, i1=i+1) - - render_encipher(write, indent+1) - - write(indent+1, """\ - p%(i)d, p%(i1)d = l, r = r ^ p17, l - - """, i=i, i1=i+1) - - write(indent+1, """\ - - #------------------------------------------------ - # save changes to original P array - #------------------------------------------------ - P[:] = (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, - p10, p11, p12, p13, p14, p15, p16, p17) - - #============================================================= - # update S - #============================================================= - - for box in S: - j = 0 - while j < 256: - l ^= p0 - - """) - - render_encipher(write, indent+3) - - write(indent+3, """\ - - box[j], box[j+1] = l, r = r ^ p17, l - j += 2 - """) - -#============================================================================= -# main -#============================================================================= - -def main(): - target = os.path.join(os.path.dirname(__file__), "unrolled.py") - fh = file(target, "w") - - def write(indent, msg, **kwds): - literal = kwds.pop("literal", False) - if kwds: - msg %= kwds - if not literal: - msg = textwrap.dedent(msg.rstrip(" ")) - if indent: - msg = indent_block(msg, " " * (indent*4)) - fh.write(msg) - - write(0, """\ - \"""passlib.crypto._blowfish.unrolled - unrolled loop implementation of bcrypt, - autogenerated by _gen_files.py - - currently this override the encipher() and expand() methods - with optimized versions, and leaves the other base.py methods alone. - \""" - #================================================================= - # imports - #================================================================= - # pkg - from passlib.crypto._blowfish.base import BlowfishEngine as _BlowfishEngine - # local - __all__ = [ - "BlowfishEngine", - ] - #================================================================= - # - #================================================================= - class BlowfishEngine(_BlowfishEngine): - - """) - - write_encipher_function(write, indent=1) - write_expand_function(write, indent=1) - - write(0, """\ - #================================================================= - # eoc - #================================================================= - - #================================================================= - # eof - #================================================================= - """) - -if __name__ == "__main__": - main() - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/_blowfish/base.py b/.venv/Lib/site-packages/passlib/crypto/_blowfish/base.py deleted file mode 100644 index 7b4f2cb..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/_blowfish/base.py +++ /dev/null @@ -1,441 +0,0 @@ -"""passlib.crypto._blowfish.base - unoptimized pure-python blowfish engine""" -#============================================================================= -# imports -#============================================================================= -# core -import struct -# pkg -from passlib.utils import repeat_string -# local -__all__ = [ - "BlowfishEngine", -] - -#============================================================================= -# blowfish constants -#============================================================================= -BLOWFISH_P = BLOWFISH_S = None - -def _init_constants(): - global BLOWFISH_P, BLOWFISH_S - - # NOTE: blowfish's spec states these numbers are the hex representation - # of the fractional portion of PI, in order. - - # Initial contents of key schedule - 18 integers - BLOWFISH_P = [ - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b, - ] - - # all 4 blowfish S boxes in one array - 256 integers per S box - BLOWFISH_S = [ - # sbox 1 - [ - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, - ], - # sbox 2 - [ - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, - ], - # sbox 3 - [ - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, - ], - # sbox 4 - [ - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, - ] - ] - -#============================================================================= -# engine -#============================================================================= -class BlowfishEngine(object): - - def __init__(self): - if BLOWFISH_P is None: - _init_constants() - self.P = list(BLOWFISH_P) - self.S = [ list(box) for box in BLOWFISH_S ] - - #=================================================================== - # common helpers - #=================================================================== - @staticmethod - def key_to_words(data, size=18): - """convert data to tuple of 4-byte integers, repeating or - truncating data as needed to reach specified size""" - assert isinstance(data, bytes) - dlen = len(data) - if not dlen: - # return all zeros - original C code would just read the NUL after - # the password, so mimicing that behavior for this edge case. - return [0]*size - - # repeat data until it fills up 4*size bytes - data = repeat_string(data, size<<2) - - # unpack - return struct.unpack(">%dI" % (size,), data) - - #=================================================================== - # blowfish routines - #=================================================================== - def encipher(self, l, r): - """loop version of blowfish encipher routine""" - P, S = self.P, self.S - l ^= P[0] - i = 1 - while i < 17: - # Feistel substitution on left word - r = ((((S[0][l >> 24] + S[1][(l >> 16) & 0xff]) ^ S[2][(l >> 8) & 0xff]) + - S[3][l & 0xff]) & 0xffffffff) ^ P[i] ^ r - # swap vars so even rounds do Feistel substition on right word - l, r = r, l - i += 1 - return r ^ P[17], l - - # NOTE: decipher is same as above, just with reversed(P) instead. - - def expand(self, key_words): - """perform stock Blowfish keyschedule setup""" - assert len(key_words) >= 18, "key_words must be at least as large as P" - P, S, encipher = self.P, self.S, self.encipher - - i = 0 - while i < 18: - P[i] ^= key_words[i] - i += 1 - - i = l = r = 0 - while i < 18: - P[i], P[i+1] = l,r = encipher(l,r) - i += 2 - - for box in S: - i = 0 - while i < 256: - box[i], box[i+1] = l,r = encipher(l,r) - i += 2 - - #=================================================================== - # eks-blowfish routines - #=================================================================== - def eks_salted_expand(self, key_words, salt_words): - """perform EKS' salted version of Blowfish keyschedule setup""" - # NOTE: this is the same as expand(), except for the addition - # of the operations involving *salt_words*. - - assert len(key_words) >= 18, "key_words must be at least as large as P" - salt_size = len(salt_words) - assert salt_size, "salt_words must not be empty" - assert not salt_size & 1, "salt_words must have even length" - P, S, encipher = self.P, self.S, self.encipher - - i = 0 - while i < 18: - P[i] ^= key_words[i] - i += 1 - - s = i = l = r = 0 - while i < 18: - l ^= salt_words[s] - r ^= salt_words[s+1] - s += 2 - if s == salt_size: - s = 0 - P[i], P[i+1] = l,r = encipher(l,r) # next() - i += 2 - - for box in S: - i = 0 - while i < 256: - l ^= salt_words[s] - r ^= salt_words[s+1] - s += 2 - if s == salt_size: - s = 0 - box[i], box[i+1] = l,r = encipher(l,r) # next() - i += 2 - - def eks_repeated_expand(self, key_words, salt_words, rounds): - """perform rounds stage of EKS keyschedule setup""" - expand = self.expand - n = 0 - while n < rounds: - expand(key_words) - expand(salt_words) - n += 1 - - def repeat_encipher(self, l, r, count): - """repeatedly apply encipher operation to a block""" - encipher = self.encipher - n = 0 - while n < count: - l, r = encipher(l, r) - n += 1 - return l, r - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/_blowfish/unrolled.py b/.venv/Lib/site-packages/passlib/crypto/_blowfish/unrolled.py deleted file mode 100644 index 4acf6e1..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/_blowfish/unrolled.py +++ /dev/null @@ -1,771 +0,0 @@ -"""passlib.crypto._blowfish.unrolled - unrolled loop implementation of bcrypt, -autogenerated by _gen_files.py - -currently this override the encipher() and expand() methods -with optimized versions, and leaves the other base.py methods alone. -""" -#============================================================================= -# imports -#============================================================================= -# pkg -from passlib.crypto._blowfish.base import BlowfishEngine as _BlowfishEngine -# local -__all__ = [ - "BlowfishEngine", -] -#============================================================================= -# -#============================================================================= -class BlowfishEngine(_BlowfishEngine): - - def encipher(self, l, r): - """blowfish encipher a single 64-bit block encoded as two 32-bit ints""" - - (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, - p10, p11, p12, p13, p14, p15, p16, p17) = self.P - S0, S1, S2, S3 = self.S - - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - - return r ^ p17, l - - def expand(self, key_words): - """unrolled version of blowfish key expansion""" - ##assert len(key_words) >= 18, "size of key_words must be >= 18" - - P, S = self.P, self.S - S0, S1, S2, S3 = S - - #============================================================= - # integrate key - #============================================================= - p0 = P[0] ^ key_words[0] - p1 = P[1] ^ key_words[1] - p2 = P[2] ^ key_words[2] - p3 = P[3] ^ key_words[3] - p4 = P[4] ^ key_words[4] - p5 = P[5] ^ key_words[5] - p6 = P[6] ^ key_words[6] - p7 = P[7] ^ key_words[7] - p8 = P[8] ^ key_words[8] - p9 = P[9] ^ key_words[9] - p10 = P[10] ^ key_words[10] - p11 = P[11] ^ key_words[11] - p12 = P[12] ^ key_words[12] - p13 = P[13] ^ key_words[13] - p14 = P[14] ^ key_words[14] - p15 = P[15] ^ key_words[15] - p16 = P[16] ^ key_words[16] - p17 = P[17] ^ key_words[17] - - #============================================================= - # update P - #============================================================= - - #------------------------------------------------ - # update P[0] and P[1] - #------------------------------------------------ - l, r = p0, 0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - - p0, p1 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[2] and P[3] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p2, p3 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[4] and P[5] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p4, p5 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[6] and P[7] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p6, p7 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[8] and P[9] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p8, p9 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[10] and P[11] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p10, p11 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[12] and P[13] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p12, p13 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[14] and P[15] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p14, p15 = l, r = r ^ p17, l - - #------------------------------------------------ - # update P[16] and P[17] - #------------------------------------------------ - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - p16, p17 = l, r = r ^ p17, l - - - #------------------------------------------------ - # save changes to original P array - #------------------------------------------------ - P[:] = (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, - p10, p11, p12, p13, p14, p15, p16, p17) - - #============================================================= - # update S - #============================================================= - - for box in S: - j = 0 - while j < 256: - l ^= p0 - - # Feistel substitution on left word (round 0) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p1 - - # Feistel substitution on right word (round 1) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p2 - # Feistel substitution on left word (round 2) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p3 - - # Feistel substitution on right word (round 3) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p4 - # Feistel substitution on left word (round 4) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p5 - - # Feistel substitution on right word (round 5) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p6 - # Feistel substitution on left word (round 6) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p7 - - # Feistel substitution on right word (round 7) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p8 - # Feistel substitution on left word (round 8) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p9 - - # Feistel substitution on right word (round 9) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p10 - # Feistel substitution on left word (round 10) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p11 - - # Feistel substitution on right word (round 11) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p12 - # Feistel substitution on left word (round 12) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p13 - - # Feistel substitution on right word (round 13) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p14 - # Feistel substitution on left word (round 14) - r ^= ((((S0[l >> 24] + S1[(l >> 16) & 0xff]) ^ S2[(l >> 8) & 0xff]) + - S3[l & 0xff]) & 0xffffffff) ^ p15 - - # Feistel substitution on right word (round 15) - l ^= ((((S0[r >> 24] + S1[(r >> 16) & 0xff]) ^ S2[(r >> 8) & 0xff]) + - S3[r & 0xff]) & 0xffffffff) ^ p16 - - box[j], box[j+1] = l, r = r ^ p17, l - j += 2 - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/_md4.py b/.venv/Lib/site-packages/passlib/crypto/_md4.py deleted file mode 100644 index bdc211f..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/_md4.py +++ /dev/null @@ -1,244 +0,0 @@ -""" -passlib.crypto._md4 -- fallback implementation of MD4 - -Helper implementing insecure and obsolete md4 algorithm. -used for NTHASH format, which is also insecure and broken, -since it's just md4(password). - -Implementated based on rfc at http://www.faqs.org/rfcs/rfc1320.html - -.. note:: - - This shouldn't be imported directly, it's merely used conditionally - by ``passlib.crypto.lookup_hash()`` when a native implementation can't be found. -""" - -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify -import struct -# site -from passlib.utils.compat import bascii_to_str, irange, PY3 -# local -__all__ = ["md4"] - -#============================================================================= -# utils -#============================================================================= -def F(x,y,z): - return (x&y) | ((~x) & z) - -def G(x,y,z): - return (x&y) | (x&z) | (y&z) - -##def H(x,y,z): -## return x ^ y ^ z - -MASK_32 = 2**32-1 - -#============================================================================= -# main class -#============================================================================= -class md4(object): - """pep-247 compatible implementation of MD4 hash algorithm - - .. attribute:: digest_size - - size of md4 digest in bytes (16 bytes) - - .. method:: update - - update digest by appending additional content - - .. method:: copy - - create clone of digest object, including current state - - .. method:: digest - - return bytes representing md4 digest of current content - - .. method:: hexdigest - - return hexadecimal version of digest - """ - # FIXME: make this follow hash object PEP better. - # FIXME: this isn't threadsafe - - name = "md4" - digest_size = digestsize = 16 - block_size = 64 - - _count = 0 # number of 64-byte blocks processed so far (not including _buf) - _state = None # list of [a,b,c,d] 32 bit ints used as internal register - _buf = None # data processed in 64 byte blocks, this holds leftover from last update - - def __init__(self, content=None): - self._count = 0 - self._state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] - self._buf = b'' - if content: - self.update(content) - - # round 1 table - [abcd k s] - _round1 = [ - [0,1,2,3, 0,3], - [3,0,1,2, 1,7], - [2,3,0,1, 2,11], - [1,2,3,0, 3,19], - - [0,1,2,3, 4,3], - [3,0,1,2, 5,7], - [2,3,0,1, 6,11], - [1,2,3,0, 7,19], - - [0,1,2,3, 8,3], - [3,0,1,2, 9,7], - [2,3,0,1, 10,11], - [1,2,3,0, 11,19], - - [0,1,2,3, 12,3], - [3,0,1,2, 13,7], - [2,3,0,1, 14,11], - [1,2,3,0, 15,19], - ] - - # round 2 table - [abcd k s] - _round2 = [ - [0,1,2,3, 0,3], - [3,0,1,2, 4,5], - [2,3,0,1, 8,9], - [1,2,3,0, 12,13], - - [0,1,2,3, 1,3], - [3,0,1,2, 5,5], - [2,3,0,1, 9,9], - [1,2,3,0, 13,13], - - [0,1,2,3, 2,3], - [3,0,1,2, 6,5], - [2,3,0,1, 10,9], - [1,2,3,0, 14,13], - - [0,1,2,3, 3,3], - [3,0,1,2, 7,5], - [2,3,0,1, 11,9], - [1,2,3,0, 15,13], - ] - - # round 3 table - [abcd k s] - _round3 = [ - [0,1,2,3, 0,3], - [3,0,1,2, 8,9], - [2,3,0,1, 4,11], - [1,2,3,0, 12,15], - - [0,1,2,3, 2,3], - [3,0,1,2, 10,9], - [2,3,0,1, 6,11], - [1,2,3,0, 14,15], - - [0,1,2,3, 1,3], - [3,0,1,2, 9,9], - [2,3,0,1, 5,11], - [1,2,3,0, 13,15], - - [0,1,2,3, 3,3], - [3,0,1,2, 11,9], - [2,3,0,1, 7,11], - [1,2,3,0, 15,15], - ] - - def _process(self, block): - """process 64 byte block""" - # unpack block into 16 32-bit ints - X = struct.unpack("<16I", block) - - # clone state - orig = self._state - state = list(orig) - - # round 1 - F function - (x&y)|(~x & z) - for a,b,c,d,k,s in self._round1: - t = (state[a] + F(state[b],state[c],state[d]) + X[k]) & MASK_32 - state[a] = ((t<>(32-s)) - - # round 2 - G function - for a,b,c,d,k,s in self._round2: - t = (state[a] + G(state[b],state[c],state[d]) + X[k] + 0x5a827999) & MASK_32 - state[a] = ((t<>(32-s)) - - # round 3 - H function - x ^ y ^ z - for a,b,c,d,k,s in self._round3: - t = (state[a] + (state[b] ^ state[c] ^ state[d]) + X[k] + 0x6ed9eba1) & MASK_32 - state[a] = ((t<>(32-s)) - - # add back into original state - for i in irange(4): - orig[i] = (orig[i]+state[i]) & MASK_32 - - def update(self, content): - if not isinstance(content, bytes): - if PY3: - raise TypeError("expected bytes") - else: - # replicate behavior of hashlib under py2 - content = content.encode("ascii") - buf = self._buf - if buf: - content = buf + content - idx = 0 - end = len(content) - while True: - next = idx + 64 - if next <= end: - self._process(content[idx:next]) - self._count += 1 - idx = next - else: - self._buf = content[idx:] - return - - def copy(self): - other = md4() - other._count = self._count - other._state = list(self._state) - other._buf = self._buf - return other - - def digest(self): - # NOTE: backing up state so we can restore it after _process is called, - # in case object is updated again (this is only attr altered by this method) - orig = list(self._state) - - # final block: buf + 0x80, - # then 0x00 padding until congruent w/ 56 mod 64 bytes - # then last 8 bytes = msg length in bits - buf = self._buf - msglen = self._count*512 + len(buf)*8 - block = buf + b'\x80' + b'\x00' * ((119-len(buf)) % 64) + \ - struct.pack("<2I", msglen & MASK_32, (msglen>>32) & MASK_32) - if len(block) == 128: - self._process(block[:64]) - self._process(block[64:]) - else: - assert len(block) == 64 - self._process(block) - - # render digest & restore un-finalized state - out = struct.pack("<4I", *self._state) - self._state = orig - return out - - def hexdigest(self): - return bascii_to_str(hexlify(self.digest())) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/des.py b/.venv/Lib/site-packages/passlib/crypto/des.py deleted file mode 100644 index 3f87aef..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/des.py +++ /dev/null @@ -1,848 +0,0 @@ -"""passlib.crypto.des -- DES block encryption routines - -History -======= -These routines (which have since been drastically modified for python) -are based on a Java implementation of the des-crypt algorithm, -found at ``_. - -The copyright & license for that source is as follows:: - - UnixCrypt.java 0.9 96/11/25 - Copyright (c) 1996 Aki Yoshida. All rights reserved. - Permission to use, copy, modify and distribute this software - for non-commercial or commercial purposes and without fee is - hereby granted provided that this copyright notice appears in - all copies. - - --- - - Unix crypt(3C) utility - @version 0.9, 11/25/96 - @author Aki Yoshida - - --- - - modified April 2001 - by Iris Van den Broeke, Daniel Deville - - --- - Unix Crypt. - Implements the one way cryptography used by Unix systems for - simple password protection. - @version $Id: UnixCrypt2.txt,v 1.1.1.1 2005/09/13 22:20:13 christos Exp $ - @author Greg Wilkins (gregw) - -The netbsd des-crypt implementation has some nice notes on how this all works - - http://fxr.googlebit.com/source/lib/libcrypt/crypt.c?v=NETBSD-CURRENT -""" - -# TODO: could use an accelerated C version of this module to speed up lmhash, -# des-crypt, and ext-des-crypt - -#============================================================================= -# imports -#============================================================================= -# core -import struct -# pkg -from passlib import exc -from passlib.utils.compat import join_byte_values, byte_elem_value, \ - irange, irange, int_types -# local -__all__ = [ - "expand_des_key", - "des_encrypt_block", -] - -#============================================================================= -# constants -#============================================================================= - -# masks/upper limits for various integer sizes -INT_24_MASK = 0xffffff -INT_56_MASK = 0xffffffffffffff -INT_64_MASK = 0xffffffffffffffff - -# mask to clear parity bits from 64-bit key -_KDATA_MASK = 0xfefefefefefefefe -_KPARITY_MASK = 0x0101010101010101 - -# mask used to setup key schedule -_KS_MASK = 0xfcfcfcfcffffffff - -#============================================================================= -# static DES tables -#============================================================================= - -# placeholders filled in by _load_tables() -PCXROT = IE3264 = SPE = CF6464 = None - -def _load_tables(): - """delay loading tables until they are actually needed""" - global PCXROT, IE3264, SPE, CF6464 - - #--------------------------------------------------------------- - # Initial key schedule permutation - # PC1ROT - bit reverse, then PC1, then Rotate, then PC2 - #--------------------------------------------------------------- - # NOTE: this was reordered from original table to make perm3264 logic simpler - PC1ROT=( - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000002000, 0x0000000000002000, - 0x0000000000000020, 0x0000000000000020, 0x0000000000002020, 0x0000000000002020, - 0x0000000000000400, 0x0000000000000400, 0x0000000000002400, 0x0000000000002400, - 0x0000000000000420, 0x0000000000000420, 0x0000000000002420, 0x0000000000002420, ), - ( 0x0000000000000000, 0x2000000000000000, 0x0000000400000000, 0x2000000400000000, - 0x0000800000000000, 0x2000800000000000, 0x0000800400000000, 0x2000800400000000, - 0x0008000000000000, 0x2008000000000000, 0x0008000400000000, 0x2008000400000000, - 0x0008800000000000, 0x2008800000000000, 0x0008800400000000, 0x2008800400000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000040, 0x0000000000000040, - 0x0000000020000000, 0x0000000020000000, 0x0000000020000040, 0x0000000020000040, - 0x0000000000200000, 0x0000000000200000, 0x0000000000200040, 0x0000000000200040, - 0x0000000020200000, 0x0000000020200000, 0x0000000020200040, 0x0000000020200040, ), - ( 0x0000000000000000, 0x0002000000000000, 0x0800000000000000, 0x0802000000000000, - 0x0100000000000000, 0x0102000000000000, 0x0900000000000000, 0x0902000000000000, - 0x4000000000000000, 0x4002000000000000, 0x4800000000000000, 0x4802000000000000, - 0x4100000000000000, 0x4102000000000000, 0x4900000000000000, 0x4902000000000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000040000, 0x0000000000040000, - 0x0000020000000000, 0x0000020000000000, 0x0000020000040000, 0x0000020000040000, - 0x0000000000000004, 0x0000000000000004, 0x0000000000040004, 0x0000000000040004, - 0x0000020000000004, 0x0000020000000004, 0x0000020000040004, 0x0000020000040004, ), - ( 0x0000000000000000, 0x0000400000000000, 0x0200000000000000, 0x0200400000000000, - 0x0080000000000000, 0x0080400000000000, 0x0280000000000000, 0x0280400000000000, - 0x0000008000000000, 0x0000408000000000, 0x0200008000000000, 0x0200408000000000, - 0x0080008000000000, 0x0080408000000000, 0x0280008000000000, 0x0280408000000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000010000000, 0x0000000010000000, - 0x0000000000001000, 0x0000000000001000, 0x0000000010001000, 0x0000000010001000, - 0x0000000040000000, 0x0000000040000000, 0x0000000050000000, 0x0000000050000000, - 0x0000000040001000, 0x0000000040001000, 0x0000000050001000, 0x0000000050001000, ), - ( 0x0000000000000000, 0x0000001000000000, 0x0000080000000000, 0x0000081000000000, - 0x1000000000000000, 0x1000001000000000, 0x1000080000000000, 0x1000081000000000, - 0x0004000000000000, 0x0004001000000000, 0x0004080000000000, 0x0004081000000000, - 0x1004000000000000, 0x1004001000000000, 0x1004080000000000, 0x1004081000000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000080, 0x0000000000000080, - 0x0000000000080000, 0x0000000000080000, 0x0000000000080080, 0x0000000000080080, - 0x0000000000800000, 0x0000000000800000, 0x0000000000800080, 0x0000000000800080, - 0x0000000000880000, 0x0000000000880000, 0x0000000000880080, 0x0000000000880080, ), - ( 0x0000000000000000, 0x0000000008000000, 0x0000002000000000, 0x0000002008000000, - 0x0000100000000000, 0x0000100008000000, 0x0000102000000000, 0x0000102008000000, - 0x0000200000000000, 0x0000200008000000, 0x0000202000000000, 0x0000202008000000, - 0x0000300000000000, 0x0000300008000000, 0x0000302000000000, 0x0000302008000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000400000, 0x0000000000400000, - 0x0000000004000000, 0x0000000004000000, 0x0000000004400000, 0x0000000004400000, - 0x0000000000000800, 0x0000000000000800, 0x0000000000400800, 0x0000000000400800, - 0x0000000004000800, 0x0000000004000800, 0x0000000004400800, 0x0000000004400800, ), - ( 0x0000000000000000, 0x0000000000008000, 0x0040000000000000, 0x0040000000008000, - 0x0000004000000000, 0x0000004000008000, 0x0040004000000000, 0x0040004000008000, - 0x8000000000000000, 0x8000000000008000, 0x8040000000000000, 0x8040000000008000, - 0x8000004000000000, 0x8000004000008000, 0x8040004000000000, 0x8040004000008000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000004000, 0x0000000000004000, - 0x0000000000000008, 0x0000000000000008, 0x0000000000004008, 0x0000000000004008, - 0x0000000000000010, 0x0000000000000010, 0x0000000000004010, 0x0000000000004010, - 0x0000000000000018, 0x0000000000000018, 0x0000000000004018, 0x0000000000004018, ), - ( 0x0000000000000000, 0x0000000200000000, 0x0001000000000000, 0x0001000200000000, - 0x0400000000000000, 0x0400000200000000, 0x0401000000000000, 0x0401000200000000, - 0x0020000000000000, 0x0020000200000000, 0x0021000000000000, 0x0021000200000000, - 0x0420000000000000, 0x0420000200000000, 0x0421000000000000, 0x0421000200000000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000010000000000, 0x0000010000000000, - 0x0000000100000000, 0x0000000100000000, 0x0000010100000000, 0x0000010100000000, - 0x0000000000100000, 0x0000000000100000, 0x0000010000100000, 0x0000010000100000, - 0x0000000100100000, 0x0000000100100000, 0x0000010100100000, 0x0000010100100000, ), - ( 0x0000000000000000, 0x0000000080000000, 0x0000040000000000, 0x0000040080000000, - 0x0010000000000000, 0x0010000080000000, 0x0010040000000000, 0x0010040080000000, - 0x0000000800000000, 0x0000000880000000, 0x0000040800000000, 0x0000040880000000, - 0x0010000800000000, 0x0010000880000000, 0x0010040800000000, 0x0010040880000000, ), - ) - #--------------------------------------------------------------- - # Subsequent key schedule rotation permutations - # PC2ROT - PC2 inverse, then Rotate, then PC2 - #--------------------------------------------------------------- - # NOTE: this was reordered from original table to make perm3264 logic simpler - PC2ROTA=( - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, 0x0000000000200000, - 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, 0x0000000004000000, - 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, 0x0000000004200000, ), - ( 0x0000000000000000, 0x0000000000000800, 0x0000010000000000, 0x0000010000000800, - 0x0000000000002000, 0x0000000000002800, 0x0000010000002000, 0x0000010000002800, - 0x0000000010000000, 0x0000000010000800, 0x0000010010000000, 0x0000010010000800, - 0x0000000010002000, 0x0000000010002800, 0x0000010010002000, 0x0000010010002800, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, 0x0000000100000000, - 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, 0x0000000000800000, - 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, 0x0000000100800000, ), - ( 0x0000000000000000, 0x0000020000000000, 0x0000000080000000, 0x0000020080000000, - 0x0000000000400000, 0x0000020000400000, 0x0000000080400000, 0x0000020080400000, - 0x0000000008000000, 0x0000020008000000, 0x0000000088000000, 0x0000020088000000, - 0x0000000008400000, 0x0000020008400000, 0x0000000088400000, 0x0000020088400000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, 0x0000000000000040, - 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, - 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, 0x0000000000001040, ), - ( 0x0000000000000000, 0x0000000000000010, 0x0000000000000400, 0x0000000000000410, - 0x0000000000000080, 0x0000000000000090, 0x0000000000000480, 0x0000000000000490, - 0x0000000040000000, 0x0000000040000010, 0x0000000040000400, 0x0000000040000410, - 0x0000000040000080, 0x0000000040000090, 0x0000000040000480, 0x0000000040000490, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, - 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, 0x0000000000100000, - 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, 0x0000000000180000, ), - ( 0x0000000000000000, 0x0000000000040000, 0x0000000000000020, 0x0000000000040020, - 0x0000000000000004, 0x0000000000040004, 0x0000000000000024, 0x0000000000040024, - 0x0000000200000000, 0x0000000200040000, 0x0000000200000020, 0x0000000200040020, - 0x0000000200000004, 0x0000000200040004, 0x0000000200000024, 0x0000000200040024, ), - ( 0x0000000000000000, 0x0000000000000008, 0x0000000000008000, 0x0000000000008008, - 0x0010000000000000, 0x0010000000000008, 0x0010000000008000, 0x0010000000008008, - 0x0020000000000000, 0x0020000000000008, 0x0020000000008000, 0x0020000000008008, - 0x0030000000000000, 0x0030000000000008, 0x0030000000008000, 0x0030000000008008, ), - ( 0x0000000000000000, 0x0000400000000000, 0x0000080000000000, 0x0000480000000000, - 0x0000100000000000, 0x0000500000000000, 0x0000180000000000, 0x0000580000000000, - 0x4000000000000000, 0x4000400000000000, 0x4000080000000000, 0x4000480000000000, - 0x4000100000000000, 0x4000500000000000, 0x4000180000000000, 0x4000580000000000, ), - ( 0x0000000000000000, 0x0000000000004000, 0x0000000020000000, 0x0000000020004000, - 0x0001000000000000, 0x0001000000004000, 0x0001000020000000, 0x0001000020004000, - 0x0200000000000000, 0x0200000000004000, 0x0200000020000000, 0x0200000020004000, - 0x0201000000000000, 0x0201000000004000, 0x0201000020000000, 0x0201000020004000, ), - ( 0x0000000000000000, 0x1000000000000000, 0x0004000000000000, 0x1004000000000000, - 0x0002000000000000, 0x1002000000000000, 0x0006000000000000, 0x1006000000000000, - 0x0000000800000000, 0x1000000800000000, 0x0004000800000000, 0x1004000800000000, - 0x0002000800000000, 0x1002000800000000, 0x0006000800000000, 0x1006000800000000, ), - ( 0x0000000000000000, 0x0040000000000000, 0x2000000000000000, 0x2040000000000000, - 0x0000008000000000, 0x0040008000000000, 0x2000008000000000, 0x2040008000000000, - 0x0000001000000000, 0x0040001000000000, 0x2000001000000000, 0x2040001000000000, - 0x0000009000000000, 0x0040009000000000, 0x2000009000000000, 0x2040009000000000, ), - ( 0x0000000000000000, 0x0400000000000000, 0x8000000000000000, 0x8400000000000000, - 0x0000002000000000, 0x0400002000000000, 0x8000002000000000, 0x8400002000000000, - 0x0100000000000000, 0x0500000000000000, 0x8100000000000000, 0x8500000000000000, - 0x0100002000000000, 0x0500002000000000, 0x8100002000000000, 0x8500002000000000, ), - ( 0x0000000000000000, 0x0000800000000000, 0x0800000000000000, 0x0800800000000000, - 0x0000004000000000, 0x0000804000000000, 0x0800004000000000, 0x0800804000000000, - 0x0000000400000000, 0x0000800400000000, 0x0800000400000000, 0x0800800400000000, - 0x0000004400000000, 0x0000804400000000, 0x0800004400000000, 0x0800804400000000, ), - ( 0x0000000000000000, 0x0080000000000000, 0x0000040000000000, 0x0080040000000000, - 0x0008000000000000, 0x0088000000000000, 0x0008040000000000, 0x0088040000000000, - 0x0000200000000000, 0x0080200000000000, 0x0000240000000000, 0x0080240000000000, - 0x0008200000000000, 0x0088200000000000, 0x0008240000000000, 0x0088240000000000, ), - ) - - # NOTE: this was reordered from original table to make perm3264 logic simpler - PC2ROTB=( - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, 0x0000000000000400, - 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, 0x0000000000080000, - 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, 0x0000000000080400, ), - ( 0x0000000000000000, 0x0000000000800000, 0x0000000000004000, 0x0000000000804000, - 0x0000000080000000, 0x0000000080800000, 0x0000000080004000, 0x0000000080804000, - 0x0000000000040000, 0x0000000000840000, 0x0000000000044000, 0x0000000000844000, - 0x0000000080040000, 0x0000000080840000, 0x0000000080044000, 0x0000000080844000, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, 0x0000000000000008, - 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, 0x0000000040000000, - 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, 0x0000000040000008, ), - ( 0x0000000000000000, 0x0000000020000000, 0x0000000200000000, 0x0000000220000000, - 0x0000000000000080, 0x0000000020000080, 0x0000000200000080, 0x0000000220000080, - 0x0000000000100000, 0x0000000020100000, 0x0000000200100000, 0x0000000220100000, - 0x0000000000100080, 0x0000000020100080, 0x0000000200100080, 0x0000000220100080, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, 0x0000000000002000, - 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, 0x0000020000000000, - 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, 0x0000020000002000, ), - ( 0x0000000000000000, 0x0000000000000800, 0x0000000100000000, 0x0000000100000800, - 0x0000000010000000, 0x0000000010000800, 0x0000000110000000, 0x0000000110000800, - 0x0000000000000004, 0x0000000000000804, 0x0000000100000004, 0x0000000100000804, - 0x0000000010000004, 0x0000000010000804, 0x0000000110000004, 0x0000000110000804, ), - ( 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, - 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, 0x0000000000001000, - 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, 0x0000000000000010, - 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, 0x0000000000001010, ), - ( 0x0000000000000000, 0x0000000000000040, 0x0000010000000000, 0x0000010000000040, - 0x0000000000200000, 0x0000000000200040, 0x0000010000200000, 0x0000010000200040, - 0x0000000000008000, 0x0000000000008040, 0x0000010000008000, 0x0000010000008040, - 0x0000000000208000, 0x0000000000208040, 0x0000010000208000, 0x0000010000208040, ), - ( 0x0000000000000000, 0x0000000004000000, 0x0000000008000000, 0x000000000c000000, - 0x0400000000000000, 0x0400000004000000, 0x0400000008000000, 0x040000000c000000, - 0x8000000000000000, 0x8000000004000000, 0x8000000008000000, 0x800000000c000000, - 0x8400000000000000, 0x8400000004000000, 0x8400000008000000, 0x840000000c000000, ), - ( 0x0000000000000000, 0x0002000000000000, 0x0200000000000000, 0x0202000000000000, - 0x1000000000000000, 0x1002000000000000, 0x1200000000000000, 0x1202000000000000, - 0x0008000000000000, 0x000a000000000000, 0x0208000000000000, 0x020a000000000000, - 0x1008000000000000, 0x100a000000000000, 0x1208000000000000, 0x120a000000000000, ), - ( 0x0000000000000000, 0x0000000000400000, 0x0000000000000020, 0x0000000000400020, - 0x0040000000000000, 0x0040000000400000, 0x0040000000000020, 0x0040000000400020, - 0x0800000000000000, 0x0800000000400000, 0x0800000000000020, 0x0800000000400020, - 0x0840000000000000, 0x0840000000400000, 0x0840000000000020, 0x0840000000400020, ), - ( 0x0000000000000000, 0x0080000000000000, 0x0000008000000000, 0x0080008000000000, - 0x2000000000000000, 0x2080000000000000, 0x2000008000000000, 0x2080008000000000, - 0x0020000000000000, 0x00a0000000000000, 0x0020008000000000, 0x00a0008000000000, - 0x2020000000000000, 0x20a0000000000000, 0x2020008000000000, 0x20a0008000000000, ), - ( 0x0000000000000000, 0x0000002000000000, 0x0000040000000000, 0x0000042000000000, - 0x4000000000000000, 0x4000002000000000, 0x4000040000000000, 0x4000042000000000, - 0x0000400000000000, 0x0000402000000000, 0x0000440000000000, 0x0000442000000000, - 0x4000400000000000, 0x4000402000000000, 0x4000440000000000, 0x4000442000000000, ), - ( 0x0000000000000000, 0x0000004000000000, 0x0000200000000000, 0x0000204000000000, - 0x0000080000000000, 0x0000084000000000, 0x0000280000000000, 0x0000284000000000, - 0x0000800000000000, 0x0000804000000000, 0x0000a00000000000, 0x0000a04000000000, - 0x0000880000000000, 0x0000884000000000, 0x0000a80000000000, 0x0000a84000000000, ), - ( 0x0000000000000000, 0x0000000800000000, 0x0000000400000000, 0x0000000c00000000, - 0x0000100000000000, 0x0000100800000000, 0x0000100400000000, 0x0000100c00000000, - 0x0010000000000000, 0x0010000800000000, 0x0010000400000000, 0x0010000c00000000, - 0x0010100000000000, 0x0010100800000000, 0x0010100400000000, 0x0010100c00000000, ), - ( 0x0000000000000000, 0x0100000000000000, 0x0001000000000000, 0x0101000000000000, - 0x0000001000000000, 0x0100001000000000, 0x0001001000000000, 0x0101001000000000, - 0x0004000000000000, 0x0104000000000000, 0x0005000000000000, 0x0105000000000000, - 0x0004001000000000, 0x0104001000000000, 0x0005001000000000, 0x0105001000000000, ), - ) - #--------------------------------------------------------------- - # PCXROT - PC1ROT, PC2ROTA, PC2ROTB listed in order - # of the PC1 rotation schedule, as used by des_setkey - #--------------------------------------------------------------- - ##ROTATES = (1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1) - ##PCXROT = ( - ## PC1ROT, PC2ROTA, PC2ROTB, PC2ROTB, - ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTB, - ## PC2ROTA, PC2ROTB, PC2ROTB, PC2ROTB, - ## PC2ROTB, PC2ROTB, PC2ROTB, PC2ROTA, - ## ) - - # NOTE: modified PCXROT to contain entrys broken into pairs, - # to help generate them in format best used by encoder. - PCXROT = ( - (PC1ROT, PC2ROTA), (PC2ROTB, PC2ROTB), - (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTB), - (PC2ROTA, PC2ROTB), (PC2ROTB, PC2ROTB), - (PC2ROTB, PC2ROTB), (PC2ROTB, PC2ROTA), - ) - - #--------------------------------------------------------------- - # Bit reverse, intial permupation, expantion - # Initial permutation/expansion table - #--------------------------------------------------------------- - # NOTE: this was reordered from original table to make perm3264 logic simpler - IE3264=( - ( 0x0000000000000000, 0x0000000000800800, 0x0000000000008008, 0x0000000000808808, - 0x0000008008000000, 0x0000008008800800, 0x0000008008008008, 0x0000008008808808, - 0x0000000080080000, 0x0000000080880800, 0x0000000080088008, 0x0000000080888808, - 0x0000008088080000, 0x0000008088880800, 0x0000008088088008, 0x0000008088888808, ), - ( 0x0000000000000000, 0x0080080000000000, 0x0000800800000000, 0x0080880800000000, - 0x0800000000000080, 0x0880080000000080, 0x0800800800000080, 0x0880880800000080, - 0x8008000000000000, 0x8088080000000000, 0x8008800800000000, 0x8088880800000000, - 0x8808000000000080, 0x8888080000000080, 0x8808800800000080, 0x8888880800000080, ), - ( 0x0000000000000000, 0x0000000000001000, 0x0000000000000010, 0x0000000000001010, - 0x0000000010000000, 0x0000000010001000, 0x0000000010000010, 0x0000000010001010, - 0x0000000000100000, 0x0000000000101000, 0x0000000000100010, 0x0000000000101010, - 0x0000000010100000, 0x0000000010101000, 0x0000000010100010, 0x0000000010101010, ), - ( 0x0000000000000000, 0x0000100000000000, 0x0000001000000000, 0x0000101000000000, - 0x1000000000000000, 0x1000100000000000, 0x1000001000000000, 0x1000101000000000, - 0x0010000000000000, 0x0010100000000000, 0x0010001000000000, 0x0010101000000000, - 0x1010000000000000, 0x1010100000000000, 0x1010001000000000, 0x1010101000000000, ), - ( 0x0000000000000000, 0x0000000000002000, 0x0000000000000020, 0x0000000000002020, - 0x0000000020000000, 0x0000000020002000, 0x0000000020000020, 0x0000000020002020, - 0x0000000000200000, 0x0000000000202000, 0x0000000000200020, 0x0000000000202020, - 0x0000000020200000, 0x0000000020202000, 0x0000000020200020, 0x0000000020202020, ), - ( 0x0000000000000000, 0x0000200000000000, 0x0000002000000000, 0x0000202000000000, - 0x2000000000000000, 0x2000200000000000, 0x2000002000000000, 0x2000202000000000, - 0x0020000000000000, 0x0020200000000000, 0x0020002000000000, 0x0020202000000000, - 0x2020000000000000, 0x2020200000000000, 0x2020002000000000, 0x2020202000000000, ), - ( 0x0000000000000000, 0x0000000000004004, 0x0400000000000040, 0x0400000000004044, - 0x0000000040040000, 0x0000000040044004, 0x0400000040040040, 0x0400000040044044, - 0x0000000000400400, 0x0000000000404404, 0x0400000000400440, 0x0400000000404444, - 0x0000000040440400, 0x0000000040444404, 0x0400000040440440, 0x0400000040444444, ), - ( 0x0000000000000000, 0x0000400400000000, 0x0000004004000000, 0x0000404404000000, - 0x4004000000000000, 0x4004400400000000, 0x4004004004000000, 0x4004404404000000, - 0x0040040000000000, 0x0040440400000000, 0x0040044004000000, 0x0040444404000000, - 0x4044040000000000, 0x4044440400000000, 0x4044044004000000, 0x4044444404000000, ), - ) - - #--------------------------------------------------------------- - # Table that combines the S, P, and E operations. - #--------------------------------------------------------------- - SPE=( - ( 0x0080088008200000, 0x0000008008000000, 0x0000000000200020, 0x0080088008200020, - 0x0000000000200000, 0x0080088008000020, 0x0000008008000020, 0x0000000000200020, - 0x0080088008000020, 0x0080088008200000, 0x0000008008200000, 0x0080080000000020, - 0x0080080000200020, 0x0000000000200000, 0x0000000000000000, 0x0000008008000020, - 0x0000008008000000, 0x0000000000000020, 0x0080080000200000, 0x0080088008000000, - 0x0080088008200020, 0x0000008008200000, 0x0080080000000020, 0x0080080000200000, - 0x0000000000000020, 0x0080080000000000, 0x0080088008000000, 0x0000008008200020, - 0x0080080000000000, 0x0080080000200020, 0x0000008008200020, 0x0000000000000000, - 0x0000000000000000, 0x0080088008200020, 0x0080080000200000, 0x0000008008000020, - 0x0080088008200000, 0x0000008008000000, 0x0080080000000020, 0x0080080000200000, - 0x0000008008200020, 0x0080080000000000, 0x0080088008000000, 0x0000000000200020, - 0x0080088008000020, 0x0000000000000020, 0x0000000000200020, 0x0000008008200000, - 0x0080088008200020, 0x0080088008000000, 0x0000008008200000, 0x0080080000200020, - 0x0000000000200000, 0x0080080000000020, 0x0000008008000020, 0x0000000000000000, - 0x0000008008000000, 0x0000000000200000, 0x0080080000200020, 0x0080088008200000, - 0x0000000000000020, 0x0000008008200020, 0x0080080000000000, 0x0080088008000020, ), - ( 0x1000800810004004, 0x0000000000000000, 0x0000800810000000, 0x0000000010004004, - 0x1000000000004004, 0x1000800800000000, 0x0000800800004004, 0x0000800810000000, - 0x0000800800000000, 0x1000000010004004, 0x1000000000000000, 0x0000800800004004, - 0x1000000010000000, 0x0000800810004004, 0x0000000010004004, 0x1000000000000000, - 0x0000000010000000, 0x1000800800004004, 0x1000000010004004, 0x0000800800000000, - 0x1000800810000000, 0x0000000000004004, 0x0000000000000000, 0x1000000010000000, - 0x1000800800004004, 0x1000800810000000, 0x0000800810004004, 0x1000000000004004, - 0x0000000000004004, 0x0000000010000000, 0x1000800800000000, 0x1000800810004004, - 0x1000000010000000, 0x0000800810004004, 0x0000800800004004, 0x1000800810000000, - 0x1000800810004004, 0x1000000010000000, 0x1000000000004004, 0x0000000000000000, - 0x0000000000004004, 0x1000800800000000, 0x0000000010000000, 0x1000000010004004, - 0x0000800800000000, 0x0000000000004004, 0x1000800810000000, 0x1000800800004004, - 0x0000800810004004, 0x0000800800000000, 0x0000000000000000, 0x1000000000004004, - 0x1000000000000000, 0x1000800810004004, 0x0000800810000000, 0x0000000010004004, - 0x1000000010004004, 0x0000000010000000, 0x1000800800000000, 0x0000800800004004, - 0x1000800800004004, 0x1000000000000000, 0x0000000010004004, 0x0000800810000000, ), - ( 0x0000000000400410, 0x0010004004400400, 0x0010000000000000, 0x0010000000400410, - 0x0000004004000010, 0x0000000000400400, 0x0010000000400410, 0x0010004004000000, - 0x0010000000400400, 0x0000004004000000, 0x0000004004400400, 0x0000000000000010, - 0x0010004004400410, 0x0010000000000010, 0x0000000000000010, 0x0000004004400410, - 0x0000000000000000, 0x0000004004000010, 0x0010004004400400, 0x0010000000000000, - 0x0010000000000010, 0x0010004004400410, 0x0000004004000000, 0x0000000000400410, - 0x0000004004400410, 0x0010000000400400, 0x0010004004000010, 0x0000004004400400, - 0x0010004004000000, 0x0000000000000000, 0x0000000000400400, 0x0010004004000010, - 0x0010004004400400, 0x0010000000000000, 0x0000000000000010, 0x0000004004000000, - 0x0010000000000010, 0x0000004004000010, 0x0000004004400400, 0x0010000000400410, - 0x0000000000000000, 0x0010004004400400, 0x0010004004000000, 0x0000004004400410, - 0x0000004004000010, 0x0000000000400400, 0x0010004004400410, 0x0000000000000010, - 0x0010004004000010, 0x0000000000400410, 0x0000000000400400, 0x0010004004400410, - 0x0000004004000000, 0x0010000000400400, 0x0010000000400410, 0x0010004004000000, - 0x0010000000400400, 0x0000000000000000, 0x0000004004400410, 0x0010000000000010, - 0x0000000000400410, 0x0010004004000010, 0x0010000000000000, 0x0000004004400400, ), - ( 0x0800100040040080, 0x0000100000001000, 0x0800000000000080, 0x0800100040041080, - 0x0000000000000000, 0x0000000040041000, 0x0800100000001080, 0x0800000040040080, - 0x0000100040041000, 0x0800000000001080, 0x0000000000001000, 0x0800100000000080, - 0x0800000000001080, 0x0800100040040080, 0x0000000040040000, 0x0000000000001000, - 0x0800000040041080, 0x0000100040040000, 0x0000100000000000, 0x0800000000000080, - 0x0000100040040000, 0x0800100000001080, 0x0000000040041000, 0x0000100000000000, - 0x0800100000000080, 0x0000000000000000, 0x0800000040040080, 0x0000100040041000, - 0x0000100000001000, 0x0800000040041080, 0x0800100040041080, 0x0000000040040000, - 0x0800000040041080, 0x0800100000000080, 0x0000000040040000, 0x0800000000001080, - 0x0000100040040000, 0x0000100000001000, 0x0800000000000080, 0x0000000040041000, - 0x0800100000001080, 0x0000000000000000, 0x0000100000000000, 0x0800000040040080, - 0x0000000000000000, 0x0800000040041080, 0x0000100040041000, 0x0000100000000000, - 0x0000000000001000, 0x0800100040041080, 0x0800100040040080, 0x0000000040040000, - 0x0800100040041080, 0x0800000000000080, 0x0000100000001000, 0x0800100040040080, - 0x0800000040040080, 0x0000100040040000, 0x0000000040041000, 0x0800100000001080, - 0x0800100000000080, 0x0000000000001000, 0x0800000000001080, 0x0000100040041000, ), - ( 0x0000000000800800, 0x0000001000000000, 0x0040040000000000, 0x2040041000800800, - 0x2000001000800800, 0x0040040000800800, 0x2040041000000000, 0x0000001000800800, - 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040041000000000, - 0x2040040000800800, 0x2000001000800800, 0x0040041000800800, 0x0000000000000000, - 0x0040041000000000, 0x0000000000800800, 0x2000001000000000, 0x2040040000000000, - 0x0040040000800800, 0x2040041000000000, 0x0000000000000000, 0x2000000000800800, - 0x2000000000000000, 0x2040040000800800, 0x2040041000800800, 0x2000001000000000, - 0x0000001000800800, 0x0040040000000000, 0x2040040000000000, 0x0040041000800800, - 0x0040041000800800, 0x2040040000800800, 0x2000001000000000, 0x0000001000800800, - 0x0000001000000000, 0x2000000000000000, 0x2000000000800800, 0x0040040000800800, - 0x0000000000800800, 0x0040041000000000, 0x2040041000800800, 0x0000000000000000, - 0x2040041000000000, 0x0000000000800800, 0x0040040000000000, 0x2000001000000000, - 0x2040040000800800, 0x0040040000000000, 0x0000000000000000, 0x2040041000800800, - 0x2000001000800800, 0x0040041000800800, 0x2040040000000000, 0x0000001000000000, - 0x0040041000000000, 0x2000001000800800, 0x0040040000800800, 0x2040040000000000, - 0x2000000000000000, 0x2040041000000000, 0x0000001000800800, 0x2000000000800800, ), - ( 0x4004000000008008, 0x4004000020000000, 0x0000000000000000, 0x0000200020008008, - 0x4004000020000000, 0x0000200000000000, 0x4004200000008008, 0x0000000020000000, - 0x4004200000000000, 0x4004200020008008, 0x0000200020000000, 0x0000000000008008, - 0x0000200000008008, 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, - 0x0000000020000000, 0x4004200000008008, 0x4004000020008008, 0x0000000000000000, - 0x0000200000000000, 0x4004000000000000, 0x0000200020008008, 0x4004000020008008, - 0x4004200020008008, 0x0000000020008008, 0x0000000000008008, 0x4004200000000000, - 0x4004000000000000, 0x0000200020000000, 0x4004200020000000, 0x0000200000008008, - 0x4004200000000000, 0x0000000000008008, 0x0000200000008008, 0x4004200020000000, - 0x0000200020008008, 0x4004000020000000, 0x0000000000000000, 0x0000200000008008, - 0x0000000000008008, 0x0000200000000000, 0x4004000020008008, 0x0000000020000000, - 0x4004000020000000, 0x4004200020008008, 0x0000200020000000, 0x4004000000000000, - 0x4004200020008008, 0x0000200020000000, 0x0000000020000000, 0x4004200000008008, - 0x4004000000008008, 0x0000000020008008, 0x4004200020000000, 0x0000000000000000, - 0x0000200000000000, 0x4004000000008008, 0x4004200000008008, 0x0000200020008008, - 0x0000000020008008, 0x4004200000000000, 0x4004000000000000, 0x4004000020008008, ), - ( 0x0000400400000000, 0x0020000000000000, 0x0020000000100000, 0x0400000000100040, - 0x0420400400100040, 0x0400400400000040, 0x0020400400000000, 0x0000000000000000, - 0x0000000000100000, 0x0420000000100040, 0x0420000000000040, 0x0000400400100000, - 0x0400000000000040, 0x0020400400100000, 0x0000400400100000, 0x0420000000000040, - 0x0420000000100040, 0x0000400400000000, 0x0400400400000040, 0x0420400400100040, - 0x0000000000000000, 0x0020000000100000, 0x0400000000100040, 0x0020400400000000, - 0x0400400400100040, 0x0420400400000040, 0x0020400400100000, 0x0400000000000040, - 0x0420400400000040, 0x0400400400100040, 0x0020000000000000, 0x0000000000100000, - 0x0420400400000040, 0x0000400400100000, 0x0400400400100040, 0x0420000000000040, - 0x0000400400000000, 0x0020000000000000, 0x0000000000100000, 0x0400400400100040, - 0x0420000000100040, 0x0420400400000040, 0x0020400400000000, 0x0000000000000000, - 0x0020000000000000, 0x0400000000100040, 0x0400000000000040, 0x0020000000100000, - 0x0000000000000000, 0x0420000000100040, 0x0020000000100000, 0x0020400400000000, - 0x0420000000000040, 0x0000400400000000, 0x0420400400100040, 0x0000000000100000, - 0x0020400400100000, 0x0400000000000040, 0x0400400400000040, 0x0420400400100040, - 0x0400000000100040, 0x0020400400100000, 0x0000400400100000, 0x0400400400000040, ), - ( 0x8008000080082000, 0x0000002080082000, 0x8008002000000000, 0x0000000000000000, - 0x0000002000002000, 0x8008000080080000, 0x0000000080082000, 0x8008002080082000, - 0x8008000000000000, 0x0000000000002000, 0x0000002080080000, 0x8008002000000000, - 0x8008002080080000, 0x8008002000002000, 0x8008000000002000, 0x0000000080082000, - 0x0000002000000000, 0x8008002080080000, 0x8008000080080000, 0x0000002000002000, - 0x8008002080082000, 0x8008000000002000, 0x0000000000000000, 0x0000002080080000, - 0x0000000000002000, 0x0000000080080000, 0x8008002000002000, 0x8008000080082000, - 0x0000000080080000, 0x0000002000000000, 0x0000002080082000, 0x8008000000000000, - 0x0000000080080000, 0x0000002000000000, 0x8008000000002000, 0x8008002080082000, - 0x8008002000000000, 0x0000000000002000, 0x0000000000000000, 0x0000002080080000, - 0x8008000080082000, 0x8008002000002000, 0x0000002000002000, 0x8008000080080000, - 0x0000002080082000, 0x8008000000000000, 0x8008000080080000, 0x0000002000002000, - 0x8008002080082000, 0x0000000080080000, 0x0000000080082000, 0x8008000000002000, - 0x0000002080080000, 0x8008002000000000, 0x8008002000002000, 0x0000000080082000, - 0x8008000000000000, 0x0000002080082000, 0x8008002080080000, 0x0000000000000000, - 0x0000000000002000, 0x8008000080082000, 0x0000002000000000, 0x8008002080080000, ), - ) - - #--------------------------------------------------------------- - # compressed/interleaved => final permutation table - # Compression, final permutation, bit reverse - #--------------------------------------------------------------- - # NOTE: this was reordered from original table to make perm6464 logic simpler - CF6464=( - ( 0x0000000000000000, 0x0000002000000000, 0x0000200000000000, 0x0000202000000000, - 0x0020000000000000, 0x0020002000000000, 0x0020200000000000, 0x0020202000000000, - 0x2000000000000000, 0x2000002000000000, 0x2000200000000000, 0x2000202000000000, - 0x2020000000000000, 0x2020002000000000, 0x2020200000000000, 0x2020202000000000, ), - ( 0x0000000000000000, 0x0000000200000000, 0x0000020000000000, 0x0000020200000000, - 0x0002000000000000, 0x0002000200000000, 0x0002020000000000, 0x0002020200000000, - 0x0200000000000000, 0x0200000200000000, 0x0200020000000000, 0x0200020200000000, - 0x0202000000000000, 0x0202000200000000, 0x0202020000000000, 0x0202020200000000, ), - ( 0x0000000000000000, 0x0000000000000020, 0x0000000000002000, 0x0000000000002020, - 0x0000000000200000, 0x0000000000200020, 0x0000000000202000, 0x0000000000202020, - 0x0000000020000000, 0x0000000020000020, 0x0000000020002000, 0x0000000020002020, - 0x0000000020200000, 0x0000000020200020, 0x0000000020202000, 0x0000000020202020, ), - ( 0x0000000000000000, 0x0000000000000002, 0x0000000000000200, 0x0000000000000202, - 0x0000000000020000, 0x0000000000020002, 0x0000000000020200, 0x0000000000020202, - 0x0000000002000000, 0x0000000002000002, 0x0000000002000200, 0x0000000002000202, - 0x0000000002020000, 0x0000000002020002, 0x0000000002020200, 0x0000000002020202, ), - ( 0x0000000000000000, 0x0000008000000000, 0x0000800000000000, 0x0000808000000000, - 0x0080000000000000, 0x0080008000000000, 0x0080800000000000, 0x0080808000000000, - 0x8000000000000000, 0x8000008000000000, 0x8000800000000000, 0x8000808000000000, - 0x8080000000000000, 0x8080008000000000, 0x8080800000000000, 0x8080808000000000, ), - ( 0x0000000000000000, 0x0000000800000000, 0x0000080000000000, 0x0000080800000000, - 0x0008000000000000, 0x0008000800000000, 0x0008080000000000, 0x0008080800000000, - 0x0800000000000000, 0x0800000800000000, 0x0800080000000000, 0x0800080800000000, - 0x0808000000000000, 0x0808000800000000, 0x0808080000000000, 0x0808080800000000, ), - ( 0x0000000000000000, 0x0000000000000080, 0x0000000000008000, 0x0000000000008080, - 0x0000000000800000, 0x0000000000800080, 0x0000000000808000, 0x0000000000808080, - 0x0000000080000000, 0x0000000080000080, 0x0000000080008000, 0x0000000080008080, - 0x0000000080800000, 0x0000000080800080, 0x0000000080808000, 0x0000000080808080, ), - ( 0x0000000000000000, 0x0000000000000008, 0x0000000000000800, 0x0000000000000808, - 0x0000000000080000, 0x0000000000080008, 0x0000000000080800, 0x0000000000080808, - 0x0000000008000000, 0x0000000008000008, 0x0000000008000800, 0x0000000008000808, - 0x0000000008080000, 0x0000000008080008, 0x0000000008080800, 0x0000000008080808, ), - ( 0x0000000000000000, 0x0000001000000000, 0x0000100000000000, 0x0000101000000000, - 0x0010000000000000, 0x0010001000000000, 0x0010100000000000, 0x0010101000000000, - 0x1000000000000000, 0x1000001000000000, 0x1000100000000000, 0x1000101000000000, - 0x1010000000000000, 0x1010001000000000, 0x1010100000000000, 0x1010101000000000, ), - ( 0x0000000000000000, 0x0000000100000000, 0x0000010000000000, 0x0000010100000000, - 0x0001000000000000, 0x0001000100000000, 0x0001010000000000, 0x0001010100000000, - 0x0100000000000000, 0x0100000100000000, 0x0100010000000000, 0x0100010100000000, - 0x0101000000000000, 0x0101000100000000, 0x0101010000000000, 0x0101010100000000, ), - ( 0x0000000000000000, 0x0000000000000010, 0x0000000000001000, 0x0000000000001010, - 0x0000000000100000, 0x0000000000100010, 0x0000000000101000, 0x0000000000101010, - 0x0000000010000000, 0x0000000010000010, 0x0000000010001000, 0x0000000010001010, - 0x0000000010100000, 0x0000000010100010, 0x0000000010101000, 0x0000000010101010, ), - ( 0x0000000000000000, 0x0000000000000001, 0x0000000000000100, 0x0000000000000101, - 0x0000000000010000, 0x0000000000010001, 0x0000000000010100, 0x0000000000010101, - 0x0000000001000000, 0x0000000001000001, 0x0000000001000100, 0x0000000001000101, - 0x0000000001010000, 0x0000000001010001, 0x0000000001010100, 0x0000000001010101, ), - ( 0x0000000000000000, 0x0000004000000000, 0x0000400000000000, 0x0000404000000000, - 0x0040000000000000, 0x0040004000000000, 0x0040400000000000, 0x0040404000000000, - 0x4000000000000000, 0x4000004000000000, 0x4000400000000000, 0x4000404000000000, - 0x4040000000000000, 0x4040004000000000, 0x4040400000000000, 0x4040404000000000, ), - ( 0x0000000000000000, 0x0000000400000000, 0x0000040000000000, 0x0000040400000000, - 0x0004000000000000, 0x0004000400000000, 0x0004040000000000, 0x0004040400000000, - 0x0400000000000000, 0x0400000400000000, 0x0400040000000000, 0x0400040400000000, - 0x0404000000000000, 0x0404000400000000, 0x0404040000000000, 0x0404040400000000, ), - ( 0x0000000000000000, 0x0000000000000040, 0x0000000000004000, 0x0000000000004040, - 0x0000000000400000, 0x0000000000400040, 0x0000000000404000, 0x0000000000404040, - 0x0000000040000000, 0x0000000040000040, 0x0000000040004000, 0x0000000040004040, - 0x0000000040400000, 0x0000000040400040, 0x0000000040404000, 0x0000000040404040, ), - ( 0x0000000000000000, 0x0000000000000004, 0x0000000000000400, 0x0000000000000404, - 0x0000000000040000, 0x0000000000040004, 0x0000000000040400, 0x0000000000040404, - 0x0000000004000000, 0x0000000004000004, 0x0000000004000400, 0x0000000004000404, - 0x0000000004040000, 0x0000000004040004, 0x0000000004040400, 0x0000000004040404, ), - ) - #=================================================================== - # eof _load_tables() - #=================================================================== - -#============================================================================= -# support -#============================================================================= - -def _permute(c, p): - """Returns the permutation of the given 32-bit or 64-bit code with - the specified permutation table.""" - # NOTE: only difference between 32 & 64 bit permutations - # is that len(p)==8 for 32 bit, and len(p)==16 for 64 bit. - out = 0 - for r in p: - out |= r[c&0xf] - c >>= 4 - return out - -#============================================================================= -# packing & unpacking -#============================================================================= -# FIXME: more properly named _uint8_struct... -_uint64_struct = struct.Struct(">Q") - -def _pack64(value): - return _uint64_struct.pack(value) - -def _unpack64(value): - return _uint64_struct.unpack(value)[0] - -def _pack56(value): - return _uint64_struct.pack(value)[1:] - -def _unpack56(value): - return _uint64_struct.unpack(b'\x00' + value)[0] - -#============================================================================= -# 56->64 key manipulation -#============================================================================= - -##def expand_7bit(value): -## "expand 7-bit integer => 7-bits + 1 odd-parity bit" -## # parity calc adapted from 32-bit even parity alg found at -## # http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel -## assert 0 <= value < 0x80, "value out of range" -## return (value<<1) | (0x9669 >> ((value ^ (value >> 4)) & 0xf)) & 1 - -_EXPAND_ITER = irange(49,-7,-7) - -def expand_des_key(key): - """convert DES from 7 bytes to 8 bytes (by inserting empty parity bits)""" - if isinstance(key, bytes): - if len(key) != 7: - raise ValueError("key must be 7 bytes in size") - elif isinstance(key, int_types): - if key < 0 or key > INT_56_MASK: - raise ValueError("key must be 56-bit non-negative integer") - return _unpack64(expand_des_key(_pack56(key))) - else: - raise exc.ExpectedTypeError(key, "bytes or int", "key") - key = _unpack56(key) - # NOTE: the following would insert correctly-valued parity bits in each key, - # but the parity bit would just be ignored in des_encrypt_block(), - # so not bothering to use it. - # XXX: could make parity-restoring optionally available via flag - ##return join_byte_values(expand_7bit((key >> shift) & 0x7f) - ## for shift in _EXPAND_ITER) - return join_byte_values(((key>>shift) & 0x7f)<<1 for shift in _EXPAND_ITER) - -def shrink_des_key(key): - """convert DES key from 8 bytes to 7 bytes (by discarding the parity bits)""" - if isinstance(key, bytes): - if len(key) != 8: - raise ValueError("key must be 8 bytes in size") - return _pack56(shrink_des_key(_unpack64(key))) - elif isinstance(key, int_types): - if key < 0 or key > INT_64_MASK: - raise ValueError("key must be 64-bit non-negative integer") - else: - raise exc.ExpectedTypeError(key, "bytes or int", "key") - key >>= 1 - result = 0 - offset = 0 - while offset < 56: - result |= (key & 0x7f)<>= 8 - offset += 7 - assert not (result & ~INT_64_MASK) - return result - -#============================================================================= -# des encryption -#============================================================================= -def des_encrypt_block(key, input, salt=0, rounds=1): - """encrypt single block of data using DES, operates on 8-byte strings. - - :arg key: - DES key as 7 byte string, or 8 byte string with parity bits - (parity bit values are ignored). - - :arg input: - plaintext block to encrypt, as 8 byte string. - - :arg salt: - Optional 24-bit integer used to mutate the base DES algorithm in a - manner specific to :class:`~passlib.hash.des_crypt` and its variants. - The default value ``0`` provides the normal (unsalted) DES behavior. - The salt functions as follows: - if the ``i``'th bit of ``salt`` is set, - bits ``i`` and ``i+24`` are swapped in the DES E-box output. - - :arg rounds: - Optional number of rounds of to apply the DES key schedule. - the default (``rounds=1``) provides the normal DES behavior, - but :class:`~passlib.hash.des_crypt` and its variants use - alternate rounds values. - - :raises TypeError: if any of the provided args are of the wrong type. - :raises ValueError: - if any of the input blocks are the wrong size, - or the salt/rounds values are out of range. - - :returns: - resulting 8-byte ciphertext block. - """ - # validate & unpack key - if isinstance(key, bytes): - if len(key) == 7: - key = expand_des_key(key) - elif len(key) != 8: - raise ValueError("key must be 7 or 8 bytes") - key = _unpack64(key) - else: - raise exc.ExpectedTypeError(key, "bytes", "key") - - # validate & unpack input - if isinstance(input, bytes): - if len(input) != 8: - raise ValueError("input block must be 8 bytes") - input = _unpack64(input) - else: - raise exc.ExpectedTypeError(input, "bytes", "input") - - # hand things off to other func - result = des_encrypt_int_block(key, input, salt, rounds) - - # repack result - return _pack64(result) - -def des_encrypt_int_block(key, input, salt=0, rounds=1): - """encrypt single block of data using DES, operates on 64-bit integers. - - this function is essentially the same as :func:`des_encrypt_block`, - except that it operates on integers, and will NOT automatically - expand 56-bit keys if provided (since there's no way to detect them). - - :arg key: - DES key as 64-bit integer (the parity bits are ignored). - - :arg input: - input block as 64-bit integer - - :arg salt: - optional 24-bit integer used to mutate the base DES algorithm. - defaults to ``0`` (no mutation applied). - - :arg rounds: - optional number of rounds of to apply the DES key schedule. - defaults to ``1``. - - :raises TypeError: if any of the provided args are of the wrong type. - :raises ValueError: - if any of the input blocks are the wrong size, - or the salt/rounds values are out of range. - - :returns: - resulting ciphertext as 64-bit integer. - """ - #--------------------------------------------------------------- - # input validation - #--------------------------------------------------------------- - - # validate salt, rounds - if rounds < 1: - raise ValueError("rounds must be positive integer") - if salt < 0 or salt > INT_24_MASK: - raise ValueError("salt must be 24-bit non-negative integer") - - # validate & unpack key - if not isinstance(key, int_types): - raise exc.ExpectedTypeError(key, "int", "key") - elif key < 0 or key > INT_64_MASK: - raise ValueError("key must be 64-bit non-negative integer") - - # validate & unpack input - if not isinstance(input, int_types): - raise exc.ExpectedTypeError(input, "int", "input") - elif input < 0 or input > INT_64_MASK: - raise ValueError("input must be 64-bit non-negative integer") - - #--------------------------------------------------------------- - # DES setup - #--------------------------------------------------------------- - # load tables if not already done - global SPE, PCXROT, IE3264, CF6464 - if PCXROT is None: - _load_tables() - - # load SPE into local vars to speed things up and remove an array access call - SPE0, SPE1, SPE2, SPE3, SPE4, SPE5, SPE6, SPE7 = SPE - - # NOTE: parity bits are ignored completely - # (UTs do fuzz testing to ensure this) - - # generate key schedule - # NOTE: generation was modified to output two elements at a time, - # so that per-round loop could do two passes at once. - def _iter_key_schedule(ks_odd): - """given 64-bit key, iterates over the 8 (even,odd) key schedule pairs""" - for p_even, p_odd in PCXROT: - ks_even = _permute(ks_odd, p_even) - ks_odd = _permute(ks_even, p_odd) - yield ks_even & _KS_MASK, ks_odd & _KS_MASK - ks_list = list(_iter_key_schedule(key)) - - # expand 24 bit salt -> 32 bit per des_crypt & bsdi_crypt - salt = ( - ((salt & 0x00003f) << 26) | - ((salt & 0x000fc0) << 12) | - ((salt & 0x03f000) >> 2) | - ((salt & 0xfc0000) >> 16) - ) - - # init L & R - if input == 0: - L = R = 0 - else: - L = ((input >> 31) & 0xaaaaaaaa) | (input & 0x55555555) - L = _permute(L, IE3264) - - R = ((input >> 32) & 0xaaaaaaaa) | ((input >> 1) & 0x55555555) - R = _permute(R, IE3264) - - #--------------------------------------------------------------- - # main DES loop - run for specified number of rounds - #--------------------------------------------------------------- - while rounds: - rounds -= 1 - - # run over each part of the schedule, 2 parts at a time - for ks_even, ks_odd in ks_list: - k = ((R>>32) ^ R) & salt # use the salt to flip specific bits - B = (k<<32) ^ k ^ R ^ ks_even - - L ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^ - SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^ - SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^ - SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f]) - - k = ((L>>32) ^ L) & salt # use the salt to flip specific bits - B = (k<<32) ^ k ^ L ^ ks_odd - - R ^= (SPE0[(B>>58)&0x3f] ^ SPE1[(B>>50)&0x3f] ^ - SPE2[(B>>42)&0x3f] ^ SPE3[(B>>34)&0x3f] ^ - SPE4[(B>>26)&0x3f] ^ SPE5[(B>>18)&0x3f] ^ - SPE6[(B>>10)&0x3f] ^ SPE7[(B>>2)&0x3f]) - - # swap L and R - L, R = R, L - - #--------------------------------------------------------------- - # return final result - #--------------------------------------------------------------- - C = ( - ((L>>3) & 0x0f0f0f0f00000000) - | - ((L<<33) & 0xf0f0f0f000000000) - | - ((R>>35) & 0x000000000f0f0f0f) - | - ((R<<1) & 0x00000000f0f0f0f0) - ) - return _permute(C, CF6464) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/digest.py b/.venv/Lib/site-packages/passlib/crypto/digest.py deleted file mode 100644 index 90e0cad..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/digest.py +++ /dev/null @@ -1,1057 +0,0 @@ -"""passlib.crypto.digest -- crytographic helpers used by the password hashes in passlib - -.. versionadded:: 1.7 -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import division -# core -import hashlib -import logging; log = logging.getLogger(__name__) -try: - # new in py3.4 - from hashlib import pbkdf2_hmac as _stdlib_pbkdf2_hmac - if _stdlib_pbkdf2_hmac.__module__ == "hashlib": - # builtin pure-python backends are slightly faster than stdlib's pure python fallback, - # so only using stdlib's version if it's backed by openssl's pbkdf2_hmac() - log.debug("ignoring pure-python hashlib.pbkdf2_hmac()") - _stdlib_pbkdf2_hmac = None -except ImportError: - _stdlib_pbkdf2_hmac = None -import re -import os -from struct import Struct -from warnings import warn -# site -try: - # https://pypi.python.org/pypi/fastpbkdf2/ - from fastpbkdf2 import pbkdf2_hmac as _fast_pbkdf2_hmac -except ImportError: - _fast_pbkdf2_hmac = None -# pkg -from passlib import exc -from passlib.utils import join_bytes, to_native_str, join_byte_values, to_bytes, \ - SequenceMixin, as_bool -from passlib.utils.compat import irange, int_types, unicode_or_bytes_types, PY3, error_from -from passlib.utils.decor import memoized_property -# local -__all__ = [ - # hash utils - "lookup_hash", - "HashInfo", - "norm_hash_name", - - # hmac utils - "compile_hmac", - - # kdfs - "pbkdf1", - "pbkdf2_hmac", -] - -#============================================================================= -# generic constants -#============================================================================= - -#: max 32-bit value -MAX_UINT32 = (1 << 32) - 1 - -#: max 64-bit value -MAX_UINT64 = (1 << 64) - 1 - -#============================================================================= -# hash utils -#============================================================================= - -#: list of known hash names, used by lookup_hash()'s _norm_hash_name() helper -_known_hash_names = [ - # format: (hashlib/ssl name, iana name or standin, other known aliases ...) - - #---------------------------------------------------- - # hashes with official IANA-assigned names - # (as of 2012-03 - http://www.iana.org/assignments/hash-function-text-names) - #---------------------------------------------------- - ("md2", "md2"), # NOTE: openssl dropped md2 support in v1.0.0 - ("md5", "md5"), - ("sha1", "sha-1"), - ("sha224", "sha-224", "sha2-224"), - ("sha256", "sha-256", "sha2-256"), - ("sha384", "sha-384", "sha2-384"), - ("sha512", "sha-512", "sha2-512"), - - # TODO: add sha3 to this table. - - #---------------------------------------------------- - # hashlib/ssl-supported hashes without official IANA names, - # (hopefully-) compatible stand-ins have been chosen. - #---------------------------------------------------- - - ("blake2b", "blake-2b"), - ("blake2s", "blake-2s"), - ("md4", "md4"), - # NOTE: there was an older "ripemd" and "ripemd-128", - # but python 2.7+ resolves "ripemd" -> "ripemd160", - # so treating "ripemd" as alias here. - ("ripemd160", "ripemd-160", "ripemd"), -] - - -#: dict mapping hashlib names to hardcoded digest info; -#: so this is available even when hashes aren't present. -_fallback_info = { - # name: (digest_size, block_size) - 'blake2b': (64, 128), - 'blake2s': (32, 64), - 'md4': (16, 64), - 'md5': (16, 64), - 'sha1': (20, 64), - 'sha224': (28, 64), - 'sha256': (32, 64), - 'sha384': (48, 128), - 'sha3_224': (28, 144), - 'sha3_256': (32, 136), - 'sha3_384': (48, 104), - 'sha3_512': (64, 72), - 'sha512': (64, 128), - 'shake128': (16, 168), - 'shake256': (32, 136), -} - - -def _gen_fallback_info(): - """ - internal helper used to generate ``_fallback_info`` dict. - currently only run manually to update the above list; - not invoked at runtime. - """ - out = {} - for alg in sorted(hashlib.algorithms_available | set(["md4"])): - info = lookup_hash(alg) - out[info.name] = (info.digest_size, info.block_size) - return out - - -#: cache of hash info instances used by lookup_hash() -_hash_info_cache = {} - -def _get_hash_aliases(name): - """ - internal helper used by :func:`lookup_hash` -- - normalize arbitrary hash name to hashlib format. - if name not recognized, returns dummy record and issues a warning. - - :arg name: - unnormalized name - - :returns: - tuple with 2+ elements: ``(hashlib_name, iana_name|None, ... 0+ aliases)``. - """ - - # normalize input - orig = name - if not isinstance(name, str): - name = to_native_str(name, 'utf-8', 'hash name') - name = re.sub("[_ /]", "-", name.strip().lower()) - if name.startswith("scram-"): # helper for SCRAM protocol (see passlib.handlers.scram) - name = name[6:] - if name.endswith("-plus"): - name = name[:-5] - - # look through standard names and known aliases - def check_table(name): - for row in _known_hash_names: - if name in row: - return row - result = check_table(name) - if result: - return result - - # try to clean name up some more - m = re.match(r"(?i)^(?P[a-z]+)-?(?P\d)?-?(?P\d{3,4})?$", name) - if m: - # roughly follows "SHA2-256" style format, normalize representation, - # and checked table. - iana_name, rev, size = m.group("name", "rev", "size") - if rev: - iana_name += rev - hashlib_name = iana_name - if size: - iana_name += "-" + size - if rev: - hashlib_name += "_" - hashlib_name += size - result = check_table(iana_name) - if result: - return result - - # not found in table, but roughly recognize format. use names we built up as fallback. - log.info("normalizing unrecognized hash name %r => %r / %r", - orig, hashlib_name, iana_name) - - else: - # just can't make sense of it. return something - iana_name = name - hashlib_name = name.replace("-", "_") - log.warning("normalizing unrecognized hash name and format %r => %r / %r", - orig, hashlib_name, iana_name) - - return hashlib_name, iana_name - - -def _get_hash_const(name): - """ - internal helper used by :func:`lookup_hash` -- - lookup hash constructor by name - - :arg name: - name (normalized to hashlib format, e.g. ``"sha256"``) - - :returns: - hash constructor, e.g. ``hashlib.sha256()``; - or None if hash can't be located. - """ - # check hashlib. for an efficient constructor - if not name.startswith("_") and name not in ("new", "algorithms"): - try: - return getattr(hashlib, name) - except AttributeError: - pass - - # check hashlib.new() in case SSL supports the digest - new_ssl_hash = hashlib.new - try: - # new() should throw ValueError if alg is unknown - new_ssl_hash(name, b"") - except ValueError: - pass - else: - # create wrapper function - # XXX: is there a faster way to wrap this? - def const(msg=b""): - return new_ssl_hash(name, msg) - const.__name__ = name - const.__module__ = "hashlib" - const.__doc__ = ("wrapper for hashlib.new(%r),\n" - "generated by passlib.crypto.digest.lookup_hash()") % name - return const - - # use builtin md4 as fallback when not supported by hashlib - if name == "md4": - from passlib.crypto._md4 import md4 - return md4 - - # XXX: any other modules / registries we should check? - # TODO: add pysha3 support. - - return None - - -def lookup_hash(digest, # *, - return_unknown=False, required=True): - """ - Returns a :class:`HashInfo` record containing information about a given hash function. - Can be used to look up a hash constructor by name, normalize hash name representation, etc. - - :arg digest: - This can be any of: - - * A string containing a :mod:`!hashlib` digest name (e.g. ``"sha256"``), - * A string containing an IANA-assigned hash name, - * A digest constructor function (e.g. ``hashlib.sha256``). - - Case is ignored, underscores are converted to hyphens, - and various other cleanups are made. - - :param required: - By default (True), this function will throw an :exc:`~passlib.exc.UnknownHashError` if no hash constructor - can be found, or if the hash is not actually available. - - If this flag is False, it will instead return a dummy :class:`!HashInfo` record - which will defer throwing the error until it's constructor function is called. - This is mainly used by :func:`norm_hash_name`. - - :param return_unknown: - - .. deprecated:: 1.7.3 - - deprecated, and will be removed in passlib 2.0. - this acts like inverse of **required**. - - :returns HashInfo: - :class:`HashInfo` instance containing information about specified digest. - - Multiple calls resolving to the same hash should always - return the same :class:`!HashInfo` instance. - """ - # check for cached entry - cache = _hash_info_cache - try: - return cache[digest] - except (KeyError, TypeError): - # NOTE: TypeError is to catch 'TypeError: unhashable type' (e.g. HashInfo) - pass - - # legacy alias - if return_unknown: - required = False - - # resolve ``digest`` to ``const`` & ``name_record`` - cache_by_name = True - if isinstance(digest, unicode_or_bytes_types): - # normalize name - name_list = _get_hash_aliases(digest) - name = name_list[0] - assert name - - # if name wasn't normalized to hashlib format, - # get info for normalized name and reuse it. - if name != digest: - info = lookup_hash(name, required=required) - cache[digest] = info - return info - - # else look up constructor - # NOTE: may return None, which is handled by HashInfo constructor - const = _get_hash_const(name) - - # if mock fips mode is enabled, replace with dummy constructor - # (to replicate how it would behave on a real fips system). - if const and mock_fips_mode and name not in _fips_algorithms: - def const(source=b""): - raise ValueError("%r disabled for fips by passlib set_mock_fips_mode()" % name) - - elif isinstance(digest, HashInfo): - # handle border case where HashInfo is passed in. - return digest - - elif callable(digest): - # try to lookup digest based on it's self-reported name - # (which we trust to be the canonical "hashlib" name) - const = digest - name_list = _get_hash_aliases(const().name) - name = name_list[0] - other_const = _get_hash_const(name) - if other_const is None: - # this is probably a third-party digest we don't know about, - # so just pass it on through, and register reverse lookup for it's name. - pass - - elif other_const is const: - # if we got back same constructor, this is just a known stdlib constructor, - # which was passed in before we had cached it by name. proceed normally. - pass - - else: - # if we got back different object, then ``const`` is something else - # (such as a mock object), in which case we want to skip caching it by name, - # as that would conflict with real hash. - cache_by_name = False - - else: - raise exc.ExpectedTypeError(digest, "digest name or constructor", "digest") - - # create new instance - info = HashInfo(const=const, names=name_list, required=required) - - # populate cache - if const is not None: - cache[const] = info - if cache_by_name: - for name in name_list: - if name: # (skips iana name if it's empty) - assert cache.get(name) in [None, info], "%r already in cache" % name - cache[name] = info - return info - -#: UT helper for clearing internal cache -lookup_hash.clear_cache = _hash_info_cache.clear - - -def norm_hash_name(name, format="hashlib"): - """Normalize hash function name (convenience wrapper for :func:`lookup_hash`). - - :arg name: - Original hash function name. - - This name can be a Python :mod:`~hashlib` digest name, - a SCRAM mechanism name, IANA assigned hash name, etc. - Case is ignored, and underscores are converted to hyphens. - - :param format: - Naming convention to normalize to. - Possible values are: - - * ``"hashlib"`` (the default) - normalizes name to be compatible - with Python's :mod:`!hashlib`. - - * ``"iana"`` - normalizes name to IANA-assigned hash function name. - For hashes which IANA hasn't assigned a name for, this issues a warning, - and then uses a heuristic to return a "best guess" name. - - :returns: - Hash name, returned as native :class:`!str`. - """ - info = lookup_hash(name, required=False) - if info.unknown: - warn("norm_hash_name(): " + info.error_text, exc.PasslibRuntimeWarning) - if format == "hashlib": - return info.name - elif format == "iana": - return info.iana_name - else: - raise ValueError("unknown format: %r" % (format,)) - - -class HashInfo(SequenceMixin): - """ - Record containing information about a given hash algorithm, as returned :func:`lookup_hash`. - - This class exposes the following attributes: - - .. autoattribute:: const - .. autoattribute:: digest_size - .. autoattribute:: block_size - .. autoattribute:: name - .. autoattribute:: iana_name - .. autoattribute:: aliases - .. autoattribute:: supported - - This object can also be treated a 3-element sequence - containing ``(const, digest_size, block_size)``. - """ - #========================================================================= - # instance attrs - #========================================================================= - - #: Canonical / hashlib-compatible name (e.g. ``"sha256"``). - name = None - - #: IANA assigned name (e.g. ``"sha-256"``), may be ``None`` if unknown. - iana_name = None - - #: Tuple of other known aliases (may be empty) - aliases = () - - #: Hash constructor function (e.g. :func:`hashlib.sha256`) - const = None - - #: Hash's digest size - digest_size = None - - #: Hash's block size - block_size = None - - #: set when hash isn't available, will be filled in with string containing error text - #: that const() will raise. - error_text = None - - #: set when error_text is due to hash algorithm being completely unknown - #: (not just unavailable on current system) - unknown = False - - #========================================================================= - # init - #========================================================================= - - def __init__(self, # *, - const, names, required=True): - """ - initialize new instance. - :arg const: - hash constructor - :arg names: - list of 2+ names. should be list of ``(name, iana_name, ... 0+ aliases)``. - names must be lower-case. only iana name may be None. - """ - # init names - name = self.name = names[0] - self.iana_name = names[1] - self.aliases = names[2:] - - def use_stub_const(msg): - """ - helper that installs stub constructor which throws specified error . - """ - def const(source=b""): - raise exc.UnknownHashError(msg, name) - if required: - # if caller only wants supported digests returned, - # just throw error immediately... - const() - assert "shouldn't get here" - self.error_text = msg - self.const = const - try: - self.digest_size, self.block_size = _fallback_info[name] - except KeyError: - pass - - # handle "constructor not available" case - if const is None: - if names in _known_hash_names: - msg = "unsupported hash: %r" % name - else: - msg = "unknown hash: %r" % name - self.unknown = True - use_stub_const(msg) - # TODO: load in preset digest size info for known hashes. - return - - # create hash instance to inspect - try: - hash = const() - except ValueError as err: - # per issue 116, FIPS compliant systems will have a constructor; - # but it will throw a ValueError with this message. As of 1.7.3, - # translating this into DisabledHashError. - # "ValueError: error:060800A3:digital envelope routines:EVP_DigestInit_ex:disabled for fips" - if "disabled for fips" in str(err).lower(): - msg = "%r hash disabled for fips" % name - else: - msg = "internal error in %r constructor\n(%s: %s)" % (name, type(err).__name__, err) - use_stub_const(msg) - return - - # store stats about hash - self.const = const - self.digest_size = hash.digest_size - self.block_size = hash.block_size - - # do sanity check on digest size - if len(hash.digest()) != hash.digest_size: - raise RuntimeError("%r constructor failed sanity check" % self.name) - - # do sanity check on name. - if hash.name != self.name: - warn("inconsistent digest name: %r resolved to %r, which reports name as %r" % - (self.name, const, hash.name), exc.PasslibRuntimeWarning) - - #========================================================================= - # methods - #========================================================================= - def __repr__(self): - return " digest output``. - - However, if ``multipart=True``, the returned function has the signature - ``hmac() -> update, finalize``, where ``update(msg)`` may be called multiple times, - and ``finalize() -> digest_output`` may be repeatedly called at any point to - calculate the HMAC digest so far. - - The returned object will also have a ``digest_info`` attribute, containing - a :class:`lookup_hash` instance for the specified digest. - - This function exists, and has the weird signature it does, in order to squeeze as - provide as much efficiency as possible, by omitting much of the setup cost - and features of the stdlib :mod:`hmac` module. - """ - # all the following was adapted from stdlib's hmac module - - # resolve digest (cached) - digest_info = lookup_hash(digest) - const, digest_size, block_size = digest_info - assert block_size >= 16, "block size too small" - - # prepare key - if not isinstance(key, bytes): - key = to_bytes(key, param="key") - klen = len(key) - if klen > block_size: - key = const(key).digest() - klen = digest_size - if klen < block_size: - key += b'\x00' * (block_size - klen) - - # create pre-initialized hash constructors - _inner_copy = const(key.translate(_TRANS_36)).copy - _outer_copy = const(key.translate(_TRANS_5C)).copy - - if multipart: - # create multi-part function - # NOTE: this is slightly slower than the single-shot version, - # and should only be used if needed. - def hmac(): - """generated by compile_hmac(multipart=True)""" - inner = _inner_copy() - def finalize(): - outer = _outer_copy() - outer.update(inner.digest()) - return outer.digest() - return inner.update, finalize - else: - - # single-shot function - def hmac(msg): - """generated by compile_hmac()""" - inner = _inner_copy() - inner.update(msg) - outer = _outer_copy() - outer.update(inner.digest()) - return outer.digest() - - # add info attr - hmac.digest_info = digest_info - return hmac - -#============================================================================= -# pbkdf1 -#============================================================================= -def pbkdf1(digest, secret, salt, rounds, keylen=None): - """pkcs#5 password-based key derivation v1.5 - - :arg digest: - digest name or constructor. - - :arg secret: - secret to use when generating the key. - may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). - - :arg salt: - salt string to use when generating key. - may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). - - :param rounds: - number of rounds to use to generate key. - - :arg keylen: - number of bytes to generate (if omitted / ``None``, uses digest's native size) - - :returns: - raw :class:`bytes` of generated key - - .. note:: - - This algorithm has been deprecated, new code should use PBKDF2. - Among other limitations, ``keylen`` cannot be larger - than the digest size of the specified hash. - """ - # resolve digest - const, digest_size, block_size = lookup_hash(digest) - - # validate secret & salt - secret = to_bytes(secret, param="secret") - salt = to_bytes(salt, param="salt") - - # validate rounds - if not isinstance(rounds, int_types): - raise exc.ExpectedTypeError(rounds, "int", "rounds") - if rounds < 1: - raise ValueError("rounds must be at least 1") - - # validate keylen - if keylen is None: - keylen = digest_size - elif not isinstance(keylen, int_types): - raise exc.ExpectedTypeError(keylen, "int or None", "keylen") - elif keylen < 0: - raise ValueError("keylen must be at least 0") - elif keylen > digest_size: - raise ValueError("keylength too large for digest: %r > %r" % - (keylen, digest_size)) - - # main pbkdf1 loop - block = secret + salt - for _ in irange(rounds): - block = const(block).digest() - return block[:keylen] - -#============================================================================= -# pbkdf2 -#============================================================================= - -_pack_uint32 = Struct(">L").pack - -def pbkdf2_hmac(digest, secret, salt, rounds, keylen=None): - """pkcs#5 password-based key derivation v2.0 using HMAC + arbitrary digest. - - :arg digest: - digest name or constructor. - - :arg secret: - passphrase to use to generate key. - may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). - - :arg salt: - salt string to use when generating key. - may be :class:`!bytes` or :class:`unicode` (encoded using UTF-8). - - :param rounds: - number of rounds to use to generate key. - - :arg keylen: - number of bytes to generate. - if omitted / ``None``, will use digest's native output size. - - :returns: - raw bytes of generated key - - .. versionchanged:: 1.7 - - This function will use the first available of the following backends: - - * `fastpbk2 `_ - * :func:`hashlib.pbkdf2_hmac` (only available in py2 >= 2.7.8, and py3 >= 3.4) - * builtin pure-python backend - - See :data:`passlib.crypto.digest.PBKDF2_BACKENDS` to determine - which backend(s) are in use. - """ - # validate secret & salt - secret = to_bytes(secret, param="secret") - salt = to_bytes(salt, param="salt") - - # resolve digest - digest_info = lookup_hash(digest) - digest_size = digest_info.digest_size - - # validate rounds - if not isinstance(rounds, int_types): - raise exc.ExpectedTypeError(rounds, "int", "rounds") - if rounds < 1: - raise ValueError("rounds must be at least 1") - - # validate keylen - if keylen is None: - keylen = digest_size - elif not isinstance(keylen, int_types): - raise exc.ExpectedTypeError(keylen, "int or None", "keylen") - elif keylen < 1: - # XXX: could allow keylen=0, but want to be compat w/ stdlib - raise ValueError("keylen must be at least 1") - - # find smallest block count s.t. keylen <= block_count * digest_size; - # make sure block count won't overflow (per pbkdf2 spec) - # this corresponds to throwing error if keylen > digest_size * MAX_UINT32 - # NOTE: stdlib will throw error at lower bound (keylen > MAX_SINT32) - # NOTE: have do this before other backends checked, since fastpbkdf2 raises wrong error - # (InvocationError, not OverflowError) - block_count = (keylen + digest_size - 1) // digest_size - if block_count > MAX_UINT32: - raise OverflowError("keylen too long for digest") - - # - # check for various high-speed backends - # - - # ~3x faster than pure-python backend - # NOTE: have to do this after above guards since fastpbkdf2 lacks bounds checks. - if digest_info.supported_by_fastpbkdf2: - return _fast_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) - - # ~1.4x faster than pure-python backend - # NOTE: have to do this after fastpbkdf2 since hashlib-ssl is slower, - # will support larger number of hashes. - if digest_info.supported_by_hashlib_pbkdf2: - return _stdlib_pbkdf2_hmac(digest_info.name, secret, salt, rounds, keylen) - - # - # otherwise use our own implementation - # - - # generated keyed hmac - keyed_hmac = compile_hmac(digest, secret) - - # get helper to calculate pbkdf2 inner loop efficiently - calc_block = _get_pbkdf2_looper(digest_size) - - # assemble & return result - return join_bytes( - calc_block(keyed_hmac, keyed_hmac(salt + _pack_uint32(i)), rounds) - for i in irange(1, block_count + 1) - )[:keylen] - -#------------------------------------------------------------------------------------- -# pick best choice for pure-python helper -# TODO: consider some alternatives, such as C-accelerated xor_bytes helper if available -#------------------------------------------------------------------------------------- -# NOTE: this env var is only present to support the admin/benchmark_pbkdf2 script -_force_backend = os.environ.get("PASSLIB_PBKDF2_BACKEND") or "any" - -if PY3 and _force_backend in ["any", "from-bytes"]: - from functools import partial - - def _get_pbkdf2_looper(digest_size): - return partial(_pbkdf2_looper, digest_size) - - def _pbkdf2_looper(digest_size, keyed_hmac, digest, rounds): - """ - py3-only implementation of pbkdf2 inner loop; - uses 'int.from_bytes' + integer XOR - """ - from_bytes = int.from_bytes - BIG = "big" # endianess doesn't matter, just has to be consistent - accum = from_bytes(digest, BIG) - for _ in irange(rounds - 1): - digest = keyed_hmac(digest) - accum ^= from_bytes(digest, BIG) - return accum.to_bytes(digest_size, BIG) - - _builtin_backend = "from-bytes" - -elif _force_backend in ["any", "unpack", "from-bytes"]: - from struct import Struct - from passlib.utils import sys_bits - - _have_64_bit = (sys_bits >= 64) - - #: cache used by _get_pbkdf2_looper - _looper_cache = {} - - def _get_pbkdf2_looper(digest_size): - """ - We want a helper function which performs equivalent of the following:: - - def helper(keyed_hmac, digest, rounds): - accum = digest - for _ in irange(rounds - 1): - digest = keyed_hmac(digest) - accum ^= digest - return accum - - However, no efficient way to implement "bytes ^ bytes" in python. - Instead, using approach where we dynamically compile a helper function based - on digest size. Instead of a single `accum` var, this helper breaks the digest - into a series of integers. - - It stores these in a series of`accum_` vars, and performs `accum ^= digest` - by unpacking digest and perform xor for each "accum_ ^= digest_". - this keeps everything in locals, avoiding excessive list creation, encoding or decoding, - etc. - - :param digest_size: - digest size to compile for, in bytes. (must be multiple of 4). - - :return: - helper function with call signature outlined above. - """ - # - # cache helpers - # - try: - return _looper_cache[digest_size] - except KeyError: - pass - - # - # figure out most efficient struct format to unpack digest into list of native ints - # - if _have_64_bit and not digest_size & 0x7: - # digest size multiple of 8, on a 64 bit system -- use array of UINT64 - count = (digest_size >> 3) - fmt = "=%dQ" % count - elif not digest_size & 0x3: - if _have_64_bit: - # digest size multiple of 4, on a 64 bit system -- use array of UINT64 + 1 UINT32 - count = (digest_size >> 3) - fmt = "=%dQI" % count - count += 1 - else: - # digest size multiple of 4, on a 32 bit system -- use array of UINT32 - count = (digest_size >> 2) - fmt = "=%dI" % count - else: - # stopping here, cause no known hashes have digest size that isn't multiple of 4 bytes. - # if needed, could go crazy w/ "H" & "B" - raise NotImplementedError("unsupported digest size: %d" % digest_size) - struct = Struct(fmt) - - # - # build helper source - # - tdict = dict( - digest_size=digest_size, - accum_vars=", ".join("acc_%d" % i for i in irange(count)), - digest_vars=", ".join("dig_%d" % i for i in irange(count)), - ) - - # head of function - source = ( - "def helper(keyed_hmac, digest, rounds):\n" - " '''pbkdf2 loop helper for digest_size={digest_size}'''\n" - " unpack_digest = struct.unpack\n" - " {accum_vars} = unpack_digest(digest)\n" - " for _ in irange(1, rounds):\n" - " digest = keyed_hmac(digest)\n" - " {digest_vars} = unpack_digest(digest)\n" - ).format(**tdict) - - # xor digest - for i in irange(count): - source += " acc_%d ^= dig_%d\n" % (i, i) - - # return result - source += " return struct.pack({accum_vars})\n".format(**tdict) - - # - # compile helper - # - code = compile(source, "", "exec") - gdict = dict(irange=irange, struct=struct) - ldict = dict() - eval(code, gdict, ldict) - helper = ldict['helper'] - if __debug__: - helper.__source__ = source - - # - # store in cache - # - _looper_cache[digest_size] = helper - return helper - - _builtin_backend = "unpack" - -else: - assert _force_backend in ["any", "hexlify"] - - # XXX: older & slower approach that used int(hexlify()), - # keeping it around for a little while just for benchmarking. - - from binascii import hexlify as _hexlify - from passlib.utils import int_to_bytes - - def _get_pbkdf2_looper(digest_size): - return _pbkdf2_looper - - def _pbkdf2_looper(keyed_hmac, digest, rounds): - hexlify = _hexlify - accum = int(hexlify(digest), 16) - for _ in irange(rounds - 1): - digest = keyed_hmac(digest) - accum ^= int(hexlify(digest), 16) - return int_to_bytes(accum, len(digest)) - - _builtin_backend = "hexlify" - -# helper for benchmark script -- disable hashlib, fastpbkdf2 support if builtin requested -if _force_backend == _builtin_backend: - _fast_pbkdf2_hmac = _stdlib_pbkdf2_hmac = None - -# expose info about what backends are active -PBKDF2_BACKENDS = [b for b in [ - "fastpbkdf2" if _fast_pbkdf2_hmac else None, - "hashlib-ssl" if _stdlib_pbkdf2_hmac else None, - "builtin-" + _builtin_backend -] if b] - -# *very* rough estimate of relative speed (compared to sha256 using 'unpack' backend on 64bit arch) -if "fastpbkdf2" in PBKDF2_BACKENDS: - PBKDF2_SPEED_FACTOR = 3 -elif "hashlib-ssl" in PBKDF2_BACKENDS: - PBKDF2_SPEED_FACTOR = 1.4 -else: - # remaining backends have *some* difference in performance, but not enough to matter - PBKDF2_SPEED_FACTOR = 1 - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py b/.venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py deleted file mode 100644 index c71873a..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/scrypt/__init__.py +++ /dev/null @@ -1,281 +0,0 @@ -""" -passlib.utils.scrypt -- scrypt hash frontend and help utilities - -XXX: add this module to public docs? -""" -#========================================================================== -# imports -#========================================================================== -from __future__ import absolute_import -# core -import logging; log = logging.getLogger(__name__) -from warnings import warn -# pkg -from passlib import exc -from passlib.utils import to_bytes -from passlib.utils.compat import PYPY -# local -__all__ =[ - "validate", - "scrypt", -] - -#========================================================================== -# config validation -#========================================================================== - -#: internal global constant for setting stdlib scrypt's maxmem (int bytes). -#: set to -1 to auto-calculate (see _load_stdlib_backend() below) -#: set to 0 for openssl default (32mb according to python docs) -#: TODO: standardize this across backends, and expose support via scrypt hash config; -#: currently not very configurable, and only applies to stdlib backend. -SCRYPT_MAXMEM = -1 - -#: max output length in bytes -MAX_KEYLEN = ((1 << 32) - 1) * 32 - -#: max ``r * p`` limit -MAX_RP = (1 << 30) - 1 - -# TODO: unittests for this function -def validate(n, r, p): - """ - helper which validates a set of scrypt config parameters. - scrypt will take ``O(n * r * p)`` time and ``O(n * r)`` memory. - limitations are that ``n = 2**``, ``n < 2**(16*r)``, ``r * p < 2 ** 30``. - - :param n: scrypt rounds - :param r: scrypt block size - :param p: scrypt parallel factor - """ - if r < 1: - raise ValueError("r must be > 0: r=%r" % r) - - if p < 1: - raise ValueError("p must be > 0: p=%r" % p) - - if r * p > MAX_RP: - # pbkdf2-hmac-sha256 limitation - it will be requested to generate ``p*(2*r)*64`` bytes, - # but pbkdf2 can do max of (2**31-1) blocks, and sha-256 has 32 byte block size... - # so ``(2**31-1)*32 >= p*r*128`` -> ``r*p < 2**30`` - raise ValueError("r * p must be < 2**30: r=%r, p=%r" % (r,p)) - - if n < 2 or n & (n - 1): - raise ValueError("n must be > 1, and a power of 2: n=%r" % n) - - return True - - -UINT32_SIZE = 4 - - -def estimate_maxmem(n, r, p, fudge=1.05): - """ - calculate memory required for parameter combination. - assumes parameters have already been validated. - - .. warning:: - this is derived from OpenSSL's scrypt maxmem formula; - and may not be correct for other implementations - (additional buffers, different parallelism tradeoffs, etc). - """ - # XXX: expand to provide upper bound for diff backends, or max across all of them? - # NOTE: openssl's scrypt() enforces it's maxmem parameter based on calc located at - # , ending in line containing "Blen + Vlen > maxmem" - # using the following formula: - # Blen = p * 128 * r - # Vlen = 32 * r * (N + 2) * sizeof(uint32_t) - # total_bytes = Blen + Vlen - maxmem = r * (128 * p + 32 * (n + 2) * UINT32_SIZE) - # add fudge factor so we don't have off-by-one mismatch w/ openssl - maxmem = int(maxmem * fudge) - return maxmem - - -# TODO: configuration picker (may need psutil for full effect) - -#========================================================================== -# hash frontend -#========================================================================== - -#: backend function used by scrypt(), filled in by _set_backend() -_scrypt = None - -#: name of backend currently in use, exposed for informational purposes. -backend = None - -def scrypt(secret, salt, n, r, p=1, keylen=32): - """run SCrypt key derivation function using specified parameters. - - :arg secret: - passphrase string (unicode is encoded to bytes using utf-8). - - :arg salt: - salt string (unicode is encoded to bytes using utf-8). - - :arg n: - integer 'N' parameter - - :arg r: - integer 'r' parameter - - :arg p: - integer 'p' parameter - - :arg keylen: - number of bytes of key to generate. - defaults to 32 (the internal block size). - - :returns: - a *keylen*-sized bytes instance - - SCrypt imposes a number of constraints on it's input parameters: - - * ``r * p < 2**30`` -- due to a limitation of PBKDF2-HMAC-SHA256. - * ``keylen < (2**32 - 1) * 32`` -- due to a limitation of PBKDF2-HMAC-SHA256. - * ``n`` must a be a power of 2, and > 1 -- internal limitation of scrypt() implementation - - :raises ValueError: if the provided parameters are invalid (see constraints above). - - .. warning:: - - Unless the third-party ``scrypt ``_ package - is installed, passlib will use a builtin pure-python implementation of scrypt, - which is *considerably* slower (and thus requires a much lower / less secure - ``n`` value in order to be usuable). Installing the :mod:`!scrypt` package - is strongly recommended. - """ - validate(n, r, p) - secret = to_bytes(secret, param="secret") - salt = to_bytes(salt, param="salt") - if keylen < 1: - raise ValueError("keylen must be at least 1") - if keylen > MAX_KEYLEN: - raise ValueError("keylen too large, must be <= %d" % MAX_KEYLEN) - return _scrypt(secret, salt, n, r, p, keylen) - - -def _load_builtin_backend(): - """ - Load pure-python scrypt implementation built into passlib. - """ - slowdown = 10 if PYPY else 100 - warn("Using builtin scrypt backend, which is %dx slower than is required " - "for adequate security. Installing scrypt support (via 'pip install scrypt') " - "is strongly recommended" % slowdown, exc.PasslibSecurityWarning) - from ._builtin import ScryptEngine - return ScryptEngine.execute - - -def _load_cffi_backend(): - """ - Try to import the ctypes-based scrypt hash function provided by the - ``scrypt ``_ package. - """ - try: - from scrypt import hash - return hash - except ImportError: - pass - # not available, but check to see if package present but outdated / not installed right - try: - import scrypt - except ImportError as err: - if "scrypt" not in str(err): - # e.g. if cffi isn't set up right - # user should try importing scrypt explicitly to diagnose problem. - warn("'scrypt' package failed to import correctly (possible installation issue?)", - exc.PasslibWarning) - # else: package just isn't installed - else: - warn("'scrypt' package is too old (lacks ``hash()`` method)", exc.PasslibWarning) - return None - - -def _load_stdlib_backend(): - """ - Attempt to load stdlib scrypt() implement and return wrapper. - Returns None if not found. - """ - try: - # new in python 3.6, if compiled with openssl >= 1.1 - from hashlib import scrypt as stdlib_scrypt - except ImportError: - return None - - def stdlib_scrypt_wrapper(secret, salt, n, r, p, keylen): - # work out appropriate "maxmem" parameter - # - # TODO: would like to enforce a single "maxmem" policy across all backends; - # and maybe expose this via scrypt hasher config. - # - # for now, since parameters should all be coming from internally-controlled sources - # (password hashes), using policy of "whatever memory the parameters needs". - # furthermore, since stdlib scrypt is only place that needs this, - # currently calculating exactly what maxmem needs to make things work for stdlib call. - # as hack, this can be overriden via SCRYPT_MAXMEM above, - # would like to formalize all of this. - maxmem = SCRYPT_MAXMEM - if maxmem < 0: - maxmem = estimate_maxmem(n, r, p) - return stdlib_scrypt(password=secret, salt=salt, n=n, r=r, p=p, dklen=keylen, - maxmem=maxmem) - - return stdlib_scrypt_wrapper - - -#: list of potential backends -backend_values = ("stdlib", "scrypt", "builtin") - -#: dict mapping backend name -> loader -_backend_loaders = dict( - stdlib=_load_stdlib_backend, - scrypt=_load_cffi_backend, # XXX: rename backend constant to "cffi"? - builtin=_load_builtin_backend, -) - - -def _set_backend(name, dryrun=False): - """ - set backend for scrypt(). if name not specified, loads first available. - - :raises ~passlib.exc.MissingBackendError: if backend can't be found - - .. note:: mainly intended to be called by unittests, and scrypt hash handler - """ - if name == "any": - return - elif name == "default": - for name in backend_values: - try: - return _set_backend(name, dryrun=dryrun) - except exc.MissingBackendError: - continue - raise exc.MissingBackendError("no scrypt backends available") - else: - loader = _backend_loaders.get(name) - if not loader: - raise ValueError("unknown scrypt backend: %r" % (name,)) - hash = loader() - if not hash: - raise exc.MissingBackendError("scrypt backend %r not available" % name) - if dryrun: - return - global _scrypt, backend - backend = name - _scrypt = hash - -# initialize backend -_set_backend("default") - - -def _has_backend(name): - try: - _set_backend(name, dryrun=True) - return True - except exc.MissingBackendError: - return False - -#========================================================================== -# eof -#========================================================================== diff --git a/.venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py b/.venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py deleted file mode 100644 index e9bb305..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/scrypt/_builtin.py +++ /dev/null @@ -1,244 +0,0 @@ -"""passlib.utils.scrypt._builtin -- scrypt() kdf in pure-python""" -#========================================================================== -# imports -#========================================================================== -# core -import operator -import struct -# pkg -from passlib.utils.compat import izip -from passlib.crypto.digest import pbkdf2_hmac -from passlib.crypto.scrypt._salsa import salsa20 -# local -__all__ =[ - "ScryptEngine", -] - -#========================================================================== -# scrypt engine -#========================================================================== -class ScryptEngine(object): - """ - helper class used to run scrypt kdf, see scrypt() for frontend - - .. warning:: - this class does NO validation of the input ranges or types. - - it's not intended to be used directly, - but only as a backend for :func:`passlib.utils.scrypt.scrypt()`. - """ - #================================================================= - # instance attrs - #================================================================= - - # primary scrypt config parameters - n = 0 - r = 0 - p = 0 - - # derived values & objects - smix_bytes = 0 - iv_bytes = 0 - bmix_len = 0 - bmix_half_len = 0 - bmix_struct = None - integerify = None - - #================================================================= - # frontend - #================================================================= - @classmethod - def execute(cls, secret, salt, n, r, p, keylen): - """create engine & run scrypt() hash calculation""" - return cls(n, r, p).run(secret, salt, keylen) - - #================================================================= - # init - #================================================================= - def __init__(self, n, r, p): - # store config - self.n = n - self.r = r - self.p = p - self.smix_bytes = r << 7 # num bytes in smix input - 2*r*16*4 - self.iv_bytes = self.smix_bytes * p - self.bmix_len = bmix_len = r << 5 # length of bmix block list - 32*r integers - self.bmix_half_len = r << 4 - assert struct.calcsize("I") == 4 - self.bmix_struct = struct.Struct("<" + str(bmix_len) + "I") - - # use optimized bmix for certain cases - if r == 1: - self.bmix = self._bmix_1 - - # pick best integerify function - integerify(bmix_block) should - # take last 64 bytes of block and return a little-endian integer. - # since it's immediately converted % n, we only have to extract - # the first 32 bytes if n < 2**32 - which due to the current - # internal representation, is already unpacked as a 32-bit int. - if n <= 0xFFFFffff: - integerify = operator.itemgetter(-16) - else: - assert n <= 0xFFFFffffFFFFffff - ig1 = operator.itemgetter(-16) - ig2 = operator.itemgetter(-17) - def integerify(X): - return ig1(X) | (ig2(X)<<32) - self.integerify = integerify - - #================================================================= - # frontend - #================================================================= - def run(self, secret, salt, keylen): - """ - run scrypt kdf for specified secret, salt, and keylen - - .. note:: - - * time cost is ``O(n * r * p)`` - * mem cost is ``O(n * r)`` - """ - # stretch salt into initial byte array via pbkdf2 - iv_bytes = self.iv_bytes - input = pbkdf2_hmac("sha256", secret, salt, rounds=1, keylen=iv_bytes) - - # split initial byte array into 'p' mflen-sized chunks, - # and run each chunk through smix() to generate output chunk. - smix = self.smix - if self.p == 1: - output = smix(input) - else: - # XXX: *could* use threading here, if really high p values encountered, - # but would tradeoff for more memory usage. - smix_bytes = self.smix_bytes - output = b''.join( - smix(input[offset:offset+smix_bytes]) - for offset in range(0, iv_bytes, smix_bytes) - ) - - # stretch final byte array into output via pbkdf2 - return pbkdf2_hmac("sha256", secret, output, rounds=1, keylen=keylen) - - #================================================================= - # smix() helper - #================================================================= - def smix(self, input): - """run SCrypt smix function on a single input block - - :arg input: - byte string containing input data. - interpreted as 32*r little endian 4 byte integers. - - :returns: - byte string containing output data - derived by mixing input using n & r parameters. - - .. note:: time & mem cost are both ``O(n * r)`` - """ - # gather locals - bmix = self.bmix - bmix_struct = self.bmix_struct - integerify = self.integerify - n = self.n - - # parse input into 32*r integers ('X' in scrypt source) - # mem cost -- O(r) - buffer = list(bmix_struct.unpack(input)) - - # starting with initial buffer contents, derive V s.t. - # V[0]=initial_buffer ... V[i] = bmix(V[i-1], V[i-1]) ... V[n-1] = bmix(V[n-2], V[n-2]) - # final buffer contents should equal bmix(V[n-1], V[n-1]) - # - # time cost -- O(n * r) -- n loops, bmix is O(r) - # mem cost -- O(n * r) -- V is n-element array of r-element tuples - # NOTE: could do time / memory tradeoff to shrink size of V - def vgen(): - i = 0 - while i < n: - last = tuple(buffer) - yield last - bmix(last, buffer) - i += 1 - V = list(vgen()) - - # generate result from X & V. - # - # time cost -- O(n * r) -- loops n times, calls bmix() which has O(r) time cost - # mem cost -- O(1) -- allocates nothing, calls bmix() which has O(1) mem cost - get_v_elem = V.__getitem__ - n_mask = n - 1 - i = 0 - while i < n: - j = integerify(buffer) & n_mask - result = tuple(a ^ b for a, b in izip(buffer, get_v_elem(j))) - bmix(result, buffer) - i += 1 - - # # NOTE: we could easily support arbitrary values of ``n``, not just powers of 2, - # # but very few implementations have that ability, so not enabling it for now... - # if not n_is_log_2: - # while i < n: - # j = integerify(buffer) % n - # tmp = tuple(a^b for a,b in izip(buffer, get_v_elem(j))) - # bmix(tmp,buffer) - # i += 1 - - # repack tmp - return bmix_struct.pack(*buffer) - - #================================================================= - # bmix() helper - #================================================================= - def bmix(self, source, target): - """ - block mixing function used by smix() - uses salsa20/8 core to mix block contents. - - :arg source: - source to read from. - should be list of 32*r 4-byte integers - (2*r salsa20 blocks). - - :arg target: - target to write to. - should be list with same size as source. - the existing value of this buffer is ignored. - - .. warning:: - - this operates *in place* on target, - so source & target should NOT be same list. - - .. note:: - - * time cost is ``O(r)`` -- loops 16*r times, salsa20() has ``O(1)`` cost. - - * memory cost is ``O(1)`` -- salsa20() uses 16 x uint4, - all other operations done in-place. - """ - ## assert source is not target - # Y[-1] = B[2r-1], Y[i] = hash( Y[i-1] xor B[i]) - # B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ - half = self.bmix_half_len # 16*r out of 32*r - start of Y_1 - tmp = source[-16:] # 'X' in scrypt source - siter = iter(source) - j = 0 - while j < half: - jn = j+16 - target[j:jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter)) - target[half+j:half+jn] = tmp = salsa20(a ^ b for a, b in izip(tmp, siter)) - j = jn - - def _bmix_1(self, source, target): - """special bmix() method optimized for ``r=1`` case""" - B = source[16:] - target[:16] = tmp = salsa20(a ^ b for a, b in izip(B, iter(source))) - target[16:] = salsa20(a ^ b for a, b in izip(tmp, B)) - - #================================================================= - # eoc - #================================================================= - -#========================================================================== -# eof -#========================================================================== diff --git a/.venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py b/.venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py deleted file mode 100644 index 55ddfae..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/scrypt/_gen_files.py +++ /dev/null @@ -1,154 +0,0 @@ -"""passlib.utils.scrypt._gen_files - meta script that generates _salsa.py""" -#========================================================================== -# imports -#========================================================================== -# core -import os -# pkg -# local -#========================================================================== -# constants -#========================================================================== - -_SALSA_OPS = [ - # row = (target idx, source idx 1, source idx 2, rotate) - # interpreted as salsa operation over uint32... - # target = (source1+source2)<> (32 - (b)))) - ##x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); - ##x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); - ( 4, 0, 12, 7), - ( 8, 4, 0, 9), - ( 12, 8, 4, 13), - ( 0, 12, 8, 18), - - ##x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); - ##x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); - ( 9, 5, 1, 7), - ( 13, 9, 5, 9), - ( 1, 13, 9, 13), - ( 5, 1, 13, 18), - - ##x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); - ##x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); - ( 14, 10, 6, 7), - ( 2, 14, 10, 9), - ( 6, 2, 14, 13), - ( 10, 6, 2, 18), - - ##x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); - ##x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); - ( 3, 15, 11, 7), - ( 7, 3, 15, 9), - ( 11, 7, 3, 13), - ( 15, 11, 7, 18), - - ##/* Operate on rows. */ - ##x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); - ##x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); - ( 1, 0, 3, 7), - ( 2, 1, 0, 9), - ( 3, 2, 1, 13), - ( 0, 3, 2, 18), - - ##x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); - ##x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); - ( 6, 5, 4, 7), - ( 7, 6, 5, 9), - ( 4, 7, 6, 13), - ( 5, 4, 7, 18), - - ##x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); - ##x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); - ( 11, 10, 9, 7), - ( 8, 11, 10, 9), - ( 9, 8, 11, 13), - ( 10, 9, 8, 18), - - ##x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); - ##x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); - ( 12, 15, 14, 7), - ( 13, 12, 15, 9), - ( 14, 13, 12, 13), - ( 15, 14, 13, 18), -] - -def main(): - target = os.path.join(os.path.dirname(__file__), "_salsa.py") - fh = file(target, "w") - write = fh.write - - VNAMES = ["v%d" % i for i in range(16)] - - PAD = " " * 4 - PAD2 = " " * 8 - PAD3 = " " * 12 - TLIST = ", ".join("b%d" % i for i in range(16)) - VLIST = ", ".join(VNAMES) - kwds = dict( - VLIST=VLIST, - TLIST=TLIST, - ) - - write('''\ -"""passlib.utils.scrypt._salsa - salsa 20/8 core, autogenerated by _gen_salsa.py""" -#================================================================= -# salsa function -#================================================================= - -def salsa20(input): - \"""apply the salsa20/8 core to the provided input - - :args input: input list containing 16 32-bit integers - :returns: result list containing 16 32-bit integers - \""" - - %(TLIST)s = input - %(VLIST)s = \\ - %(TLIST)s - - i = 0 - while i < 4: -''' % kwds) - - for idx, (target, source1, source2, rotate) in enumerate(_SALSA_OPS): - write('''\ - # salsa op %(idx)d: [%(it)d] ^= ([%(is1)d]+[%(is2)d])<<<%(rot1)d - t = (%(src1)s + %(src2)s) & 0xffffffff - %(dst)s ^= ((t & 0x%(rmask)08x) << %(rot1)d) | (t >> %(rot2)d) - -''' % dict( - idx=idx, is1 = source1, is2=source2, it=target, - src1=VNAMES[source1], - src2=VNAMES[source2], - dst=VNAMES[target], - rmask=(1<<(32-rotate))-1, - rot1=rotate, - rot2=32-rotate, - )) - - write('''\ - i += 1 - -''') - - for idx in range(16): - write(PAD + "b%d = (b%d + v%d) & 0xffffffff\n" % (idx,idx,idx)) - - write('''\ - - return %(TLIST)s - -#================================================================= -# eof -#================================================================= -''' % kwds) - -if __name__ == "__main__": - main() - -#========================================================================== -# eof -#========================================================================== diff --git a/.venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py b/.venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py deleted file mode 100644 index 9112732..0000000 --- a/.venv/Lib/site-packages/passlib/crypto/scrypt/_salsa.py +++ /dev/null @@ -1,170 +0,0 @@ -"""passlib.utils.scrypt._salsa - salsa 20/8 core, autogenerated by _gen_salsa.py""" -#================================================================= -# salsa function -#================================================================= - -def salsa20(input): - """apply the salsa20/8 core to the provided input - - :args input: input list containing 16 32-bit integers - :returns: result list containing 16 32-bit integers - """ - - b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 = input - v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 = \ - b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 - - i = 0 - while i < 4: - # salsa op 0: [4] ^= ([0]+[12])<<<7 - t = (v0 + v12) & 0xffffffff - v4 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 1: [8] ^= ([4]+[0])<<<9 - t = (v4 + v0) & 0xffffffff - v8 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 2: [12] ^= ([8]+[4])<<<13 - t = (v8 + v4) & 0xffffffff - v12 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 3: [0] ^= ([12]+[8])<<<18 - t = (v12 + v8) & 0xffffffff - v0 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 4: [9] ^= ([5]+[1])<<<7 - t = (v5 + v1) & 0xffffffff - v9 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 5: [13] ^= ([9]+[5])<<<9 - t = (v9 + v5) & 0xffffffff - v13 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 6: [1] ^= ([13]+[9])<<<13 - t = (v13 + v9) & 0xffffffff - v1 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 7: [5] ^= ([1]+[13])<<<18 - t = (v1 + v13) & 0xffffffff - v5 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 8: [14] ^= ([10]+[6])<<<7 - t = (v10 + v6) & 0xffffffff - v14 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 9: [2] ^= ([14]+[10])<<<9 - t = (v14 + v10) & 0xffffffff - v2 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 10: [6] ^= ([2]+[14])<<<13 - t = (v2 + v14) & 0xffffffff - v6 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 11: [10] ^= ([6]+[2])<<<18 - t = (v6 + v2) & 0xffffffff - v10 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 12: [3] ^= ([15]+[11])<<<7 - t = (v15 + v11) & 0xffffffff - v3 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 13: [7] ^= ([3]+[15])<<<9 - t = (v3 + v15) & 0xffffffff - v7 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 14: [11] ^= ([7]+[3])<<<13 - t = (v7 + v3) & 0xffffffff - v11 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 15: [15] ^= ([11]+[7])<<<18 - t = (v11 + v7) & 0xffffffff - v15 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 16: [1] ^= ([0]+[3])<<<7 - t = (v0 + v3) & 0xffffffff - v1 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 17: [2] ^= ([1]+[0])<<<9 - t = (v1 + v0) & 0xffffffff - v2 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 18: [3] ^= ([2]+[1])<<<13 - t = (v2 + v1) & 0xffffffff - v3 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 19: [0] ^= ([3]+[2])<<<18 - t = (v3 + v2) & 0xffffffff - v0 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 20: [6] ^= ([5]+[4])<<<7 - t = (v5 + v4) & 0xffffffff - v6 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 21: [7] ^= ([6]+[5])<<<9 - t = (v6 + v5) & 0xffffffff - v7 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 22: [4] ^= ([7]+[6])<<<13 - t = (v7 + v6) & 0xffffffff - v4 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 23: [5] ^= ([4]+[7])<<<18 - t = (v4 + v7) & 0xffffffff - v5 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 24: [11] ^= ([10]+[9])<<<7 - t = (v10 + v9) & 0xffffffff - v11 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 25: [8] ^= ([11]+[10])<<<9 - t = (v11 + v10) & 0xffffffff - v8 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 26: [9] ^= ([8]+[11])<<<13 - t = (v8 + v11) & 0xffffffff - v9 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 27: [10] ^= ([9]+[8])<<<18 - t = (v9 + v8) & 0xffffffff - v10 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - # salsa op 28: [12] ^= ([15]+[14])<<<7 - t = (v15 + v14) & 0xffffffff - v12 ^= ((t & 0x01ffffff) << 7) | (t >> 25) - - # salsa op 29: [13] ^= ([12]+[15])<<<9 - t = (v12 + v15) & 0xffffffff - v13 ^= ((t & 0x007fffff) << 9) | (t >> 23) - - # salsa op 30: [14] ^= ([13]+[12])<<<13 - t = (v13 + v12) & 0xffffffff - v14 ^= ((t & 0x0007ffff) << 13) | (t >> 19) - - # salsa op 31: [15] ^= ([14]+[13])<<<18 - t = (v14 + v13) & 0xffffffff - v15 ^= ((t & 0x00003fff) << 18) | (t >> 14) - - i += 1 - - b0 = (b0 + v0) & 0xffffffff - b1 = (b1 + v1) & 0xffffffff - b2 = (b2 + v2) & 0xffffffff - b3 = (b3 + v3) & 0xffffffff - b4 = (b4 + v4) & 0xffffffff - b5 = (b5 + v5) & 0xffffffff - b6 = (b6 + v6) & 0xffffffff - b7 = (b7 + v7) & 0xffffffff - b8 = (b8 + v8) & 0xffffffff - b9 = (b9 + v9) & 0xffffffff - b10 = (b10 + v10) & 0xffffffff - b11 = (b11 + v11) & 0xffffffff - b12 = (b12 + v12) & 0xffffffff - b13 = (b13 + v13) & 0xffffffff - b14 = (b14 + v14) & 0xffffffff - b15 = (b15 + v15) & 0xffffffff - - return b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 - -#================================================================= -# eof -#================================================================= diff --git a/.venv/Lib/site-packages/passlib/exc.py b/.venv/Lib/site-packages/passlib/exc.py deleted file mode 100644 index 755c7dc..0000000 --- a/.venv/Lib/site-packages/passlib/exc.py +++ /dev/null @@ -1,397 +0,0 @@ -"""passlib.exc -- exceptions & warnings raised by passlib""" -#============================================================================= -# exceptions -#============================================================================= -class UnknownBackendError(ValueError): - """ - Error raised if multi-backend handler doesn't recognize backend name. - Inherits from :exc:`ValueError`. - - .. versionadded:: 1.7 - """ - def __init__(self, hasher, backend): - self.hasher = hasher - self.backend = backend - message = "%s: unknown backend: %r" % (hasher.name, backend) - ValueError.__init__(self, message) - - -# XXX: add a PasslibRuntimeError as base for Missing/Internal/Security runtime errors? - - -class MissingBackendError(RuntimeError): - """Error raised if multi-backend handler has no available backends; - or if specifically requested backend is not available. - - :exc:`!MissingBackendError` derives - from :exc:`RuntimeError`, since it usually indicates - lack of an external library or OS feature. - This is primarily raised by handlers which depend on - external libraries (which is currently just - :class:`~passlib.hash.bcrypt`). - """ - - -class InternalBackendError(RuntimeError): - """ - Error raised if something unrecoverable goes wrong with backend call; - such as if ``crypt.crypt()`` returning a malformed hash. - - .. versionadded:: 1.7.3 - """ - - -class PasswordValueError(ValueError): - """ - Error raised if a password can't be hashed / verified for various reasons. - This exception derives from the builtin :exc:`!ValueError`. - - May be thrown directly when password violates internal invariants of hasher - (e.g. some don't support NULL characters). Hashers may also throw more specific subclasses, - such as :exc:`!PasswordSizeError`. - - .. versionadded:: 1.7.3 - """ - pass - - -class PasswordSizeError(PasswordValueError): - """ - Error raised if a password exceeds the maximum size allowed - by Passlib (by default, 4096 characters); or if password exceeds - a hash-specific size limitation. - - This exception derives from :exc:`PasswordValueError` (above). - - Many password hash algorithms take proportionately larger amounts of time and/or - memory depending on the size of the password provided. This could present - a potential denial of service (DOS) situation if a maliciously large - password is provided to an application. Because of this, Passlib enforces - a maximum size limit, but one which should be *much* larger - than any legitimate password. :exc:`PasswordSizeError` derives - from :exc:`!ValueError`. - - .. note:: - Applications wishing to use a different limit should set the - ``PASSLIB_MAX_PASSWORD_SIZE`` environmental variable before - Passlib is loaded. The value can be any large positive integer. - - .. attribute:: max_size - - indicates the maximum allowed size. - - .. versionadded:: 1.6 - """ - - max_size = None - - def __init__(self, max_size, msg=None): - self.max_size = max_size - if msg is None: - msg = "password exceeds maximum allowed size" - PasswordValueError.__init__(self, msg) - - # this also prevents a glibc crypt segfault issue, detailed here ... - # http://www.openwall.com/lists/oss-security/2011/11/15/1 - -class PasswordTruncateError(PasswordSizeError): - """ - Error raised if password would be truncated by hash. - This derives from :exc:`PasswordSizeError` (above). - - Hashers such as :class:`~passlib.hash.bcrypt` can be configured to raises - this error by setting ``truncate_error=True``. - - .. attribute:: max_size - - indicates the maximum allowed size. - - .. versionadded:: 1.7 - """ - - def __init__(self, cls, msg=None): - if msg is None: - msg = ("Password too long (%s truncates to %d characters)" % - (cls.name, cls.truncate_size)) - PasswordSizeError.__init__(self, cls.truncate_size, msg) - - -class PasslibSecurityError(RuntimeError): - """ - Error raised if critical security issue is detected - (e.g. an attempt is made to use a vulnerable version of a bcrypt backend). - - .. versionadded:: 1.6.3 - """ - - -class TokenError(ValueError): - """ - Base error raised by v:mod:`passlib.totp` when - a token can't be parsed / isn't valid / etc. - Derives from :exc:`!ValueError`. - - Usually one of the more specific subclasses below will be raised: - - * :class:`MalformedTokenError` -- invalid chars, too few digits - * :class:`InvalidTokenError` -- no match found - * :class:`UsedTokenError` -- match found, but token already used - - .. versionadded:: 1.7 - """ - - #: default message to use if none provided -- subclasses may fill this in - _default_message = 'Token not acceptable' - - def __init__(self, msg=None, *args, **kwds): - if msg is None: - msg = self._default_message - ValueError.__init__(self, msg, *args, **kwds) - - -class MalformedTokenError(TokenError): - """ - Error raised by :mod:`passlib.totp` when a token isn't formatted correctly - (contains invalid characters, wrong number of digits, etc) - """ - _default_message = "Unrecognized token" - - -class InvalidTokenError(TokenError): - """ - Error raised by :mod:`passlib.totp` when a token is formatted correctly, - but doesn't match any tokens within valid range. - """ - _default_message = "Token did not match" - - -class UsedTokenError(TokenError): - """ - Error raised by :mod:`passlib.totp` if a token is reused. - Derives from :exc:`TokenError`. - - .. autoattribute:: expire_time - - .. versionadded:: 1.7 - """ - _default_message = "Token has already been used, please wait for another." - - #: optional value indicating when current counter period will end, - #: and a new token can be generated. - expire_time = None - - def __init__(self, *args, **kwds): - self.expire_time = kwds.pop("expire_time", None) - TokenError.__init__(self, *args, **kwds) - - -class UnknownHashError(ValueError): - """ - Error raised by :class:`~passlib.crypto.lookup_hash` if hash name is not recognized. - This exception derives from :exc:`!ValueError`. - - As of version 1.7.3, this may also be raised if hash algorithm is known, - but has been disabled due to FIPS mode (message will include phrase "disabled for fips"). - - As of version 1.7.4, this may be raised if a :class:`~passlib.context.CryptContext` - is unable to identify the algorithm used by a password hash. - - .. versionadded:: 1.7 - - .. versionchanged: 1.7.3 - added 'message' argument. - - .. versionchanged:: 1.7.4 - altered call signature. - """ - def __init__(self, message=None, value=None): - self.value = value - if message is None: - message = "unknown hash algorithm: %r" % value - self.message = message - ValueError.__init__(self, message, value) - - def __str__(self): - return self.message - - -#============================================================================= -# warnings -#============================================================================= -class PasslibWarning(UserWarning): - """base class for Passlib's user warnings, - derives from the builtin :exc:`UserWarning`. - - .. versionadded:: 1.6 - """ - -# XXX: there's only one reference to this class, and it will go away in 2.0; -# so can probably remove this along with this / roll this into PasslibHashWarning. -class PasslibConfigWarning(PasslibWarning): - """Warning issued when non-fatal issue is found related to the configuration - of a :class:`~passlib.context.CryptContext` instance. - - This occurs primarily in one of two cases: - - * The CryptContext contains rounds limits which exceed the hard limits - imposed by the underlying algorithm. - * An explicit rounds value was provided which exceeds the limits - imposed by the CryptContext. - - In both of these cases, the code will perform correctly & securely; - but the warning is issued as a sign the configuration may need updating. - - .. versionadded:: 1.6 - """ - -class PasslibHashWarning(PasslibWarning): - """Warning issued when non-fatal issue is found with parameters - or hash string passed to a passlib hash class. - - This occurs primarily in one of two cases: - - * A rounds value or other setting was explicitly provided which - exceeded the handler's limits (and has been clamped - by the :ref:`relaxed` flag). - - * A malformed hash string was encountered which (while parsable) - should be re-encoded. - - .. versionadded:: 1.6 - """ - -class PasslibRuntimeWarning(PasslibWarning): - """Warning issued when something unexpected happens during runtime. - - The fact that it's a warning instead of an error means Passlib - was able to correct for the issue, but that it's anomalous enough - that the developers would love to hear under what conditions it occurred. - - .. versionadded:: 1.6 - """ - -class PasslibSecurityWarning(PasslibWarning): - """Special warning issued when Passlib encounters something - that might affect security. - - .. versionadded:: 1.6 - """ - -#============================================================================= -# error constructors -# -# note: these functions are used by the hashes in Passlib to raise common -# error messages. They are currently just functions which return ValueError, -# rather than subclasses of ValueError, since the specificity isn't needed -# yet; and who wants to import a bunch of error classes when catching -# ValueError will do? -#============================================================================= - -def _get_name(handler): - return handler.name if handler else "" - -#------------------------------------------------------------------------ -# generic helpers -#------------------------------------------------------------------------ -def type_name(value): - """return pretty-printed string containing name of value's type""" - cls = value.__class__ - if cls.__module__ and cls.__module__ not in ["__builtin__", "builtins"]: - return "%s.%s" % (cls.__module__, cls.__name__) - elif value is None: - return 'None' - else: - return cls.__name__ - -def ExpectedTypeError(value, expected, param): - """error message when param was supposed to be one type, but found another""" - # NOTE: value is never displayed, since it may sometimes be a password. - name = type_name(value) - return TypeError("%s must be %s, not %s" % (param, expected, name)) - -def ExpectedStringError(value, param): - """error message when param was supposed to be unicode or bytes""" - return ExpectedTypeError(value, "unicode or bytes", param) - -#------------------------------------------------------------------------ -# hash/verify parameter errors -#------------------------------------------------------------------------ -def MissingDigestError(handler=None): - """raised when verify() method gets passed config string instead of hash""" - name = _get_name(handler) - return ValueError("expected %s hash, got %s config string instead" % - (name, name)) - -def NullPasswordError(handler=None): - """raised by OS crypt() supporting hashes, which forbid NULLs in password""" - name = _get_name(handler) - return PasswordValueError("%s does not allow NULL bytes in password" % name) - -#------------------------------------------------------------------------ -# errors when parsing hashes -#------------------------------------------------------------------------ -def InvalidHashError(handler=None): - """error raised if unrecognized hash provided to handler""" - return ValueError("not a valid %s hash" % _get_name(handler)) - -def MalformedHashError(handler=None, reason=None): - """error raised if recognized-but-malformed hash provided to handler""" - text = "malformed %s hash" % _get_name(handler) - if reason: - text = "%s (%s)" % (text, reason) - return ValueError(text) - -def ZeroPaddedRoundsError(handler=None): - """error raised if hash was recognized but contained zero-padded rounds field""" - return MalformedHashError(handler, "zero-padded rounds") - -#------------------------------------------------------------------------ -# settings / hash component errors -#------------------------------------------------------------------------ -def ChecksumSizeError(handler, raw=False): - """error raised if hash was recognized, but checksum was wrong size""" - # TODO: if handler.use_defaults is set, this came from app-provided value, - # not from parsing a hash string, might want different error msg. - checksum_size = handler.checksum_size - unit = "bytes" if raw else "chars" - reason = "checksum must be exactly %d %s" % (checksum_size, unit) - return MalformedHashError(handler, reason) - -#============================================================================= -# sensitive info helpers -#============================================================================= - -#: global flag, set temporarily by UTs to allow debug_only_repr() to display sensitive values. -ENABLE_DEBUG_ONLY_REPR = False - - -def debug_only_repr(value, param="hash"): - """ - helper used to display sensitive data (hashes etc) within error messages. - currently returns placeholder test UNLESS unittests are running, - in which case the real value is displayed. - - mainly useful to prevent hashes / secrets from being exposed in production tracebacks; - while still being visible from test failures. - - NOTE: api subject to change, may formalize this more in the future. - """ - if ENABLE_DEBUG_ONLY_REPR or value is None or isinstance(value, bool): - return repr(value) - return "<%s %s value omitted>" % (param, type(value)) - - -def CryptBackendError(handler, config, hash, # * - source="crypt.crypt()"): - """ - helper to generate standard message when ``crypt.crypt()`` returns invalid result. - takes care of automatically masking contents of config & hash outside of UTs. - """ - name = _get_name(handler) - msg = "%s returned invalid %s hash: config=%s hash=%s" % \ - (source, name, debug_only_repr(config), debug_only_repr(hash)) - raise InternalBackendError(msg) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/ext/__init__.py b/.venv/Lib/site-packages/passlib/ext/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/.venv/Lib/site-packages/passlib/ext/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/.venv/Lib/site-packages/passlib/ext/django/__init__.py b/.venv/Lib/site-packages/passlib/ext/django/__init__.py deleted file mode 100644 index 2dc9b28..0000000 --- a/.venv/Lib/site-packages/passlib/ext/django/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""passlib.ext.django.models -- monkeypatch django hashing framework - -this plugin monkeypatches django's hashing framework -so that it uses a passlib context object, allowing handling of arbitrary -hashes in Django databases. -""" diff --git a/.venv/Lib/site-packages/passlib/ext/django/models.py b/.venv/Lib/site-packages/passlib/ext/django/models.py deleted file mode 100644 index e766c2d..0000000 --- a/.venv/Lib/site-packages/passlib/ext/django/models.py +++ /dev/null @@ -1,36 +0,0 @@ -"""passlib.ext.django.models -- monkeypatch django hashing framework""" -#============================================================================= -# imports -#============================================================================= -# core -# site -# pkg -from passlib.context import CryptContext -from passlib.ext.django.utils import DjangoContextAdapter -# local -__all__ = ["password_context"] - -#============================================================================= -# global attrs -#============================================================================= - -#: adapter instance used to drive most of this -adapter = DjangoContextAdapter() - -# the context object which this patches contrib.auth to use for password hashing. -# configuration controlled by ``settings.PASSLIB_CONFIG``. -password_context = adapter.context - -#: hook callers should use if context is changed -context_changed = adapter.reset_hashers - -#============================================================================= -# main code -#============================================================================= - -# load config & install monkeypatch -adapter.load_model() - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/ext/django/utils.py b/.venv/Lib/site-packages/passlib/ext/django/utils.py deleted file mode 100644 index 2f8a2ef..0000000 --- a/.venv/Lib/site-packages/passlib/ext/django/utils.py +++ /dev/null @@ -1,1276 +0,0 @@ -"""passlib.ext.django.utils - helper functions used by this plugin""" -#============================================================================= -# imports -#============================================================================= -# core -from functools import update_wrapper, wraps -import logging; log = logging.getLogger(__name__) -import sys -import weakref -from warnings import warn -# site -try: - from django import VERSION as DJANGO_VERSION - log.debug("found django %r installation", DJANGO_VERSION) -except ImportError: - log.debug("django installation not found") - DJANGO_VERSION = () -# pkg -from passlib import exc, registry -from passlib.context import CryptContext -from passlib.exc import PasslibRuntimeWarning -from passlib.utils.compat import get_method_function, iteritems, OrderedDict, unicode -from passlib.utils.decor import memoized_property -# local -__all__ = [ - "DJANGO_VERSION", - "MIN_DJANGO_VERSION", - "get_preset_config", - "quirks", -] - -#: minimum version supported by passlib.ext.django -MIN_DJANGO_VERSION = (1, 8) - -#============================================================================= -# quirk detection -#============================================================================= - -class quirks: - - #: django check_password() started throwing error on encoded=None - #: (really identify_hasher did) - none_causes_check_password_error = DJANGO_VERSION >= (2, 1) - - #: django is_usable_password() started returning True for password = {None, ""} values. - empty_is_usable_password = DJANGO_VERSION >= (2, 1) - - #: django is_usable_password() started returning True for non-hash strings in 2.1 - invalid_is_usable_password = DJANGO_VERSION >= (2, 1) - -#============================================================================= -# default policies -#============================================================================= - -# map preset names -> passlib.app attrs -_preset_map = { - "django-1.0": "django10_context", - "django-1.4": "django14_context", - "django-1.6": "django16_context", - "django-latest": "django_context", -} - -def get_preset_config(name): - """Returns configuration string for one of the preset strings - supported by the ``PASSLIB_CONFIG`` setting. - Currently supported presets: - - * ``"passlib-default"`` - default config used by this release of passlib. - * ``"django-default"`` - config matching currently installed django version. - * ``"django-latest"`` - config matching newest django version (currently same as ``"django-1.6"``). - * ``"django-1.0"`` - config used by stock Django 1.0 - 1.3 installs - * ``"django-1.4"`` - config used by stock Django 1.4 installs - * ``"django-1.6"`` - config used by stock Django 1.6 installs - """ - # TODO: add preset which includes HASHERS + PREFERRED_HASHERS, - # after having imported any custom hashers. e.g. "django-current" - if name == "django-default": - if not DJANGO_VERSION: - raise ValueError("can't resolve django-default preset, " - "django not installed") - name = "django-1.6" - if name == "passlib-default": - return PASSLIB_DEFAULT - try: - attr = _preset_map[name] - except KeyError: - raise ValueError("unknown preset config name: %r" % name) - import passlib.apps - return getattr(passlib.apps, attr).to_string() - -# default context used by passlib 1.6 -PASSLIB_DEFAULT = """ -[passlib] - -; list of schemes supported by configuration -; currently all django 1.6, 1.4, and 1.0 hashes, -; and three common modular crypt format hashes. -schemes = - django_pbkdf2_sha256, django_pbkdf2_sha1, django_bcrypt, django_bcrypt_sha256, - django_salted_sha1, django_salted_md5, django_des_crypt, hex_md5, - sha512_crypt, bcrypt, phpass - -; default scheme to use for new hashes -default = django_pbkdf2_sha256 - -; hashes using these schemes will automatically be re-hashed -; when the user logs in (currently all django 1.0 hashes) -deprecated = - django_pbkdf2_sha1, django_salted_sha1, django_salted_md5, - django_des_crypt, hex_md5 - -; sets some common options, including minimum rounds for two primary hashes. -; if a hash has less than this number of rounds, it will be re-hashed. -sha512_crypt__min_rounds = 80000 -django_pbkdf2_sha256__min_rounds = 10000 - -; set somewhat stronger iteration counts for ``User.is_staff`` -staff__sha512_crypt__default_rounds = 100000 -staff__django_pbkdf2_sha256__default_rounds = 12500 - -; and even stronger ones for ``User.is_superuser`` -superuser__sha512_crypt__default_rounds = 120000 -superuser__django_pbkdf2_sha256__default_rounds = 15000 -""" - -#============================================================================= -# helpers -#============================================================================= - -#: prefix used to shoehorn passlib's handler names into django hasher namespace -PASSLIB_WRAPPER_PREFIX = "passlib_" - -#: prefix used by all the django-specific hash formats in passlib; -#: all of these hashes should have a ``.django_name`` attribute. -DJANGO_COMPAT_PREFIX = "django_" - -#: set of hashes w/o "django_" prefix, but which also expose ``.django_name``. -_other_django_hashes = set(["hex_md5"]) - -def _wrap_method(method): - """wrap method object in bare function""" - @wraps(method) - def wrapper(*args, **kwds): - return method(*args, **kwds) - return wrapper - -#============================================================================= -# translator -#============================================================================= -class DjangoTranslator(object): - """ - Object which helps translate passlib hasher objects / names - to and from django hasher objects / names. - - These methods are wrapped in a class so that results can be cached, - but with the ability to have independant caches, since django hasher - names may / may not correspond to the same instance (or even class). - """ - #============================================================================= - # instance attrs - #============================================================================= - - #: CryptContext instance - #: (if any -- generally only set by DjangoContextAdapter subclass) - context = None - - #: internal cache of passlib hasher -> django hasher instance. - #: key stores weakref to passlib hasher. - _django_hasher_cache = None - - #: special case -- unsalted_sha1 - _django_unsalted_sha1 = None - - #: internal cache of django name -> passlib hasher - #: value stores weakrefs to passlib hasher. - _passlib_hasher_cache = None - - #============================================================================= - # init - #============================================================================= - - def __init__(self, context=None, **kwds): - super(DjangoTranslator, self).__init__(**kwds) - if context is not None: - self.context = context - - self._django_hasher_cache = weakref.WeakKeyDictionary() - self._passlib_hasher_cache = weakref.WeakValueDictionary() - - def reset_hashers(self): - self._django_hasher_cache.clear() - self._passlib_hasher_cache.clear() - self._django_unsalted_sha1 = None - - def _get_passlib_hasher(self, passlib_name): - """ - resolve passlib hasher by name, using context if available. - """ - context = self.context - if context is None: - return registry.get_crypt_handler(passlib_name) - else: - return context.handler(passlib_name) - - #============================================================================= - # resolve passlib hasher -> django hasher - #============================================================================= - - def passlib_to_django_name(self, passlib_name): - """ - Convert passlib hasher / name to Django hasher name. - """ - return self.passlib_to_django(passlib_name).algorithm - - # XXX: add option (in class, or call signature) to always return a wrapper, - # rather than native builtin -- would let HashersTest check that - # our own wrapper + implementations are matching up with their tests. - def passlib_to_django(self, passlib_hasher, cached=True): - """ - Convert passlib hasher / name to Django hasher. - - :param passlib_hasher: - passlib hasher / name - - :returns: - django hasher instance - """ - # resolve names to hasher - if not hasattr(passlib_hasher, "name"): - passlib_hasher = self._get_passlib_hasher(passlib_hasher) - - # check cache - if cached: - cache = self._django_hasher_cache - try: - return cache[passlib_hasher] - except KeyError: - pass - result = cache[passlib_hasher] = \ - self.passlib_to_django(passlib_hasher, cached=False) - return result - - # find native equivalent, and return wrapper if there isn't one - django_name = getattr(passlib_hasher, "django_name", None) - if django_name: - return self._create_django_hasher(django_name) - else: - return _PasslibHasherWrapper(passlib_hasher) - - _builtin_django_hashers = dict( - md5="MD5PasswordHasher", - ) - - if DJANGO_VERSION > (2, 1): - # present but disabled by default as of django 2.1; not sure when added, - # so not listing it by default. - _builtin_django_hashers.update( - bcrypt="BCryptPasswordHasher", - ) - - def _create_django_hasher(self, django_name): - """ - helper to create new django hasher by name. - wraps underlying django methods. - """ - # if we haven't patched django, can use it directly - module = sys.modules.get("passlib.ext.django.models") - if module is None or not module.adapter.patched: - from django.contrib.auth.hashers import get_hasher - try: - return get_hasher(django_name) - except ValueError as err: - if not str(err).startswith("Unknown password hashing algorithm"): - raise - else: - # We've patched django's get_hashers(), so calling django's get_hasher() - # or get_hashers_by_algorithm() would only land us back here. - # As non-ideal workaround, have to use original get_hashers(), - get_hashers = module.adapter._manager.getorig("django.contrib.auth.hashers:get_hashers").__wrapped__ - for hasher in get_hashers(): - if hasher.algorithm == django_name: - return hasher - - # hardcode a few for cases where get_hashers() lookup won't work - # (mainly, hashers that are present in django, but disabled by their default config) - path = self._builtin_django_hashers.get(django_name) - if path: - if "." not in path: - path = "django.contrib.auth.hashers." + path - from django.utils.module_loading import import_string - return import_string(path)() - - raise ValueError("unknown hasher: %r" % django_name) - - #============================================================================= - # reverse django -> passlib - #============================================================================= - - def django_to_passlib_name(self, django_name): - """ - Convert Django hasher / name to Passlib hasher name. - """ - return self.django_to_passlib(django_name).name - - def django_to_passlib(self, django_name, cached=True): - """ - Convert Django hasher / name to Passlib hasher / name. - If present, CryptContext will be checked instead of main registry. - - :param django_name: - Django hasher class or algorithm name. - "default" allowed if context provided. - - :raises ValueError: - if can't resolve hasher. - - :returns: - passlib hasher or name - """ - # check for django hasher - if hasattr(django_name, "algorithm"): - - # check for passlib adapter - if isinstance(django_name, _PasslibHasherWrapper): - return django_name.passlib_handler - - # resolve django hasher -> name - django_name = django_name.algorithm - - # check cache - if cached: - cache = self._passlib_hasher_cache - try: - return cache[django_name] - except KeyError: - pass - result = cache[django_name] = \ - self.django_to_passlib(django_name, cached=False) - return result - - # check if it's an obviously-wrapped name - if django_name.startswith(PASSLIB_WRAPPER_PREFIX): - passlib_name = django_name[len(PASSLIB_WRAPPER_PREFIX):] - return self._get_passlib_hasher(passlib_name) - - # resolve default - if django_name == "default": - context = self.context - if context is None: - raise TypeError("can't determine default scheme w/ context") - return context.handler() - - # special case: Django uses a separate hasher for "sha1$$digest" - # hashes (unsalted_sha1) and "sha1$salt$digest" (sha1); - # but passlib uses "django_salted_sha1" for both of these. - if django_name == "unsalted_sha1": - django_name = "sha1" - - # resolve name - # XXX: bother caching these lists / mapping? - # not needed in long-term due to cache above. - context = self.context - if context is None: - # check registry - # TODO: should make iteration via registry easier - candidates = ( - registry.get_crypt_handler(passlib_name) - for passlib_name in registry.list_crypt_handlers() - if passlib_name.startswith(DJANGO_COMPAT_PREFIX) or - passlib_name in _other_django_hashes - ) - else: - # check context - candidates = context.schemes(resolve=True) - for handler in candidates: - if getattr(handler, "django_name", None) == django_name: - return handler - - # give up - # NOTE: this should only happen for custom django hashers that we don't - # know the equivalents for. _HasherHandler (below) is work in - # progress that would allow us to at least return a wrapper. - raise ValueError("can't translate django name to passlib name: %r" % - (django_name,)) - - #============================================================================= - # django hasher lookup - #============================================================================= - - def resolve_django_hasher(self, django_name, cached=True): - """ - Take in a django algorithm name, return django hasher. - """ - # check for django hasher - if hasattr(django_name, "algorithm"): - return django_name - - # resolve to passlib hasher - passlib_hasher = self.django_to_passlib(django_name, cached=cached) - - # special case: Django uses a separate hasher for "sha1$$digest" - # hashes (unsalted_sha1) and "sha1$salt$digest" (sha1); - # but passlib uses "django_salted_sha1" for both of these. - # XXX: this isn't ideal way to handle this. would like to do something - # like pass "django_variant=django_name" into passlib_to_django(), - # and have it cache separate hasher there. - # but that creates a LOT of complication in it's cache structure, - # for what is just one special case. - if django_name == "unsalted_sha1" and passlib_hasher.name == "django_salted_sha1": - if not cached: - return self._create_django_hasher(django_name) - result = self._django_unsalted_sha1 - if result is None: - result = self._django_unsalted_sha1 = self._create_django_hasher(django_name) - return result - - # lookup corresponding django hasher - return self.passlib_to_django(passlib_hasher, cached=cached) - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# adapter -#============================================================================= -class DjangoContextAdapter(DjangoTranslator): - """ - Object which tries to adapt a Passlib CryptContext object, - using a Django-hasher compatible API. - - When installed in django, :mod:`!passlib.ext.django` will create - an instance of this class, and then monkeypatch the appropriate - methods into :mod:`!django.contrib.auth` and other appropriate places. - """ - #============================================================================= - # instance attrs - #============================================================================= - - #: CryptContext instance we're wrapping - context = None - - #: ref to original make_password(), - #: needed to generate usuable passwords that match django - _orig_make_password = None - - #: ref to django helper of this name -- not monkeypatched - is_password_usable = None - - #: PatchManager instance used to track installation - _manager = None - - #: whether config=disabled flag was set - enabled = True - - #: patch status - patched = False - - #============================================================================= - # init - #============================================================================= - def __init__(self, context=None, get_user_category=None, **kwds): - - # init log - self.log = logging.getLogger(__name__ + ".DjangoContextAdapter") - - # init parent, filling in default context object - if context is None: - context = CryptContext() - super(DjangoContextAdapter, self).__init__(context=context, **kwds) - - # setup user category - if get_user_category: - assert callable(get_user_category) - self.get_user_category = get_user_category - - # install lru cache wrappers - try: - from functools import lru_cache # new py32 - except ImportError: - from django.utils.lru_cache import lru_cache # py2 compat, removed in django 3 (or earlier?) - self.get_hashers = lru_cache()(self.get_hashers) - - # get copy of original make_password - from django.contrib.auth.hashers import make_password - if make_password.__module__.startswith("passlib."): - make_password = _PatchManager.peek_unpatched_func(make_password) - self._orig_make_password = make_password - - # get other django helpers - from django.contrib.auth.hashers import is_password_usable - self.is_password_usable = is_password_usable - - # init manager - mlog = logging.getLogger(__name__ + ".DjangoContextAdapter._manager") - self._manager = _PatchManager(log=mlog) - - def reset_hashers(self): - """ - Wrapper to manually reset django's hasher lookup cache - """ - # resets cache for .get_hashers() & .get_hashers_by_algorithm() - from django.contrib.auth.hashers import reset_hashers - reset_hashers(setting="PASSWORD_HASHERS") - - # reset internal caches - super(DjangoContextAdapter, self).reset_hashers() - - #============================================================================= - # django hashers helpers -- hasher lookup - #============================================================================= - - # lru_cache()'ed by init - def get_hashers(self): - """ - Passlib replacement for get_hashers() -- - Return list of available django hasher classes - """ - passlib_to_django = self.passlib_to_django - return [passlib_to_django(hasher) - for hasher in self.context.schemes(resolve=True)] - - def get_hasher(self, algorithm="default"): - """ - Passlib replacement for get_hasher() -- - Return django hasher by name - """ - return self.resolve_django_hasher(algorithm) - - def identify_hasher(self, encoded): - """ - Passlib replacement for identify_hasher() -- - Identify django hasher based on hash. - """ - handler = self.context.identify(encoded, resolve=True, required=True) - if handler.name == "django_salted_sha1" and encoded.startswith("sha1$$"): - # Django uses a separate hasher for "sha1$$digest" hashes, but - # passlib identifies it as belonging to "sha1$salt$digest" handler. - # We want to resolve to correct django hasher. - return self.get_hasher("unsalted_sha1") - return self.passlib_to_django(handler) - - #============================================================================= - # django.contrib.auth.hashers helpers -- password helpers - #============================================================================= - - def make_password(self, password, salt=None, hasher="default"): - """ - Passlib replacement for make_password() - """ - if password is None: - return self._orig_make_password(None) - # NOTE: relying on hasher coming from context, and thus having - # context-specific config baked into it. - passlib_hasher = self.django_to_passlib(hasher) - if "salt" not in passlib_hasher.setting_kwds: - # ignore salt param even if preset - pass - elif hasher.startswith("unsalted_"): - # Django uses a separate 'unsalted_sha1' hasher for "sha1$$digest", - # but passlib just reuses it's "sha1" handler ("sha1$salt$digest"). To make - # this work, have to explicitly tell the sha1 handler to use an empty salt. - passlib_hasher = passlib_hasher.using(salt="") - elif salt: - # Django make_password() autogenerates a salt if salt is bool False (None / ''), - # so we only pass the keyword on if there's actually a fixed salt. - passlib_hasher = passlib_hasher.using(salt=salt) - return passlib_hasher.hash(password) - - def check_password(self, password, encoded, setter=None, preferred="default"): - """ - Passlib replacement for check_password() - """ - # XXX: this currently ignores "preferred" keyword, since its purpose - # was for hash migration, and that's handled by the context. - # XXX: honor "none_causes_check_password_error" quirk for django 2.2+? - # seems safer to return False. - if password is None or not self.is_password_usable(encoded): - return False - - # verify password - context = self.context - try: - correct = context.verify(password, encoded) - except exc.UnknownHashError: - # As of django 1.5, unidentifiable hashes returns False - # (side-effect of django issue 18453) - return False - - if not (correct and setter): - return correct - - # check if we need to rehash - if preferred == "default": - if not context.needs_update(encoded, secret=password): - return correct - else: - # Django's check_password() won't call setter() on a - # 'preferred' alg, even if it's otherwise deprecated. To try and - # replicate this behavior if preferred is set, we look up the - # passlib hasher, and call it's original needs_update() method. - # TODO: Solve redundancy that verify() call - # above is already identifying hash. - hasher = self.django_to_passlib(preferred) - if (hasher.identify(encoded) and - not hasher.needs_update(encoded, secret=password)): - # alg is 'preferred' and hash itself doesn't need updating, - # so nothing to do. - return correct - # else: either hash isn't preferred, or it needs updating. - - # call setter to rehash - setter(password) - return correct - - #============================================================================= - # django users helpers - #============================================================================= - - def user_check_password(self, user, password): - """ - Passlib replacement for User.check_password() - """ - if password is None: - return False - hash = user.password - if not self.is_password_usable(hash): - return False - cat = self.get_user_category(user) - try: - ok, new_hash = self.context.verify_and_update(password, hash, category=cat) - except exc.UnknownHashError: - # As of django 1.5, unidentifiable hashes returns False - # (side-effect of django issue 18453) - return False - if ok and new_hash is not None: - # migrate to new hash if needed. - user.password = new_hash - user.save() - return ok - - def user_set_password(self, user, password): - """ - Passlib replacement for User.set_password() - """ - if password is None: - user.set_unusable_password() - else: - cat = self.get_user_category(user) - user.password = self.context.hash(password, category=cat) - - def get_user_category(self, user): - """ - Helper for hashing passwords per-user -- - figure out the CryptContext category for specified Django user object. - .. note:: - This may be overridden via PASSLIB_GET_CATEGORY django setting - """ - if user.is_superuser: - return "superuser" - elif user.is_staff: - return "staff" - else: - return None - - #============================================================================= - # patch control - #============================================================================= - - HASHERS_PATH = "django.contrib.auth.hashers" - MODELS_PATH = "django.contrib.auth.models" - USER_CLASS_PATH = MODELS_PATH + ":User" - FORMS_PATH = "django.contrib.auth.forms" - - #: list of locations to patch - patch_locations = [ - # - # User object - # NOTE: could leave defaults alone, but want to have user available - # so that we can support get_user_category() - # - (USER_CLASS_PATH + ".check_password", "user_check_password", dict(method=True)), - (USER_CLASS_PATH + ".set_password", "user_set_password", dict(method=True)), - - # - # Hashers module - # - (HASHERS_PATH + ":", "check_password"), - (HASHERS_PATH + ":", "make_password"), - (HASHERS_PATH + ":", "get_hashers"), - (HASHERS_PATH + ":", "get_hasher"), - (HASHERS_PATH + ":", "identify_hasher"), - - # - # Patch known imports from hashers module - # - (MODELS_PATH + ":", "check_password"), - (MODELS_PATH + ":", "make_password"), - (FORMS_PATH + ":", "get_hasher"), - (FORMS_PATH + ":", "identify_hasher"), - - ] - - def install_patch(self): - """ - Install monkeypatch to replace django hasher framework. - """ - # don't reapply - log = self.log - if self.patched: - log.warning("monkeypatching already applied, refusing to reapply") - return False - - # version check - if DJANGO_VERSION < MIN_DJANGO_VERSION: - raise RuntimeError("passlib.ext.django requires django >= %s" % - (MIN_DJANGO_VERSION,)) - - # log start - log.debug("preparing to monkeypatch django ...") - - # run through patch locations - manager = self._manager - for record in self.patch_locations: - if len(record) == 2: - record += ({},) - target, source, opts = record - if target.endswith((":", ",")): - target += source - value = getattr(self, source) - if opts.get("method"): - # have to wrap our method in a function, - # since we're installing it in a class *as* a method - # XXX: make this a flag for .patch()? - value = _wrap_method(value) - manager.patch(target, value) - - # reset django's caches (e.g. get_hash_by_algorithm) - self.reset_hashers() - - # done! - self.patched = True - log.debug("... finished monkeypatching django") - return True - - def remove_patch(self): - """ - Remove monkeypatch from django hasher framework. - As precaution in case there are lingering refs to context, - context object will be wiped. - - .. warning:: - This may cause problems if any other Django modules have imported - their own copies of the patched functions, though the patched - code has been designed to throw an error as soon as possible in - this case. - """ - log = self.log - manager = self._manager - - if self.patched: - log.debug("removing django monkeypatching...") - manager.unpatch_all(unpatch_conflicts=True) - self.context.load({}) - self.patched = False - self.reset_hashers() - log.debug("...finished removing django monkeypatching") - return True - - if manager.isactive(): # pragma: no cover -- sanity check - log.warning("reverting partial monkeypatching of django...") - manager.unpatch_all() - self.context.load({}) - self.reset_hashers() - log.debug("...finished removing django monkeypatching") - return True - - log.debug("django not monkeypatched") - return False - - #============================================================================= - # loading config - #============================================================================= - - def load_model(self): - """ - Load configuration from django, and install patch. - """ - self._load_settings() - if self.enabled: - try: - self.install_patch() - except: - # try to undo what we can - self.remove_patch() - raise - else: - if self.patched: # pragma: no cover -- sanity check - log.error("didn't expect monkeypatching would be applied!") - self.remove_patch() - log.debug("passlib.ext.django loaded") - - def _load_settings(self): - """ - Update settings from django - """ - from django.conf import settings - - # TODO: would like to add support for inheriting config from a preset - # (or from existing hasher state) and letting PASSLIB_CONFIG - # be an update, not a replacement. - - # TODO: wrap and import any custom hashers as passlib handlers, - # so they could be used in the passlib config. - - # load config from settings - _UNSET = object() - config = getattr(settings, "PASSLIB_CONFIG", _UNSET) - if config is _UNSET: - # XXX: should probably deprecate this alias - config = getattr(settings, "PASSLIB_CONTEXT", _UNSET) - if config is _UNSET: - config = "passlib-default" - if config is None: - warn("setting PASSLIB_CONFIG=None is deprecated, " - "and support will be removed in Passlib 1.8, " - "use PASSLIB_CONFIG='disabled' instead.", - DeprecationWarning) - config = "disabled" - elif not isinstance(config, (unicode, bytes, dict)): - raise exc.ExpectedTypeError(config, "str or dict", "PASSLIB_CONFIG") - - # load custom category func (if any) - get_category = getattr(settings, "PASSLIB_GET_CATEGORY", None) - if get_category and not callable(get_category): - raise exc.ExpectedTypeError(get_category, "callable", "PASSLIB_GET_CATEGORY") - - # check if we've been disabled - if config == "disabled": - self.enabled = False - return - else: - self.__dict__.pop("enabled", None) - - # resolve any preset aliases - if isinstance(config, str) and '\n' not in config: - config = get_preset_config(config) - - # setup category func - if get_category: - self.get_user_category = get_category - else: - self.__dict__.pop("get_category", None) - - # setup context - self.context.load(config) - self.reset_hashers() - - #============================================================================= - # eof - #============================================================================= - -#============================================================================= -# wrapping passlib handlers as django hashers -#============================================================================= -_GEN_SALT_SIGNAL = "--!!!generate-new-salt!!!--" - -class ProxyProperty(object): - """helper that proxies another attribute""" - - def __init__(self, attr): - self.attr = attr - - def __get__(self, obj, cls): - if obj is None: - cls = obj - return getattr(obj, self.attr) - - def __set__(self, obj, value): - setattr(obj, self.attr, value) - - def __delete__(self, obj): - delattr(obj, self.attr) - - -class _PasslibHasherWrapper(object): - """ - adapter which which wraps a :cls:`passlib.ifc.PasswordHash` class, - and provides an interface compatible with the Django hasher API. - - :param passlib_handler: - passlib hash handler (e.g. :cls:`passlib.hash.sha256_crypt`. - """ - #===================================================================== - # instance attrs - #===================================================================== - - #: passlib handler that we're adapting. - passlib_handler = None - - # NOTE: 'rounds' attr will store variable rounds, IF handler supports it. - # 'iterations' will act as proxy, for compatibility with django pbkdf2 hashers. - # rounds = None - # iterations = None - - #===================================================================== - # init - #===================================================================== - def __init__(self, passlib_handler): - # init handler - if getattr(passlib_handler, "django_name", None): - raise ValueError("handlers that reflect an official django " - "hasher shouldn't be wrapped: %r" % - (passlib_handler.name,)) - if passlib_handler.is_disabled: - # XXX: could this be implemented? - raise ValueError("can't wrap disabled-hash handlers: %r" % - (passlib_handler.name)) - self.passlib_handler = passlib_handler - - # init rounds support - if self._has_rounds: - self.rounds = passlib_handler.default_rounds - self.iterations = ProxyProperty("rounds") - - #===================================================================== - # internal methods - #===================================================================== - def __repr__(self): - return "" % self.passlib_handler - - #===================================================================== - # internal properties - #===================================================================== - - @memoized_property - def __name__(self): - return "Passlib_%s_PasswordHasher" % self.passlib_handler.name.title() - - @memoized_property - def _has_rounds(self): - return "rounds" in self.passlib_handler.setting_kwds - - @memoized_property - def _translate_kwds(self): - """ - internal helper for safe_summary() -- - used to translate passlib hash options -> django keywords - """ - out = dict(checksum="hash") - if self._has_rounds and "pbkdf2" in self.passlib_handler.name: - out['rounds'] = 'iterations' - return out - - #===================================================================== - # hasher properties - #===================================================================== - - @memoized_property - def algorithm(self): - return PASSLIB_WRAPPER_PREFIX + self.passlib_handler.name - - #===================================================================== - # hasher api - #===================================================================== - def salt(self): - # NOTE: passlib's handler.hash() should generate new salt each time, - # so this just returns a special constant which tells - # encode() (below) not to pass a salt keyword along. - return _GEN_SALT_SIGNAL - - def verify(self, password, encoded): - return self.passlib_handler.verify(password, encoded) - - def encode(self, password, salt=None, rounds=None, iterations=None): - kwds = {} - if salt is not None and salt != _GEN_SALT_SIGNAL: - kwds['salt'] = salt - if self._has_rounds: - if rounds is not None: - kwds['rounds'] = rounds - elif iterations is not None: - kwds['rounds'] = iterations - else: - kwds['rounds'] = self.rounds - elif rounds is not None or iterations is not None: - warn("%s.hash(): 'rounds' and 'iterations' are ignored" % self.__name__) - handler = self.passlib_handler - if kwds: - handler = handler.using(**kwds) - return handler.hash(password) - - def safe_summary(self, encoded): - from django.contrib.auth.hashers import mask_hash - from django.utils.translation import ugettext_noop as _ - handler = self.passlib_handler - items = [ - # since this is user-facing, we're reporting passlib's name, - # without the distracting PASSLIB_HASHER_PREFIX prepended. - (_('algorithm'), handler.name), - ] - if hasattr(handler, "parsehash"): - kwds = handler.parsehash(encoded, sanitize=mask_hash) - for key, value in iteritems(kwds): - key = self._translate_kwds.get(key, key) - items.append((_(key), value)) - return OrderedDict(items) - - def must_update(self, encoded): - # TODO: would like access CryptContext, would need caller to pass it to get_passlib_hasher(). - # for now (as of passlib 1.6.6), replicating django policy that this returns True - # if 'encoded' hash has different rounds value from self.rounds - if self._has_rounds: - # XXX: could cache this subclass somehow (would have to intercept writes to self.rounds) - # TODO: always call subcls/handler.needs_update() in case there's other things to check - subcls = self.passlib_handler.using(min_rounds=self.rounds, max_rounds=self.rounds) - if subcls.needs_update(encoded): - return True - return False - - #===================================================================== - # eoc - #===================================================================== - -#============================================================================= -# adapting django hashers -> passlib handlers -#============================================================================= -# TODO: this code probably halfway works, mainly just needs -# a routine to read HASHERS and PREFERRED_HASHER. - -##from passlib.registry import register_crypt_handler -##from passlib.utils import classproperty, to_native_str, to_unicode -##from passlib.utils.compat import unicode -## -## -##class _HasherHandler(object): -## "helper for wrapping Hasher instances as passlib handlers" -## # FIXME: this generic wrapper doesn't handle custom settings -## # FIXME: genconfig / genhash not supported. -## -## def __init__(self, hasher): -## self.django_hasher = hasher -## if hasattr(hasher, "iterations"): -## # assume encode() accepts an "iterations" parameter. -## # fake min/max rounds -## self.min_rounds = 1 -## self.max_rounds = 0xFFFFffff -## self.default_rounds = self.django_hasher.iterations -## self.setting_kwds += ("rounds",) -## -## # hasher instance - filled in by constructor -## django_hasher = None -## -## setting_kwds = ("salt",) -## context_kwds = () -## -## @property -## def name(self): -## # XXX: need to make sure this wont' collide w/ builtin django hashes. -## # maybe by renaming this to django compatible aliases? -## return DJANGO_PASSLIB_PREFIX + self.django_name -## -## @property -## def django_name(self): -## # expose this so hasher_to_passlib_name() extracts original name -## return self.django_hasher.algorithm -## -## @property -## def ident(self): -## # this should always be correct, as django relies on ident prefix. -## return unicode(self.django_name + "$") -## -## @property -## def identify(self, hash): -## # this should always work, as django relies on ident prefix. -## return to_unicode(hash, "latin-1", "hash").startswith(self.ident) -## -## @property -## def hash(self, secret, salt=None, **kwds): -## # NOTE: from how make_password() is coded, all hashers -## # should have salt param. but only some will have -## # 'iterations' parameter. -## opts = {} -## if 'rounds' in self.setting_kwds and 'rounds' in kwds: -## opts['iterations'] = kwds.pop("rounds") -## if kwds: -## raise TypeError("unexpected keyword arguments: %r" % list(kwds)) -## if isinstance(secret, unicode): -## secret = secret.encode("utf-8") -## if salt is None: -## salt = self.django_hasher.salt() -## return to_native_str(self.django_hasher(secret, salt, **opts)) -## -## @property -## def verify(self, secret, hash): -## hash = to_native_str(hash, "utf-8", "hash") -## if isinstance(secret, unicode): -## secret = secret.encode("utf-8") -## return self.django_hasher.verify(secret, hash) -## -##def register_hasher(hasher): -## handler = _HasherHandler(hasher) -## register_crypt_handler(handler) -## return handler - -#============================================================================= -# monkeypatch helpers -#============================================================================= -# private singleton indicating lack-of-value -_UNSET = object() - -class _PatchManager(object): - """helper to manage monkeypatches and run sanity checks""" - - # NOTE: this could easily use a dict interface, - # but keeping it distinct to make clear that it's not a dict, - # since it has important side-effects. - - #=================================================================== - # init and support - #=================================================================== - def __init__(self, log=None): - # map of key -> (original value, patched value) - # original value may be _UNSET - self.log = log or logging.getLogger(__name__ + "._PatchManager") - self._state = {} - - def isactive(self): - return bool(self._state) - - # bool value tests if any patches are currently applied. - # NOTE: this behavior is deprecated in favor of .isactive - __bool__ = __nonzero__ = isactive - - def _import_path(self, path): - """retrieve obj and final attribute name from resource path""" - name, attr = path.split(":") - obj = __import__(name, fromlist=[attr], level=0) - while '.' in attr: - head, attr = attr.split(".", 1) - obj = getattr(obj, head) - return obj, attr - - @staticmethod - def _is_same_value(left, right): - """check if two values are the same (stripping method wrappers, etc)""" - return get_method_function(left) == get_method_function(right) - - #=================================================================== - # reading - #=================================================================== - def _get_path(self, key, default=_UNSET): - obj, attr = self._import_path(key) - return getattr(obj, attr, default) - - def get(self, path, default=None): - """return current value for path""" - return self._get_path(path, default) - - def getorig(self, path, default=None): - """return original (unpatched) value for path""" - try: - value, _= self._state[path] - except KeyError: - value = self._get_path(path) - return default if value is _UNSET else value - - def check_all(self, strict=False): - """run sanity check on all keys, issue warning if out of sync""" - same = self._is_same_value - for path, (orig, expected) in iteritems(self._state): - if same(self._get_path(path), expected): - continue - msg = "another library has patched resource: %r" % path - if strict: - raise RuntimeError(msg) - else: - warn(msg, PasslibRuntimeWarning) - - #=================================================================== - # patching - #=================================================================== - def _set_path(self, path, value): - obj, attr = self._import_path(path) - if value is _UNSET: - if hasattr(obj, attr): - delattr(obj, attr) - else: - setattr(obj, attr, value) - - def patch(self, path, value, wrap=False): - """monkeypatch object+attr at to have , stores original""" - assert value != _UNSET - current = self._get_path(path) - try: - orig, expected = self._state[path] - except KeyError: - self.log.debug("patching resource: %r", path) - orig = current - else: - self.log.debug("modifying resource: %r", path) - if not self._is_same_value(current, expected): - warn("overridding resource another library has patched: %r" - % path, PasslibRuntimeWarning) - if wrap: - assert callable(value) - wrapped = orig - wrapped_by = value - def wrapper(*args, **kwds): - return wrapped_by(wrapped, *args, **kwds) - update_wrapper(wrapper, value) - value = wrapper - if callable(value): - # needed by DjangoContextAdapter init - get_method_function(value)._patched_original_value = orig - self._set_path(path, value) - self._state[path] = (orig, value) - - @classmethod - def peek_unpatched_func(cls, value): - return value._patched_original_value - - ##def patch_many(self, **kwds): - ## "override specified resources with new values" - ## for path, value in iteritems(kwds): - ## self.patch(path, value) - - def monkeypatch(self, parent, name=None, enable=True, wrap=False): - """function decorator which patches function of same name in """ - def builder(func): - if enable: - sep = "." if ":" in parent else ":" - path = parent + sep + (name or func.__name__) - self.patch(path, func, wrap=wrap) - return func - if callable(name): - # called in non-decorator mode - func = name - name = None - builder(func) - return None - return builder - - #=================================================================== - # unpatching - #=================================================================== - def unpatch(self, path, unpatch_conflicts=True): - try: - orig, expected = self._state[path] - except KeyError: - return - current = self._get_path(path) - self.log.debug("unpatching resource: %r", path) - if not self._is_same_value(current, expected): - if unpatch_conflicts: - warn("reverting resource another library has patched: %r" - % path, PasslibRuntimeWarning) - else: - warn("not reverting resource another library has patched: %r" - % path, PasslibRuntimeWarning) - del self._state[path] - return - self._set_path(path, orig) - del self._state[path] - - def unpatch_all(self, **kwds): - for key in list(self._state): - self.unpatch(key, **kwds) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/__init__.py b/.venv/Lib/site-packages/passlib/handlers/__init__.py deleted file mode 100644 index 0a0338c..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""passlib.handlers -- holds implementations of all passlib's builtin hash formats""" diff --git a/.venv/Lib/site-packages/passlib/handlers/argon2.py b/.venv/Lib/site-packages/passlib/handlers/argon2.py deleted file mode 100644 index 4a5691b..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/argon2.py +++ /dev/null @@ -1,1009 +0,0 @@ -"""passlib.handlers.argon2 -- argon2 password hash wrapper - -References -========== -* argon2 - - home: https://github.com/P-H-C/phc-winner-argon2 - - whitepaper: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf -* argon2 cffi wrapper - - pypi: https://pypi.python.org/pypi/argon2_cffi - - home: https://github.com/hynek/argon2_cffi -* argon2 pure python - - pypi: https://pypi.python.org/pypi/argon2pure - - home: https://github.com/bwesterb/argon2pure -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, absolute_import -# core -import logging -log = logging.getLogger(__name__) -import re -import types -from warnings import warn -# site -_argon2_cffi = None # loaded below -_argon2pure = None # dynamically imported by _load_backend_argon2pure() -# pkg -from passlib import exc -from passlib.crypto.digest import MAX_UINT32 -from passlib.utils import classproperty, to_bytes, render_bytes -from passlib.utils.binary import b64s_encode, b64s_decode -from passlib.utils.compat import u, unicode, bascii_to_str, uascii_to_str, PY2 -import passlib.utils.handlers as uh -# local -__all__ = [ - "argon2", -] - -#============================================================================= -# helpers -#============================================================================= - -# NOTE: when adding a new argon2 hash type, need to do the following: -# * add TYPE_XXX constant, and add to ALL_TYPES -# * make sure "_backend_type_map" constructors handle it correctly for all backends -# * make sure _hash_regex & _ident_regex (below) support type string. -# * add reference vectors for testing. - -#: argon2 type constants -- subclasses handle mapping these to backend-specific type constants. -#: (should be lowercase, to match representation in hash string) -TYPE_I = u("i") -TYPE_D = u("d") -TYPE_ID = u("id") # new 2016-10-29; passlib 1.7.2 requires backends new enough for support - -#: list of all known types; first (supported) type will be used as default. -ALL_TYPES = (TYPE_ID, TYPE_I, TYPE_D) -ALL_TYPES_SET = set(ALL_TYPES) - -#============================================================================= -# import argon2 package (https://pypi.python.org/pypi/argon2_cffi) -#============================================================================= - -# import cffi package -# NOTE: we try to do this even if caller is going to use argon2pure, -# so that we can always use the libargon2 default settings when possible. -_argon2_cffi_error = None -try: - import argon2 as _argon2_cffi -except ImportError: - _argon2_cffi = None -else: - if not hasattr(_argon2_cffi, "Type"): - # they have incompatible "argon2" package installed, instead of "argon2_cffi" package. - _argon2_cffi_error = ( - "'argon2' module points to unsupported 'argon2' pypi package; " - "please install 'argon2-cffi' instead." - ) - _argon2_cffi = None - elif not hasattr(_argon2_cffi, "low_level"): - # they have pre-v16 argon2_cffi package - _argon2_cffi_error = "'argon2-cffi' is too old, please update to argon2_cffi >= 18.2.0" - _argon2_cffi = None - -# init default settings for our hasher class -- -# if we have argon2_cffi >= 16.0, use their default hasher settings, otherwise use static default -if hasattr(_argon2_cffi, "PasswordHasher"): - # use cffi's default settings - _default_settings = _argon2_cffi.PasswordHasher() - _default_version = _argon2_cffi.low_level.ARGON2_VERSION -else: - # use fallback settings (for no backend, or argon2pure) - class _DummyCffiHasher: - """ - dummy object to use as source of defaults when argon2_cffi isn't present. - this tries to mimic the attributes of ``argon2.PasswordHasher()`` which the rest of - this module reads. - - .. note:: values last synced w/ argon2 19.2 as of 2019-11-09 - """ - time_cost = 2 - memory_cost = 512 - parallelism = 2 - salt_len = 16 - hash_len = 16 - # NOTE: "type" attribute added in argon2_cffi v18.2; but currently not reading it - # type = _argon2_cffi.Type.ID - - _default_settings = _DummyCffiHasher() - _default_version = 0x13 # v1.9 - -#============================================================================= -# handler -#============================================================================= -class _Argon2Common(uh.SubclassBackendMixin, uh.ParallelismMixin, - uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, - uh.GenericHandler): - """ - Base class which implements brunt of Argon2 code. - This is then subclassed by the various backends, - to override w/ backend-specific methods. - - When a backend is loaded, the bases of the 'argon2' class proper - are modified to prepend the correct backend-specific subclass. - """ - #=================================================================== - # class attrs - #=================================================================== - - #------------------------ - # PasswordHash - #------------------------ - - name = "argon2" - setting_kwds = ("salt", - "salt_size", - "salt_len", # 'salt_size' alias for compat w/ argon2 package - "rounds", - "time_cost", # 'rounds' alias for compat w/ argon2 package - "memory_cost", - "parallelism", - "digest_size", - "hash_len", # 'digest_size' alias for compat w/ argon2 package - "type", # the type of argon2 hash used - ) - - # TODO: could support the optional 'data' parameter, - # but need to research the uses, what a more descriptive name would be, - # and deal w/ fact that argon2_cffi 16.1 doesn't currently support it. - # (argon2_pure does though) - - #------------------------ - # GenericHandler - #------------------------ - - # NOTE: ident -- all argon2 hashes start with "$argon2$" - # XXX: could programmaticaly generate "ident_values" string from ALL_TYPES above - - checksum_size = _default_settings.hash_len - - #: force parsing these kwds - _always_parse_settings = uh.GenericHandler._always_parse_settings + \ - ("type",) - - #: exclude these kwds from parsehash() result (most are aliases for other keys) - _unparsed_settings = uh.GenericHandler._unparsed_settings + \ - ("salt_len", "time_cost", "hash_len", "digest_size") - - #------------------------ - # HasSalt - #------------------------ - default_salt_size = _default_settings.salt_len - min_salt_size = 8 - max_salt_size = MAX_UINT32 - - #------------------------ - # HasRounds - # TODO: once rounds limit logic is factored out, - # make 'rounds' and 'cost' an alias for 'time_cost' - #------------------------ - default_rounds = _default_settings.time_cost - min_rounds = 1 - max_rounds = MAX_UINT32 - rounds_cost = "linear" - - #------------------------ - # ParalleismMixin - #------------------------ - max_parallelism = (1 << 24) - 1 # from argon2.h / ARGON2_MAX_LANES - - #------------------------ - # custom - #------------------------ - - #: max version support - #: NOTE: this is dependant on the backend, and initialized/modified by set_backend() - max_version = _default_version - - #: minimum version before needs_update() marks the hash; if None, defaults to max_version - min_desired_version = None - - #: minimum valid memory_cost - min_memory_cost = 8 # from argon2.h / ARGON2_MIN_MEMORY - - #: maximum number of threads (-1=unlimited); - #: number of threads used by .hash() will be min(parallelism, max_threads) - max_threads = -1 - - #: global flag signalling argon2pure backend to use threads - #: rather than subprocesses. - pure_use_threads = False - - #: internal helper used to store mapping of TYPE_XXX constants -> backend-specific type constants; - #: this is populated by _load_backend_mixin(); and used to detect which types are supported. - #: XXX: could expose keys as class-level .supported_types property? - _backend_type_map = {} - - @classproperty - def type_values(cls): - """ - return tuple of types supported by this backend - - .. versionadded:: 1.7.2 - """ - cls.get_backend() # make sure backend is loaded - return tuple(cls._backend_type_map) - - #=================================================================== - # instance attrs - #=================================================================== - - #: argon2 hash type, one of ALL_TYPES -- class value controls the default - #: .. versionadded:: 1.7.2 - type = TYPE_ID - - #: parallelism setting -- class value controls the default - parallelism = _default_settings.parallelism - - #: hash version (int) - #: NOTE: this is modified by set_backend() - version = _default_version - - #: memory cost -- class value controls the default - memory_cost = _default_settings.memory_cost - - @property - def type_d(self): - """ - flag indicating a Type D hash - - .. deprecated:: 1.7.2; will be removed in passlib 2.0 - """ - return self.type == TYPE_D - - #: optional secret data - data = None - - #=================================================================== - # variant constructor - #=================================================================== - - @classmethod - def using(cls, type=None, memory_cost=None, salt_len=None, time_cost=None, digest_size=None, - checksum_size=None, hash_len=None, max_threads=None, **kwds): - # support aliases which match argon2 naming convention - if time_cost is not None: - if "rounds" in kwds: - raise TypeError("'time_cost' and 'rounds' are mutually exclusive") - kwds['rounds'] = time_cost - - if salt_len is not None: - if "salt_size" in kwds: - raise TypeError("'salt_len' and 'salt_size' are mutually exclusive") - kwds['salt_size'] = salt_len - - if hash_len is not None: - if digest_size is not None: - raise TypeError("'hash_len' and 'digest_size' are mutually exclusive") - digest_size = hash_len - - if checksum_size is not None: - if digest_size is not None: - raise TypeError("'checksum_size' and 'digest_size' are mutually exclusive") - digest_size = checksum_size - - # create variant - subcls = super(_Argon2Common, cls).using(**kwds) - - # set type - if type is not None: - subcls.type = subcls._norm_type(type) - - # set checksum size - relaxed = kwds.get("relaxed") - if digest_size is not None: - if isinstance(digest_size, uh.native_string_types): - digest_size = int(digest_size) - # NOTE: this isn't *really* digest size minimum, but want to enforce secure minimum. - subcls.checksum_size = uh.norm_integer(subcls, digest_size, min=16, max=MAX_UINT32, - param="digest_size", relaxed=relaxed) - - # set memory cost - if memory_cost is not None: - if isinstance(memory_cost, uh.native_string_types): - memory_cost = int(memory_cost) - subcls.memory_cost = subcls._norm_memory_cost(memory_cost, relaxed=relaxed) - - # validate constraints - subcls._validate_constraints(subcls.memory_cost, subcls.parallelism) - - # set max threads - if max_threads is not None: - if isinstance(max_threads, uh.native_string_types): - max_threads = int(max_threads) - if max_threads < 1 and max_threads != -1: - raise ValueError("max_threads (%d) must be -1 (unlimited), or at least 1." % - (max_threads,)) - subcls.max_threads = max_threads - - return subcls - - @classmethod - def _validate_constraints(cls, memory_cost, parallelism): - # NOTE: this is used by class & instance, hence passing in via arguments. - # could switch and make this a hybrid method. - min_memory_cost = 8 * parallelism - if memory_cost < min_memory_cost: - raise ValueError("%s: memory_cost (%d) is too low, must be at least " - "8 * parallelism (8 * %d = %d)" % - (cls.name, memory_cost, - parallelism, min_memory_cost)) - - #=================================================================== - # public api - #=================================================================== - - #: shorter version of _hash_regex, used to quickly identify hashes - _ident_regex = re.compile(r"^\$argon2[a-z]+\$") - - @classmethod - def identify(cls, hash): - hash = uh.to_unicode_for_identify(hash) - return cls._ident_regex.match(hash) is not None - - # hash(), verify(), genhash() -- implemented by backend subclass - - #=================================================================== - # hash parsing / rendering - #=================================================================== - - # info taken from source of decode_string() function in - # - # - # hash format: - # $argon2[$v=]$m=,t=,p=[,keyid=][,data=][$[$]] - # - # NOTE: as of 2016-6-17, the official source (above) lists the "keyid" param in the comments, - # but the actual source of decode_string & encode_string don't mention it at all. - # we're supporting parsing it, but throw NotImplementedError if encountered. - # - # sample hashes: - # v1.0: '$argon2i$m=512,t=2,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ' - # v1.3: '$argon2i$v=19$m=512,t=2,p=2$5VtWOO3cGWYQHEMaYGbsfQ$AcmqasQgW/wI6wAHAMk4aQ' - - #: regex to parse argon hash - _hash_regex = re.compile(br""" - ^ - \$argon2(?P[a-z]+)\$ - (?: - v=(?P\d+) - \$ - )? - m=(?P\d+) - , - t=(?P\d+) - , - p=(?P\d+) - (?: - ,keyid=(?P[^,$]+) - )? - (?: - ,data=(?P[^,$]+) - )? - (?: - \$ - (?P[^$]+) - (?: - \$ - (?P.+) - )? - )? - $ - """, re.X) - - @classmethod - def from_string(cls, hash): - # NOTE: assuming hash will be unicode, or use ascii-compatible encoding. - # TODO: switch to working w/ str or unicode - if isinstance(hash, unicode): - hash = hash.encode("utf-8") - if not isinstance(hash, bytes): - raise exc.ExpectedStringError(hash, "hash") - m = cls._hash_regex.match(hash) - if not m: - raise exc.MalformedHashError(cls) - type, version, memory_cost, time_cost, parallelism, keyid, data, salt, digest = \ - m.group("type", "version", "memory_cost", "time_cost", "parallelism", - "keyid", "data", "salt", "digest") - if keyid: - raise NotImplementedError("argon2 'keyid' parameter not supported") - return cls( - type=type.decode("ascii"), - version=int(version) if version else 0x10, - memory_cost=int(memory_cost), - rounds=int(time_cost), - parallelism=int(parallelism), - salt=b64s_decode(salt) if salt else None, - data=b64s_decode(data) if data else None, - checksum=b64s_decode(digest) if digest else None, - ) - - def to_string(self): - version = self.version - if version == 0x10: - vstr = "" - else: - vstr = "v=%d$" % version - - data = self.data - if data: - kdstr = ",data=" + bascii_to_str(b64s_encode(self.data)) - else: - kdstr = "" - - # NOTE: 'keyid' param currently not supported - return "$argon2%s$%sm=%d,t=%d,p=%d%s$%s$%s" % ( - uascii_to_str(self.type), - vstr, - self.memory_cost, - self.rounds, - self.parallelism, - kdstr, - bascii_to_str(b64s_encode(self.salt)), - bascii_to_str(b64s_encode(self.checksum)), - ) - - #=================================================================== - # init - #=================================================================== - def __init__(self, type=None, type_d=False, version=None, memory_cost=None, data=None, **kwds): - - # handle deprecated kwds - if type_d: - warn('argon2 `type_d=True` keyword is deprecated, and will be removed in passlib 2.0; ' - 'please use ``type="d"`` instead') - assert type is None - type = TYPE_D - - # TODO: factor out variable checksum size support into a mixin. - # set checksum size to specific value before _norm_checksum() is called - checksum = kwds.get("checksum") - if checksum is not None: - self.checksum_size = len(checksum) - - # call parent - super(_Argon2Common, self).__init__(**kwds) - - # init type - if type is None: - assert uh.validate_default_value(self, self.type, self._norm_type, param="type") - else: - self.type = self._norm_type(type) - - # init version - if version is None: - assert uh.validate_default_value(self, self.version, self._norm_version, - param="version") - else: - self.version = self._norm_version(version) - - # init memory cost - if memory_cost is None: - assert uh.validate_default_value(self, self.memory_cost, self._norm_memory_cost, - param="memory_cost") - else: - self.memory_cost = self._norm_memory_cost(memory_cost) - - # init data - if data is None: - assert self.data is None - else: - if not isinstance(data, bytes): - raise uh.exc.ExpectedTypeError(data, "bytes", "data") - self.data = data - - #------------------------------------------------------------------- - # parameter guards - #------------------------------------------------------------------- - - @classmethod - def _norm_type(cls, value): - # type check - if not isinstance(value, unicode): - if PY2 and isinstance(value, bytes): - value = value.decode('ascii') - else: - raise uh.exc.ExpectedTypeError(value, "str", "type") - - # check if type is valid - if value in ALL_TYPES_SET: - return value - - # translate from uppercase - temp = value.lower() - if temp in ALL_TYPES_SET: - return temp - - # failure! - raise ValueError("unknown argon2 hash type: %r" % (value,)) - - @classmethod - def _norm_version(cls, version): - if not isinstance(version, uh.int_types): - raise uh.exc.ExpectedTypeError(version, "integer", "version") - - # minimum valid version - if version < 0x13 and version != 0x10: - raise ValueError("invalid argon2 hash version: %d" % (version,)) - - # check this isn't past backend's max version - backend = cls.get_backend() - if version > cls.max_version: - raise ValueError("%s: hash version 0x%X not supported by %r backend " - "(max version is 0x%X); try updating or switching backends" % - (cls.name, version, backend, cls.max_version)) - return version - - @classmethod - def _norm_memory_cost(cls, memory_cost, relaxed=False): - return uh.norm_integer(cls, memory_cost, min=cls.min_memory_cost, - param="memory_cost", relaxed=relaxed) - - #=================================================================== - # digest calculation - #=================================================================== - - # NOTE: _calc_checksum implemented by backend subclass - - @classmethod - def _get_backend_type(cls, value): - """ - helper to resolve backend constant from type - """ - try: - return cls._backend_type_map[value] - except KeyError: - pass - # XXX: pick better error class? - msg = "unsupported argon2 hash (type %r not supported by %s backend)" % \ - (value, cls.get_backend()) - raise ValueError(msg) - - #=================================================================== - # hash migration - #=================================================================== - - def _calc_needs_update(self, **kwds): - cls = type(self) - if self.type != cls.type: - return True - minver = cls.min_desired_version - if minver is None or minver > cls.max_version: - minver = cls.max_version - if self.version < minver: - # version is too old. - return True - if self.memory_cost != cls.memory_cost: - return True - if self.checksum_size != cls.checksum_size: - return True - return super(_Argon2Common, self)._calc_needs_update(**kwds) - - #=================================================================== - # backend loading - #=================================================================== - - _no_backend_suggestion = " -- recommend you install one (e.g. 'pip install argon2_cffi')" - - @classmethod - def _finalize_backend_mixin(mixin_cls, name, dryrun): - """ - helper called by from backend mixin classes' _load_backend_mixin() -- - invoked after backend imports have been loaded, and performs - feature detection & testing common to all backends. - """ - # check argon2 version - max_version = mixin_cls.max_version - assert isinstance(max_version, int) and max_version >= 0x10 - if max_version < 0x13: - warn("%r doesn't support argon2 v1.3, and should be upgraded" % name, - uh.exc.PasslibSecurityWarning) - - # prefer best available type - for type in ALL_TYPES: - if type in mixin_cls._backend_type_map: - mixin_cls.type = type - break - else: - warn("%r lacks support for all known hash types" % name, uh.exc.PasslibRuntimeWarning) - # NOTE: class will just throw "unsupported argon2 hash" error if they try to use it... - mixin_cls.type = TYPE_ID - - return True - - @classmethod - def _adapt_backend_error(cls, err, hash=None, self=None): - """ - internal helper invoked when backend has hash/verification error; - used to adapt to passlib message. - """ - backend = cls.get_backend() - - # parse hash to throw error if format was invalid, parameter out of range, etc. - if self is None and hash is not None: - self = cls.from_string(hash) - - # check constraints on parsed object - # XXX: could move this to __init__, but not needed by needs_update calls - if self is not None: - self._validate_constraints(self.memory_cost, self.parallelism) - - # as of cffi 16.1, lacks support in hash_secret(), so genhash() will get here. - # as of cffi 16.2, support removed from verify_secret() as well. - if backend == "argon2_cffi" and self.data is not None: - raise NotImplementedError("argon2_cffi backend doesn't support the 'data' parameter") - - # fallback to reporting a malformed hash - text = str(err) - if text not in [ - "Decoding failed" # argon2_cffi's default message - ]: - reason = "%s reported: %s: hash=%r" % (backend, text, hash) - else: - reason = repr(hash) - raise exc.MalformedHashError(cls, reason=reason) - - #=================================================================== - # eoc - #=================================================================== - -#----------------------------------------------------------------------- -# stub backend -#----------------------------------------------------------------------- -class _NoBackend(_Argon2Common): - """ - mixin used before any backend has been loaded. - contains stubs that force loading of one of the available backends. - """ - #=================================================================== - # primary methods - #=================================================================== - @classmethod - def hash(cls, secret): - cls._stub_requires_backend() - return cls.hash(secret) - - @classmethod - def verify(cls, secret, hash): - cls._stub_requires_backend() - return cls.verify(secret, hash) - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genhash(cls, secret, config): - cls._stub_requires_backend() - return cls.genhash(secret, config) - - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - # NOTE: since argon2_cffi takes care of rendering hash, - # _calc_checksum() is only used by the argon2pure backend. - self._stub_requires_backend() - # NOTE: have to use super() here so that we don't recursively - # call subclass's wrapped _calc_checksum - return super(argon2, self)._calc_checksum(secret) - - #=================================================================== - # eoc - #=================================================================== - -#----------------------------------------------------------------------- -# argon2_cffi backend -#----------------------------------------------------------------------- -class _CffiBackend(_Argon2Common): - """ - argon2_cffi backend - """ - #=================================================================== - # backend loading - #=================================================================== - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - # make sure we write info to base class's __dict__, not that of a subclass - assert mixin_cls is _CffiBackend - - # we automatically import this at top, so just grab info - if _argon2_cffi is None: - if _argon2_cffi_error: - raise exc.PasslibSecurityError(_argon2_cffi_error) - return False - max_version = _argon2_cffi.low_level.ARGON2_VERSION - log.debug("detected 'argon2_cffi' backend, version %r, with support for 0x%x argon2 hashes", - _argon2_cffi.__version__, max_version) - - # build type map - TypeEnum = _argon2_cffi.Type - type_map = {} - for type in ALL_TYPES: - try: - type_map[type] = getattr(TypeEnum, type.upper()) - except AttributeError: - # TYPE_ID support not added until v18.2 - assert type not in (TYPE_I, TYPE_D), "unexpected missing type: %r" % type - mixin_cls._backend_type_map = type_map - - # set version info, and run common setup - mixin_cls.version = mixin_cls.max_version = max_version - return mixin_cls._finalize_backend_mixin(name, dryrun) - - #=================================================================== - # primary methods - #=================================================================== - @classmethod - def hash(cls, secret): - # TODO: add in 'encoding' support once that's finalized in 1.8 / 1.9. - uh.validate_secret(secret) - secret = to_bytes(secret, "utf-8") - # XXX: doesn't seem to be a way to make this honor max_threads - try: - return bascii_to_str(_argon2_cffi.low_level.hash_secret( - type=cls._get_backend_type(cls.type), - memory_cost=cls.memory_cost, - time_cost=cls.default_rounds, - parallelism=cls.parallelism, - salt=to_bytes(cls._generate_salt()), - hash_len=cls.checksum_size, - secret=secret, - )) - except _argon2_cffi.exceptions.HashingError as err: - raise cls._adapt_backend_error(err) - - #: helper for verify() method below -- maps prefixes to type constants - _byte_ident_map = dict((render_bytes(b"$argon2%s$", type.encode("ascii")), type) - for type in ALL_TYPES) - - @classmethod - def verify(cls, secret, hash): - # TODO: add in 'encoding' support once that's finalized in 1.8 / 1.9. - uh.validate_secret(secret) - secret = to_bytes(secret, "utf-8") - hash = to_bytes(hash, "ascii") - - # read type from start of hash - # NOTE: don't care about malformed strings, lowlevel will throw error for us - type = cls._byte_ident_map.get(hash[:1+hash.find(b"$", 1)], TYPE_I) - type_code = cls._get_backend_type(type) - - # XXX: doesn't seem to be a way to make this honor max_threads - try: - result = _argon2_cffi.low_level.verify_secret(hash, secret, type_code) - assert result is True - return True - except _argon2_cffi.exceptions.VerifyMismatchError: - return False - except _argon2_cffi.exceptions.VerificationError as err: - raise cls._adapt_backend_error(err, hash=hash) - - # NOTE: deprecated, will be removed in 2.0 - @classmethod - def genhash(cls, secret, config): - # TODO: add in 'encoding' support once that's finalized in 1.8 / 1.9. - uh.validate_secret(secret) - secret = to_bytes(secret, "utf-8") - self = cls.from_string(config) - # XXX: doesn't seem to be a way to make this honor max_threads - try: - result = bascii_to_str(_argon2_cffi.low_level.hash_secret( - type=cls._get_backend_type(self.type), - memory_cost=self.memory_cost, - time_cost=self.rounds, - parallelism=self.parallelism, - salt=to_bytes(self.salt), - hash_len=self.checksum_size, - secret=secret, - version=self.version, - )) - except _argon2_cffi.exceptions.HashingError as err: - raise cls._adapt_backend_error(err, hash=config) - if self.version == 0x10: - # workaround: argon2 0x13 always returns "v=" segment, even for 0x10 hashes - result = result.replace("$v=16$", "$") - return result - - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - raise AssertionError("shouldn't be called under argon2_cffi backend") - - #=================================================================== - # eoc - #=================================================================== - -#----------------------------------------------------------------------- -# argon2pure backend -#----------------------------------------------------------------------- -class _PureBackend(_Argon2Common): - """ - argon2pure backend - """ - #=================================================================== - # backend loading - #=================================================================== - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - # make sure we write info to base class's __dict__, not that of a subclass - assert mixin_cls is _PureBackend - - # import argon2pure - global _argon2pure - try: - import argon2pure as _argon2pure - except ImportError: - return False - - # get default / max supported version -- added in v1.2.2 - try: - from argon2pure import ARGON2_DEFAULT_VERSION as max_version - except ImportError: - log.warning("detected 'argon2pure' backend, but package is too old " - "(passlib requires argon2pure >= 1.2.3)") - return False - - log.debug("detected 'argon2pure' backend, with support for 0x%x argon2 hashes", - max_version) - - if not dryrun: - warn("Using argon2pure backend, which is 100x+ slower than is required " - "for adequate security. Installing argon2_cffi (via 'pip install argon2_cffi') " - "is strongly recommended", exc.PasslibSecurityWarning) - - # build type map - type_map = {} - for type in ALL_TYPES: - try: - type_map[type] = getattr(_argon2pure, "ARGON2" + type.upper()) - except AttributeError: - # TYPE_ID support not added until v1.3 - assert type not in (TYPE_I, TYPE_D), "unexpected missing type: %r" % type - mixin_cls._backend_type_map = type_map - - mixin_cls.version = mixin_cls.max_version = max_version - return mixin_cls._finalize_backend_mixin(name, dryrun) - - #=================================================================== - # primary methods - #=================================================================== - - # NOTE: this backend uses default .hash() & .verify() implementations. - - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - # TODO: add in 'encoding' support once that's finalized in 1.8 / 1.9. - uh.validate_secret(secret) - secret = to_bytes(secret, "utf-8") - kwds = dict( - password=secret, - salt=self.salt, - time_cost=self.rounds, - memory_cost=self.memory_cost, - parallelism=self.parallelism, - tag_length=self.checksum_size, - type_code=self._get_backend_type(self.type), - version=self.version, - ) - if self.max_threads > 0: - kwds['threads'] = self.max_threads - if self.pure_use_threads: - kwds['use_threads'] = True - if self.data: - kwds['associated_data'] = self.data - # NOTE: should return raw bytes - # NOTE: this may raise _argon2pure.Argon2ParameterError, - # but it if does that, there's a bug in our own parameter checking code. - try: - return _argon2pure.argon2(**kwds) - except _argon2pure.Argon2Error as err: - raise self._adapt_backend_error(err, self=self) - - #=================================================================== - # eoc - #=================================================================== - -class argon2(_NoBackend, _Argon2Common): - """ - This class implements the Argon2 password hash [#argon2-home]_, and follows the :ref:`password-hash-api`. - - Argon2 supports a variable-length salt, and variable time & memory cost, - and a number of other configurable parameters. - - The :meth:`~passlib.ifc.PasswordHash.replace` method accepts the following optional keywords: - - :type type: str - :param type: - Specify the type of argon2 hash to generate. - Can be one of "ID", "I", "D". - - This defaults to "ID" if supported by the backend, otherwise "I". - - :type salt: str - :param salt: - Optional salt string. - If specified, the length must be between 0-1024 bytes. - If not specified, one will be auto-generated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - This corresponds linearly to the amount of time hashing will take. - - :type time_cost: int - :param time_cost: - An alias for **rounds**, for compatibility with underlying argon2 library. - - :param int memory_cost: - Defines the memory usage in kibibytes. - This corresponds linearly to the amount of memory hashing will take. - - :param int parallelism: - Defines the parallelization factor. - *NOTE: this will affect the resulting hash value.* - - :param int digest_size: - Length of the digest in bytes. - - :param int max_threads: - Maximum number of threads that will be used. - -1 means unlimited; otherwise hashing will use ``min(parallelism, max_threads)`` threads. - - .. note:: - - This option is currently only honored by the argon2pure backend. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionchanged:: 1.7.2 - - Added the "type" keyword, and support for type "D" and "ID" hashes. - (Prior versions could verify type "D" hashes, but not generate them). - - .. todo:: - - * Support configurable threading limits. - """ - #============================================================================= - # backend - #============================================================================= - - # NOTE: the brunt of the argon2 class is implemented in _Argon2Common. - # there are then subclass for each backend (e.g. _PureBackend), - # these are dynamically prepended to this class's bases - # in order to load the appropriate backend. - - #: list of potential backends - backends = ("argon2_cffi", "argon2pure") - - #: flag that this class's bases should be modified by SubclassBackendMixin - _backend_mixin_target = True - - #: map of backend -> mixin class, used by _get_backend_loader() - _backend_mixin_map = { - None: _NoBackend, - "argon2_cffi": _CffiBackend, - "argon2pure": _PureBackend, - } - - #============================================================================= - # - #============================================================================= - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/bcrypt.py b/.venv/Lib/site-packages/passlib/handlers/bcrypt.py deleted file mode 100644 index b83b110..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/bcrypt.py +++ /dev/null @@ -1,1243 +0,0 @@ -"""passlib.bcrypt -- implementation of OpenBSD's BCrypt algorithm. - -TODO: - -* support 2x and altered-2a hashes? - http://www.openwall.com/lists/oss-security/2011/06/27/9 - -* deal with lack of PY3-compatibile c-ext implementation -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, absolute_import -# core -from base64 import b64encode -from hashlib import sha256 -import os -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -_bcrypt = None # dynamically imported by _load_backend_bcrypt() -_pybcrypt = None # dynamically imported by _load_backend_pybcrypt() -_bcryptor = None # dynamically imported by _load_backend_bcryptor() -# pkg -_builtin_bcrypt = None # dynamically imported by _load_backend_builtin() -from passlib.crypto.digest import compile_hmac -from passlib.exc import PasslibHashWarning, PasslibSecurityWarning, PasslibSecurityError -from passlib.utils import safe_crypt, repeat_string, to_bytes, parse_version, \ - rng, getrandstr, test_crypt, to_unicode, \ - utf8_truncate, utf8_repeat_string, crypt_accepts_bytes -from passlib.utils.binary import bcrypt64 -from passlib.utils.compat import get_unbound_method_function -from passlib.utils.compat import u, uascii_to_str, unicode, str_to_uascii, PY3, error_from -import passlib.utils.handlers as uh - -# local -__all__ = [ - "bcrypt", -] - -#============================================================================= -# support funcs & constants -#============================================================================= -IDENT_2 = u("$2$") -IDENT_2A = u("$2a$") -IDENT_2X = u("$2x$") -IDENT_2Y = u("$2y$") -IDENT_2B = u("$2b$") -_BNULL = b'\x00' - -# reference hash of "test", used in various self-checks -TEST_HASH_2A = "$2a$04$5BJqKfqMQvV7nS.yUguNcueVirQqDBGaLXSqj.rs.pZPlNR0UX/HK" - -def _detect_pybcrypt(): - """ - internal helper which tries to distinguish pybcrypt vs bcrypt. - - :returns: - True if cext-based py-bcrypt, - False if ffi-based bcrypt, - None if 'bcrypt' module not found. - - .. versionchanged:: 1.6.3 - - Now assuming bcrypt installed, unless py-bcrypt explicitly detected. - Previous releases assumed py-bcrypt by default. - - Making this change since py-bcrypt is (apparently) unmaintained and static, - whereas bcrypt is being actively maintained, and it's internal structure may shift. - """ - # NOTE: this is also used by the unittests. - - # check for module. - try: - import bcrypt - except ImportError: - # XXX: this is ignoring case where py-bcrypt's "bcrypt._bcrypt" C Ext fails to import; - # would need to inspect actual ImportError message to catch that. - return None - - # py-bcrypt has a "._bcrypt.__version__" attribute (confirmed for v0.1 - 0.4), - # which bcrypt lacks (confirmed for v1.0 - 2.0) - # "._bcrypt" alone isn't sufficient, since bcrypt 2.0 now has that attribute. - try: - from bcrypt._bcrypt import __version__ - except ImportError: - return False - return True - -#============================================================================= -# backend mixins -#============================================================================= -class _BcryptCommon(uh.SubclassBackendMixin, uh.TruncateMixin, uh.HasManyIdents, - uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """ - Base class which implements brunt of BCrypt code. - This is then subclassed by the various backends, - to override w/ backend-specific methods. - - When a backend is loaded, the bases of the 'bcrypt' class proper - are modified to prepend the correct backend-specific subclass. - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "bcrypt" - setting_kwds = ("salt", "rounds", "ident", "truncate_error") - - #-------------------- - # GenericHandler - #-------------------- - checksum_size = 31 - checksum_chars = bcrypt64.charmap - - #-------------------- - # HasManyIdents - #-------------------- - default_ident = IDENT_2B - ident_values = (IDENT_2, IDENT_2A, IDENT_2X, IDENT_2Y, IDENT_2B) - ident_aliases = {u("2"): IDENT_2, u("2a"): IDENT_2A, u("2y"): IDENT_2Y, - u("2b"): IDENT_2B} - - #-------------------- - # HasSalt - #-------------------- - min_salt_size = max_salt_size = 22 - salt_chars = bcrypt64.charmap - - # NOTE: 22nd salt char must be in restricted set of ``final_salt_chars``, not full set above. - final_salt_chars = ".Oeu" # bcrypt64._padinfo2[1] - - #-------------------- - # HasRounds - #-------------------- - default_rounds = 12 # current passlib default - min_rounds = 4 # minimum from bcrypt specification - max_rounds = 31 # 32-bit integer limit (since real_rounds=1< class - - # NOTE: set_backend() will execute the ._load_backend_mixin() - # of the matching mixin class, which will handle backend detection - - # appended to HasManyBackends' "no backends available" error message - _no_backend_suggestion = " -- recommend you install one (e.g. 'pip install bcrypt')" - - @classmethod - def _finalize_backend_mixin(mixin_cls, backend, dryrun): - """ - helper called by from backend mixin classes' _load_backend_mixin() -- - invoked after backend imports have been loaded, and performs - feature detection & testing common to all backends. - """ - #---------------------------------------------------------------- - # setup helpers - #---------------------------------------------------------------- - assert mixin_cls is bcrypt._backend_mixin_map[backend], \ - "_configure_workarounds() invoked from wrong class" - - if mixin_cls._workrounds_initialized: - return True - - verify = mixin_cls.verify - - err_types = (ValueError, uh.exc.MissingBackendError) - if _bcryptor: - err_types += (_bcryptor.engine.SaltError,) - - def safe_verify(secret, hash): - """verify() wrapper which traps 'unknown identifier' errors""" - try: - return verify(secret, hash) - except err_types: - # backends without support for given ident will throw various - # errors about unrecognized version: - # os_crypt -- internal code below throws - # - PasswordValueError if there's encoding issue w/ password. - # - InternalBackendError if crypt fails for unknown reason - # (trapped below so we can debug it) - # pybcrypt, bcrypt -- raises ValueError - # bcryptor -- raises bcryptor.engine.SaltError - return NotImplemented - except uh.exc.InternalBackendError: - # _calc_checksum() code may also throw CryptBackendError - # if correct hash isn't returned (e.g. 2y hash converted to 2b, - # such as happens with bcrypt 3.0.0) - log.debug("trapped unexpected response from %r backend: verify(%r, %r):", - backend, secret, hash, exc_info=True) - return NotImplemented - - def assert_lacks_8bit_bug(ident): - """ - helper to check for cryptblowfish 8bit bug (fixed in 2y/2b); - even though it's not known to be present in any of passlib's backends. - this is treated as FATAL, because it can easily result in seriously malformed hashes, - and we can't correct for it ourselves. - - test cases from - reference hash is the incorrectly generated $2x$ hash taken from above url - """ - # NOTE: passlib 1.7.2 and earlier used the commented-out LATIN-1 test vector to detect - # this bug; but python3's crypt.crypt() only supports unicode inputs (and - # always encodes them as UTF8 before passing to crypt); so passlib 1.7.3 - # switched to the UTF8-compatible test vector below. This one's bug_hash value - # ("$2x$...rcAS") was drawn from the same openwall source (above); and the correct - # hash ("$2a$...X6eu") was generated by passing the raw bytes to python2's - # crypt.crypt() using OpenBSD 6.7 (hash confirmed as same for $2a$ & $2b$). - - # LATIN-1 test vector - # secret = b"\xA3" - # bug_hash = ident.encode("ascii") + b"05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e" - # correct_hash = ident.encode("ascii") + b"05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq" - - # UTF-8 test vector - secret = b"\xd1\x91" # aka "\u0451" - bug_hash = ident.encode("ascii") + b"05$6bNw2HLQYeqHYyBfLMsv/OiwqTymGIGzFsA4hOTWebfehXHNprcAS" - correct_hash = ident.encode("ascii") + b"05$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6eu" - - if verify(secret, bug_hash): - # NOTE: this only EVER be observed in (broken) 2a and (backward-compat) 2x hashes - # generated by crypt_blowfish library. 2y/2b hashes should not have the bug - # (but we check w/ them anyways). - raise PasslibSecurityError( - "passlib.hash.bcrypt: Your installation of the %r backend is vulnerable to " - "the crypt_blowfish 8-bit bug (CVE-2011-2483) under %r hashes, " - "and should be upgraded or replaced with another backend" % (backend, ident)) - - # it doesn't have wraparound bug, but make sure it *does* verify against the correct - # hash, or we're in some weird third case! - if not verify(secret, correct_hash): - raise RuntimeError("%s backend failed to verify %s 8bit hash" % (backend, ident)) - - def detect_wrap_bug(ident): - """ - check for bsd wraparound bug (fixed in 2b) - this is treated as a warning, because it's rare in the field, - and pybcrypt (as of 2015-7-21) is unpatched, but some people may be stuck with it. - - test cases from - - NOTE: reference hash is of password "0"*72 - - NOTE: if in future we need to deliberately create hashes which have this bug, - can use something like 'hashpw(repeat_string(secret[:((1+secret) % 256) or 1]), 72)' - """ - # check if it exhibits wraparound bug - secret = (b"0123456789"*26)[:255] - bug_hash = ident.encode("ascii") + b"04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6" - if verify(secret, bug_hash): - return True - - # if it doesn't have wraparound bug, make sure it *does* handle things - # correctly -- or we're in some weird third case. - correct_hash = ident.encode("ascii") + b"04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi" - if not verify(secret, correct_hash): - raise RuntimeError("%s backend failed to verify %s wraparound hash" % (backend, ident)) - - return False - - def assert_lacks_wrap_bug(ident): - if not detect_wrap_bug(ident): - return - # should only see in 2a, later idents should NEVER exhibit this bug: - # * 2y implementations should have been free of it - # * 2b was what (supposedly) fixed it - raise RuntimeError("%s backend unexpectedly has wraparound bug for %s" % (backend, ident)) - - #---------------------------------------------------------------- - # check for old 20 support - #---------------------------------------------------------------- - test_hash_20 = b"$2$04$5BJqKfqMQvV7nS.yUguNcuRfMMOXK0xPWavM7pOzjEi5ze5T1k8/S" - result = safe_verify("test", test_hash_20) - if result is NotImplemented: - mixin_cls._lacks_20_support = True - log.debug("%r backend lacks $2$ support, enabling workaround", backend) - elif not result: - raise RuntimeError("%s incorrectly rejected $2$ hash" % backend) - - #---------------------------------------------------------------- - # check for 2a support - #---------------------------------------------------------------- - result = safe_verify("test", TEST_HASH_2A) - if result is NotImplemented: - # 2a support is required, and should always be present - raise RuntimeError("%s lacks support for $2a$ hashes" % backend) - elif not result: - raise RuntimeError("%s incorrectly rejected $2a$ hash" % backend) - else: - assert_lacks_8bit_bug(IDENT_2A) - if detect_wrap_bug(IDENT_2A): - if backend == "os_crypt": - # don't make this a warning for os crypt (e.g. openbsd); - # they'll have proper 2b implementation which will be used for new hashes. - # so even if we didn't have a workaround, this bug wouldn't be a concern. - log.debug("%r backend has $2a$ bsd wraparound bug, enabling workaround", backend) - else: - # installed library has the bug -- want to let users know, - # so they can upgrade it to something better (e.g. bcrypt cffi library) - warn("passlib.hash.bcrypt: Your installation of the %r backend is vulnerable to " - "the bsd wraparound bug, " - "and should be upgraded or replaced with another backend " - "(enabling workaround for now)." % backend, - uh.exc.PasslibSecurityWarning) - mixin_cls._has_2a_wraparound_bug = True - - #---------------------------------------------------------------- - # check for 2y support - #---------------------------------------------------------------- - test_hash_2y = TEST_HASH_2A.replace("2a", "2y") - result = safe_verify("test", test_hash_2y) - if result is NotImplemented: - mixin_cls._lacks_2y_support = True - log.debug("%r backend lacks $2y$ support, enabling workaround", backend) - elif not result: - raise RuntimeError("%s incorrectly rejected $2y$ hash" % backend) - else: - # NOTE: Not using this as fallback candidate, - # lacks wide enough support across implementations. - assert_lacks_8bit_bug(IDENT_2Y) - assert_lacks_wrap_bug(IDENT_2Y) - - #---------------------------------------------------------------- - # TODO: check for 2x support - #---------------------------------------------------------------- - - #---------------------------------------------------------------- - # check for 2b support - #---------------------------------------------------------------- - test_hash_2b = TEST_HASH_2A.replace("2a", "2b") - result = safe_verify("test", test_hash_2b) - if result is NotImplemented: - mixin_cls._lacks_2b_support = True - log.debug("%r backend lacks $2b$ support, enabling workaround", backend) - elif not result: - raise RuntimeError("%s incorrectly rejected $2b$ hash" % backend) - else: - mixin_cls._fallback_ident = IDENT_2B - assert_lacks_8bit_bug(IDENT_2B) - assert_lacks_wrap_bug(IDENT_2B) - - # set flag so we don't have to run this again - mixin_cls._workrounds_initialized = True - return True - - #=================================================================== - # digest calculation - #=================================================================== - - # _calc_checksum() defined by backends - - def _prepare_digest_args(self, secret): - """ - common helper for backends to implement _calc_checksum(). - takes in secret, returns (secret, ident) pair, - """ - return self._norm_digest_args(secret, self.ident, new=self.use_defaults) - - @classmethod - def _norm_digest_args(cls, secret, ident, new=False): - # make sure secret is unicode - require_valid_utf8_bytes = cls._require_valid_utf8_bytes - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - elif require_valid_utf8_bytes: - # if backend requires utf8 bytes (os_crypt); - # make sure input actually is utf8, or don't bother enabling utf-8 specific helpers. - try: - secret.decode("utf-8") - except UnicodeDecodeError: - # XXX: could just throw PasswordValueError here, backend will just do that - # when _calc_digest() is actually called. - require_valid_utf8_bytes = False - - # check max secret size - uh.validate_secret(secret) - - # check for truncation (during .hash() calls only) - if new: - cls._check_truncate_policy(secret) - - # NOTE: especially important to forbid NULLs for bcrypt, since many - # backends (bcryptor, bcrypt) happily accept them, and then - # silently truncate the password at first NULL they encounter! - if _BNULL in secret: - raise uh.exc.NullPasswordError(cls) - - # TODO: figure out way to skip these tests when not needed... - - # protect from wraparound bug by truncating secret before handing it to the backend. - # bcrypt only uses first 72 bytes anyways. - # NOTE: not needed for 2y/2b, but might use 2a as fallback for them. - if cls._has_2a_wraparound_bug and len(secret) >= 255: - if require_valid_utf8_bytes: - # backend requires valid utf8 bytes, so truncate secret to nearest valid segment. - # want to do this in constant time to not give away info about secret. - # NOTE: this only works because bcrypt will ignore everything past - # secret[71], so padding to include a full utf8 sequence - # won't break anything about the final output. - secret = utf8_truncate(secret, 72) - else: - secret = secret[:72] - - # special case handling for variants (ordered most common first) - if ident == IDENT_2A: - # nothing needs to be done. - pass - - elif ident == IDENT_2B: - if cls._lacks_2b_support: - # handle $2b$ hash format even if backend is too old. - # have it generate a 2A/2Y digest, then return it as a 2B hash. - # 2a-only backend could potentially exhibit wraparound bug -- - # but we work around that issue above. - ident = cls._fallback_ident - - elif ident == IDENT_2Y: - if cls._lacks_2y_support: - # handle $2y$ hash format (not supported by BSDs, being phased out on others) - # have it generate a 2A/2B digest, then return it as a 2Y hash. - ident = cls._fallback_ident - - elif ident == IDENT_2: - if cls._lacks_20_support: - # handle legacy $2$ format (not supported by most backends except BSD os_crypt) - # we can fake $2$ behavior using the 2A/2Y/2B algorithm - # by repeating the password until it's at least 72 chars in length. - if secret: - if require_valid_utf8_bytes: - # NOTE: this only works because bcrypt will ignore everything past - # secret[71], so padding to include a full utf8 sequence - # won't break anything about the final output. - secret = utf8_repeat_string(secret, 72) - else: - secret = repeat_string(secret, 72) - ident = cls._fallback_ident - - elif ident == IDENT_2X: - - # NOTE: shouldn't get here. - # XXX: could check if backend does actually offer 'support' - raise RuntimeError("$2x$ hashes not currently supported by passlib") - - else: - raise AssertionError("unexpected ident value: %r" % ident) - - return secret, ident - -#----------------------------------------------------------------------- -# stub backend -#----------------------------------------------------------------------- -class _NoBackend(_BcryptCommon): - """ - mixin used before any backend has been loaded. - contains stubs that force loading of one of the available backends. - """ - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - self._stub_requires_backend() - # NOTE: have to use super() here so that we don't recursively - # call subclass's wrapped _calc_checksum, e.g. bcrypt_sha256._calc_checksum - return super(bcrypt, self)._calc_checksum(secret) - - #=================================================================== - # eoc - #=================================================================== - -#----------------------------------------------------------------------- -# bcrypt backend -#----------------------------------------------------------------------- -class _BcryptBackend(_BcryptCommon): - """ - backend which uses 'bcrypt' package - """ - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - # try to import bcrypt - global _bcrypt - if _detect_pybcrypt(): - # pybcrypt was installed instead - return False - try: - import bcrypt as _bcrypt - except ImportError: # pragma: no cover - return False - try: - version = _bcrypt.__about__.__version__ - except: - log.warning("(trapped) error reading bcrypt version", exc_info=True) - version = '' - - log.debug("detected 'bcrypt' backend, version %r", version) - return mixin_cls._finalize_backend_mixin(name, dryrun) - - # # TODO: would like to implementing verify() directly, - # # to skip need for parsing hash strings. - # # below method has a few edge cases where it chokes though. - # @classmethod - # def verify(cls, secret, hash): - # if isinstance(hash, unicode): - # hash = hash.encode("ascii") - # ident = hash[:hash.index(b"$", 1)+1].decode("ascii") - # if ident not in cls.ident_values: - # raise uh.exc.InvalidHashError(cls) - # secret, eff_ident = cls._norm_digest_args(secret, ident) - # if eff_ident != ident: - # # lacks support for original ident, replace w/ new one. - # hash = eff_ident.encode("ascii") + hash[len(ident):] - # result = _bcrypt.hashpw(secret, hash) - # assert result.startswith(eff_ident) - # return consteq(result, hash) - - def _calc_checksum(self, secret): - # bcrypt behavior: - # secret must be bytes - # config must be ascii bytes - # returns ascii bytes - secret, ident = self._prepare_digest_args(secret) - config = self._get_config(ident) - if isinstance(config, unicode): - config = config.encode("ascii") - hash = _bcrypt.hashpw(secret, config) - assert isinstance(hash, bytes) - if not hash.startswith(config) or len(hash) != len(config)+31: - raise uh.exc.CryptBackendError(self, config, hash, source="`bcrypt` package") - return hash[-31:].decode("ascii") - -#----------------------------------------------------------------------- -# bcryptor backend -#----------------------------------------------------------------------- -class _BcryptorBackend(_BcryptCommon): - """ - backend which uses 'bcryptor' package - """ - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - # try to import bcryptor - global _bcryptor - try: - import bcryptor as _bcryptor - except ImportError: # pragma: no cover - return False - - # deprecated as of 1.7.2 - if not dryrun: - warn("Support for `bcryptor` is deprecated, and will be removed in Passlib 1.8; " - "Please use `pip install bcrypt` instead", DeprecationWarning) - - return mixin_cls._finalize_backend_mixin(name, dryrun) - - def _calc_checksum(self, secret): - # bcryptor behavior: - # py2: unicode secret/hash encoded as ascii bytes before use, - # bytes taken as-is; returns ascii bytes. - # py3: not supported - secret, ident = self._prepare_digest_args(secret) - config = self._get_config(ident) - hash = _bcryptor.engine.Engine(False).hash_key(secret, config) - if not hash.startswith(config) or len(hash) != len(config) + 31: - raise uh.exc.CryptBackendError(self, config, hash, source="bcryptor library") - return str_to_uascii(hash[-31:]) - -#----------------------------------------------------------------------- -# pybcrypt backend -#----------------------------------------------------------------------- -class _PyBcryptBackend(_BcryptCommon): - """ - backend which uses 'pybcrypt' package - """ - - #: classwide thread lock used for pybcrypt < 0.3 - _calc_lock = None - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - # try to import pybcrypt - global _pybcrypt - if not _detect_pybcrypt(): - # not installed, or bcrypt installed instead - return False - try: - import bcrypt as _pybcrypt - except ImportError: # pragma: no cover - # XXX: should we raise AssertionError here? (if get here, _detect_pybcrypt() is broken) - return False - - # deprecated as of 1.7.2 - if not dryrun: - warn("Support for `py-bcrypt` is deprecated, and will be removed in Passlib 1.8; " - "Please use `pip install bcrypt` instead", DeprecationWarning) - - # determine pybcrypt version - try: - version = _pybcrypt._bcrypt.__version__ - except: - log.warning("(trapped) error reading pybcrypt version", exc_info=True) - version = "" - log.debug("detected 'pybcrypt' backend, version %r", version) - - # return calc function based on version - vinfo = parse_version(version) or (0, 0) - if vinfo < (0, 3): - warn("py-bcrypt %s has a major security vulnerability, " - "you should upgrade to py-bcrypt 0.3 immediately." - % version, uh.exc.PasslibSecurityWarning) - if mixin_cls._calc_lock is None: - import threading - mixin_cls._calc_lock = threading.Lock() - mixin_cls._calc_checksum = get_unbound_method_function(mixin_cls._calc_checksum_threadsafe) - - return mixin_cls._finalize_backend_mixin(name, dryrun) - - def _calc_checksum_threadsafe(self, secret): - # as workaround for pybcrypt < 0.3's concurrency issue, - # we wrap everything in a thread lock. as long as bcrypt is only - # used through passlib, this should be safe. - with self._calc_lock: - return self._calc_checksum_raw(secret) - - def _calc_checksum_raw(self, secret): - # py-bcrypt behavior: - # py2: unicode secret/hash encoded as ascii bytes before use, - # bytes taken as-is; returns ascii bytes. - # py3: unicode secret encoded as utf-8 bytes, - # hash encoded as ascii bytes, returns ascii unicode. - secret, ident = self._prepare_digest_args(secret) - config = self._get_config(ident) - hash = _pybcrypt.hashpw(secret, config) - if not hash.startswith(config) or len(hash) != len(config) + 31: - raise uh.exc.CryptBackendError(self, config, hash, source="pybcrypt library") - return str_to_uascii(hash[-31:]) - - _calc_checksum = _calc_checksum_raw - -#----------------------------------------------------------------------- -# os crypt backend -#----------------------------------------------------------------------- -class _OsCryptBackend(_BcryptCommon): - """ - backend which uses :func:`crypt.crypt` - """ - - #: set flag to ensure _prepare_digest_args() doesn't create invalid utf8 string - #: when truncating bytes. - _require_valid_utf8_bytes = not crypt_accepts_bytes - - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - if not test_crypt("test", TEST_HASH_2A): - return False - return mixin_cls._finalize_backend_mixin(name, dryrun) - - def _calc_checksum(self, secret): - # - # run secret through crypt.crypt(). - # if everything goes right, we'll get back a properly formed bcrypt hash. - # - secret, ident = self._prepare_digest_args(secret) - config = self._get_config(ident) - hash = safe_crypt(secret, config) - if hash is not None: - if not hash.startswith(config) or len(hash) != len(config) + 31: - raise uh.exc.CryptBackendError(self, config, hash) - return hash[-31:] - - # - # Check if this failed due to non-UTF8 bytes - # In detail: under py3, crypt.crypt() requires unicode inputs, which are then encoded to - # utf8 before passing them to os crypt() call. this is done according to the "s" format - # specifier for PyArg_ParseTuple (https://docs.python.org/3/c-api/arg.html). - # There appears no way to get around that to pass raw bytes; so we just throw error here - # to let user know they need to use another backend if they want raw bytes support. - # - # XXX: maybe just let safe_crypt() throw UnicodeDecodeError under passlib 2.0, - # and then catch it above? maybe have safe_crypt ALWAYS throw error - # instead of returning None? (would save re-detecting what went wrong) - # XXX: isn't secret ALWAYS bytes at this point? - # - if PY3 and isinstance(secret, bytes): - try: - secret.decode("utf-8") - except UnicodeDecodeError: - raise error_from(uh.exc.PasswordValueError( - "python3 crypt.crypt() ony supports bytes passwords using UTF8; " - "passlib recommends running `pip install bcrypt` for general bcrypt support.", - ), None) - - # - # else crypt() call failed for unknown reason. - # - # NOTE: getting here should be considered a bug in passlib -- - # if os_crypt backend detection said there's support, - # and we've already checked all known reasons above; - # want them to file bug so we can figure out what happened. - # in the meantime, users can avoid this by installing bcrypt-cffi backend; - # which won't have this (or utf8) edgecases. - # - # XXX: throw something more specific, like an "InternalBackendError"? - # NOTE: if do change this error, need to update test_81_crypt_fallback() expectations - # about what will be thrown; as well as safe_verify() above. - # - debug_only_repr = uh.exc.debug_only_repr - raise uh.exc.InternalBackendError( - "crypt.crypt() failed for unknown reason; " - "passlib recommends running `pip install bcrypt` for general bcrypt support." - # for debugging UTs -- - "(config=%s, secret=%s)" % (debug_only_repr(config), debug_only_repr(secret)), - ) - -#----------------------------------------------------------------------- -# builtin backend -#----------------------------------------------------------------------- -class _BuiltinBackend(_BcryptCommon): - """ - backend which uses passlib's pure-python implementation - """ - @classmethod - def _load_backend_mixin(mixin_cls, name, dryrun): - from passlib.utils import as_bool - if not as_bool(os.environ.get("PASSLIB_BUILTIN_BCRYPT")): - log.debug("bcrypt 'builtin' backend not enabled via $PASSLIB_BUILTIN_BCRYPT") - return False - global _builtin_bcrypt - from passlib.crypto._blowfish import raw_bcrypt as _builtin_bcrypt - return mixin_cls._finalize_backend_mixin(name, dryrun) - - def _calc_checksum(self, secret): - secret, ident = self._prepare_digest_args(secret) - chk = _builtin_bcrypt(secret, ident[1:-1], - self.salt.encode("ascii"), self.rounds) - return chk.decode("ascii") - -#============================================================================= -# handler -#============================================================================= -class bcrypt(_NoBackend, _BcryptCommon): - """This class implements the BCrypt password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 22 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 12, must be between 4 and 31, inclusive. - This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}` - -- increasing the rounds by +1 will double the amount of time taken. - - :type ident: str - :param ident: - Specifies which version of the BCrypt algorithm will be used when creating a new hash. - Typically this option is not needed, as the default (``"2b"``) is usually the correct choice. - If specified, it must be one of the following: - - * ``"2"`` - the first revision of BCrypt, which suffers from a minor security flaw and is generally not used anymore. - * ``"2a"`` - some implementations suffered from rare security flaws, replaced by 2b. - * ``"2y"`` - format specific to the *crypt_blowfish* BCrypt implementation, - identical to ``"2b"`` in all but name. - * ``"2b"`` - latest revision of the official BCrypt algorithm, current default. - - :param bool truncate_error: - By default, BCrypt will silently truncate passwords larger than 72 bytes. - Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` - to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. - - .. versionadded:: 1.7 - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. versionchanged:: 1.6 - This class now supports ``"2y"`` hashes, and recognizes - (but does not support) the broken ``"2x"`` hashes. - (see the :ref:`crypt_blowfish bug ` - for details). - - .. versionchanged:: 1.6 - Added a pure-python backend. - - .. versionchanged:: 1.6.3 - - Added support for ``"2b"`` variant. - - .. versionchanged:: 1.7 - - Now defaults to ``"2b"`` variant. - """ - #============================================================================= - # backend - #============================================================================= - - # NOTE: the brunt of the bcrypt class is implemented in _BcryptCommon. - # there are then subclass for each backend (e.g. _PyBcryptBackend), - # these are dynamically prepended to this class's bases - # in order to load the appropriate backend. - - #: list of potential backends - backends = ("bcrypt", "pybcrypt", "bcryptor", "os_crypt", "builtin") - - #: flag that this class's bases should be modified by SubclassBackendMixin - _backend_mixin_target = True - - #: map of backend -> mixin class, used by _get_backend_loader() - _backend_mixin_map = { - None: _NoBackend, - "bcrypt": _BcryptBackend, - "pybcrypt": _PyBcryptBackend, - "bcryptor": _BcryptorBackend, - "os_crypt": _OsCryptBackend, - "builtin": _BuiltinBackend, - } - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# variants -#============================================================================= -_UDOLLAR = u("$") - -# XXX: it might be better to have all the bcrypt variants share a common base class, -# and have the (django_)bcrypt_sha256 wrappers just proxy bcrypt instead of subclassing it. -class _wrapped_bcrypt(bcrypt): - """ - abstracts out some bits bcrypt_sha256 & django_bcrypt_sha256 share. - - bypass backend-loading wrappers for hash() etc - - disable truncation support, sha256 wrappers don't need it. - """ - setting_kwds = tuple(elem for elem in bcrypt.setting_kwds if elem not in ["truncate_error"]) - truncate_size = None - - # XXX: these will be needed if any bcrypt backends directly implement this... - # @classmethod - # def hash(cls, secret, **kwds): - # # bypass bcrypt backend overriding this method - # # XXX: would wrapping bcrypt make this easier than subclassing it? - # return super(_BcryptCommon, cls).hash(secret, **kwds) - # - # @classmethod - # def verify(cls, secret, hash): - # # bypass bcrypt backend overriding this method - # return super(_BcryptCommon, cls).verify(secret, hash) - # - # @classmethod - # def genhash(cls, secret, hash): - # # bypass bcrypt backend overriding this method - # return super(_BcryptCommon, cls).genhash(secret, hash) - - @classmethod - def _check_truncate_policy(cls, secret): - # disable check performed by bcrypt(), since this doesn't truncate passwords. - pass - -#============================================================================= -# bcrypt sha256 wrapper -#============================================================================= - -class bcrypt_sha256(_wrapped_bcrypt): - """ - This class implements a composition of BCrypt + HMAC_SHA256, - and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept - all the same optional keywords as the base :class:`bcrypt` hash. - - .. versionadded:: 1.6.2 - - .. versionchanged:: 1.7 - - Now defaults to ``"2b"`` bcrypt variant; though supports older hashes - generated using the ``"2a"`` bcrypt variant. - - .. versionchanged:: 1.7.3 - - For increased security, updated to use HMAC-SHA256 instead of plain SHA256. - Now only supports the ``"2b"`` bcrypt variant. Hash format updated to "v=2". - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "bcrypt_sha256" - - #-------------------- - # GenericHandler - #-------------------- - # this is locked at 2b for now (with 2a allowed only for legacy v1 format) - ident_values = (IDENT_2A, IDENT_2B) - - # clone bcrypt's ident aliases so they can be used here as well... - ident_aliases = (lambda ident_values: dict(item for item in bcrypt.ident_aliases.items() - if item[1] in ident_values))(ident_values) - default_ident = IDENT_2B - - #-------------------- - # class specific - #-------------------- - - _supported_versions = set([1, 2]) - - #=================================================================== - # instance attrs - #=================================================================== - - #: wrapper version. - #: v1 -- used prior to passlib 1.7.3; performs ``bcrypt(sha256(secret), salt, cost)`` - #: v2 -- new in passlib 1.7.3; performs `bcrypt(sha256_hmac(salt, secret), salt, cost)`` - version = 2 - - #=================================================================== - # configuration - #=================================================================== - - @classmethod - def using(cls, version=None, **kwds): - subcls = super(bcrypt_sha256, cls).using(**kwds) - if version is not None: - subcls.version = subcls._norm_version(version) - ident = subcls.default_ident - if subcls.version > 1 and ident != IDENT_2B: - raise ValueError("bcrypt %r hashes not allowed for version %r" % - (ident, subcls.version)) - return subcls - - #=================================================================== - # formatting - #=================================================================== - - # sample hash: - # $bcrypt-sha256$2a,6$/3OeRpbOf8/l6nPPRdZPp.$nRiyYqPobEZGdNRBWihQhiFDh1ws1tu - # $bcrypt-sha256$ -- prefix/identifier - # 2a -- bcrypt variant - # , -- field separator - # 6 -- bcrypt work factor - # $ -- section separator - # /3OeRpbOf8/l6nPPRdZPp. -- salt - # $ -- section separator - # nRiyYqPobEZGdNRBWihQhiFDh1ws1tu -- digest - - # XXX: we can't use .ident attr due to bcrypt code using it. - # working around that via prefix. - prefix = u('$bcrypt-sha256$') - - #: current version 2 hash format - _v2_hash_re = re.compile(r"""(?x) - ^ - [$]bcrypt-sha256[$] - v=(?P\d+), - t=(?P2b), - r=(?P\d{1,2}) - [$](?P[^$]{22}) - (?:[$](?P[^$]{31}))? - $ - """) - - #: old version 1 hash format - _v1_hash_re = re.compile(r"""(?x) - ^ - [$]bcrypt-sha256[$] - (?P2[ab]), - (?P\d{1,2}) - [$](?P[^$]{22}) - (?:[$](?P[^$]{31}))? - $ - """) - - @classmethod - def identify(cls, hash): - hash = uh.to_unicode_for_identify(hash) - if not hash: - return False - return hash.startswith(cls.prefix) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - if not hash.startswith(cls.prefix): - raise uh.exc.InvalidHashError(cls) - m = cls._v2_hash_re.match(hash) - if m: - version = int(m.group("version")) - if version < 2: - raise uh.exc.MalformedHashError(cls) - else: - m = cls._v1_hash_re.match(hash) - if m: - version = 1 - else: - raise uh.exc.MalformedHashError(cls) - rounds = m.group("rounds") - if rounds.startswith(uh._UZERO) and rounds != uh._UZERO: - raise uh.exc.ZeroPaddedRoundsError(cls) - return cls( - version=version, - ident=m.group("type"), - rounds=int(rounds), - salt=m.group("salt"), - checksum=m.group("digest"), - ) - - _v2_template = u("$bcrypt-sha256$v=2,t=%s,r=%d$%s$%s") - _v1_template = u("$bcrypt-sha256$%s,%d$%s$%s") - - def to_string(self): - if self.version == 1: - template = self._v1_template - else: - template = self._v2_template - hash = template % (self.ident.strip(_UDOLLAR), self.rounds, self.salt, self.checksum) - return uascii_to_str(hash) - - #=================================================================== - # init - #=================================================================== - - def __init__(self, version=None, **kwds): - if version is not None: - self.version = self._norm_version(version) - super(bcrypt_sha256, self).__init__(**kwds) - - #=================================================================== - # version - #=================================================================== - - @classmethod - def _norm_version(cls, version): - if version not in cls._supported_versions: - raise ValueError("%s: unknown or unsupported version: %r" % (cls.name, version)) - return version - - #=================================================================== - # checksum - #=================================================================== - - def _calc_checksum(self, secret): - # NOTE: can't use digest directly, since bcrypt stops at first NULL. - # NOTE: bcrypt doesn't fully mix entropy for bytes 55-72 of password - # (XXX: citation needed), so we don't want key to be > 55 bytes. - # thus, have to use base64 (44 bytes) rather than hex (64 bytes). - # XXX: it's later come out that 55-72 may be ok, so later revision of bcrypt_sha256 - # may switch to hex encoding, since it's simpler to implement elsewhere. - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - - if self.version == 1: - # version 1 -- old version just ran secret through sha256(), - # though this could be vulnerable to a breach attach - # (c.f. issue 114); which is why v2 switched to hmac wrapper. - digest = sha256(secret).digest() - else: - # version 2 -- running secret through HMAC keyed off salt. - # this prevents known secret -> sha256 password tables from being - # used to test against a bcrypt_sha256 hash. - # keying off salt (instead of constant string) should minimize chances of this - # colliding with existing table of hmac digest lookups as well. - # NOTE: salt in this case is the "bcrypt64"-encoded value, not the raw salt bytes, - # to make things easier for parallel implementations of this hash -- - # saving them the trouble of implementing a "bcrypt64" decoder. - salt = self.salt - if salt[-1] not in self.final_salt_chars: - # forbidding salts with padding bits set, because bcrypt implementations - # won't consistently hash them the same. since we control this format, - # just prevent these from even getting used. - raise ValueError("invalid salt string") - digest = compile_hmac("sha256", salt.encode("ascii"))(secret) - - # NOTE: output of b64encode() uses "+/" altchars, "=" padding chars, - # and no leading/trailing whitespace. - key = b64encode(digest) - - # hand result off to normal bcrypt algorithm - return super(bcrypt_sha256, self)._calc_checksum(key) - - #=================================================================== - # other - #=================================================================== - - def _calc_needs_update(self, **kwds): - if self.version < type(self).version: - return True - return super(bcrypt_sha256, self)._calc_needs_update(**kwds) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/cisco.py b/.venv/Lib/site-packages/passlib/handlers/cisco.py deleted file mode 100644 index e715e1a..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/cisco.py +++ /dev/null @@ -1,440 +0,0 @@ -""" -passlib.handlers.cisco -- Cisco password hashes -""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify, unhexlify -from hashlib import md5 -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import right_pad_string, to_unicode, repeat_string, to_bytes -from passlib.utils.binary import h64 -from passlib.utils.compat import unicode, u, join_byte_values, \ - join_byte_elems, iter_byte_values, uascii_to_str -import passlib.utils.handlers as uh -# local -__all__ = [ - "cisco_pix", - "cisco_asa", - "cisco_type7", -] - -#============================================================================= -# utils -#============================================================================= - -#: dummy bytes used by spoil_digest var in cisco_pix._calc_checksum() -_DUMMY_BYTES = b'\xFF' * 32 - -#============================================================================= -# cisco pix firewall hash -#============================================================================= -class cisco_pix(uh.HasUserContext, uh.StaticHandler): - """ - This class implements the password hash used by older Cisco PIX firewalls, - and follows the :ref:`password-hash-api`. - It does a single round of hashing, and relies on the username - as the salt. - - This class only allows passwords <= 16 bytes, anything larger - will result in a :exc:`~passlib.exc.PasswordSizeError` if passed to :meth:`~cisco_pix.hash`, - and be silently rejected if passed to :meth:`~cisco_pix.verify`. - - The :meth:`~passlib.ifc.PasswordHash.hash`, - :meth:`~passlib.ifc.PasswordHash.genhash`, and - :meth:`~passlib.ifc.PasswordHash.verify` methods - all support the following extra keyword: - - :param str user: - String containing name of user account this password is associated with. - - This is *required* in order to correctly hash passwords associated - with a user account on the Cisco device, as it is used to salt - the hash. - - Conversely, this *must* be omitted or set to ``""`` in order to correctly - hash passwords which don't have an associated user account - (such as the "enable" password). - - .. versionadded:: 1.6 - - .. versionchanged:: 1.7.1 - - Passwords > 16 bytes are now rejected / throw error instead of being silently truncated, - to match Cisco behavior. A number of :ref:`bugs ` were fixed - which caused prior releases to generate unverifiable hashes in certain cases. - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "cisco_pix" - - truncate_size = 16 - - # NOTE: these are the default policy for PasswordHash, - # but want to set them explicitly for now. - truncate_error = True - truncate_verify_reject = True - - #-------------------- - # GenericHandler - #-------------------- - checksum_size = 16 - checksum_chars = uh.HASH64_CHARS - - #-------------------- - # custom - #-------------------- - - #: control flag signalling "cisco_asa" mode, set by cisco_asa class - _is_asa = False - - #=================================================================== - # methods - #=================================================================== - def _calc_checksum(self, secret): - """ - This function implements the "encrypted" hash format used by Cisco - PIX & ASA. It's behavior has been confirmed for ASA 9.6, - but is presumed correct for PIX & other ASA releases, - as it fits with known test vectors, and existing literature. - - While nearly the same, the PIX & ASA hashes have slight differences, - so this function performs differently based on the _is_asa class flag. - Noteable changes from PIX to ASA include password size limit - increased from 16 -> 32, and other internal changes. - """ - # select PIX vs or ASA mode - asa = self._is_asa - - # - # encode secret - # - # per ASA 8.4 documentation, - # http://www.cisco.com/c/en/us/td/docs/security/asa/asa84/configuration/guide/asa_84_cli_config/ref_cli.html#Supported_Character_Sets, - # it supposedly uses UTF-8 -- though some double-encoding issues have - # been observed when trying to actually *set* a non-ascii password - # via ASDM, and access via SSH seems to strip 8-bit chars. - # - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - - # - # check if password too large - # - # Per ASA 9.6 changes listed in - # http://www.cisco.com/c/en/us/td/docs/security/asa/roadmap/asa_new_features.html, - # prior releases had a maximum limit of 32 characters. - # Testing with an ASA 9.6 system bears this out -- - # setting 32-char password for a user account, - # and logins will fail if any chars are appended. - # (ASA 9.6 added new PBKDF2-based hash algorithm, - # which supports larger passwords). - # - # Per PIX documentation - # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html, - # it would not allow passwords > 16 chars. - # - # Thus, we unconditionally throw a password size error here, - # as nothing valid can come from a larger password. - # NOTE: assuming PIX has same behavior, but at 16 char limit. - # - spoil_digest = None - if len(secret) > self.truncate_size: - if self.use_defaults: - # called from hash() - msg = "Password too long (%s allows at most %d bytes)" % \ - (self.name, self.truncate_size) - raise uh.exc.PasswordSizeError(self.truncate_size, msg=msg) - else: - # called from verify() -- - # We don't want to throw error, or return early, - # as that would let attacker know too much. Instead, we set a - # flag to add some dummy data into the md5 digest, so that - # output won't match truncated version of secret, or anything - # else that's fixed and predictable. - spoil_digest = secret + _DUMMY_BYTES - - # - # append user to secret - # - # Policy appears to be: - # - # * Nothing appended for enable password (user = "") - # - # * ASA: If user present, but secret is >= 28 chars, nothing appended. - # - # * 1-2 byte users not allowed. - # DEVIATION: we're letting them through, and repeating their - # chars ala 3-char user, to simplify testing. - # Could issue warning in the future though. - # - # * 3 byte user has first char repeated, to pad to 4. - # (observed under ASA 9.6, assuming true elsewhere) - # - # * 4 byte users are used directly. - # - # * 5+ byte users are truncated to 4 bytes. - # - user = self.user - if user: - if isinstance(user, unicode): - user = user.encode("utf-8") - if not asa or len(secret) < 28: - secret += repeat_string(user, 4) - - # - # pad / truncate result to limit - # - # While PIX always pads to 16 bytes, ASA increases to 32 bytes IFF - # secret+user > 16 bytes. This makes PIX & ASA have different results - # where secret size in range(13,16), and user is present -- - # PIX will truncate to 16, ASA will truncate to 32. - # - if asa and len(secret) > 16: - pad_size = 32 - else: - pad_size = 16 - secret = right_pad_string(secret, pad_size) - - # - # md5 digest - # - if spoil_digest: - # make sure digest won't match truncated version of secret - secret += spoil_digest - digest = md5(secret).digest() - - # - # drop every 4th byte - # NOTE: guessing this was done because it makes output exactly - # 16 bytes, which may have been a general 'char password[]' - # size limit under PIX - # - digest = join_byte_elems(c for i, c in enumerate(digest) if (i + 1) & 3) - - # - # encode using Hash64 - # - return h64.encode_bytes(digest).decode("ascii") - - # NOTE: works, but needs UTs. - # @classmethod - # def same_as_pix(cls, secret, user=""): - # """ - # test whether (secret + user) combination should - # have the same hash under PIX and ASA. - # - # mainly present to help unittests. - # """ - # # see _calc_checksum() above for details of this logic. - # size = len(to_bytes(secret, "utf-8")) - # if user and size < 28: - # size += 4 - # return size < 17 - - #=================================================================== - # eoc - #=================================================================== - - -class cisco_asa(cisco_pix): - """ - This class implements the password hash used by Cisco ASA/PIX 7.0 and newer (2005). - Aside from a different internal algorithm, it's use and format is identical - to the older :class:`cisco_pix` class. - - For passwords less than 13 characters, this should be identical to :class:`!cisco_pix`, - but will generate a different hash for most larger inputs - (See the `Format & Algorithm`_ section for the details). - - This class only allows passwords <= 32 bytes, anything larger - will result in a :exc:`~passlib.exc.PasswordSizeError` if passed to :meth:`~cisco_asa.hash`, - and be silently rejected if passed to :meth:`~cisco_asa.verify`. - - .. versionadded:: 1.7 - - .. versionchanged:: 1.7.1 - - Passwords > 32 bytes are now rejected / throw error instead of being silently truncated, - to match Cisco behavior. A number of :ref:`bugs ` were fixed - which caused prior releases to generate unverifiable hashes in certain cases. - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "cisco_asa" - - #-------------------- - # TruncateMixin - #-------------------- - truncate_size = 32 - - #-------------------- - # cisco_pix - #-------------------- - _is_asa = True - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# type 7 -#============================================================================= -class cisco_type7(uh.GenericHandler): - """ - This class implements the "Type 7" password encoding used by Cisco IOS, - and follows the :ref:`password-hash-api`. - It has a simple 4-5 bit salt, but is nonetheless a reversible encoding - instead of a real hash. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: int - :param salt: - This may be an optional salt integer drawn from ``range(0,16)``. - If omitted, one will be chosen at random. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` values that are out of range. - - Note that while this class outputs digests in upper-case hexadecimal, - it will accept lower-case as well. - - This class also provides the following additional method: - - .. automethod:: decode - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "cisco_type7" - setting_kwds = ("salt",) - - #-------------------- - # GenericHandler - #-------------------- - checksum_chars = uh.UPPER_HEX_CHARS - - #-------------------- - # HasSalt - #-------------------- - - # NOTE: encoding could handle max_salt_value=99, but since key is only 52 - # chars in size, not sure what appropriate behavior is for that edge case. - min_salt_value = 0 - max_salt_value = 52 - - #=================================================================== - # methods - #=================================================================== - @classmethod - def using(cls, salt=None, **kwds): - subcls = super(cisco_type7, cls).using(**kwds) - if salt is not None: - salt = subcls._norm_salt(salt, relaxed=kwds.get("relaxed")) - subcls._generate_salt = staticmethod(lambda: salt) - return subcls - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - if len(hash) < 2: - raise uh.exc.InvalidHashError(cls) - salt = int(hash[:2]) # may throw ValueError - return cls(salt=salt, checksum=hash[2:].upper()) - - def __init__(self, salt=None, **kwds): - super(cisco_type7, self).__init__(**kwds) - if salt is not None: - salt = self._norm_salt(salt) - elif self.use_defaults: - salt = self._generate_salt() - assert self._norm_salt(salt) == salt, "generated invalid salt: %r" % (salt,) - else: - raise TypeError("no salt specified") - self.salt = salt - - @classmethod - def _norm_salt(cls, salt, relaxed=False): - """ - validate & normalize salt value. - .. note:: - the salt for this algorithm is an integer 0-52, not a string - """ - if not isinstance(salt, int): - raise uh.exc.ExpectedTypeError(salt, "integer", "salt") - if 0 <= salt <= cls.max_salt_value: - return salt - msg = "salt/offset must be in 0..52 range" - if relaxed: - warn(msg, uh.PasslibHashWarning) - return 0 if salt < 0 else cls.max_salt_value - else: - raise ValueError(msg) - - @staticmethod - def _generate_salt(): - return uh.rng.randint(0, 15) - - def to_string(self): - return "%02d%s" % (self.salt, uascii_to_str(self.checksum)) - - def _calc_checksum(self, secret): - # XXX: no idea what unicode policy is, but all examples are - # 7-bit ascii compatible, so using UTF-8 - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return hexlify(self._cipher(secret, self.salt)).decode("ascii").upper() - - @classmethod - def decode(cls, hash, encoding="utf-8"): - """decode hash, returning original password. - - :arg hash: encoded password - :param encoding: optional encoding to use (defaults to ``UTF-8``). - :returns: password as unicode - """ - self = cls.from_string(hash) - tmp = unhexlify(self.checksum.encode("ascii")) - raw = self._cipher(tmp, self.salt) - return raw.decode(encoding) if encoding else raw - - # type7 uses a xor-based vingere variant, using the following secret key: - _key = u("dsfd;kfoA,.iyewrkldJKDHSUBsgvca69834ncxv9873254k;fg87") - - @classmethod - def _cipher(cls, data, salt): - """xor static key against data - encrypts & decrypts""" - key = cls._key - key_size = len(key) - return join_byte_values( - value ^ ord(key[(salt + idx) % key_size]) - for idx, value in enumerate(iter_byte_values(data)) - ) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/des_crypt.py b/.venv/Lib/site-packages/passlib/handlers/des_crypt.py deleted file mode 100644 index 68a4ca7..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/des_crypt.py +++ /dev/null @@ -1,607 +0,0 @@ -"""passlib.handlers.des_crypt - traditional unix (DES) crypt and variants""" -#============================================================================= -# imports -#============================================================================= -# core -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import safe_crypt, test_crypt, to_unicode -from passlib.utils.binary import h64, h64big -from passlib.utils.compat import byte_elem_value, u, uascii_to_str, unicode, suppress_cause -from passlib.crypto.des import des_encrypt_int_block -import passlib.utils.handlers as uh -# local -__all__ = [ - "des_crypt", - "bsdi_crypt", - "bigcrypt", - "crypt16", -] - -#============================================================================= -# pure-python backend for des_crypt family -#============================================================================= -_BNULL = b'\x00' - -def _crypt_secret_to_key(secret): - """convert secret to 64-bit DES key. - - this only uses the first 8 bytes of the secret, - and discards the high 8th bit of each byte at that. - a null parity bit is inserted after every 7th bit of the output. - """ - # NOTE: this would set the parity bits correctly, - # but des_encrypt_int_block() would just ignore them... - ##return sum(expand_7bit(byte_elem_value(c) & 0x7f) << (56-i*8) - ## for i, c in enumerate(secret[:8])) - return sum((byte_elem_value(c) & 0x7f) << (57-i*8) - for i, c in enumerate(secret[:8])) - -def _raw_des_crypt(secret, salt): - """pure-python backed for des_crypt""" - assert len(salt) == 2 - - # NOTE: some OSes will accept non-HASH64 characters in the salt, - # but what value they assign these characters varies wildy, - # so just rejecting them outright. - # the same goes for single-character salts... - # some OSes duplicate the char, some insert a '.' char, - # and openbsd does (something) which creates an invalid hash. - salt_value = h64.decode_int12(salt) - - # gotta do something - no official policy since this predates unicode - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - assert isinstance(secret, bytes) - - # forbidding NULL char because underlying crypt() rejects them too. - if _BNULL in secret: - raise uh.exc.NullPasswordError(des_crypt) - - # convert first 8 bytes of secret string into an integer - key_value = _crypt_secret_to_key(secret) - - # run data through des using input of 0 - result = des_encrypt_int_block(key_value, 0, salt_value, 25) - - # run h64 encode on result - return h64big.encode_int64(result) - -def _bsdi_secret_to_key(secret): - """convert secret to DES key used by bsdi_crypt""" - key_value = _crypt_secret_to_key(secret) - idx = 8 - end = len(secret) - while idx < end: - next = idx + 8 - tmp_value = _crypt_secret_to_key(secret[idx:next]) - key_value = des_encrypt_int_block(key_value, key_value) ^ tmp_value - idx = next - return key_value - -def _raw_bsdi_crypt(secret, rounds, salt): - """pure-python backend for bsdi_crypt""" - - # decode salt - salt_value = h64.decode_int24(salt) - - # gotta do something - no official policy since this predates unicode - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - assert isinstance(secret, bytes) - - # forbidding NULL char because underlying crypt() rejects them too. - if _BNULL in secret: - raise uh.exc.NullPasswordError(bsdi_crypt) - - # convert secret string into an integer - key_value = _bsdi_secret_to_key(secret) - - # run data through des using input of 0 - result = des_encrypt_int_block(key_value, 0, salt_value, rounds) - - # run h64 encode on result - return h64big.encode_int64(result) - -#============================================================================= -# handlers -#============================================================================= -class des_crypt(uh.TruncateMixin, uh.HasManyBackends, uh.HasSalt, uh.GenericHandler): - """This class implements the des-crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 2 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :param bool truncate_error: - By default, des_crypt will silently truncate passwords larger than 8 bytes. - Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` - to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. - - .. versionadded:: 1.7 - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "des_crypt" - setting_kwds = ("salt", "truncate_error") - - #-------------------- - # GenericHandler - #-------------------- - checksum_chars = uh.HASH64_CHARS - checksum_size = 11 - - #-------------------- - # HasSalt - #-------------------- - min_salt_size = max_salt_size = 2 - salt_chars = uh.HASH64_CHARS - - #-------------------- - # TruncateMixin - #-------------------- - truncate_size = 8 - - #=================================================================== - # formatting - #=================================================================== - # FORMAT: 2 chars of H64-encoded salt + 11 chars of H64-encoded checksum - - _hash_regex = re.compile(u(r""" - ^ - (?P[./a-z0-9]{2}) - (?P[./a-z0-9]{11})? - $"""), re.X|re.I) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - salt, chk = hash[:2], hash[2:] - return cls(salt=salt, checksum=chk or None) - - def to_string(self): - hash = u("%s%s") % (self.salt, self.checksum) - return uascii_to_str(hash) - - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - # check for truncation (during .hash() calls only) - if self.use_defaults: - self._check_truncate_policy(secret) - - return self._calc_checksum_backend(secret) - - #=================================================================== - # backend - #=================================================================== - backends = ("os_crypt", "builtin") - - #--------------------------------------------------------------- - # os_crypt backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_os_crypt(cls): - if test_crypt("test", 'abgOeLfPimXQo'): - cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) - return True - else: - return False - - def _calc_checksum_os_crypt(self, secret): - # NOTE: we let safe_crypt() encode unicode secret -> utf8; - # no official policy since des-crypt predates unicode - hash = safe_crypt(secret, self.salt) - if hash is None: - # py3's crypt.crypt() can't handle non-utf8 bytes. - # fallback to builtin alg, which is always available. - return self._calc_checksum_builtin(secret) - if not hash.startswith(self.salt) or len(hash) != 13: - raise uh.exc.CryptBackendError(self, self.salt, hash) - return hash[2:] - - #--------------------------------------------------------------- - # builtin backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_builtin(cls): - cls._set_calc_checksum_backend(cls._calc_checksum_builtin) - return True - - def _calc_checksum_builtin(self, secret): - return _raw_des_crypt(secret, self.salt.encode("ascii")).decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """This class implements the BSDi-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 4 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 5001, must be between 1 and 16777215, inclusive. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. versionchanged:: 1.6 - :meth:`hash` will now issue a warning if an even number of rounds is used - (see :ref:`bsdi-crypt-security-issues` regarding weak DES keys). - """ - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "bsdi_crypt" - setting_kwds = ("salt", "rounds") - checksum_size = 11 - checksum_chars = uh.HASH64_CHARS - - #--HasSalt-- - min_salt_size = max_salt_size = 4 - salt_chars = uh.HASH64_CHARS - - #--HasRounds-- - default_rounds = 5001 - min_rounds = 1 - max_rounds = 16777215 # (1<<24)-1 - rounds_cost = "linear" - - # NOTE: OpenBSD login.conf reports 7250 as minimum allowed rounds, - # but that seems to be an OS policy, not a algorithm limitation. - - #=================================================================== - # parsing - #=================================================================== - _hash_regex = re.compile(u(r""" - ^ - _ - (?P[./a-z0-9]{4}) - (?P[./a-z0-9]{4}) - (?P[./a-z0-9]{11})? - $"""), re.X|re.I) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - rounds, salt, chk = m.group("rounds", "salt", "chk") - return cls( - rounds=h64.decode_int24(rounds.encode("ascii")), - salt=salt, - checksum=chk, - ) - - def to_string(self): - hash = u("_%s%s%s") % (h64.encode_int24(self.rounds).decode("ascii"), - self.salt, self.checksum) - return uascii_to_str(hash) - - #=================================================================== - # validation - #=================================================================== - - # NOTE: keeping this flag for admin/choose_rounds.py script. - # want to eventually expose rounds logic to that script in better way. - _avoid_even_rounds = True - - @classmethod - def using(cls, **kwds): - subcls = super(bsdi_crypt, cls).using(**kwds) - if not subcls.default_rounds & 1: - # issue warning if caller set an even 'rounds' value. - warn("bsdi_crypt rounds should be odd, as even rounds may reveal weak DES keys", - uh.exc.PasslibSecurityWarning) - return subcls - - @classmethod - def _generate_rounds(cls): - rounds = super(bsdi_crypt, cls)._generate_rounds() - # ensure autogenerated rounds are always odd - # NOTE: doing this even for default_rounds so needs_update() doesn't get - # caught in a loop. - # FIXME: this technically might generate a rounds value 1 larger - # than the requested upper bound - but better to err on side of safety. - return rounds|1 - - #=================================================================== - # migration - #=================================================================== - - def _calc_needs_update(self, **kwds): - # mark bsdi_crypt hashes as deprecated if they have even rounds. - if not self.rounds & 1: - return True - # hand off to base implementation - return super(bsdi_crypt, self)._calc_needs_update(**kwds) - - #=================================================================== - # backends - #=================================================================== - backends = ("os_crypt", "builtin") - - #--------------------------------------------------------------- - # os_crypt backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_os_crypt(cls): - if test_crypt("test", '_/...lLDAxARksGCHin.'): - cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) - return True - else: - return False - - def _calc_checksum_os_crypt(self, secret): - config = self.to_string() - hash = safe_crypt(secret, config) - if hash is None: - # py3's crypt.crypt() can't handle non-utf8 bytes. - # fallback to builtin alg, which is always available. - return self._calc_checksum_builtin(secret) - if not hash.startswith(config[:9]) or len(hash) != 20: - raise uh.exc.CryptBackendError(self, config, hash) - return hash[-11:] - - #--------------------------------------------------------------- - # builtin backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_builtin(cls): - cls._set_calc_checksum_backend(cls._calc_checksum_builtin) - return True - - def _calc_checksum_builtin(self, secret): - return _raw_bsdi_crypt(secret, self.rounds, self.salt.encode("ascii")).decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -class bigcrypt(uh.HasSalt, uh.GenericHandler): - """This class implements the BigCrypt password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 22 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "bigcrypt" - setting_kwds = ("salt",) - checksum_chars = uh.HASH64_CHARS - # NOTE: checksum chars must be multiple of 11 - - #--HasSalt-- - min_salt_size = max_salt_size = 2 - salt_chars = uh.HASH64_CHARS - - #=================================================================== - # internal helpers - #=================================================================== - _hash_regex = re.compile(u(r""" - ^ - (?P[./a-z0-9]{2}) - (?P([./a-z0-9]{11})+)? - $"""), re.X|re.I) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - salt, chk = m.group("salt", "chk") - return cls(salt=salt, checksum=chk) - - def to_string(self): - hash = u("%s%s") % (self.salt, self.checksum) - return uascii_to_str(hash) - - def _norm_checksum(self, checksum, relaxed=False): - checksum = super(bigcrypt, self)._norm_checksum(checksum, relaxed=relaxed) - if len(checksum) % 11: - raise uh.exc.InvalidHashError(self) - return checksum - - #=================================================================== - # backend - #=================================================================== - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - chk = _raw_des_crypt(secret, self.salt.encode("ascii")) - idx = 8 - end = len(secret) - while idx < end: - next = idx + 8 - chk += _raw_des_crypt(secret[idx:next], chk[-11:-9]) - idx = next - return chk.decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -class crypt16(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler): - """This class implements the crypt16 password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 2 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :param bool truncate_error: - By default, crypt16 will silently truncate passwords larger than 16 bytes. - Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` - to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. - - .. versionadded:: 1.7 - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "crypt16" - setting_kwds = ("salt", "truncate_error") - - #-------------------- - # GenericHandler - #-------------------- - checksum_size = 22 - checksum_chars = uh.HASH64_CHARS - - #-------------------- - # HasSalt - #-------------------- - min_salt_size = max_salt_size = 2 - salt_chars = uh.HASH64_CHARS - - #-------------------- - # TruncateMixin - #-------------------- - truncate_size = 16 - - #=================================================================== - # internal helpers - #=================================================================== - _hash_regex = re.compile(u(r""" - ^ - (?P[./a-z0-9]{2}) - (?P[./a-z0-9]{22})? - $"""), re.X|re.I) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - salt, chk = m.group("salt", "chk") - return cls(salt=salt, checksum=chk) - - def to_string(self): - hash = u("%s%s") % (self.salt, self.checksum) - return uascii_to_str(hash) - - #=================================================================== - # backend - #=================================================================== - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - - # check for truncation (during .hash() calls only) - if self.use_defaults: - self._check_truncate_policy(secret) - - # parse salt value - try: - salt_value = h64.decode_int12(self.salt.encode("ascii")) - except ValueError: # pragma: no cover - caught by class - raise suppress_cause(ValueError("invalid chars in salt")) - - # convert first 8 byts of secret string into an integer, - key1 = _crypt_secret_to_key(secret) - - # run data through des using input of 0 - result1 = des_encrypt_int_block(key1, 0, salt_value, 20) - - # convert next 8 bytes of secret string into integer (key=0 if secret < 8 chars) - key2 = _crypt_secret_to_key(secret[8:16]) - - # run data through des using input of 0 - result2 = des_encrypt_int_block(key2, 0, salt_value, 5) - - # done - chk = h64big.encode_int64(result1) + h64big.encode_int64(result2) - return chk.decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/digests.py b/.venv/Lib/site-packages/passlib/handlers/digests.py deleted file mode 100644 index 982155c..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/digests.py +++ /dev/null @@ -1,168 +0,0 @@ -"""passlib.handlers.digests - plain hash digests -""" -#============================================================================= -# imports -#============================================================================= -# core -import hashlib -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import to_native_str, to_bytes, render_bytes, consteq -from passlib.utils.compat import unicode, str_to_uascii -import passlib.utils.handlers as uh -from passlib.crypto.digest import lookup_hash -# local -__all__ = [ - "create_hex_hash", - "hex_md4", - "hex_md5", - "hex_sha1", - "hex_sha256", - "hex_sha512", -] - -#============================================================================= -# helpers for hexadecimal hashes -#============================================================================= -class HexDigestHash(uh.StaticHandler): - """this provides a template for supporting passwords stored as plain hexadecimal hashes""" - #=================================================================== - # class attrs - #=================================================================== - _hash_func = None # hash function to use - filled in by create_hex_hash() - checksum_size = None # filled in by create_hex_hash() - checksum_chars = uh.HEX_CHARS - - #: special for detecting if _hash_func is just a stub method. - supported = True - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(self._hash_func(secret).hexdigest()) - - #=================================================================== - # eoc - #=================================================================== - -def create_hex_hash(digest, module=__name__, django_name=None, required=True): - """ - create hex-encoded unsalted hasher for specified digest algorithm. - - .. versionchanged:: 1.7.3 - If called with unknown/supported digest, won't throw error immediately, - but instead return a dummy hasher that will throw error when called. - - set ``required=True`` to restore old behavior. - """ - info = lookup_hash(digest, required=required) - name = "hex_" + info.name - if not info.supported: - info.digest_size = 0 - hasher = type(name, (HexDigestHash,), dict( - name=name, - __module__=module, # so ABCMeta won't clobber it - _hash_func=staticmethod(info.const), # sometimes it's a function, sometimes not. so wrap it. - checksum_size=info.digest_size*2, - __doc__="""This class implements a plain hexadecimal %s hash, and follows the :ref:`password-hash-api`. - -It supports no optional or contextual keywords. -""" % (info.name,) - )) - if not info.supported: - hasher.supported = False - if django_name: - hasher.django_name = django_name - return hasher - -#============================================================================= -# predefined handlers -#============================================================================= - -# NOTE: some digests below are marked as "required=False", because these may not be present on -# FIPS systems (see issue 116). if missing, will return stub hasher that throws error -# if an attempt is made to actually use hash/verify with them. - -hex_md4 = create_hex_hash("md4", required=False) -hex_md5 = create_hex_hash("md5", django_name="unsalted_md5", required=False) -hex_sha1 = create_hex_hash("sha1", required=False) -hex_sha256 = create_hex_hash("sha256") -hex_sha512 = create_hex_hash("sha512") - -#============================================================================= -# htdigest -#============================================================================= -class htdigest(uh.MinimalHandler): - """htdigest hash function. - - .. todo:: - document this hash - """ - name = "htdigest" - setting_kwds = () - context_kwds = ("user", "realm", "encoding") - default_encoding = "utf-8" - - @classmethod - def hash(cls, secret, user, realm, encoding=None): - # NOTE: this was deliberately written so that raw bytes are passed through - # unchanged, the encoding kwd is only used to handle unicode values. - if not encoding: - encoding = cls.default_encoding - uh.validate_secret(secret) - if isinstance(secret, unicode): - secret = secret.encode(encoding) - user = to_bytes(user, encoding, "user") - realm = to_bytes(realm, encoding, "realm") - data = render_bytes("%s:%s:%s", user, realm, secret) - return hashlib.md5(data).hexdigest() - - @classmethod - def _norm_hash(cls, hash): - """normalize hash to native string, and validate it""" - hash = to_native_str(hash, param="hash") - if len(hash) != 32: - raise uh.exc.MalformedHashError(cls, "wrong size") - for char in hash: - if char not in uh.LC_HEX_CHARS: - raise uh.exc.MalformedHashError(cls, "invalid chars in hash") - return hash - - @classmethod - def verify(cls, secret, hash, user, realm, encoding="utf-8"): - hash = cls._norm_hash(hash) - other = cls.hash(secret, user, realm, encoding) - return consteq(hash, other) - - @classmethod - def identify(cls, hash): - try: - cls._norm_hash(hash) - except ValueError: - return False - return True - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genconfig(cls): - return cls.hash("", "", "") - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genhash(cls, secret, config, user, realm, encoding=None): - # NOTE: 'config' is ignored, as this hash has no salting / other configuration. - # just have to make sure it's valid. - cls._norm_hash(config) - return cls.hash(secret, user, realm, encoding) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/django.py b/.venv/Lib/site-packages/passlib/handlers/django.py deleted file mode 100644 index 6dd499a..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/django.py +++ /dev/null @@ -1,512 +0,0 @@ -"""passlib.handlers.django- Django password hash support""" -#============================================================================= -# imports -#============================================================================= -# core -from base64 import b64encode -from binascii import hexlify -from hashlib import md5, sha1, sha256 -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.handlers.bcrypt import _wrapped_bcrypt -from passlib.hash import argon2, bcrypt, pbkdf2_sha1, pbkdf2_sha256 -from passlib.utils import to_unicode, rng, getrandstr -from passlib.utils.binary import BASE64_CHARS -from passlib.utils.compat import str_to_uascii, uascii_to_str, unicode, u -from passlib.crypto.digest import pbkdf2_hmac -import passlib.utils.handlers as uh -# local -__all__ = [ - "django_salted_sha1", - "django_salted_md5", - "django_bcrypt", - "django_pbkdf2_sha1", - "django_pbkdf2_sha256", - "django_argon2", - "django_des_crypt", - "django_disabled", -] - -#============================================================================= -# lazy imports & constants -#============================================================================= - -# imported by django_des_crypt._calc_checksum() -des_crypt = None - -def _import_des_crypt(): - global des_crypt - if des_crypt is None: - from passlib.hash import des_crypt - return des_crypt - -# django 1.4's salt charset -SALT_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' - -#============================================================================= -# salted hashes -#============================================================================= -class DjangoSaltedHash(uh.HasSalt, uh.GenericHandler): - """base class providing common code for django hashes""" - # name, ident, checksum_size must be set by subclass. - # ident must include "$" suffix. - setting_kwds = ("salt", "salt_size") - - # NOTE: django 1.0-1.3 would accept empty salt strings. - # django 1.4 won't, but this appears to be regression - # (https://code.djangoproject.com/ticket/18144) - # so presumably it will be fixed in a later release. - default_salt_size = 12 - max_salt_size = None - salt_chars = SALT_CHARS - - checksum_chars = uh.LOWER_HEX_CHARS - - @classmethod - def from_string(cls, hash): - salt, chk = uh.parse_mc2(hash, cls.ident, handler=cls) - return cls(salt=salt, checksum=chk) - - def to_string(self): - return uh.render_mc2(self.ident, self.salt, self.checksum) - -# NOTE: only used by PBKDF2 -class DjangoVariableHash(uh.HasRounds, DjangoSaltedHash): - """base class providing common code for django hashes w/ variable rounds""" - setting_kwds = DjangoSaltedHash.setting_kwds + ("rounds",) - - min_rounds = 1 - - @classmethod - def from_string(cls, hash): - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self): - return uh.render_mc3(self.ident, self.rounds, self.salt, self.checksum) - -class django_salted_sha1(DjangoSaltedHash): - """This class implements Django's Salted SHA1 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and uses a single round of SHA1. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, a 12 character one will be autogenerated (this is recommended). - If specified, may be any series of characters drawn from the regexp range ``[0-9a-zA-Z]``. - - :type salt_size: int - :param salt_size: - Optional number of characters to use when autogenerating new salts. - Defaults to 12, but can be any positive value. - - This should be compatible with Django 1.4's :class:`!SHA1PasswordHasher` class. - - .. versionchanged: 1.6 - This class now generates 12-character salts instead of 5, - and generated salts uses the character range ``[0-9a-zA-Z]`` instead of - the ``[0-9a-f]``. This is to be compatible with how Django >= 1.4 - generates these hashes; but hashes generated in this manner will still be - correctly interpreted by earlier versions of Django. - """ - name = "django_salted_sha1" - django_name = "sha1" - ident = u("sha1$") - checksum_size = 40 - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(sha1(self.salt.encode("ascii") + secret).hexdigest()) - -class django_salted_md5(DjangoSaltedHash): - """This class implements Django's Salted MD5 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and uses a single round of MD5. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, a 12 character one will be autogenerated (this is recommended). - If specified, may be any series of characters drawn from the regexp range ``[0-9a-zA-Z]``. - - :type salt_size: int - :param salt_size: - Optional number of characters to use when autogenerating new salts. - Defaults to 12, but can be any positive value. - - This should be compatible with the hashes generated by - Django 1.4's :class:`!MD5PasswordHasher` class. - - .. versionchanged: 1.6 - This class now generates 12-character salts instead of 5, - and generated salts uses the character range ``[0-9a-zA-Z]`` instead of - the ``[0-9a-f]``. This is to be compatible with how Django >= 1.4 - generates these hashes; but hashes generated in this manner will still be - correctly interpreted by earlier versions of Django. - """ - name = "django_salted_md5" - django_name = "md5" - ident = u("md5$") - checksum_size = 32 - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(md5(self.salt.encode("ascii") + secret).hexdigest()) - -#============================================================================= -# BCrypt -#============================================================================= - -django_bcrypt = uh.PrefixWrapper("django_bcrypt", bcrypt, - prefix=u('bcrypt$'), ident=u("bcrypt$"), - # NOTE: this docstring is duplicated in the docs, since sphinx - # seems to be having trouble reading it via autodata:: - doc="""This class implements Django 1.4's BCrypt wrapper, and follows the :ref:`password-hash-api`. - - This is identical to :class:`!bcrypt` itself, but with - the Django-specific prefix ``"bcrypt$"`` prepended. - - See :doc:`/lib/passlib.hash.bcrypt` for more details, - the usage and behavior is identical. - - This should be compatible with the hashes generated by - Django 1.4's :class:`!BCryptPasswordHasher` class. - - .. versionadded:: 1.6 - """) -django_bcrypt.django_name = "bcrypt" -django_bcrypt._using_clone_attrs += ("django_name",) - -#============================================================================= -# BCRYPT + SHA256 -#============================================================================= - -class django_bcrypt_sha256(_wrapped_bcrypt): - """This class implements Django 1.6's Bcrypt+SHA256 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - While the algorithm and format is somewhat different, - the api and options for this hash are identical to :class:`!bcrypt` itself, - see :doc:`bcrypt ` for more details. - - .. versionadded:: 1.6.2 - """ - name = "django_bcrypt_sha256" - django_name = "bcrypt_sha256" - _digest = sha256 - - # sample hash: - # bcrypt_sha256$$2a$06$/3OeRpbOf8/l6nPPRdZPp.nRiyYqPobEZGdNRBWihQhiFDh1ws1tu - - # XXX: we can't use .ident attr due to bcrypt code using it. - # working around that via django_prefix - django_prefix = u('bcrypt_sha256$') - - @classmethod - def identify(cls, hash): - hash = uh.to_unicode_for_identify(hash) - if not hash: - return False - return hash.startswith(cls.django_prefix) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - if not hash.startswith(cls.django_prefix): - raise uh.exc.InvalidHashError(cls) - bhash = hash[len(cls.django_prefix):] - if not bhash.startswith("$2"): - raise uh.exc.MalformedHashError(cls) - return super(django_bcrypt_sha256, cls).from_string(bhash) - - def to_string(self): - bhash = super(django_bcrypt_sha256, self).to_string() - return uascii_to_str(self.django_prefix) + bhash - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - secret = hexlify(self._digest(secret).digest()) - return super(django_bcrypt_sha256, self)._calc_checksum(secret) - -#============================================================================= -# PBKDF2 variants -#============================================================================= - -class django_pbkdf2_sha256(DjangoVariableHash): - """This class implements Django's PBKDF2-HMAC-SHA256 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, a 12 character one will be autogenerated (this is recommended). - If specified, may be any series of characters drawn from the regexp range ``[0-9a-zA-Z]``. - - :type salt_size: int - :param salt_size: - Optional number of characters to use when autogenerating new salts. - Defaults to 12, but can be any positive value. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 29000, but must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - This should be compatible with the hashes generated by - Django 1.4's :class:`!PBKDF2PasswordHasher` class. - - .. versionadded:: 1.6 - """ - name = "django_pbkdf2_sha256" - django_name = "pbkdf2_sha256" - ident = u('pbkdf2_sha256$') - min_salt_size = 1 - max_rounds = 0xffffffff # setting at 32-bit limit for now - checksum_chars = uh.PADDED_BASE64_CHARS - checksum_size = 44 # 32 bytes -> base64 - default_rounds = pbkdf2_sha256.default_rounds # NOTE: django 1.6 uses 12000 - _digest = "sha256" - - def _calc_checksum(self, secret): - # NOTE: secret & salt will be encoded using UTF-8 by pbkdf2_hmac() - hash = pbkdf2_hmac(self._digest, secret, self.salt, self.rounds) - return b64encode(hash).rstrip().decode("ascii") - -class django_pbkdf2_sha1(django_pbkdf2_sha256): - """This class implements Django's PBKDF2-HMAC-SHA1 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, a 12 character one will be autogenerated (this is recommended). - If specified, may be any series of characters drawn from the regexp range ``[0-9a-zA-Z]``. - - :type salt_size: int - :param salt_size: - Optional number of characters to use when autogenerating new salts. - Defaults to 12, but can be any positive value. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 131000, but must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - This should be compatible with the hashes generated by - Django 1.4's :class:`!PBKDF2SHA1PasswordHasher` class. - - .. versionadded:: 1.6 - """ - name = "django_pbkdf2_sha1" - django_name = "pbkdf2_sha1" - ident = u('pbkdf2_sha1$') - checksum_size = 28 # 20 bytes -> base64 - default_rounds = pbkdf2_sha1.default_rounds # NOTE: django 1.6 uses 12000 - _digest = "sha1" - -#============================================================================= -# Argon2 -#============================================================================= - -# NOTE: as of 2019-11-11, Django's Argon2PasswordHasher only supports Type I; -# so limiting this to ensure that as well. - -django_argon2 = uh.PrefixWrapper( - name="django_argon2", - wrapped=argon2.using(type="I"), - prefix=u('argon2'), - ident=u('argon2$argon2i$'), - # NOTE: this docstring is duplicated in the docs, since sphinx - # seems to be having trouble reading it via autodata:: - doc="""This class implements Django 1.10's Argon2 wrapper, and follows the :ref:`password-hash-api`. - - This is identical to :class:`!argon2` itself, but with - the Django-specific prefix ``"argon2$"`` prepended. - - See :doc:`argon2 ` for more details, - the usage and behavior is identical. - - This should be compatible with the hashes generated by - Django 1.10's :class:`!Argon2PasswordHasher` class. - - .. versionadded:: 1.7 - """) -django_argon2.django_name = "argon2" -django_argon2._using_clone_attrs += ("django_name",) - -#============================================================================= -# DES -#============================================================================= -class django_des_crypt(uh.TruncateMixin, uh.HasSalt, uh.GenericHandler): - """This class implements Django's :class:`des_crypt` wrapper, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 2 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :param bool truncate_error: - By default, django_des_crypt will silently truncate passwords larger than 8 bytes. - Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` - to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. - - .. versionadded:: 1.7 - - This should be compatible with the hashes generated by - Django 1.4's :class:`!CryptPasswordHasher` class. - Note that Django only supports this hash on Unix systems - (though :class:`!django_des_crypt` is available cross-platform - under Passlib). - - .. versionchanged:: 1.6 - This class will now accept hashes with empty salt strings, - since Django 1.4 generates them this way. - """ - name = "django_des_crypt" - django_name = "crypt" - setting_kwds = ("salt", "salt_size", "truncate_error") - ident = u("crypt$") - checksum_chars = salt_chars = uh.HASH64_CHARS - checksum_size = 11 - min_salt_size = default_salt_size = 2 - truncate_size = 8 - - # NOTE: regarding duplicate salt field: - # - # django 1.0 had a "crypt$$" hash format, - # used [a-z0-9] to generate a 5 char salt, stored it in salt1, - # duplicated the first two chars of salt1 as salt2. - # it would throw an error if salt1 was empty. - # - # django 1.4 started generating 2 char salt using the full alphabet, - # left salt1 empty, and only paid attention to salt2. - # - # in order to be compatible with django 1.0, the hashes generated - # by this function will always include salt1, unless the following - # class-level field is disabled (mainly used for testing) - use_duplicate_salt = True - - @classmethod - def from_string(cls, hash): - salt, chk = uh.parse_mc2(hash, cls.ident, handler=cls) - if chk: - # chk should be full des_crypt hash - if not salt: - # django 1.4 always uses empty salt field, - # so extract salt from des_crypt hash - salt = chk[:2] - elif salt[:2] != chk[:2]: - # django 1.0 stored 5 chars in salt field, and duplicated - # the first two chars in . we keep the full salt, - # but make sure the first two chars match as sanity check. - raise uh.exc.MalformedHashError(cls, - "first two digits of salt and checksum must match") - # in all cases, strip salt chars from - chk = chk[2:] - return cls(salt=salt, checksum=chk) - - def to_string(self): - salt = self.salt - chk = salt[:2] + self.checksum - if self.use_duplicate_salt: - # filling in salt field, so that we're compatible with django 1.0 - return uh.render_mc2(self.ident, salt, chk) - else: - # django 1.4+ style hash - return uh.render_mc2(self.ident, "", chk) - - def _calc_checksum(self, secret): - # NOTE: we lazily import des_crypt, - # since most django deploys won't use django_des_crypt - global des_crypt - if des_crypt is None: - _import_des_crypt() - # check for truncation (during .hash() calls only) - if self.use_defaults: - self._check_truncate_policy(secret) - return des_crypt(salt=self.salt[:2])._calc_checksum(secret) - -class django_disabled(uh.ifc.DisabledHash, uh.StaticHandler): - """This class provides disabled password behavior for Django, and follows the :ref:`password-hash-api`. - - This class does not implement a hash, but instead - claims the special hash string ``"!"`` which Django uses - to indicate an account's password has been disabled. - - * newly encrypted passwords will hash to ``"!"``. - * it rejects all passwords. - - .. note:: - - Django 1.6 prepends a randomly generated 40-char alphanumeric string - to each unusuable password. This class recognizes such strings, - but for backwards compatibility, still returns ``"!"``. - - See ``_ for why - Django appends an alphanumeric string. - - .. versionchanged:: 1.6.2 added Django 1.6 support - - .. versionchanged:: 1.7 started appending an alphanumeric string. - """ - name = "django_disabled" - _hash_prefix = u("!") - suffix_length = 40 - - # XXX: move this to StaticHandler, or wherever _hash_prefix is being used? - @classmethod - def identify(cls, hash): - hash = uh.to_unicode_for_identify(hash) - return hash.startswith(cls._hash_prefix) - - def _calc_checksum(self, secret): - # generate random suffix to match django's behavior - return getrandstr(rng, BASE64_CHARS[:-2], self.suffix_length) - - @classmethod - def verify(cls, secret, hash): - uh.validate_secret(secret) - if not cls.identify(hash): - raise uh.exc.InvalidHashError(cls) - return False - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/fshp.py b/.venv/Lib/site-packages/passlib/handlers/fshp.py deleted file mode 100644 index db13e74..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/fshp.py +++ /dev/null @@ -1,214 +0,0 @@ -"""passlib.handlers.fshp -""" - -#============================================================================= -# imports -#============================================================================= -# core -from base64 import b64encode, b64decode -import re -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import to_unicode -import passlib.utils.handlers as uh -from passlib.utils.compat import bascii_to_str, iteritems, u,\ - unicode -from passlib.crypto.digest import pbkdf1 -# local -__all__ = [ - 'fshp', -] -#============================================================================= -# sha1-crypt -#============================================================================= -class fshp(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements the FSHP password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :param salt: - Optional raw salt string. - If not specified, one will be autogenerated (this is recommended). - - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 16 bytes, but can be any non-negative value. - - :param rounds: - Optional number of rounds to use. - Defaults to 480000, must be between 1 and 4294967295, inclusive. - - :param variant: - Optionally specifies variant of FSHP to use. - - * ``0`` - uses SHA-1 digest (deprecated). - * ``1`` - uses SHA-2/256 digest (default). - * ``2`` - uses SHA-2/384 digest. - * ``3`` - uses SHA-2/512 digest. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "fshp" - setting_kwds = ("salt", "salt_size", "rounds", "variant") - checksum_chars = uh.PADDED_BASE64_CHARS - ident = u("{FSHP") - # checksum_size is property() that depends on variant - - #--HasRawSalt-- - default_salt_size = 16 # current passlib default, FSHP uses 8 - max_salt_size = None - - #--HasRounds-- - # FIXME: should probably use different default rounds - # based on the variant. setting for default variant (sha256) for now. - default_rounds = 480000 # current passlib default, FSHP uses 4096 - min_rounds = 1 # set by FSHP - max_rounds = 4294967295 # 32-bit integer limit - not set by FSHP - rounds_cost = "linear" - - #--variants-- - default_variant = 1 - _variant_info = { - # variant: (hash name, digest size) - 0: ("sha1", 20), - 1: ("sha256", 32), - 2: ("sha384", 48), - 3: ("sha512", 64), - } - _variant_aliases = dict( - [(unicode(k),k) for k in _variant_info] + - [(v[0],k) for k,v in iteritems(_variant_info)] - ) - - #=================================================================== - # configuration - #=================================================================== - @classmethod - def using(cls, variant=None, **kwds): - subcls = super(fshp, cls).using(**kwds) - if variant is not None: - subcls.default_variant = cls._norm_variant(variant) - return subcls - - #=================================================================== - # instance attrs - #=================================================================== - variant = None - - #=================================================================== - # init - #=================================================================== - def __init__(self, variant=None, **kwds): - # NOTE: variant must be set first, since it controls checksum size, etc. - self.use_defaults = kwds.get("use_defaults") # load this early - if variant is not None: - variant = self._norm_variant(variant) - elif self.use_defaults: - variant = self.default_variant - assert self._norm_variant(variant) == variant, "invalid default variant: %r" % (variant,) - else: - raise TypeError("no variant specified") - self.variant = variant - super(fshp, self).__init__(**kwds) - - @classmethod - def _norm_variant(cls, variant): - if isinstance(variant, bytes): - variant = variant.decode("ascii") - if isinstance(variant, unicode): - try: - variant = cls._variant_aliases[variant] - except KeyError: - raise ValueError("invalid fshp variant") - if not isinstance(variant, int): - raise TypeError("fshp variant must be int or known alias") - if variant not in cls._variant_info: - raise ValueError("invalid fshp variant") - return variant - - @property - def checksum_alg(self): - return self._variant_info[self.variant][0] - - @property - def checksum_size(self): - return self._variant_info[self.variant][1] - - #=================================================================== - # formatting - #=================================================================== - - _hash_regex = re.compile(u(r""" - ^ - \{FSHP - (\d+)\| # variant - (\d+)\| # salt size - (\d+)\} # rounds - ([a-zA-Z0-9+/]+={0,3}) # digest - $"""), re.X) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - variant, salt_size, rounds, data = m.group(1,2,3,4) - variant = int(variant) - salt_size = int(salt_size) - rounds = int(rounds) - try: - data = b64decode(data.encode("ascii")) - except TypeError: - raise uh.exc.MalformedHashError(cls) - salt = data[:salt_size] - chk = data[salt_size:] - return cls(salt=salt, checksum=chk, rounds=rounds, variant=variant) - - def to_string(self): - chk = self.checksum - salt = self.salt - data = bascii_to_str(b64encode(salt+chk)) - return "{FSHP%d|%d|%d}%s" % (self.variant, len(salt), self.rounds, data) - - #=================================================================== - # backend - #=================================================================== - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - # NOTE: for some reason, FSHP uses pbkdf1 with password & salt reversed. - # this has only a minimal impact on security, - # but it is worth noting this deviation. - return pbkdf1( - digest=self.checksum_alg, - secret=self.salt, - salt=secret, - rounds=self.rounds, - keylen=self.checksum_size, - ) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/ldap_digests.py b/.venv/Lib/site-packages/passlib/handlers/ldap_digests.py deleted file mode 100644 index 30254f0..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/ldap_digests.py +++ /dev/null @@ -1,359 +0,0 @@ -"""passlib.handlers.digests - plain hash digests -""" -#============================================================================= -# imports -#============================================================================= -# core -from base64 import b64encode, b64decode -from hashlib import md5, sha1, sha256, sha512 -import logging; log = logging.getLogger(__name__) -import re -# site -# pkg -from passlib.handlers.misc import plaintext -from passlib.utils import unix_crypt_schemes, to_unicode -from passlib.utils.compat import uascii_to_str, unicode, u -from passlib.utils.decor import classproperty -import passlib.utils.handlers as uh -# local -__all__ = [ - "ldap_plaintext", - "ldap_md5", - "ldap_sha1", - "ldap_salted_md5", - "ldap_salted_sha1", - "ldap_salted_sha256", - "ldap_salted_sha512", - - ##"get_active_ldap_crypt_schemes", - "ldap_des_crypt", - "ldap_bsdi_crypt", - "ldap_md5_crypt", - "ldap_sha1_crypt", - "ldap_bcrypt", - "ldap_sha256_crypt", - "ldap_sha512_crypt", -] - -#============================================================================= -# ldap helpers -#============================================================================= -class _Base64DigestHelper(uh.StaticHandler): - """helper for ldap_md5 / ldap_sha1""" - # XXX: could combine this with hex digests in digests.py - - ident = None # required - prefix identifier - _hash_func = None # required - hash function - _hash_regex = None # required - regexp to recognize hash - checksum_chars = uh.PADDED_BASE64_CHARS - - @classproperty - def _hash_prefix(cls): - """tell StaticHandler to strip ident from checksum""" - return cls.ident - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - chk = self._hash_func(secret).digest() - return b64encode(chk).decode("ascii") - -class _SaltedBase64DigestHelper(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """helper for ldap_salted_md5 / ldap_salted_sha1""" - setting_kwds = ("salt", "salt_size") - checksum_chars = uh.PADDED_BASE64_CHARS - - ident = None # required - prefix identifier - _hash_func = None # required - hash function - _hash_regex = None # required - regexp to recognize hash - min_salt_size = max_salt_size = 4 - - # NOTE: openldap implementation uses 4 byte salt, - # but it's been reported (issue 30) that some servers use larger salts. - # the semi-related rfc3112 recommends support for up to 16 byte salts. - min_salt_size = 4 - default_salt_size = 4 - max_salt_size = 16 - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - try: - data = b64decode(m.group("tmp").encode("ascii")) - except TypeError: - raise uh.exc.MalformedHashError(cls) - cs = cls.checksum_size - assert cs - return cls(checksum=data[:cs], salt=data[cs:]) - - def to_string(self): - data = self.checksum + self.salt - hash = self.ident + b64encode(data).decode("ascii") - return uascii_to_str(hash) - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return self._hash_func(secret + self.salt).digest() - -#============================================================================= -# implementations -#============================================================================= -class ldap_md5(_Base64DigestHelper): - """This class stores passwords using LDAP's plain MD5 format, and follows the :ref:`password-hash-api`. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords. - """ - name = "ldap_md5" - ident = u("{MD5}") - _hash_func = md5 - _hash_regex = re.compile(u(r"^\{MD5\}(?P[+/a-zA-Z0-9]{22}==)$")) - -class ldap_sha1(_Base64DigestHelper): - """This class stores passwords using LDAP's plain SHA1 format, and follows the :ref:`password-hash-api`. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods have no optional keywords. - """ - name = "ldap_sha1" - ident = u("{SHA}") - _hash_func = sha1 - _hash_regex = re.compile(u(r"^\{SHA\}(?P[+/a-zA-Z0-9]{27}=)$")) - -class ldap_salted_md5(_SaltedBase64DigestHelper): - """This class stores passwords using LDAP's salted MD5 format, and follows the :ref:`password-hash-api`. - - It supports a 4-16 byte salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it may be any 4-16 byte string. - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 4 bytes for compatibility with the LDAP spec, - but some systems use larger salts, and Passlib supports - any value between 4-16. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. versionchanged:: 1.6 - This format now supports variable length salts, instead of a fix 4 bytes. - """ - name = "ldap_salted_md5" - ident = u("{SMD5}") - checksum_size = 16 - _hash_func = md5 - _hash_regex = re.compile(u(r"^\{SMD5\}(?P[+/a-zA-Z0-9]{27,}={0,2})$")) - -class ldap_salted_sha1(_SaltedBase64DigestHelper): - """ - This class stores passwords using LDAP's "Salted SHA1" format, - and follows the :ref:`password-hash-api`. - - It supports a 4-16 byte salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it may be any 4-16 byte string. - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 4 bytes for compatibility with the LDAP spec, - but some systems use larger salts, and Passlib supports - any value between 4-16. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. versionchanged:: 1.6 - This format now supports variable length salts, instead of a fix 4 bytes. - """ - name = "ldap_salted_sha1" - ident = u("{SSHA}") - checksum_size = 20 - _hash_func = sha1 - # NOTE: 32 = ceil((20 + 4) * 4/3) - _hash_regex = re.compile(u(r"^\{SSHA\}(?P[+/a-zA-Z0-9]{32,}={0,2})$")) - - - -class ldap_salted_sha256(_SaltedBase64DigestHelper): - """ - This class stores passwords using LDAP's "Salted SHA2-256" format, - and follows the :ref:`password-hash-api`. - - It supports a 4-16 byte salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it may be any 4-16 byte string. - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 8 bytes for compatibility with the LDAP spec, - but Passlib supports any value between 4-16. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.7.3 - """ - name = "ldap_salted_sha256" - ident = u("{SSHA256}") - checksum_size = 32 - default_salt_size = 8 - _hash_func = sha256 - # NOTE: 48 = ceil((32 + 4) * 4/3) - _hash_regex = re.compile(u(r"^\{SSHA256\}(?P[+/a-zA-Z0-9]{48,}={0,2})$")) - - -class ldap_salted_sha512(_SaltedBase64DigestHelper): - """ - This class stores passwords using LDAP's "Salted SHA2-512" format, - and follows the :ref:`password-hash-api`. - - It supports a 4-16 byte salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it may be any 4-16 byte string. - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 8 bytes for compatibility with the LDAP spec, - but Passlib supports any value between 4-16. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.7.3 - """ - name = "ldap_salted_sha512" - ident = u("{SSHA512}") - checksum_size = 64 - default_salt_size = 8 - _hash_func = sha512 - # NOTE: 91 = ceil((64 + 4) * 4/3) - _hash_regex = re.compile(u(r"^\{SSHA512\}(?P[+/a-zA-Z0-9]{91,}={0,2})$")) - - -class ldap_plaintext(plaintext): - """This class stores passwords in plaintext, and follows the :ref:`password-hash-api`. - - This class acts much like the generic :class:`!passlib.hash.plaintext` handler, - except that it will identify a hash only if it does NOT begin with the ``{XXX}`` identifier prefix - used by RFC2307 passwords. - - The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the - following additional contextual keyword: - - :type encoding: str - :param encoding: - This controls the character encoding to use (defaults to ``utf-8``). - - This encoding will be used to encode :class:`!unicode` passwords - under Python 2, and decode :class:`!bytes` hashes under Python 3. - - .. versionchanged:: 1.6 - The ``encoding`` keyword was added. - """ - # NOTE: this subclasses plaintext, since all it does differently - # is override identify() - - name = "ldap_plaintext" - _2307_pat = re.compile(u(r"^\{\w+\}.*$")) - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genconfig(cls): - # Overridding plaintext.genconfig() since it returns "", - # but have to return non-empty value due to identify() below - return "!" - - @classmethod - def identify(cls, hash): - # NOTE: identifies all strings EXCEPT those with {XXX} prefix - hash = uh.to_unicode_for_identify(hash) - return bool(hash) and cls._2307_pat.match(hash) is None - -#============================================================================= -# {CRYPT} wrappers -# the following are wrappers around the base crypt algorithms, -# which add the ldap required {CRYPT} prefix -#============================================================================= -ldap_crypt_schemes = [ 'ldap_' + name for name in unix_crypt_schemes ] - -def _init_ldap_crypt_handlers(): - # NOTE: I don't like to implicitly modify globals() like this, - # but don't want to write out all these handlers out either :) - g = globals() - for wname in unix_crypt_schemes: - name = 'ldap_' + wname - g[name] = uh.PrefixWrapper(name, wname, prefix=u("{CRYPT}"), lazy=True) - del g -_init_ldap_crypt_handlers() - -##_lcn_host = None -##def get_host_ldap_crypt_schemes(): -## global _lcn_host -## if _lcn_host is None: -## from passlib.hosts import host_context -## schemes = host_context.schemes() -## _lcn_host = [ -## "ldap_" + name -## for name in unix_crypt_names -## if name in schemes -## ] -## return _lcn_host - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/md5_crypt.py b/.venv/Lib/site-packages/passlib/handlers/md5_crypt.py deleted file mode 100644 index e3a2dfa..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/md5_crypt.py +++ /dev/null @@ -1,346 +0,0 @@ -"""passlib.handlers.md5_crypt - md5-crypt algorithm""" -#============================================================================= -# imports -#============================================================================= -# core -from hashlib import md5 -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import safe_crypt, test_crypt, repeat_string -from passlib.utils.binary import h64 -from passlib.utils.compat import unicode, u -import passlib.utils.handlers as uh -# local -__all__ = [ - "md5_crypt", - "apr_md5_crypt", -] - -#============================================================================= -# pure-python backend -#============================================================================= -_BNULL = b"\x00" -_MD5_MAGIC = b"$1$" -_APR_MAGIC = b"$apr1$" - -# pre-calculated offsets used to speed up C digest stage (see notes below). -# sequence generated using the following: - ##perms_order = "p,pp,ps,psp,sp,spp".split(",") - ##def offset(i): - ## key = (("p" if i % 2 else "") + ("s" if i % 3 else "") + - ## ("p" if i % 7 else "") + ("" if i % 2 else "p")) - ## return perms_order.index(key) - ##_c_digest_offsets = [(offset(i), offset(i+1)) for i in range(0,42,2)] -_c_digest_offsets = ( - (0, 3), (5, 1), (5, 3), (1, 2), (5, 1), (5, 3), (1, 3), - (4, 1), (5, 3), (1, 3), (5, 0), (5, 3), (1, 3), (5, 1), - (4, 3), (1, 3), (5, 1), (5, 2), (1, 3), (5, 1), (5, 3), - ) - -# map used to transpose bytes when encoding final digest -_transpose_map = (12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11) - -def _raw_md5_crypt(pwd, salt, use_apr=False): - """perform raw md5-crypt calculation - - this function provides a pure-python implementation of the internals - for the MD5-Crypt algorithms; it doesn't handle any of the - parsing/validation of the hash strings themselves. - - :arg pwd: password chars/bytes to hash - :arg salt: salt chars to use - :arg use_apr: use apache variant - - :returns: - encoded checksum chars - """ - # NOTE: regarding 'apr' format: - # really, apache? you had to invent a whole new "$apr1$" format, - # when all you did was change the ident incorporated into the hash? - # would love to find webpage explaining why just using a portable - # implementation of $1$ wasn't sufficient. *nothing else* was changed. - - #=================================================================== - # init & validate inputs - #=================================================================== - - # validate secret - # XXX: not sure what official unicode policy is, using this as default - if isinstance(pwd, unicode): - pwd = pwd.encode("utf-8") - assert isinstance(pwd, bytes), "pwd not unicode or bytes" - if _BNULL in pwd: - raise uh.exc.NullPasswordError(md5_crypt) - pwd_len = len(pwd) - - # validate salt - should have been taken care of by caller - assert isinstance(salt, unicode), "salt not unicode" - salt = salt.encode("ascii") - assert len(salt) < 9, "salt too large" - # NOTE: spec says salts larger than 8 bytes should be truncated, - # instead of causing an error. this function assumes that's been - # taken care of by the handler class. - - # load APR specific constants - if use_apr: - magic = _APR_MAGIC - else: - magic = _MD5_MAGIC - - #=================================================================== - # digest B - used as subinput to digest A - #=================================================================== - db = md5(pwd + salt + pwd).digest() - - #=================================================================== - # digest A - used to initialize first round of digest C - #=================================================================== - # start out with pwd + magic + salt - a_ctx = md5(pwd + magic + salt) - a_ctx_update = a_ctx.update - - # add pwd_len bytes of b, repeating b as many times as needed. - a_ctx_update(repeat_string(db, pwd_len)) - - # add null chars & first char of password - # NOTE: this may have historically been a bug, - # where they meant to use db[0] instead of B_NULL, - # but the original code memclear'ed db, - # and now all implementations have to use this. - i = pwd_len - evenchar = pwd[:1] - while i: - a_ctx_update(_BNULL if i & 1 else evenchar) - i >>= 1 - - # finish A - da = a_ctx.digest() - - #=================================================================== - # digest C - for a 1000 rounds, combine A, S, and P - # digests in various ways; in order to burn CPU time. - #=================================================================== - - # NOTE: the original MD5-Crypt implementation performs the C digest - # calculation using the following loop: - # - ##dc = da - ##i = 0 - ##while i < rounds: - ## tmp_ctx = md5(pwd if i & 1 else dc) - ## if i % 3: - ## tmp_ctx.update(salt) - ## if i % 7: - ## tmp_ctx.update(pwd) - ## tmp_ctx.update(dc if i & 1 else pwd) - ## dc = tmp_ctx.digest() - ## i += 1 - # - # The code Passlib uses (below) implements an equivalent algorithm, - # it's just been heavily optimized to pre-calculate a large number - # of things beforehand. It works off of a couple of observations - # about the original algorithm: - # - # 1. each round is a combination of 'dc', 'salt', and 'pwd'; and the exact - # combination is determined by whether 'i' a multiple of 2,3, and/or 7. - # 2. since lcm(2,3,7)==42, the series of combinations will repeat - # every 42 rounds. - # 3. even rounds 0-40 consist of 'hash(dc + round-specific-constant)'; - # while odd rounds 1-41 consist of hash(round-specific-constant + dc) - # - # Using these observations, the following code... - # * calculates the round-specific combination of salt & pwd for each round 0-41 - # * runs through as many 42-round blocks as possible (23) - # * runs through as many pairs of rounds as needed for remaining rounds (17) - # * this results in the required 42*23+2*17=1000 rounds required by md5_crypt. - # - # this cuts out a lot of the control overhead incurred when running the - # original loop 1000 times in python, resulting in ~20% increase in - # speed under CPython (though still 2x slower than glibc crypt) - - # prepare the 6 combinations of pwd & salt which are needed - # (order of 'perms' must match how _c_digest_offsets was generated) - pwd_pwd = pwd+pwd - pwd_salt = pwd+salt - perms = [pwd, pwd_pwd, pwd_salt, pwd_salt+pwd, salt+pwd, salt+pwd_pwd] - - # build up list of even-round & odd-round constants, - # and store in 21-element list as (even,odd) pairs. - data = [ (perms[even], perms[odd]) for even, odd in _c_digest_offsets] - - # perform 23 blocks of 42 rounds each (for a total of 966 rounds) - dc = da - blocks = 23 - while blocks: - for even, odd in data: - dc = md5(odd + md5(dc + even).digest()).digest() - blocks -= 1 - - # perform 17 more pairs of rounds (34 more rounds, for a total of 1000) - for even, odd in data[:17]: - dc = md5(odd + md5(dc + even).digest()).digest() - - #=================================================================== - # encode digest using appropriate transpose map - #=================================================================== - return h64.encode_transposed_bytes(dc, _transpose_map).decode("ascii") - -#============================================================================= -# handler -#============================================================================= -class _MD5_Common(uh.HasSalt, uh.GenericHandler): - """common code for md5_crypt and apr_md5_crypt""" - #=================================================================== - # class attrs - #=================================================================== - # name - set in subclass - setting_kwds = ("salt", "salt_size") - # ident - set in subclass - checksum_size = 22 - checksum_chars = uh.HASH64_CHARS - - max_salt_size = 8 - salt_chars = uh.HASH64_CHARS - - #=================================================================== - # methods - #=================================================================== - - @classmethod - def from_string(cls, hash): - salt, chk = uh.parse_mc2(hash, cls.ident, handler=cls) - return cls(salt=salt, checksum=chk) - - def to_string(self): - return uh.render_mc2(self.ident, self.salt, self.checksum) - - # _calc_checksum() - provided by subclass - - #=================================================================== - # eoc - #=================================================================== - -class md5_crypt(uh.HasManyBackends, _MD5_Common): - """This class implements the MD5-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 0-8 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type salt_size: int - :param salt_size: - Optional number of characters to use when autogenerating new salts. - Defaults to 8, but can be any value between 0 and 8. - (This is mainly needed when generating Cisco-compatible hashes, - which require ``salt_size=4``). - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - name = "md5_crypt" - ident = u("$1$") - - #=================================================================== - # methods - #=================================================================== - # FIXME: can't find definitive policy on how md5-crypt handles non-ascii. - # all backends currently coerce -> utf-8 - - backends = ("os_crypt", "builtin") - - #--------------------------------------------------------------- - # os_crypt backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_os_crypt(cls): - if test_crypt("test", '$1$test$pi/xDtU5WFVRqYS6BMU8X/'): - cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) - return True - else: - return False - - def _calc_checksum_os_crypt(self, secret): - config = self.ident + self.salt - hash = safe_crypt(secret, config) - if hash is None: - # py3's crypt.crypt() can't handle non-utf8 bytes. - # fallback to builtin alg, which is always available. - return self._calc_checksum_builtin(secret) - if not hash.startswith(config) or len(hash) != len(config) + 23: - raise uh.exc.CryptBackendError(self, config, hash) - return hash[-22:] - - #--------------------------------------------------------------- - # builtin backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_builtin(cls): - cls._set_calc_checksum_backend(cls._calc_checksum_builtin) - return True - - def _calc_checksum_builtin(self, secret): - return _raw_md5_crypt(secret, self.salt) - - #=================================================================== - # eoc - #=================================================================== - -class apr_md5_crypt(_MD5_Common): - """This class implements the Apr-MD5-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 0-8 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - name = "apr_md5_crypt" - ident = u("$apr1$") - - #=================================================================== - # methods - #=================================================================== - def _calc_checksum(self, secret): - return _raw_md5_crypt(secret, self.salt, use_apr=True) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/misc.py b/.venv/Lib/site-packages/passlib/handlers/misc.py deleted file mode 100644 index 44abc34..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/misc.py +++ /dev/null @@ -1,269 +0,0 @@ -"""passlib.handlers.misc - misc generic handlers -""" -#============================================================================= -# imports -#============================================================================= -# core -import sys -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import to_native_str, str_consteq -from passlib.utils.compat import unicode, u, unicode_or_bytes_types -import passlib.utils.handlers as uh -# local -__all__ = [ - "unix_disabled", - "unix_fallback", - "plaintext", -] - -#============================================================================= -# handler -#============================================================================= -class unix_fallback(uh.ifc.DisabledHash, uh.StaticHandler): - """This class provides the fallback behavior for unix shadow files, and follows the :ref:`password-hash-api`. - - This class does not implement a hash, but instead provides fallback - behavior as found in /etc/shadow on most unix variants. - If used, should be the last scheme in the context. - - * this class will positively identify all hash strings. - * for security, passwords will always hash to ``!``. - * it rejects all passwords if the hash is NOT an empty string (``!`` or ``*`` are frequently used). - * by default it rejects all passwords if the hash is an empty string, - but if ``enable_wildcard=True`` is passed to verify(), - all passwords will be allowed through if the hash is an empty string. - - .. deprecated:: 1.6 - This has been deprecated due to its "wildcard" feature, - and will be removed in Passlib 1.8. Use :class:`unix_disabled` instead. - """ - name = "unix_fallback" - context_kwds = ("enable_wildcard",) - - @classmethod - def identify(cls, hash): - if isinstance(hash, unicode_or_bytes_types): - return True - else: - raise uh.exc.ExpectedStringError(hash, "hash") - - def __init__(self, enable_wildcard=False, **kwds): - warn("'unix_fallback' is deprecated, " - "and will be removed in Passlib 1.8; " - "please use 'unix_disabled' instead.", - DeprecationWarning) - super(unix_fallback, self).__init__(**kwds) - self.enable_wildcard = enable_wildcard - - def _calc_checksum(self, secret): - if self.checksum: - # NOTE: hash will generally be "!", but we want to preserve - # it in case it's something else, like "*". - return self.checksum - else: - return u("!") - - @classmethod - def verify(cls, secret, hash, enable_wildcard=False): - uh.validate_secret(secret) - if not isinstance(hash, unicode_or_bytes_types): - raise uh.exc.ExpectedStringError(hash, "hash") - elif hash: - return False - else: - return enable_wildcard - -_MARKER_CHARS = u("*!") -_MARKER_BYTES = b"*!" - -class unix_disabled(uh.ifc.DisabledHash, uh.MinimalHandler): - """This class provides disabled password behavior for unix shadow files, - and follows the :ref:`password-hash-api`. - - This class does not implement a hash, but instead matches the "disabled account" - strings found in ``/etc/shadow`` on most Unix variants. "encrypting" a password - will simply return the disabled account marker. It will reject all passwords, - no matter the hash string. The :meth:`~passlib.ifc.PasswordHash.hash` - method supports one optional keyword: - - :type marker: str - :param marker: - Optional marker string which overrides the platform default - used to indicate a disabled account. - - If not specified, this will default to ``"*"`` on BSD systems, - and use the Linux default ``"!"`` for all other platforms. - (:attr:`!unix_disabled.default_marker` will contain the default value) - - .. versionadded:: 1.6 - This class was added as a replacement for the now-deprecated - :class:`unix_fallback` class, which had some undesirable features. - """ - name = "unix_disabled" - setting_kwds = ("marker",) - context_kwds = () - - _disable_prefixes = tuple(str(_MARKER_CHARS)) - - # TODO: rename attr to 'marker'... - if 'bsd' in sys.platform: # pragma: no cover -- runtime detection - default_marker = u("*") - else: - # use the linux default for other systems - # (glibc also supports adding old hash after the marker - # so it can be restored later). - default_marker = u("!") - - @classmethod - def using(cls, marker=None, **kwds): - subcls = super(unix_disabled, cls).using(**kwds) - if marker is not None: - if not cls.identify(marker): - raise ValueError("invalid marker: %r" % marker) - subcls.default_marker = marker - return subcls - - @classmethod - def identify(cls, hash): - # NOTE: technically, anything in the /etc/shadow password field - # which isn't valid crypt() output counts as "disabled". - # but that's rather ambiguous, and it's hard to predict what - # valid output is for unknown crypt() implementations. - # so to be on the safe side, we only match things *known* - # to be disabled field indicators, and will add others - # as they are found. things beginning w/ "$" should *never* match. - # - # things currently matched: - # * linux uses "!" - # * bsd uses "*" - # * linux may use "!" + hash to disable but preserve original hash - # * linux counts empty string as "any password"; - # this code recognizes it, but treats it the same as "!" - if isinstance(hash, unicode): - start = _MARKER_CHARS - elif isinstance(hash, bytes): - start = _MARKER_BYTES - else: - raise uh.exc.ExpectedStringError(hash, "hash") - return not hash or hash[0] in start - - @classmethod - def verify(cls, secret, hash): - uh.validate_secret(secret) - if not cls.identify(hash): # handles typecheck - raise uh.exc.InvalidHashError(cls) - return False - - @classmethod - def hash(cls, secret, **kwds): - if kwds: - uh.warn_hash_settings_deprecation(cls, kwds) - return cls.using(**kwds).hash(secret) - uh.validate_secret(secret) - marker = cls.default_marker - assert marker and cls.identify(marker) - return to_native_str(marker, param="marker") - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genhash(cls, secret, config, marker=None): - if not cls.identify(config): - raise uh.exc.InvalidHashError(cls) - elif config: - # preserve the existing str,since it might contain a disabled password hash ("!" + hash) - uh.validate_secret(secret) - return to_native_str(config, param="config") - else: - if marker is not None: - cls = cls.using(marker=marker) - return cls.hash(secret) - - @classmethod - def disable(cls, hash=None): - out = cls.hash("") - if hash is not None: - hash = to_native_str(hash, param="hash") - if cls.identify(hash): - # extract original hash, so that we normalize marker - hash = cls.enable(hash) - if hash: - out += hash - return out - - @classmethod - def enable(cls, hash): - hash = to_native_str(hash, param="hash") - for prefix in cls._disable_prefixes: - if hash.startswith(prefix): - orig = hash[len(prefix):] - if orig: - return orig - else: - raise ValueError("cannot restore original hash") - raise uh.exc.InvalidHashError(cls) - -class plaintext(uh.MinimalHandler): - """This class stores passwords in plaintext, and follows the :ref:`password-hash-api`. - - The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the - following additional contextual keyword: - - :type encoding: str - :param encoding: - This controls the character encoding to use (defaults to ``utf-8``). - - This encoding will be used to encode :class:`!unicode` passwords - under Python 2, and decode :class:`!bytes` hashes under Python 3. - - .. versionchanged:: 1.6 - The ``encoding`` keyword was added. - """ - # NOTE: this is subclassed by ldap_plaintext - - name = "plaintext" - setting_kwds = () - context_kwds = ("encoding",) - default_encoding = "utf-8" - - @classmethod - def identify(cls, hash): - if isinstance(hash, unicode_or_bytes_types): - return True - else: - raise uh.exc.ExpectedStringError(hash, "hash") - - @classmethod - def hash(cls, secret, encoding=None): - uh.validate_secret(secret) - if not encoding: - encoding = cls.default_encoding - return to_native_str(secret, encoding, "secret") - - @classmethod - def verify(cls, secret, hash, encoding=None): - if not encoding: - encoding = cls.default_encoding - hash = to_native_str(hash, encoding, "hash") - if not cls.identify(hash): - raise uh.exc.InvalidHashError(cls) - return str_consteq(cls.hash(secret, encoding), hash) - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genconfig(cls): - return cls.hash("") - - @uh.deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genhash(cls, secret, config, encoding=None): - # NOTE: 'config' is ignored, as this hash has no salting / etc - if not cls.identify(config): - raise uh.exc.InvalidHashError(cls) - return cls.hash(secret, encoding=encoding) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/mssql.py b/.venv/Lib/site-packages/passlib/handlers/mssql.py deleted file mode 100644 index b060b36..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/mssql.py +++ /dev/null @@ -1,244 +0,0 @@ -"""passlib.handlers.mssql - MS-SQL Password Hash - -Notes -===== -MS-SQL has used a number of hash algs over the years, -most of which were exposed through the undocumented -'pwdencrypt' and 'pwdcompare' sql functions. - -Known formats -------------- -6.5 - snefru hash, ascii encoded password - no examples found - -7.0 - snefru hash, unicode (what encoding?) - saw ref that these blobs were 16 bytes in size - no examples found - -2000 - byte string using displayed as 0x hex, using 0x0100 prefix. - contains hashes of password and upper-case password. - -2007 - same as 2000, but without the upper-case hash. - -refs ----------- -https://blogs.msdn.com/b/lcris/archive/2007/04/30/sql-server-2005-about-login-password-hashes.aspx?Redirected=true -http://us.generation-nt.com/securing-passwords-hash-help-35429432.html -http://forum.md5decrypter.co.uk/topic230-mysql-and-mssql-get-password-hashes.aspx -http://www.theregister.co.uk/2002/07/08/cracking_ms_sql_server_passwords/ -""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify, unhexlify -from hashlib import sha1 -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import consteq -from passlib.utils.compat import bascii_to_str, unicode, u -import passlib.utils.handlers as uh -# local -__all__ = [ - "mssql2000", - "mssql2005", -] - -#============================================================================= -# mssql 2000 -#============================================================================= -def _raw_mssql(secret, salt): - assert isinstance(secret, unicode) - assert isinstance(salt, bytes) - return sha1(secret.encode("utf-16-le") + salt).digest() - -BIDENT = b"0x0100" -##BIDENT2 = b("\x01\x00") -UIDENT = u("0x0100") - -def _ident_mssql(hash, csize, bsize): - """common identify for mssql 2000/2005""" - if isinstance(hash, unicode): - if len(hash) == csize and hash.startswith(UIDENT): - return True - elif isinstance(hash, bytes): - if len(hash) == csize and hash.startswith(BIDENT): - return True - ##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes - ## return True - else: - raise uh.exc.ExpectedStringError(hash, "hash") - return False - -def _parse_mssql(hash, csize, bsize, handler): - """common parser for mssql 2000/2005; returns 4 byte salt + checksum""" - if isinstance(hash, unicode): - if len(hash) == csize and hash.startswith(UIDENT): - try: - return unhexlify(hash[6:].encode("utf-8")) - except TypeError: # throw when bad char found - pass - elif isinstance(hash, bytes): - # assumes ascii-compat encoding - assert isinstance(hash, bytes) - if len(hash) == csize and hash.startswith(BIDENT): - try: - return unhexlify(hash[6:]) - except TypeError: # throw when bad char found - pass - ##elif len(hash) == bsize and hash.startswith(BIDENT2): # raw bytes - ## return hash[2:] - else: - raise uh.exc.ExpectedStringError(hash, "hash") - raise uh.exc.InvalidHashError(handler) - -class mssql2000(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements the password hash used by MS-SQL 2000, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 4 bytes in length. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - """ - #=================================================================== - # algorithm information - #=================================================================== - name = "mssql2000" - setting_kwds = ("salt",) - checksum_size = 40 - min_salt_size = max_salt_size = 4 - - #=================================================================== - # formatting - #=================================================================== - - # 0100 - 2 byte identifier - # 4 byte salt - # 20 byte checksum - # 20 byte checksum - # = 46 bytes - # encoded '0x' + 92 chars = 94 - - @classmethod - def identify(cls, hash): - return _ident_mssql(hash, 94, 46) - - @classmethod - def from_string(cls, hash): - data = _parse_mssql(hash, 94, 46, cls) - return cls(salt=data[:4], checksum=data[4:]) - - def to_string(self): - raw = self.salt + self.checksum - # raw bytes format - BIDENT2 + raw - return "0x0100" + bascii_to_str(hexlify(raw).upper()) - - def _calc_checksum(self, secret): - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - salt = self.salt - return _raw_mssql(secret, salt) + _raw_mssql(secret.upper(), salt) - - @classmethod - def verify(cls, secret, hash): - # NOTE: we only compare against the upper-case hash - # XXX: add 'full' just to verify both checksums? - uh.validate_secret(secret) - self = cls.from_string(hash) - chk = self.checksum - if chk is None: - raise uh.exc.MissingDigestError(cls) - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - result = _raw_mssql(secret.upper(), self.salt) - return consteq(result, chk[20:]) - -#============================================================================= -# handler -#============================================================================= -class mssql2005(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements the password hash used by MS-SQL 2005, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 4 bytes in length. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - """ - #=================================================================== - # algorithm information - #=================================================================== - name = "mssql2005" - setting_kwds = ("salt",) - - checksum_size = 20 - min_salt_size = max_salt_size = 4 - - #=================================================================== - # formatting - #=================================================================== - - # 0x0100 - 2 byte identifier - # 4 byte salt - # 20 byte checksum - # = 26 bytes - # encoded '0x' + 52 chars = 54 - - @classmethod - def identify(cls, hash): - return _ident_mssql(hash, 54, 26) - - @classmethod - def from_string(cls, hash): - data = _parse_mssql(hash, 54, 26, cls) - return cls(salt=data[:4], checksum=data[4:]) - - def to_string(self): - raw = self.salt + self.checksum - # raw bytes format - BIDENT2 + raw - return "0x0100" + bascii_to_str(hexlify(raw)).upper() - - def _calc_checksum(self, secret): - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - return _raw_mssql(secret, self.salt) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/mysql.py b/.venv/Lib/site-packages/passlib/handlers/mysql.py deleted file mode 100644 index 4a71253..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/mysql.py +++ /dev/null @@ -1,128 +0,0 @@ -"""passlib.handlers.mysql - -MySQL 3.2.3 / OLD_PASSWORD() - - This implements Mysql's OLD_PASSWORD algorithm, introduced in version 3.2.3, deprecated in version 4.1. - - See :mod:`passlib.handlers.mysql_41` for the new algorithm was put in place in version 4.1 - - This algorithm is known to be very insecure, and should only be used to verify existing password hashes. - - http://djangosnippets.org/snippets/1508/ - -MySQL 4.1.1 / NEW PASSWORD - This implements Mysql new PASSWORD algorithm, introduced in version 4.1. - - This function is unsalted, and therefore not very secure against rainbow attacks. - It should only be used when dealing with mysql passwords, - for all other purposes, you should use a salted hash function. - - Description taken from http://dev.mysql.com/doc/refman/6.0/en/password-hashing.html -""" -#============================================================================= -# imports -#============================================================================= -# core -from hashlib import sha1 -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import to_native_str -from passlib.utils.compat import bascii_to_str, unicode, u, \ - byte_elem_value, str_to_uascii -import passlib.utils.handlers as uh -# local -__all__ = [ - 'mysql323', - 'mysq41', -] - -#============================================================================= -# backend -#============================================================================= -class mysql323(uh.StaticHandler): - """This class implements the MySQL 3.2.3 password hash, and follows the :ref:`password-hash-api`. - - It has no salt and a single fixed round. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords. - """ - #=================================================================== - # class attrs - #=================================================================== - name = "mysql323" - checksum_size = 16 - checksum_chars = uh.HEX_CHARS - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - # FIXME: no idea if mysql has a policy about handling unicode passwords - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - - MASK_32 = 0xffffffff - MASK_31 = 0x7fffffff - WHITE = b' \t' - - nr1 = 0x50305735 - nr2 = 0x12345671 - add = 7 - for c in secret: - if c in WHITE: - continue - tmp = byte_elem_value(c) - nr1 ^= ((((nr1 & 63)+add)*tmp) + (nr1 << 8)) & MASK_32 - nr2 = (nr2+((nr2 << 8) ^ nr1)) & MASK_32 - add = (add+tmp) & MASK_32 - return u("%08x%08x") % (nr1 & MASK_31, nr2 & MASK_31) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# handler -#============================================================================= -class mysql41(uh.StaticHandler): - """This class implements the MySQL 4.1 password hash, and follows the :ref:`password-hash-api`. - - It has no salt and a single fixed round. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords. - """ - #=================================================================== - # class attrs - #=================================================================== - name = "mysql41" - _hash_prefix = u("*") - checksum_chars = uh.HEX_CHARS - checksum_size = 40 - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.upper() - - def _calc_checksum(self, secret): - # FIXME: no idea if mysql has a policy about handling unicode passwords - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(sha1(sha1(secret).digest()).hexdigest()).upper() - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/oracle.py b/.venv/Lib/site-packages/passlib/handlers/oracle.py deleted file mode 100644 index a094f37..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/oracle.py +++ /dev/null @@ -1,172 +0,0 @@ -"""passlib.handlers.oracle - Oracle DB Password Hashes""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify, unhexlify -from hashlib import sha1 -import re -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import to_unicode, xor_bytes -from passlib.utils.compat import irange, u, \ - uascii_to_str, unicode, str_to_uascii -from passlib.crypto.des import des_encrypt_block -import passlib.utils.handlers as uh -# local -__all__ = [ - "oracle10g", - "oracle11g" -] - -#============================================================================= -# oracle10 -#============================================================================= -def des_cbc_encrypt(key, value, iv=b'\x00' * 8, pad=b'\x00'): - """performs des-cbc encryption, returns only last block. - - this performs a specific DES-CBC encryption implementation - as needed by the Oracle10 hash. it probably won't be useful for - other purposes as-is. - - input value is null-padded to multiple of 8 bytes. - - :arg key: des key as bytes - :arg value: value to encrypt, as bytes. - :param iv: optional IV - :param pad: optional pad byte - - :returns: last block of DES-CBC encryption of all ``value``'s byte blocks. - """ - value += pad * (-len(value) % 8) # null pad to multiple of 8 - hash = iv # start things off - for offset in irange(0,len(value),8): - chunk = xor_bytes(hash, value[offset:offset+8]) - hash = des_encrypt_block(key, chunk) - return hash - -# magic string used as initial des key by oracle10 -ORACLE10_MAGIC = b"\x01\x23\x45\x67\x89\xAB\xCD\xEF" - -class oracle10(uh.HasUserContext, uh.StaticHandler): - """This class implements the password hash used by Oracle up to version 10g, and follows the :ref:`password-hash-api`. - - It does a single round of hashing, and relies on the username as the salt. - - The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods all require the - following additional contextual keywords: - - :type user: str - :param user: name of oracle user account this password is associated with. - """ - #=================================================================== - # algorithm information - #=================================================================== - name = "oracle10" - checksum_chars = uh.HEX_CHARS - checksum_size = 16 - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.upper() - - def _calc_checksum(self, secret): - # FIXME: not sure how oracle handles unicode. - # online docs about 10g hash indicate it puts ascii chars - # in a 2-byte encoding w/ the high byte set to null. - # they don't say how it handles other chars, or what encoding. - # - # so for now, encoding secret & user to utf-16-be, - # since that fits, and if secret/user is bytes, - # we assume utf-8, and decode first. - # - # this whole mess really needs someone w/ an oracle system, - # and some answers :) - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - user = to_unicode(self.user, "utf-8", param="user") - input = (user+secret).upper().encode("utf-16-be") - hash = des_cbc_encrypt(ORACLE10_MAGIC, input) - hash = des_cbc_encrypt(hash, input) - return hexlify(hash).decode("ascii").upper() - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# oracle11 -#============================================================================= -class oracle11(uh.HasSalt, uh.GenericHandler): - """This class implements the Oracle11g password hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 20 hexadecimal characters. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "oracle11" - setting_kwds = ("salt",) - checksum_size = 40 - checksum_chars = uh.UPPER_HEX_CHARS - - #--HasSalt-- - min_salt_size = max_salt_size = 20 - salt_chars = uh.UPPER_HEX_CHARS - - - #=================================================================== - # methods - #=================================================================== - _hash_regex = re.compile(u("^S:(?P[0-9a-f]{40})(?P[0-9a-f]{20})$"), re.I) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - m = cls._hash_regex.match(hash) - if not m: - raise uh.exc.InvalidHashError(cls) - salt, chk = m.group("salt", "chk") - return cls(salt=salt, checksum=chk.upper()) - - def to_string(self): - chk = self.checksum - hash = u("S:%s%s") % (chk.upper(), self.salt.upper()) - return uascii_to_str(hash) - - def _calc_checksum(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - chk = sha1(secret + unhexlify(self.salt.encode("ascii"))).hexdigest() - return str_to_uascii(chk).upper() - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/pbkdf2.py b/.venv/Lib/site-packages/passlib/handlers/pbkdf2.py deleted file mode 100644 index 274278d..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/pbkdf2.py +++ /dev/null @@ -1,475 +0,0 @@ -"""passlib.handlers.pbkdf - PBKDF2 based hashes""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify, unhexlify -from base64 import b64encode, b64decode -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import to_unicode -from passlib.utils.binary import ab64_decode, ab64_encode -from passlib.utils.compat import str_to_bascii, u, uascii_to_str, unicode -from passlib.crypto.digest import pbkdf2_hmac -import passlib.utils.handlers as uh -# local -__all__ = [ - "pbkdf2_sha1", - "pbkdf2_sha256", - "pbkdf2_sha512", - "cta_pbkdf2_sha1", - "dlitz_pbkdf2_sha1", - "grub_pbkdf2_sha512", -] - -#============================================================================= -# -#============================================================================= -class Pbkdf2DigestHandler(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """base class for various pbkdf2_{digest} algorithms""" - #=================================================================== - # class attrs - #=================================================================== - - #--GenericHandler-- - setting_kwds = ("salt", "salt_size", "rounds") - checksum_chars = uh.HASH64_CHARS - - #--HasSalt-- - default_salt_size = 16 - max_salt_size = 1024 - - #--HasRounds-- - default_rounds = None # set by subclass - min_rounds = 1 - max_rounds = 0xffffffff # setting at 32-bit limit for now - rounds_cost = "linear" - - #--this class-- - _digest = None # name of subclass-specified hash - - # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide sanity check. - # the underlying pbkdf2 specifies no bounds for either. - - # NOTE: defaults chosen to be at least as large as pbkdf2 rfc recommends... - # >8 bytes of entropy in salt, >1000 rounds - # increased due to time since rfc established - - #=================================================================== - # methods - #=================================================================== - - @classmethod - def from_string(cls, hash): - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls) - salt = ab64_decode(salt.encode("ascii")) - if chk: - chk = ab64_decode(chk.encode("ascii")) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self): - salt = ab64_encode(self.salt).decode("ascii") - chk = ab64_encode(self.checksum).decode("ascii") - return uh.render_mc3(self.ident, self.rounds, salt, chk) - - def _calc_checksum(self, secret): - # NOTE: pbkdf2_hmac() will encode secret & salt using UTF8 - return pbkdf2_hmac(self._digest, secret, self.salt, self.rounds, self.checksum_size) - -def create_pbkdf2_hash(hash_name, digest_size, rounds=12000, ident=None, module=__name__): - """create new Pbkdf2DigestHandler subclass for a specific hash""" - name = 'pbkdf2_' + hash_name - if ident is None: - ident = u("$pbkdf2-%s$") % (hash_name,) - base = Pbkdf2DigestHandler - return type(name, (base,), dict( - __module__=module, # so ABCMeta won't clobber it. - name=name, - ident=ident, - _digest = hash_name, - default_rounds=rounds, - checksum_size=digest_size, - encoded_checksum_size=(digest_size*4+2)//3, - __doc__="""This class implements a generic ``PBKDF2-HMAC-%(digest)s``-based password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt bytes. - If specified, the length must be between 0-1024 bytes. - If not specified, a %(dsc)d byte salt will be autogenerated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to %(dsc)d bytes, but can be any value between 0 and 1024. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to %(dr)d, but must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ % dict(digest=hash_name.upper(), dsc=base.default_salt_size, dr=rounds) - )) - -#------------------------------------------------------------------------ -# derived handlers -#------------------------------------------------------------------------ -pbkdf2_sha1 = create_pbkdf2_hash("sha1", 20, 131000, ident=u("$pbkdf2$")) -pbkdf2_sha256 = create_pbkdf2_hash("sha256", 32, 29000) -pbkdf2_sha512 = create_pbkdf2_hash("sha512", 64, 25000) - -ldap_pbkdf2_sha1 = uh.PrefixWrapper("ldap_pbkdf2_sha1", pbkdf2_sha1, "{PBKDF2}", "$pbkdf2$", ident=True) -ldap_pbkdf2_sha256 = uh.PrefixWrapper("ldap_pbkdf2_sha256", pbkdf2_sha256, "{PBKDF2-SHA256}", "$pbkdf2-sha256$", ident=True) -ldap_pbkdf2_sha512 = uh.PrefixWrapper("ldap_pbkdf2_sha512", pbkdf2_sha512, "{PBKDF2-SHA512}", "$pbkdf2-sha512$", ident=True) - -#============================================================================= -# cryptacular's pbkdf2 hash -#============================================================================= - -# bytes used by cta hash for base64 values 63 & 64 -CTA_ALTCHARS = b"-_" - -class cta_pbkdf2_sha1(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements Cryptacular's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt bytes. - If specified, it may be any length. - If not specified, a one will be autogenerated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 16 bytes, but can be any value between 0 and 1024. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 60000, must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "cta_pbkdf2_sha1" - setting_kwds = ("salt", "salt_size", "rounds") - ident = u("$p5k2$") - checksum_size = 20 - - # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a - # sanity check. underlying algorithm (and reference implementation) - # allows effectively unbounded values for both of these parameters. - - #--HasSalt-- - default_salt_size = 16 - max_salt_size = 1024 - - #--HasRounds-- - default_rounds = pbkdf2_sha1.default_rounds - min_rounds = 1 - max_rounds = 0xffffffff # setting at 32-bit limit for now - rounds_cost = "linear" - - #=================================================================== - # formatting - #=================================================================== - - # hash $p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0= - # ident $p5k2$ - # rounds 1000 - # salt ZxK4ZBJCfQg= - # chk jJZVscWtO--p1-xIZl6jhO2LKR0= - # NOTE: rounds in hex - - @classmethod - def from_string(cls, hash): - # NOTE: passlib deviation - forbidding zero-padded rounds - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16, handler=cls) - salt = b64decode(salt.encode("ascii"), CTA_ALTCHARS) - if chk: - chk = b64decode(chk.encode("ascii"), CTA_ALTCHARS) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self): - salt = b64encode(self.salt, CTA_ALTCHARS).decode("ascii") - chk = b64encode(self.checksum, CTA_ALTCHARS).decode("ascii") - return uh.render_mc3(self.ident, self.rounds, salt, chk, rounds_base=16) - - #=================================================================== - # backend - #=================================================================== - def _calc_checksum(self, secret): - # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8 - return pbkdf2_hmac("sha1", secret, self.salt, self.rounds, 20) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# dlitz's pbkdf2 hash -#============================================================================= -class dlitz_pbkdf2_sha1(uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """This class implements Dwayne Litzenberger's PBKDF2-based crypt algorithm, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If specified, it may be any length, but must use the characters in the regexp range ``[./0-9A-Za-z]``. - If not specified, a 16 character salt will be autogenerated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 16 bytes, but can be any value between 0 and 1024. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 60000, must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "dlitz_pbkdf2_sha1" - setting_kwds = ("salt", "salt_size", "rounds") - ident = u("$p5k2$") - _stub_checksum = u("0" * 48 + "=") - - # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a - # sanity check. underlying algorithm (and reference implementation) - # allows effectively unbounded values for both of these parameters. - - #--HasSalt-- - default_salt_size = 16 - max_salt_size = 1024 - salt_chars = uh.HASH64_CHARS - - #--HasRounds-- - # NOTE: for security, the default here is set to match pbkdf2_sha1, - # even though this hash's extra block makes it twice as slow. - default_rounds = pbkdf2_sha1.default_rounds - min_rounds = 1 - max_rounds = 0xffffffff # setting at 32-bit limit for now - rounds_cost = "linear" - - #=================================================================== - # formatting - #=================================================================== - - # hash $p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g - # ident $p5k2$ - # rounds c - # salt u9HvcT4d - # chk Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g - # rounds in lowercase hex, no zero padding - - @classmethod - def from_string(cls, hash): - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, rounds_base=16, - default_rounds=400, handler=cls) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self): - rounds = self.rounds - if rounds == 400: - rounds = None # omit rounds measurement if == 400 - return uh.render_mc3(self.ident, rounds, self.salt, self.checksum, rounds_base=16) - - def _get_config(self): - rounds = self.rounds - if rounds == 400: - rounds = None # omit rounds measurement if == 400 - return uh.render_mc3(self.ident, rounds, self.salt, None, rounds_base=16) - - #=================================================================== - # backend - #=================================================================== - def _calc_checksum(self, secret): - # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8 - salt = self._get_config() - result = pbkdf2_hmac("sha1", secret, salt, self.rounds, 24) - return ab64_encode(result).decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# crowd -#============================================================================= -class atlassian_pbkdf2_sha1(uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements the PBKDF2 hash used by Atlassian. - - It supports a fixed-length salt, and a fixed number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt bytes. - If specified, the length must be exactly 16 bytes. - If not specified, a salt will be autogenerated (this is recommended). - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include - ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #--GenericHandler-- - name = "atlassian_pbkdf2_sha1" - setting_kwds =("salt",) - ident = u("{PKCS5S2}") - checksum_size = 32 - - #--HasRawSalt-- - min_salt_size = max_salt_size = 16 - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - ident = cls.ident - if not hash.startswith(ident): - raise uh.exc.InvalidHashError(cls) - data = b64decode(hash[len(ident):].encode("ascii")) - salt, chk = data[:16], data[16:] - return cls(salt=salt, checksum=chk) - - def to_string(self): - data = self.salt + self.checksum - hash = self.ident + b64encode(data).decode("ascii") - return uascii_to_str(hash) - - def _calc_checksum(self, secret): - # TODO: find out what crowd's policy is re: unicode - # crowd seems to use a fixed number of rounds. - # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8 - return pbkdf2_hmac("sha1", secret, self.salt, 10000, 32) - -#============================================================================= -# grub -#============================================================================= -class grub_pbkdf2_sha512(uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.GenericHandler): - """This class implements Grub's pbkdf2-hmac-sha512 hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: bytes - :param salt: - Optional salt bytes. - If specified, the length must be between 0-1024 bytes. - If not specified, a 64 byte salt will be autogenerated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 64 bytes, but can be any value between 0 and 1024. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 19000, but must be within ``range(1,1<<32)``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - name = "grub_pbkdf2_sha512" - setting_kwds = ("salt", "salt_size", "rounds") - - ident = u("grub.pbkdf2.sha512.") - checksum_size = 64 - - # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide a - # sanity check. the underlying pbkdf2 specifies no bounds for either, - # and it's not clear what grub specifies. - - default_salt_size = 64 - max_salt_size = 1024 - - default_rounds = pbkdf2_sha512.default_rounds - min_rounds = 1 - max_rounds = 0xffffffff # setting at 32-bit limit for now - rounds_cost = "linear" - - @classmethod - def from_string(cls, hash): - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, sep=u("."), - handler=cls) - salt = unhexlify(salt.encode("ascii")) - if chk: - chk = unhexlify(chk.encode("ascii")) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self): - salt = hexlify(self.salt).decode("ascii").upper() - chk = hexlify(self.checksum).decode("ascii").upper() - return uh.render_mc3(self.ident, self.rounds, salt, chk, sep=u(".")) - - def _calc_checksum(self, secret): - # TODO: find out what grub's policy is re: unicode - # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8 - return pbkdf2_hmac("sha512", secret, self.salt, self.rounds, 64) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/phpass.py b/.venv/Lib/site-packages/passlib/handlers/phpass.py deleted file mode 100644 index 6736f0f..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/phpass.py +++ /dev/null @@ -1,135 +0,0 @@ -"""passlib.handlers.phpass - PHPass Portable Crypt - -phppass located - http://www.openwall.com/phpass/ -algorithm described - http://www.openwall.com/articles/PHP-Users-Passwords - -phpass context - blowfish, bsdi_crypt, phpass -""" -#============================================================================= -# imports -#============================================================================= -# core -from hashlib import md5 -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils.binary import h64 -from passlib.utils.compat import u, uascii_to_str, unicode -import passlib.utils.handlers as uh -# local -__all__ = [ - "phpass", -] - -#============================================================================= -# phpass -#============================================================================= -class phpass(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """This class implements the PHPass Portable Hash, and follows the :ref:`password-hash-api`. - - It supports a fixed-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 8 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 19, must be between 7 and 30, inclusive. - This value is logarithmic, the actual number of iterations used will be :samp:`2**{rounds}`. - - :type ident: str - :param ident: - phpBB3 uses ``H`` instead of ``P`` for its identifier, - this may be set to ``H`` in order to generate phpBB3 compatible hashes. - it defaults to ``P``. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "phpass" - setting_kwds = ("salt", "rounds", "ident") - checksum_chars = uh.HASH64_CHARS - - #--HasSalt-- - min_salt_size = max_salt_size = 8 - salt_chars = uh.HASH64_CHARS - - #--HasRounds-- - default_rounds = 19 - min_rounds = 7 - max_rounds = 30 - rounds_cost = "log2" - - #--HasManyIdents-- - default_ident = u("$P$") - ident_values = (u("$P$"), u("$H$")) - ident_aliases = {u("P"):u("$P$"), u("H"):u("$H$")} - - #=================================================================== - # formatting - #=================================================================== - - #$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0 - # $P$ - # 9 - # IQRaTwmf - # eRo7ud9Fh4E2PdI0S3r.L0 - - @classmethod - def from_string(cls, hash): - ident, data = cls._parse_ident(hash) - rounds, salt, chk = data[0], data[1:9], data[9:] - return cls( - ident=ident, - rounds=h64.decode_int6(rounds.encode("ascii")), - salt=salt, - checksum=chk or None, - ) - - def to_string(self): - hash = u("%s%s%s%s") % (self.ident, - h64.encode_int6(self.rounds).decode("ascii"), - self.salt, - self.checksum or u('')) - return uascii_to_str(hash) - - #=================================================================== - # backend - #=================================================================== - def _calc_checksum(self, secret): - # FIXME: can't find definitive policy on how phpass handles non-ascii. - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - real_rounds = 1<`_ - hash names. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - In addition to the standard :ref:`password-hash-api` methods, - this class also provides the following methods for manipulating Passlib - scram hashes in ways useful for pluging into a SCRAM protocol stack: - - .. automethod:: extract_digest_info - .. automethod:: extract_digest_algs - .. automethod:: derive_digest - """ - #=================================================================== - # class attrs - #=================================================================== - - # NOTE: unlike most GenericHandler classes, the 'checksum' attr of - # ScramHandler is actually a map from digest_name -> digest, so - # many of the standard methods have been overridden. - - # NOTE: max_salt_size and max_rounds are arbitrarily chosen to provide - # a sanity check; the underlying pbkdf2 specifies no bounds for either. - - #--GenericHandler-- - name = "scram" - setting_kwds = ("salt", "salt_size", "rounds", "algs") - ident = u("$scram$") - - #--HasSalt-- - default_salt_size = 12 - max_salt_size = 1024 - - #--HasRounds-- - default_rounds = 100000 - min_rounds = 1 - max_rounds = 2**32-1 - rounds_cost = "linear" - - #--custom-- - - # default algorithms when creating new hashes. - default_algs = ["sha-1", "sha-256", "sha-512"] - - # list of algs verify prefers to use, in order. - _verify_algs = ["sha-256", "sha-512", "sha-224", "sha-384", "sha-1"] - - #=================================================================== - # instance attrs - #=================================================================== - - # 'checksum' is different from most GenericHandler subclasses, - # in that it contains a dict mapping from alg -> digest, - # or None if no checksum present. - - # list of algorithms to create/compare digests for. - algs = None - - #=================================================================== - # scram frontend helpers - #=================================================================== - @classmethod - def extract_digest_info(cls, hash, alg): - """return (salt, rounds, digest) for specific hash algorithm. - - :type hash: str - :arg hash: - :class:`!scram` hash stored for desired user - - :type alg: str - :arg alg: - Name of digest algorithm (e.g. ``"sha-1"``) requested by client. - - This value is run through :func:`~passlib.crypto.digest.norm_hash_name`, - so it is case-insensitive, and can be the raw SCRAM - mechanism name (e.g. ``"SCRAM-SHA-1"``), the IANA name, - or the hashlib name. - - :raises KeyError: - If the hash does not contain an entry for the requested digest - algorithm. - - :returns: - A tuple containing ``(salt, rounds, digest)``, - where *digest* matches the raw bytes returned by - SCRAM's :func:`Hi` function for the stored password, - the provided *salt*, and the iteration count (*rounds*). - *salt* and *digest* are both raw (unencoded) bytes. - """ - # XXX: this could be sped up by writing custom parsing routine - # that just picks out relevant digest, and doesn't bother - # with full structure validation each time it's called. - alg = norm_hash_name(alg, 'iana') - self = cls.from_string(hash) - chkmap = self.checksum - if not chkmap: - raise ValueError("scram hash contains no digests") - return self.salt, self.rounds, chkmap[alg] - - @classmethod - def extract_digest_algs(cls, hash, format="iana"): - """Return names of all algorithms stored in a given hash. - - :type hash: str - :arg hash: - The :class:`!scram` hash to parse - - :type format: str - :param format: - This changes the naming convention used by the - returned algorithm names. By default the names - are IANA-compatible; possible values are ``"iana"`` or ``"hashlib"``. - - :returns: - Returns a list of digest algorithms; e.g. ``["sha-1"]`` - """ - # XXX: this could be sped up by writing custom parsing routine - # that just picks out relevant names, and doesn't bother - # with full structure validation each time it's called. - algs = cls.from_string(hash).algs - if format == "iana": - return algs - else: - return [norm_hash_name(alg, format) for alg in algs] - - @classmethod - def derive_digest(cls, password, salt, rounds, alg): - """helper to create SaltedPassword digest for SCRAM. - - This performs the step in the SCRAM protocol described as:: - - SaltedPassword := Hi(Normalize(password), salt, i) - - :type password: unicode or utf-8 bytes - :arg password: password to run through digest - - :type salt: bytes - :arg salt: raw salt data - - :type rounds: int - :arg rounds: number of iterations. - - :type alg: str - :arg alg: name of digest to use (e.g. ``"sha-1"``). - - :returns: - raw bytes of ``SaltedPassword`` - """ - if isinstance(password, bytes): - password = password.decode("utf-8") - # NOTE: pbkdf2_hmac() will encode secret & salt using utf-8, - # and handle normalizing alg name. - return pbkdf2_hmac(alg, saslprep(password), salt, rounds) - - #=================================================================== - # serialization - #=================================================================== - - @classmethod - def from_string(cls, hash): - hash = to_native_str(hash, "ascii", "hash") - if not hash.startswith("$scram$"): - raise uh.exc.InvalidHashError(cls) - parts = hash[7:].split("$") - if len(parts) != 3: - raise uh.exc.MalformedHashError(cls) - rounds_str, salt_str, chk_str = parts - - # decode rounds - rounds = int(rounds_str) - if rounds_str != str(rounds): # forbid zero padding, etc. - raise uh.exc.MalformedHashError(cls) - - # decode salt - try: - salt = ab64_decode(salt_str.encode("ascii")) - except TypeError: - raise uh.exc.MalformedHashError(cls) - - # decode algs/digest list - if not chk_str: - # scram hashes MUST have something here. - raise uh.exc.MalformedHashError(cls) - elif "=" in chk_str: - # comma-separated list of 'alg=digest' pairs - algs = None - chkmap = {} - for pair in chk_str.split(","): - alg, digest = pair.split("=") - try: - chkmap[alg] = ab64_decode(digest.encode("ascii")) - except TypeError: - raise uh.exc.MalformedHashError(cls) - else: - # comma-separated list of alg names, no digests - algs = chk_str - chkmap = None - - # return new object - return cls( - rounds=rounds, - salt=salt, - checksum=chkmap, - algs=algs, - ) - - def to_string(self): - salt = bascii_to_str(ab64_encode(self.salt)) - chkmap = self.checksum - chk_str = ",".join( - "%s=%s" % (alg, bascii_to_str(ab64_encode(chkmap[alg]))) - for alg in self.algs - ) - return '$scram$%d$%s$%s' % (self.rounds, salt, chk_str) - - #=================================================================== - # variant constructor - #=================================================================== - @classmethod - def using(cls, default_algs=None, algs=None, **kwds): - # parse aliases - if algs is not None: - assert default_algs is None - default_algs = algs - - # create subclass - subcls = super(scram, cls).using(**kwds) - - # fill in algs - if default_algs is not None: - subcls.default_algs = cls._norm_algs(default_algs) - return subcls - - #=================================================================== - # init - #=================================================================== - def __init__(self, algs=None, **kwds): - super(scram, self).__init__(**kwds) - - # init algs - digest_map = self.checksum - if algs is not None: - if digest_map is not None: - raise RuntimeError("checksum & algs kwds are mutually exclusive") - algs = self._norm_algs(algs) - elif digest_map is not None: - # derive algs list from digest map (if present). - algs = self._norm_algs(digest_map.keys()) - elif self.use_defaults: - algs = list(self.default_algs) - assert self._norm_algs(algs) == algs, "invalid default algs: %r" % (algs,) - else: - raise TypeError("no algs list specified") - self.algs = algs - - def _norm_checksum(self, checksum, relaxed=False): - if not isinstance(checksum, dict): - raise uh.exc.ExpectedTypeError(checksum, "dict", "checksum") - for alg, digest in iteritems(checksum): - if alg != norm_hash_name(alg, 'iana'): - raise ValueError("malformed algorithm name in scram hash: %r" % - (alg,)) - if len(alg) > 9: - raise ValueError("SCRAM limits algorithm names to " - "9 characters: %r" % (alg,)) - if not isinstance(digest, bytes): - raise uh.exc.ExpectedTypeError(digest, "raw bytes", "digests") - # TODO: verify digest size (if digest is known) - if 'sha-1' not in checksum: - # NOTE: required because of SCRAM spec. - raise ValueError("sha-1 must be in algorithm list of scram hash") - return checksum - - @classmethod - def _norm_algs(cls, algs): - """normalize algs parameter""" - if isinstance(algs, native_string_types): - algs = splitcomma(algs) - algs = sorted(norm_hash_name(alg, 'iana') for alg in algs) - if any(len(alg)>9 for alg in algs): - raise ValueError("SCRAM limits alg names to max of 9 characters") - if 'sha-1' not in algs: - # NOTE: required because of SCRAM spec (rfc 5802) - raise ValueError("sha-1 must be in algorithm list of scram hash") - return algs - - #=================================================================== - # migration - #=================================================================== - def _calc_needs_update(self, **kwds): - # marks hashes as deprecated if they don't include at least all default_algs. - # XXX: should we deprecate if they aren't exactly the same, - # to permit removing legacy hashes? - if not set(self.algs).issuperset(self.default_algs): - return True - - # hand off to base implementation - return super(scram, self)._calc_needs_update(**kwds) - - #=================================================================== - # digest methods - #=================================================================== - def _calc_checksum(self, secret, alg=None): - rounds = self.rounds - salt = self.salt - hash = self.derive_digest - if alg: - # if requested, generate digest for specific alg - return hash(secret, salt, rounds, alg) - else: - # by default, return dict containing digests for all algs - return dict( - (alg, hash(secret, salt, rounds, alg)) - for alg in self.algs - ) - - @classmethod - def verify(cls, secret, hash, full=False): - uh.validate_secret(secret) - self = cls.from_string(hash) - chkmap = self.checksum - if not chkmap: - raise ValueError("expected %s hash, got %s config string instead" % - (cls.name, cls.name)) - - # NOTE: to make the verify method efficient, we just calculate hash - # of shortest digest by default. apps can pass in "full=True" to - # check entire hash for consistency. - if full: - correct = failed = False - for alg, digest in iteritems(chkmap): - other = self._calc_checksum(secret, alg) - # NOTE: could do this length check in norm_algs(), - # but don't need to be that strict, and want to be able - # to parse hashes containing algs not supported by platform. - # it's fine if we fail here though. - if len(digest) != len(other): - raise ValueError("mis-sized %s digest in scram hash: %r != %r" - % (alg, len(digest), len(other))) - if consteq(other, digest): - correct = True - else: - failed = True - if correct and failed: - raise ValueError("scram hash verified inconsistently, " - "may be corrupted") - else: - return correct - else: - # XXX: should this just always use sha1 hash? would be faster. - # otherwise only verify against one hash, pick one w/ best security. - for alg in self._verify_algs: - if alg in chkmap: - other = self._calc_checksum(secret, alg) - return consteq(other, chkmap[alg]) - # there should always be sha-1 at the very least, - # or something went wrong inside _norm_algs() - raise AssertionError("sha-1 digest not found!") - - #=================================================================== - # - #=================================================================== - -#============================================================================= -# code used for testing scram against protocol examples during development. -#============================================================================= -##def _test_reference_scram(): -## "quick hack testing scram reference vectors" -## # NOTE: "n,," is GS2 header - see https://tools.ietf.org/html/rfc5801 -## from passlib.utils.compat import print_ -## -## engine = _scram_engine( -## alg="sha-1", -## salt='QSXCR+Q6sek8bf92'.decode("base64"), -## rounds=4096, -## password=u("pencil"), -## ) -## print_(engine.digest.encode("base64").rstrip()) -## -## msg = engine.format_auth_msg( -## username="user", -## client_nonce = "fyko+d2lbbFgONRv9qkxdawL", -## server_nonce = "3rfcNHYJY1ZVvWVs7j", -## header='c=biws', -## ) -## -## cp = engine.get_encoded_client_proof(msg) -## assert cp == "v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=", cp -## -## ss = engine.get_encoded_server_sig(msg) -## assert ss == "rmF9pqV8S7suAoZWja4dJRkFsKQ=", ss -## -##class _scram_engine(object): -## """helper class for verifying scram hash behavior -## against SCRAM protocol examples. not officially part of Passlib. -## -## takes in alg, salt, rounds, and a digest or password. -## -## can calculate the various keys & messages of the scram protocol. -## -## """ -## #========================================================= -## # init -## #========================================================= -## -## @classmethod -## def from_string(cls, hash, alg): -## "create record from scram hash, for given alg" -## return cls(alg, *scram.extract_digest_info(hash, alg)) -## -## def __init__(self, alg, salt, rounds, digest=None, password=None): -## self.alg = norm_hash_name(alg) -## self.salt = salt -## self.rounds = rounds -## self.password = password -## if password: -## data = scram.derive_digest(password, salt, rounds, alg) -## if digest and data != digest: -## raise ValueError("password doesn't match digest") -## else: -## digest = data -## elif not digest: -## raise TypeError("must provide password or digest") -## self.digest = digest -## -## #========================================================= -## # frontend methods -## #========================================================= -## def get_hash(self, data): -## "return hash of raw data" -## return hashlib.new(iana_to_hashlib(self.alg), data).digest() -## -## def get_client_proof(self, msg): -## "return client proof of specified auth msg text" -## return xor_bytes(self.client_key, self.get_client_sig(msg)) -## -## def get_encoded_client_proof(self, msg): -## return self.get_client_proof(msg).encode("base64").rstrip() -## -## def get_client_sig(self, msg): -## "return client signature of specified auth msg text" -## return self.get_hmac(self.stored_key, msg) -## -## def get_server_sig(self, msg): -## "return server signature of specified auth msg text" -## return self.get_hmac(self.server_key, msg) -## -## def get_encoded_server_sig(self, msg): -## return self.get_server_sig(msg).encode("base64").rstrip() -## -## def format_server_response(self, client_nonce, server_nonce): -## return 'r={client_nonce}{server_nonce},s={salt},i={rounds}'.format( -## client_nonce=client_nonce, -## server_nonce=server_nonce, -## rounds=self.rounds, -## salt=self.encoded_salt, -## ) -## -## def format_auth_msg(self, username, client_nonce, server_nonce, -## header='c=biws'): -## return ( -## 'n={username},r={client_nonce}' -## ',' -## 'r={client_nonce}{server_nonce},s={salt},i={rounds}' -## ',' -## '{header},r={client_nonce}{server_nonce}' -## ).format( -## username=username, -## client_nonce=client_nonce, -## server_nonce=server_nonce, -## salt=self.encoded_salt, -## rounds=self.rounds, -## header=header, -## ) -## -## #========================================================= -## # helpers to calculate & cache constant data -## #========================================================= -## def _calc_get_hmac(self): -## return get_prf("hmac-" + iana_to_hashlib(self.alg))[0] -## -## def _calc_client_key(self): -## return self.get_hmac(self.digest, b("Client Key")) -## -## def _calc_stored_key(self): -## return self.get_hash(self.client_key) -## -## def _calc_server_key(self): -## return self.get_hmac(self.digest, b("Server Key")) -## -## def _calc_encoded_salt(self): -## return self.salt.encode("base64").rstrip() -## -## #========================================================= -## # hacks for calculated attributes -## #========================================================= -## -## def __getattr__(self, attr): -## if not attr.startswith("_"): -## f = getattr(self, "_calc_" + attr, None) -## if f: -## value = f() -## setattr(self, attr, value) -## return value -## raise AttributeError("attribute not found") -## -## def __dir__(self): -## cdir = dir(self.__class__) -## attrs = set(cdir) -## attrs.update(self.__dict__) -## attrs.update(attr[6:] for attr in cdir -## if attr.startswith("_calc_")) -## return sorted(attrs) -## #========================================================= -## # eoc -## #========================================================= - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/scrypt.py b/.venv/Lib/site-packages/passlib/handlers/scrypt.py deleted file mode 100644 index 1686fda..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/scrypt.py +++ /dev/null @@ -1,383 +0,0 @@ -"""passlib.handlers.scrypt -- scrypt password hash""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, absolute_import -# core -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.crypto import scrypt as _scrypt -from passlib.utils import h64, to_bytes -from passlib.utils.binary import h64, b64s_decode, b64s_encode -from passlib.utils.compat import u, bascii_to_str, suppress_cause -from passlib.utils.decor import classproperty -import passlib.utils.handlers as uh -# local -__all__ = [ - "scrypt", -] - -#============================================================================= -# scrypt format identifiers -#============================================================================= - -IDENT_SCRYPT = u("$scrypt$") # identifier used by passlib -IDENT_7 = u("$7$") # used by official scrypt spec - -_UDOLLAR = u("$") - -#============================================================================= -# handler -#============================================================================= -class scrypt(uh.ParallelismMixin, uh.HasRounds, uh.HasRawSalt, uh.HasRawChecksum, uh.HasManyIdents, - uh.GenericHandler): - """This class implements an SCrypt-based password [#scrypt-home]_ hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, a variable number of rounds, - as well as some custom tuning parameters unique to scrypt (see below). - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If specified, the length must be between 0-1024 bytes. - If not specified, one will be auto-generated (this is recommended). - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 16 bytes, but can be any value between 0 and 1024. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 16, but must be within ``range(1,32)``. - - .. warning:: - - Unlike many hash algorithms, increasing the rounds value - will increase both the time *and memory* required to hash a password. - - :type block_size: int - :param block_size: - Optional block size to pass to scrypt hash function (the ``r`` parameter). - Useful for tuning scrypt to optimal performance for your CPU architecture. - Defaults to 8. - - :type parallelism: int - :param parallelism: - Optional parallelism to pass to scrypt hash function (the ``p`` parameter). - Defaults to 1. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. note:: - - The underlying scrypt hash function has a number of limitations - on it's parameter values, which forbids certain combinations of settings. - The requirements are: - - * ``linear_rounds = 2**`` - * ``linear_rounds < 2**(16 * block_size)`` - * ``block_size * parallelism <= 2**30-1`` - - .. todo:: - - This class currently does not support configuring default values - for ``block_size`` or ``parallelism`` via a :class:`~passlib.context.CryptContext` - configuration. - """ - - #=================================================================== - # class attrs - #=================================================================== - - #------------------------ - # PasswordHash - #------------------------ - name = "scrypt" - setting_kwds = ("ident", "salt", "salt_size", "rounds", "block_size", "parallelism") - - #------------------------ - # GenericHandler - #------------------------ - # NOTE: scrypt supports arbitrary output sizes. since it's output runs through - # pbkdf2-hmac-sha256 before returning, and this could be raised eventually... - # but a 256-bit digest is more than sufficient for password hashing. - # XXX: make checksum size configurable? could merge w/ argon2 code that does this. - checksum_size = 32 - - #------------------------ - # HasManyIdents - #------------------------ - default_ident = IDENT_SCRYPT - ident_values = (IDENT_SCRYPT, IDENT_7) - - #------------------------ - # HasRawSalt - #------------------------ - default_salt_size = 16 - max_salt_size = 1024 - - #------------------------ - # HasRounds - #------------------------ - # TODO: would like to dynamically pick this based on system - default_rounds = 16 - min_rounds = 1 - max_rounds = 31 # limited by scrypt alg - rounds_cost = "log2" - - # TODO: make default block size configurable via using(), and deprecatable via .needs_update() - - #=================================================================== - # instance attrs - #=================================================================== - - #: default parallelism setting (min=1 currently hardcoded in mixin) - parallelism = 1 - - #: default block size setting - block_size = 8 - - #=================================================================== - # variant constructor - #=================================================================== - - @classmethod - def using(cls, block_size=None, **kwds): - subcls = super(scrypt, cls).using(**kwds) - if block_size is not None: - if isinstance(block_size, uh.native_string_types): - block_size = int(block_size) - subcls.block_size = subcls._norm_block_size(block_size, relaxed=kwds.get("relaxed")) - - # make sure param combination is valid for scrypt() - try: - _scrypt.validate(1 << cls.default_rounds, cls.block_size, cls.parallelism) - except ValueError as err: - raise suppress_cause(ValueError("scrypt: invalid settings combination: " + str(err))) - - return subcls - - #=================================================================== - # parsing - #=================================================================== - - @classmethod - def from_string(cls, hash): - return cls(**cls.parse(hash)) - - @classmethod - def parse(cls, hash): - ident, suffix = cls._parse_ident(hash) - func = getattr(cls, "_parse_%s_string" % ident.strip(_UDOLLAR), None) - if func: - return func(suffix) - else: - raise uh.exc.InvalidHashError(cls) - - # - # passlib's format: - # $scrypt$ln=,r=,p=

$[$] - # where: - # logN, r, p -- decimal-encoded positive integer, no zero-padding - # logN -- log cost setting - # r -- block size setting (usually 8) - # p -- parallelism setting (usually 1) - # salt, digest -- b64-nopad encoded bytes - # - - @classmethod - def _parse_scrypt_string(cls, suffix): - # break params, salt, and digest sections - parts = suffix.split("$") - if len(parts) == 3: - params, salt, digest = parts - elif len(parts) == 2: - params, salt = parts - digest = None - else: - raise uh.exc.MalformedHashError(cls, "malformed hash") - - # break params apart - parts = params.split(",") - if len(parts) == 3: - nstr, bstr, pstr = parts - assert nstr.startswith("ln=") - assert bstr.startswith("r=") - assert pstr.startswith("p=") - else: - raise uh.exc.MalformedHashError(cls, "malformed settings field") - - return dict( - ident=IDENT_SCRYPT, - rounds=int(nstr[3:]), - block_size=int(bstr[2:]), - parallelism=int(pstr[2:]), - salt=b64s_decode(salt.encode("ascii")), - checksum=b64s_decode(digest.encode("ascii")) if digest else None, - ) - - # - # official format specification defined at - # https://gitlab.com/jas/scrypt-unix-crypt/blob/master/unix-scrypt.txt - # format: - # $7$[$] - # 0 12345 67890 1 - # where: - # All bytes use h64-little-endian encoding - # N: 6-bit log cost setting - # r: 30-bit block size setting - # p: 30-bit parallelism setting - # salt: variable length salt bytes - # digest: fixed 32-byte digest - # - - @classmethod - def _parse_7_string(cls, suffix): - # XXX: annoyingly, official spec embeds salt *raw*, yet doesn't specify a hash encoding. - # so assuming only h64 chars are valid for salt, and are ASCII encoded. - - # split into params & digest - parts = suffix.encode("ascii").split(b"$") - if len(parts) == 2: - params, digest = parts - elif len(parts) == 1: - params, = parts - digest = None - else: - raise uh.exc.MalformedHashError() - - # parse params & return - if len(params) < 11: - raise uh.exc.MalformedHashError(cls, "params field too short") - return dict( - ident=IDENT_7, - rounds=h64.decode_int6(params[:1]), - block_size=h64.decode_int30(params[1:6]), - parallelism=h64.decode_int30(params[6:11]), - salt=params[11:], - checksum=h64.decode_bytes(digest) if digest else None, - ) - - #=================================================================== - # formatting - #=================================================================== - def to_string(self): - ident = self.ident - if ident == IDENT_SCRYPT: - return "$scrypt$ln=%d,r=%d,p=%d$%s$%s" % ( - self.rounds, - self.block_size, - self.parallelism, - bascii_to_str(b64s_encode(self.salt)), - bascii_to_str(b64s_encode(self.checksum)), - ) - else: - assert ident == IDENT_7 - salt = self.salt - try: - salt.decode("ascii") - except UnicodeDecodeError: - raise suppress_cause(NotImplementedError("scrypt $7$ hashes dont support non-ascii salts")) - return bascii_to_str(b"".join([ - b"$7$", - h64.encode_int6(self.rounds), - h64.encode_int30(self.block_size), - h64.encode_int30(self.parallelism), - self.salt, - b"$", - h64.encode_bytes(self.checksum) - ])) - - #=================================================================== - # init - #=================================================================== - def __init__(self, block_size=None, **kwds): - super(scrypt, self).__init__(**kwds) - - # init block size - if block_size is None: - assert uh.validate_default_value(self, self.block_size, self._norm_block_size, - param="block_size") - else: - self.block_size = self._norm_block_size(block_size) - - # NOTE: if hash contains invalid complex constraint, relying on error - # being raised by scrypt call in _calc_checksum() - - @classmethod - def _norm_block_size(cls, block_size, relaxed=False): - return uh.norm_integer(cls, block_size, min=1, param="block_size", relaxed=relaxed) - - def _generate_salt(self): - salt = super(scrypt, self)._generate_salt() - if self.ident == IDENT_7: - # this format doesn't support non-ascii salts. - # as workaround, we take raw bytes, encoded to base64 - salt = b64s_encode(salt) - return salt - - #=================================================================== - # backend configuration - # NOTE: this following HasManyBackends' API, but provides it's own implementation, - # which actually switches the backend that 'passlib.crypto.scrypt.scrypt()' uses. - #=================================================================== - - @classproperty - def backends(cls): - return _scrypt.backend_values - - @classmethod - def get_backend(cls): - return _scrypt.backend - - @classmethod - def has_backend(cls, name="any"): - try: - cls.set_backend(name, dryrun=True) - return True - except uh.exc.MissingBackendError: - return False - - @classmethod - def set_backend(cls, name="any", dryrun=False): - _scrypt._set_backend(name, dryrun=dryrun) - - #=================================================================== - # digest calculation - #=================================================================== - def _calc_checksum(self, secret): - secret = to_bytes(secret, param="secret") - return _scrypt.scrypt(secret, self.salt, n=(1 << self.rounds), r=self.block_size, - p=self.parallelism, keylen=self.checksum_size) - - #=================================================================== - # hash migration - #=================================================================== - - def _calc_needs_update(self, **kwds): - """ - mark hash as needing update if rounds is outside desired bounds. - """ - # XXX: for now, marking all hashes which don't have matching block_size setting - if self.block_size != type(self).block_size: - return True - return super(scrypt, self)._calc_needs_update(**kwds) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/sha1_crypt.py b/.venv/Lib/site-packages/passlib/handlers/sha1_crypt.py deleted file mode 100644 index 8f9aa71..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/sha1_crypt.py +++ /dev/null @@ -1,158 +0,0 @@ -"""passlib.handlers.sha1_crypt -""" - -#============================================================================= -# imports -#============================================================================= - -# core -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import safe_crypt, test_crypt -from passlib.utils.binary import h64 -from passlib.utils.compat import u, unicode, irange -from passlib.crypto.digest import compile_hmac -import passlib.utils.handlers as uh -# local -__all__ = [ -] -#============================================================================= -# sha1-crypt -#============================================================================= -_BNULL = b'\x00' - -class sha1_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """This class implements the SHA1-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, an 8 character one will be autogenerated (this is recommended). - If specified, it must be 0-64 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type salt_size: int - :param salt_size: - Optional number of bytes to use when autogenerating new salts. - Defaults to 8 bytes, but can be any value between 0 and 64. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 480000, must be between 1 and 4294967295, inclusive. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - - #=================================================================== - # class attrs - #=================================================================== - #--GenericHandler-- - name = "sha1_crypt" - setting_kwds = ("salt", "salt_size", "rounds") - ident = u("$sha1$") - checksum_size = 28 - checksum_chars = uh.HASH64_CHARS - - #--HasSalt-- - default_salt_size = 8 - max_salt_size = 64 - salt_chars = uh.HASH64_CHARS - - #--HasRounds-- - default_rounds = 480000 # current passlib default - min_rounds = 1 # really, this should be higher. - max_rounds = 4294967295 # 32-bit integer limit - rounds_cost = "linear" - - #=================================================================== - # formatting - #=================================================================== - @classmethod - def from_string(cls, hash): - rounds, salt, chk = uh.parse_mc3(hash, cls.ident, handler=cls) - return cls(rounds=rounds, salt=salt, checksum=chk) - - def to_string(self, config=False): - chk = None if config else self.checksum - return uh.render_mc3(self.ident, self.rounds, self.salt, chk) - - #=================================================================== - # backend - #=================================================================== - backends = ("os_crypt", "builtin") - - #--------------------------------------------------------------- - # os_crypt backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_os_crypt(cls): - if test_crypt("test", '$sha1$1$Wq3GL2Vp$C8U25GvfHS8qGHim' - 'ExLaiSFlGkAe'): - cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) - return True - else: - return False - - def _calc_checksum_os_crypt(self, secret): - config = self.to_string(config=True) - hash = safe_crypt(secret, config) - if hash is None: - # py3's crypt.crypt() can't handle non-utf8 bytes. - # fallback to builtin alg, which is always available. - return self._calc_checksum_builtin(secret) - if not hash.startswith(config) or len(hash) != len(config) + 29: - raise uh.exc.CryptBackendError(self, config, hash) - return hash[-28:] - - #--------------------------------------------------------------- - # builtin backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_builtin(cls): - cls._set_calc_checksum_backend(cls._calc_checksum_builtin) - return True - - def _calc_checksum_builtin(self, secret): - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - if _BNULL in secret: - raise uh.exc.NullPasswordError(self) - rounds = self.rounds - # NOTE: this seed value is NOT the same as the config string - result = (u("%s$sha1$%s") % (self.salt, rounds)).encode("ascii") - # NOTE: this algorithm is essentially PBKDF1, modified to use HMAC. - keyed_hmac = compile_hmac("sha1", secret) - for _ in irange(rounds): - result = keyed_hmac(result) - return h64.encode_transposed_bytes(result, self._chk_offsets).decode("ascii") - - _chk_offsets = [ - 2,1,0, - 5,4,3, - 8,7,6, - 11,10,9, - 14,13,12, - 17,16,15, - 0,19,18, - ] - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/sha2_crypt.py b/.venv/Lib/site-packages/passlib/handlers/sha2_crypt.py deleted file mode 100644 index e6060c5..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/sha2_crypt.py +++ /dev/null @@ -1,534 +0,0 @@ -"""passlib.handlers.sha2_crypt - SHA256-Crypt / SHA512-Crypt""" -#============================================================================= -# imports -#============================================================================= -# core -import hashlib -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils import safe_crypt, test_crypt, \ - repeat_string, to_unicode -from passlib.utils.binary import h64 -from passlib.utils.compat import byte_elem_value, u, \ - uascii_to_str, unicode -import passlib.utils.handlers as uh -# local -__all__ = [ - "sha512_crypt", - "sha256_crypt", -] - -#============================================================================= -# pure-python backend, used by both sha256_crypt & sha512_crypt -# when crypt.crypt() backend is not available. -#============================================================================= -_BNULL = b'\x00' - -# pre-calculated offsets used to speed up C digest stage (see notes below). -# sequence generated using the following: - ##perms_order = "p,pp,ps,psp,sp,spp".split(",") - ##def offset(i): - ## key = (("p" if i % 2 else "") + ("s" if i % 3 else "") + - ## ("p" if i % 7 else "") + ("" if i % 2 else "p")) - ## return perms_order.index(key) - ##_c_digest_offsets = [(offset(i), offset(i+1)) for i in range(0,42,2)] -_c_digest_offsets = ( - (0, 3), (5, 1), (5, 3), (1, 2), (5, 1), (5, 3), (1, 3), - (4, 1), (5, 3), (1, 3), (5, 0), (5, 3), (1, 3), (5, 1), - (4, 3), (1, 3), (5, 1), (5, 2), (1, 3), (5, 1), (5, 3), - ) - -# map used to transpose bytes when encoding final sha256_crypt digest -_256_transpose_map = ( - 20, 10, 0, 11, 1, 21, 2, 22, 12, 23, 13, 3, 14, 4, 24, 5, - 25, 15, 26, 16, 6, 17, 7, 27, 8, 28, 18, 29, 19, 9, 30, 31, -) - -# map used to transpose bytes when encoding final sha512_crypt digest -_512_transpose_map = ( - 42, 21, 0, 1, 43, 22, 23, 2, 44, 45, 24, 3, 4, 46, 25, 26, - 5, 47, 48, 27, 6, 7, 49, 28, 29, 8, 50, 51, 30, 9, 10, 52, - 31, 32, 11, 53, 54, 33, 12, 13, 55, 34, 35, 14, 56, 57, 36, 15, - 16, 58, 37, 38, 17, 59, 60, 39, 18, 19, 61, 40, 41, 20, 62, 63, -) - -def _raw_sha2_crypt(pwd, salt, rounds, use_512=False): - """perform raw sha256-crypt / sha512-crypt - - this function provides a pure-python implementation of the internals - for the SHA256-Crypt and SHA512-Crypt algorithms; it doesn't - handle any of the parsing/validation of the hash strings themselves. - - :arg pwd: password chars/bytes to hash - :arg salt: salt chars to use - :arg rounds: linear rounds cost - :arg use_512: use sha512-crypt instead of sha256-crypt mode - - :returns: - encoded checksum chars - """ - #=================================================================== - # init & validate inputs - #=================================================================== - - # NOTE: the setup portion of this algorithm scales ~linearly in time - # with the size of the password, making it vulnerable to a DOS from - # unreasonably large inputs. the following code has some optimizations - # which would make things even worse, using O(pwd_len**2) memory - # when calculating digest P. - # - # to mitigate these two issues: 1) this code switches to a - # O(pwd_len)-memory algorithm for passwords that are much larger - # than average, and 2) Passlib enforces a library-wide max limit on - # the size of passwords it will allow, to prevent this algorithm and - # others from being DOSed in this way (see passlib.exc.PasswordSizeError - # for details). - - # validate secret - if isinstance(pwd, unicode): - # XXX: not sure what official unicode policy is, using this as default - pwd = pwd.encode("utf-8") - assert isinstance(pwd, bytes) - if _BNULL in pwd: - raise uh.exc.NullPasswordError(sha512_crypt if use_512 else sha256_crypt) - pwd_len = len(pwd) - - # validate rounds - assert 1000 <= rounds <= 999999999, "invalid rounds" - # NOTE: spec says out-of-range rounds should be clipped, instead of - # causing an error. this function assumes that's been taken care of - # by the handler class. - - # validate salt - assert isinstance(salt, unicode), "salt not unicode" - salt = salt.encode("ascii") - salt_len = len(salt) - assert salt_len < 17, "salt too large" - # NOTE: spec says salts larger than 16 bytes should be truncated, - # instead of causing an error. this function assumes that's been - # taken care of by the handler class. - - # load sha256/512 specific constants - if use_512: - hash_const = hashlib.sha512 - transpose_map = _512_transpose_map - else: - hash_const = hashlib.sha256 - transpose_map = _256_transpose_map - - #=================================================================== - # digest B - used as subinput to digest A - #=================================================================== - db = hash_const(pwd + salt + pwd).digest() - - #=================================================================== - # digest A - used to initialize first round of digest C - #=================================================================== - # start out with pwd + salt - a_ctx = hash_const(pwd + salt) - a_ctx_update = a_ctx.update - - # add pwd_len bytes of b, repeating b as many times as needed. - a_ctx_update(repeat_string(db, pwd_len)) - - # for each bit in pwd_len: add b if it's 1, or pwd if it's 0 - i = pwd_len - while i: - a_ctx_update(db if i & 1 else pwd) - i >>= 1 - - # finish A - da = a_ctx.digest() - - #=================================================================== - # digest P from password - used instead of password itself - # when calculating digest C. - #=================================================================== - if pwd_len < 96: - # this method is faster under python, but uses O(pwd_len**2) memory; - # so we don't use it for larger passwords to avoid a potential DOS. - dp = repeat_string(hash_const(pwd * pwd_len).digest(), pwd_len) - else: - # this method is slower under python, but uses a fixed amount of memory. - tmp_ctx = hash_const(pwd) - tmp_ctx_update = tmp_ctx.update - i = pwd_len-1 - while i: - tmp_ctx_update(pwd) - i -= 1 - dp = repeat_string(tmp_ctx.digest(), pwd_len) - assert len(dp) == pwd_len - - #=================================================================== - # digest S - used instead of salt itself when calculating digest C - #=================================================================== - ds = hash_const(salt * (16 + byte_elem_value(da[0]))).digest()[:salt_len] - assert len(ds) == salt_len, "salt_len somehow > hash_len!" - - #=================================================================== - # digest C - for a variable number of rounds, combine A, S, and P - # digests in various ways; in order to burn CPU time. - #=================================================================== - - # NOTE: the original SHA256/512-Crypt specification performs the C digest - # calculation using the following loop: - # - ##dc = da - ##i = 0 - ##while i < rounds: - ## tmp_ctx = hash_const(dp if i & 1 else dc) - ## if i % 3: - ## tmp_ctx.update(ds) - ## if i % 7: - ## tmp_ctx.update(dp) - ## tmp_ctx.update(dc if i & 1 else dp) - ## dc = tmp_ctx.digest() - ## i += 1 - # - # The code Passlib uses (below) implements an equivalent algorithm, - # it's just been heavily optimized to pre-calculate a large number - # of things beforehand. It works off of a couple of observations - # about the original algorithm: - # - # 1. each round is a combination of 'dc', 'ds', and 'dp'; determined - # by the whether 'i' a multiple of 2,3, and/or 7. - # 2. since lcm(2,3,7)==42, the series of combinations will repeat - # every 42 rounds. - # 3. even rounds 0-40 consist of 'hash(dc + round-specific-constant)'; - # while odd rounds 1-41 consist of hash(round-specific-constant + dc) - # - # Using these observations, the following code... - # * calculates the round-specific combination of ds & dp for each round 0-41 - # * runs through as many 42-round blocks as possible - # * runs through as many pairs of rounds as possible for remaining rounds - # * performs once last round if the total rounds should be odd. - # - # this cuts out a lot of the control overhead incurred when running the - # original loop 40,000+ times in python, resulting in ~20% increase in - # speed under CPython (though still 2x slower than glibc crypt) - - # prepare the 6 combinations of ds & dp which are needed - # (order of 'perms' must match how _c_digest_offsets was generated) - dp_dp = dp+dp - dp_ds = dp+ds - perms = [dp, dp_dp, dp_ds, dp_ds+dp, ds+dp, ds+dp_dp] - - # build up list of even-round & odd-round constants, - # and store in 21-element list as (even,odd) pairs. - data = [ (perms[even], perms[odd]) for even, odd in _c_digest_offsets] - - # perform as many full 42-round blocks as possible - dc = da - blocks, tail = divmod(rounds, 42) - while blocks: - for even, odd in data: - dc = hash_const(odd + hash_const(dc + even).digest()).digest() - blocks -= 1 - - # perform any leftover rounds - if tail: - # perform any pairs of rounds - pairs = tail>>1 - for even, odd in data[:pairs]: - dc = hash_const(odd + hash_const(dc + even).digest()).digest() - - # if rounds was odd, do one last round (since we started at 0, - # last round will be an even-numbered round) - if tail & 1: - dc = hash_const(dc + data[pairs][0]).digest() - - #=================================================================== - # encode digest using appropriate transpose map - #=================================================================== - return h64.encode_transposed_bytes(dc, transpose_map).decode("ascii") - -#============================================================================= -# handlers -#============================================================================= -_UROUNDS = u("rounds=") -_UDOLLAR = u("$") -_UZERO = u("0") - -class _SHA2_Common(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, - uh.GenericHandler): - """class containing common code shared by sha256_crypt & sha512_crypt""" - #=================================================================== - # class attrs - #=================================================================== - # name - set by subclass - setting_kwds = ("salt", "rounds", "implicit_rounds", "salt_size") - # ident - set by subclass - checksum_chars = uh.HASH64_CHARS - # checksum_size - set by subclass - - max_salt_size = 16 - salt_chars = uh.HASH64_CHARS - - min_rounds = 1000 # bounds set by spec - max_rounds = 999999999 # bounds set by spec - rounds_cost = "linear" - - _cdb_use_512 = False # flag for _calc_digest_builtin() - _rounds_prefix = None # ident + _UROUNDS - - #=================================================================== - # methods - #=================================================================== - implicit_rounds = False - - def __init__(self, implicit_rounds=None, **kwds): - super(_SHA2_Common, self).__init__(**kwds) - # if user calls hash() w/ 5000 rounds, default to compact form. - if implicit_rounds is None: - implicit_rounds = (self.use_defaults and self.rounds == 5000) - self.implicit_rounds = implicit_rounds - - def _parse_salt(self, salt): - # required per SHA2-crypt spec -- truncate config salts rather than throwing error - return self._norm_salt(salt, relaxed=self.checksum is None) - - def _parse_rounds(self, rounds): - # required per SHA2-crypt spec -- clip config rounds rather than throwing error - return self._norm_rounds(rounds, relaxed=self.checksum is None) - - @classmethod - def from_string(cls, hash): - # basic format this parses - - # $5$[rounds=$][$] - - # TODO: this *could* use uh.parse_mc3(), except that the rounds - # portion has a slightly different grammar. - - # convert to unicode, check for ident prefix, split on dollar signs. - hash = to_unicode(hash, "ascii", "hash") - ident = cls.ident - if not hash.startswith(ident): - raise uh.exc.InvalidHashError(cls) - assert len(ident) == 3 - parts = hash[3:].split(_UDOLLAR) - - # extract rounds value - if parts[0].startswith(_UROUNDS): - assert len(_UROUNDS) == 7 - rounds = parts.pop(0)[7:] - if rounds.startswith(_UZERO) and rounds != _UZERO: - raise uh.exc.ZeroPaddedRoundsError(cls) - rounds = int(rounds) - implicit_rounds = False - else: - rounds = 5000 - implicit_rounds = True - - # rest should be salt and checksum - if len(parts) == 2: - salt, chk = parts - elif len(parts) == 1: - salt = parts[0] - chk = None - else: - raise uh.exc.MalformedHashError(cls) - - # return new object - return cls( - rounds=rounds, - salt=salt, - checksum=chk or None, - implicit_rounds=implicit_rounds, - ) - - def to_string(self): - if self.rounds == 5000 and self.implicit_rounds: - hash = u("%s%s$%s") % (self.ident, self.salt, - self.checksum or u('')) - else: - hash = u("%srounds=%d$%s$%s") % (self.ident, self.rounds, - self.salt, self.checksum or u('')) - return uascii_to_str(hash) - - #=================================================================== - # backends - #=================================================================== - backends = ("os_crypt", "builtin") - - #--------------------------------------------------------------- - # os_crypt backend - #--------------------------------------------------------------- - - #: test hash for OS detection -- provided by subclass - _test_hash = None - - @classmethod - def _load_backend_os_crypt(cls): - if test_crypt(*cls._test_hash): - cls._set_calc_checksum_backend(cls._calc_checksum_os_crypt) - return True - else: - return False - - def _calc_checksum_os_crypt(self, secret): - config = self.to_string() - hash = safe_crypt(secret, config) - if hash is None: - # py3's crypt.crypt() can't handle non-utf8 bytes. - # fallback to builtin alg, which is always available. - return self._calc_checksum_builtin(secret) - # NOTE: avoiding full parsing routine via from_string().checksum, - # and just extracting the bit we need. - cs = self.checksum_size - if not hash.startswith(self.ident) or hash[-cs-1] != _UDOLLAR: - raise uh.exc.CryptBackendError(self, config, hash) - return hash[-cs:] - - #--------------------------------------------------------------- - # builtin backend - #--------------------------------------------------------------- - @classmethod - def _load_backend_builtin(cls): - cls._set_calc_checksum_backend(cls._calc_checksum_builtin) - return True - - def _calc_checksum_builtin(self, secret): - return _raw_sha2_crypt(secret, self.salt, self.rounds, - self._cdb_use_512) - - #=================================================================== - # eoc - #=================================================================== - -class sha256_crypt(_SHA2_Common): - """This class implements the SHA256-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 0-16 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 535000, must be between 1000 and 999999999, inclusive. - - .. note:: - per the official specification, when the rounds parameter is set to 5000, - it may be omitted from the hash string. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. - commented out, currently only supported by :meth:`hash`, and not via :meth:`using`: - - :type implicit_rounds: bool - :param implicit_rounds: - this is an internal option which generally doesn't need to be touched. - - this flag determines whether the hash should omit the rounds parameter - when encoding it to a string; this is only permitted by the spec for rounds=5000, - and the flag is ignored otherwise. the spec requires the two different - encodings be preserved as they are, instead of normalizing them. - """ - #=================================================================== - # class attrs - #=================================================================== - name = "sha256_crypt" - ident = u("$5$") - checksum_size = 43 - # NOTE: using 25/75 weighting of builtin & os_crypt backends - default_rounds = 535000 - - #=================================================================== - # backends - #=================================================================== - _test_hash = ("test", "$5$rounds=1000$test$QmQADEXMG8POI5W" - "Dsaeho0P36yK3Tcrgboabng6bkb/") - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# sha 512 crypt -#============================================================================= -class sha512_crypt(_SHA2_Common): - """This class implements the SHA512-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, one will be autogenerated (this is recommended). - If specified, it must be 0-16 characters, drawn from the regexp range ``[./0-9A-Za-z]``. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 656000, must be between 1000 and 999999999, inclusive. - - .. note:: - per the official specification, when the rounds parameter is set to 5000, - it may be omitted from the hash string. - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - - .. - commented out, currently only supported by :meth:`hash`, and not via :meth:`using`: - - :type implicit_rounds: bool - :param implicit_rounds: - this is an internal option which generally doesn't need to be touched. - - this flag determines whether the hash should omit the rounds parameter - when encoding it to a string; this is only permitted by the spec for rounds=5000, - and the flag is ignored otherwise. the spec requires the two different - encodings be preserved as they are, instead of normalizing them. - """ - - #=================================================================== - # class attrs - #=================================================================== - name = "sha512_crypt" - ident = u("$6$") - checksum_size = 86 - _cdb_use_512 = True - # NOTE: using 25/75 weighting of builtin & os_crypt backends - default_rounds = 656000 - - #=================================================================== - # backend - #=================================================================== - _test_hash = ("test", "$6$rounds=1000$test$2M/Lx6Mtobqj" - "Ljobw0Wmo4Q5OFx5nVLJvmgseatA6oMn" - "yWeBdRDx4DU.1H3eGmse6pgsOgDisWBG" - "I5c7TZauS0") - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/sun_md5_crypt.py b/.venv/Lib/site-packages/passlib/handlers/sun_md5_crypt.py deleted file mode 100644 index 0eeb4e7..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/sun_md5_crypt.py +++ /dev/null @@ -1,363 +0,0 @@ -"""passlib.handlers.sun_md5_crypt - Sun's Md5 Crypt, used on Solaris - -.. warning:: - - This implementation may not reproduce - the original Solaris behavior in some border cases. - See documentation for details. -""" - -#============================================================================= -# imports -#============================================================================= -# core -from hashlib import md5 -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import to_unicode -from passlib.utils.binary import h64 -from passlib.utils.compat import byte_elem_value, irange, u, \ - uascii_to_str, unicode, str_to_bascii -import passlib.utils.handlers as uh -# local -__all__ = [ - "sun_md5_crypt", -] - -#============================================================================= -# backend -#============================================================================= -# constant data used by alg - Hamlet act 3 scene 1 + null char -# exact bytes as in http://www.ibiblio.org/pub/docs/books/gutenberg/etext98/2ws2610.txt -# from Project Gutenberg. - -MAGIC_HAMLET = ( - b"To be, or not to be,--that is the question:--\n" - b"Whether 'tis nobler in the mind to suffer\n" - b"The slings and arrows of outrageous fortune\n" - b"Or to take arms against a sea of troubles,\n" - b"And by opposing end them?--To die,--to sleep,--\n" - b"No more; and by a sleep to say we end\n" - b"The heartache, and the thousand natural shocks\n" - b"That flesh is heir to,--'tis a consummation\n" - b"Devoutly to be wish'd. To die,--to sleep;--\n" - b"To sleep! perchance to dream:--ay, there's the rub;\n" - b"For in that sleep of death what dreams may come,\n" - b"When we have shuffled off this mortal coil,\n" - b"Must give us pause: there's the respect\n" - b"That makes calamity of so long life;\n" - b"For who would bear the whips and scorns of time,\n" - b"The oppressor's wrong, the proud man's contumely,\n" - b"The pangs of despis'd love, the law's delay,\n" - b"The insolence of office, and the spurns\n" - b"That patient merit of the unworthy takes,\n" - b"When he himself might his quietus make\n" - b"With a bare bodkin? who would these fardels bear,\n" - b"To grunt and sweat under a weary life,\n" - b"But that the dread of something after death,--\n" - b"The undiscover'd country, from whose bourn\n" - b"No traveller returns,--puzzles the will,\n" - b"And makes us rather bear those ills we have\n" - b"Than fly to others that we know not of?\n" - b"Thus conscience does make cowards of us all;\n" - b"And thus the native hue of resolution\n" - b"Is sicklied o'er with the pale cast of thought;\n" - b"And enterprises of great pith and moment,\n" - b"With this regard, their currents turn awry,\n" - b"And lose the name of action.--Soft you now!\n" - b"The fair Ophelia!--Nymph, in thy orisons\n" - b"Be all my sins remember'd.\n\x00" #<- apparently null at end of C string is included (test vector won't pass otherwise) -) - -# NOTE: these sequences are pre-calculated iteration ranges used by X & Y loops w/in rounds function below -xr = irange(7) -_XY_ROUNDS = [ - tuple((i,i,i+3) for i in xr), # xrounds 0 - tuple((i,i+1,i+4) for i in xr), # xrounds 1 - tuple((i,i+8,(i+11)&15) for i in xr), # yrounds 0 - tuple((i,(i+9)&15, (i+12)&15) for i in xr), # yrounds 1 -] -del xr - -def raw_sun_md5_crypt(secret, rounds, salt): - """given secret & salt, return encoded sun-md5-crypt checksum""" - global MAGIC_HAMLET - assert isinstance(secret, bytes) - assert isinstance(salt, bytes) - - # validate rounds - if rounds <= 0: - rounds = 0 - real_rounds = 4096 + rounds - # NOTE: spec seems to imply max 'rounds' is 2**32-1 - - # generate initial digest to start off round 0. - # NOTE: algorithm 'salt' includes full config string w/ trailing "$" - result = md5(secret + salt).digest() - assert len(result) == 16 - - # NOTE: many things in this function have been inlined (to speed up the loop - # as much as possible), to the point that this code barely resembles - # the algorithm as described in the docs. in particular: - # - # * all accesses to a given bit have been inlined using the formula - # rbitval(bit) = (rval((bit>>3) & 15) >> (bit & 7)) & 1 - # - # * the calculation of coinflip value R has been inlined - # - # * the conditional division of coinflip value V has been inlined as - # a shift right of 0 or 1. - # - # * the i, i+3, etc iterations are precalculated in lists. - # - # * the round-based conditional division of x & y is now performed - # by choosing an appropriate precalculated list, so that it only - # calculates the 7 bits which will actually be used. - # - X_ROUNDS_0, X_ROUNDS_1, Y_ROUNDS_0, Y_ROUNDS_1 = _XY_ROUNDS - - # NOTE: % appears to be *slightly* slower than &, so we prefer & if possible - - round = 0 - while round < real_rounds: - # convert last result byte string to list of byte-ints for easy access - rval = [ byte_elem_value(c) for c in result ].__getitem__ - - # build up X bit by bit - x = 0 - xrounds = X_ROUNDS_1 if (rval((round>>3) & 15)>>(round & 7)) & 1 else X_ROUNDS_0 - for i, ia, ib in xrounds: - a = rval(ia) - b = rval(ib) - v = rval((a >> (b % 5)) & 15) >> ((b>>(a&7)) & 1) - x |= ((rval((v>>3)&15)>>(v&7))&1) << i - - # build up Y bit by bit - y = 0 - yrounds = Y_ROUNDS_1 if (rval(((round+64)>>3) & 15)>>(round & 7)) & 1 else Y_ROUNDS_0 - for i, ia, ib in yrounds: - a = rval(ia) - b = rval(ib) - v = rval((a >> (b % 5)) & 15) >> ((b>>(a&7)) & 1) - y |= ((rval((v>>3)&15)>>(v&7))&1) << i - - # extract x'th and y'th bit, xoring them together to yeild "coin flip" - coin = ((rval(x>>3) >> (x&7)) ^ (rval(y>>3) >> (y&7))) & 1 - - # construct hash for this round - h = md5(result) - if coin: - h.update(MAGIC_HAMLET) - h.update(unicode(round).encode("ascii")) - result = h.digest() - - round += 1 - - # encode output - return h64.encode_transposed_bytes(result, _chk_offsets) - -# NOTE: same offsets as md5_crypt -_chk_offsets = ( - 12,6,0, - 13,7,1, - 14,8,2, - 15,9,3, - 5,10,4, - 11, -) - -#============================================================================= -# handler -#============================================================================= -class sun_md5_crypt(uh.HasRounds, uh.HasSalt, uh.GenericHandler): - """This class implements the Sun-MD5-Crypt password hash, and follows the :ref:`password-hash-api`. - - It supports a variable-length salt, and a variable number of rounds. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts the following optional keywords: - - :type salt: str - :param salt: - Optional salt string. - If not specified, a salt will be autogenerated (this is recommended). - If specified, it must be drawn from the regexp range ``[./0-9A-Za-z]``. - - :type salt_size: int - :param salt_size: - If no salt is specified, this parameter can be used to specify - the size (in characters) of the autogenerated salt. - It currently defaults to 8. - - :type rounds: int - :param rounds: - Optional number of rounds to use. - Defaults to 34000, must be between 0 and 4294963199, inclusive. - - :type bare_salt: bool - :param bare_salt: - Optional flag used to enable an alternate salt digest behavior - used by some hash strings in this scheme. - This flag can be ignored by most users. - Defaults to ``False``. - (see :ref:`smc-bare-salt` for details). - - :type relaxed: bool - :param relaxed: - By default, providing an invalid value for one of the other - keywords will result in a :exc:`ValueError`. If ``relaxed=True``, - and the error can be corrected, a :exc:`~passlib.exc.PasslibHashWarning` - will be issued instead. Correctable errors include ``rounds`` - that are too small or too large, and ``salt`` strings that are too long. - - .. versionadded:: 1.6 - """ - #=================================================================== - # class attrs - #=================================================================== - name = "sun_md5_crypt" - setting_kwds = ("salt", "rounds", "bare_salt", "salt_size") - checksum_chars = uh.HASH64_CHARS - checksum_size = 22 - - # NOTE: docs say max password length is 255. - # release 9u2 - - # NOTE: not sure if original crypt has a salt size limit, - # all instances that have been seen use 8 chars. - default_salt_size = 8 - max_salt_size = None - salt_chars = uh.HASH64_CHARS - - default_rounds = 34000 # current passlib default - min_rounds = 0 - max_rounds = 4294963199 ##2**32-1-4096 - # XXX: ^ not sure what it does if past this bound... does 32 int roll over? - rounds_cost = "linear" - - ident_values = (u("$md5$"), u("$md5,")) - - #=================================================================== - # instance attrs - #=================================================================== - bare_salt = False # flag to indicate legacy hashes that lack "$$" suffix - - #=================================================================== - # constructor - #=================================================================== - def __init__(self, bare_salt=False, **kwds): - self.bare_salt = bare_salt - super(sun_md5_crypt, self).__init__(**kwds) - - #=================================================================== - # internal helpers - #=================================================================== - @classmethod - def identify(cls, hash): - hash = uh.to_unicode_for_identify(hash) - return hash.startswith(cls.ident_values) - - @classmethod - def from_string(cls, hash): - hash = to_unicode(hash, "ascii", "hash") - - # - # detect if hash specifies rounds value. - # if so, parse and validate it. - # by end, set 'rounds' to int value, and 'tail' containing salt+chk - # - if hash.startswith(u("$md5$")): - rounds = 0 - salt_idx = 5 - elif hash.startswith(u("$md5,rounds=")): - idx = hash.find(u("$"), 12) - if idx == -1: - raise uh.exc.MalformedHashError(cls, "unexpected end of rounds") - rstr = hash[12:idx] - try: - rounds = int(rstr) - except ValueError: - raise uh.exc.MalformedHashError(cls, "bad rounds") - if rstr != unicode(rounds): - raise uh.exc.ZeroPaddedRoundsError(cls) - if rounds == 0: - # NOTE: not sure if this is forbidden by spec or not; - # but allowing it would complicate things, - # and it should never occur anyways. - raise uh.exc.MalformedHashError(cls, "explicit zero rounds") - salt_idx = idx+1 - else: - raise uh.exc.InvalidHashError(cls) - - # - # salt/checksum separation is kinda weird, - # to deal cleanly with some backward-compatible workarounds - # implemented by original implementation. - # - chk_idx = hash.rfind(u("$"), salt_idx) - if chk_idx == -1: - # ''-config for $-hash - salt = hash[salt_idx:] - chk = None - bare_salt = True - elif chk_idx == len(hash)-1: - if chk_idx > salt_idx and hash[-2] == u("$"): - raise uh.exc.MalformedHashError(cls, "too many '$' separators") - # $-config for $$-hash - salt = hash[salt_idx:-1] - chk = None - bare_salt = False - elif chk_idx > 0 and hash[chk_idx-1] == u("$"): - # $$-hash - salt = hash[salt_idx:chk_idx-1] - chk = hash[chk_idx+1:] - bare_salt = False - else: - # $-hash - salt = hash[salt_idx:chk_idx] - chk = hash[chk_idx+1:] - bare_salt = True - - return cls( - rounds=rounds, - salt=salt, - checksum=chk, - bare_salt=bare_salt, - ) - - def to_string(self, _withchk=True): - ss = u('') if self.bare_salt else u('$') - rounds = self.rounds - if rounds > 0: - hash = u("$md5,rounds=%d$%s%s") % (rounds, self.salt, ss) - else: - hash = u("$md5$%s%s") % (self.salt, ss) - if _withchk: - chk = self.checksum - hash = u("%s$%s") % (hash, chk) - return uascii_to_str(hash) - - #=================================================================== - # primary interface - #=================================================================== - # TODO: if we're on solaris, check for native crypt() support. - # this will require extra testing, to make sure native crypt - # actually behaves correctly. of particular importance: - # when using ""-config, make sure to append "$x" to string. - - def _calc_checksum(self, secret): - # NOTE: no reference for how sun_md5_crypt handles unicode - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - config = str_to_bascii(self.to_string(_withchk=False)) - return raw_sun_md5_crypt(secret, self.rounds, config).decode("ascii") - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/handlers/windows.py b/.venv/Lib/site-packages/passlib/handlers/windows.py deleted file mode 100644 index e17beba..0000000 --- a/.venv/Lib/site-packages/passlib/handlers/windows.py +++ /dev/null @@ -1,334 +0,0 @@ -"""passlib.handlers.nthash - Microsoft Windows -related hashes""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify -import logging; log = logging.getLogger(__name__) -from warnings import warn -# site -# pkg -from passlib.utils import to_unicode, right_pad_string -from passlib.utils.compat import unicode -from passlib.crypto.digest import lookup_hash -md4 = lookup_hash("md4").const -import passlib.utils.handlers as uh -# local -__all__ = [ - "lmhash", - "nthash", - "bsd_nthash", - "msdcc", - "msdcc2", -] - -#============================================================================= -# lanman hash -#============================================================================= -class lmhash(uh.TruncateMixin, uh.HasEncodingContext, uh.StaticHandler): - """This class implements the Lan Manager Password hash, and follows the :ref:`password-hash-api`. - - It has no salt and a single fixed round. - - The :meth:`~passlib.ifc.PasswordHash.using` method accepts a single - optional keyword: - - :param bool truncate_error: - By default, this will silently truncate passwords larger than 14 bytes. - Setting ``truncate_error=True`` will cause :meth:`~passlib.ifc.PasswordHash.hash` - to raise a :exc:`~passlib.exc.PasswordTruncateError` instead. - - .. versionadded:: 1.7 - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.verify` methods accept a single - optional keyword: - - :type encoding: str - :param encoding: - - This specifies what character encoding LMHASH should use when - calculating digest. It defaults to ``cp437``, the most - common encoding encountered. - - Note that while this class outputs digests in lower-case hexadecimal, - it will accept upper-case as well. - """ - #=================================================================== - # class attrs - #=================================================================== - - #-------------------- - # PasswordHash - #-------------------- - name = "lmhash" - setting_kwds = ("truncate_error",) - - #-------------------- - # GenericHandler - #-------------------- - checksum_chars = uh.HEX_CHARS - checksum_size = 32 - - #-------------------- - # TruncateMixin - #-------------------- - truncate_size = 14 - - #-------------------- - # custom - #-------------------- - default_encoding = "cp437" - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - # check for truncation (during .hash() calls only) - if self.use_defaults: - self._check_truncate_policy(secret) - - return hexlify(self.raw(secret, self.encoding)).decode("ascii") - - # magic constant used by LMHASH - _magic = b"KGS!@#$%" - - @classmethod - def raw(cls, secret, encoding=None): - """encode password using LANMAN hash algorithm. - - :type secret: unicode or utf-8 encoded bytes - :arg secret: secret to hash - :type encoding: str - :arg encoding: - optional encoding to use for unicode inputs. - this defaults to ``cp437``, which is the - common case for most situations. - - :returns: returns string of raw bytes - """ - if not encoding: - encoding = cls.default_encoding - # some nice empircal data re: different encodings is at... - # http://www.openwall.com/lists/john-dev/2011/08/01/2 - # http://www.freerainbowtables.com/phpBB3/viewtopic.php?t=387&p=12163 - from passlib.crypto.des import des_encrypt_block - MAGIC = cls._magic - if isinstance(secret, unicode): - # perform uppercasing while we're still unicode, - # to give a better shot at getting non-ascii chars right. - # (though some codepages do NOT upper-case the same as unicode). - secret = secret.upper().encode(encoding) - elif isinstance(secret, bytes): - # FIXME: just trusting ascii upper will work? - # and if not, how to do codepage specific case conversion? - # we could decode first using , - # but *that* might not always be right. - secret = secret.upper() - else: - raise TypeError("secret must be unicode or bytes") - secret = right_pad_string(secret, 14) - return des_encrypt_block(secret[0:7], MAGIC) + \ - des_encrypt_block(secret[7:14], MAGIC) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# ntlm hash -#============================================================================= -class nthash(uh.StaticHandler): - """This class implements the NT Password hash, and follows the :ref:`password-hash-api`. - - It has no salt and a single fixed round. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords. - - Note that while this class outputs lower-case hexadecimal digests, - it will accept upper-case digests as well. - """ - #=================================================================== - # class attrs - #=================================================================== - name = "nthash" - checksum_chars = uh.HEX_CHARS - checksum_size = 32 - - #=================================================================== - # methods - #=================================================================== - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - return hexlify(self.raw(secret)).decode("ascii") - - @classmethod - def raw(cls, secret): - """encode password using MD4-based NTHASH algorithm - - :arg secret: secret as unicode or utf-8 encoded bytes - - :returns: returns string of raw bytes - """ - secret = to_unicode(secret, "utf-8", param="secret") - # XXX: found refs that say only first 128 chars are used. - return md4(secret.encode("utf-16-le")).digest() - - @classmethod - def raw_nthash(cls, secret, hex=False): - warn("nthash.raw_nthash() is deprecated, and will be removed " - "in Passlib 1.8, please use nthash.raw() instead", - DeprecationWarning) - ret = nthash.raw(secret) - return hexlify(ret).decode("ascii") if hex else ret - - #=================================================================== - # eoc - #=================================================================== - -bsd_nthash = uh.PrefixWrapper("bsd_nthash", nthash, prefix="$3$$", ident="$3$$", - doc="""The class support FreeBSD's representation of NTHASH - (which is compatible with the :ref:`modular-crypt-format`), - and follows the :ref:`password-hash-api`. - - It has no salt and a single fixed round. - - The :meth:`~passlib.ifc.PasswordHash.hash` and :meth:`~passlib.ifc.PasswordHash.genconfig` methods accept no optional keywords. - """) - -##class ntlm_pair(object): -## "combined lmhash & nthash" -## name = "ntlm_pair" -## setting_kwds = () -## _hash_regex = re.compile(u"^(?P[0-9a-f]{32}):(?P[0-9][a-f]{32})$", -## re.I) -## -## @classmethod -## def identify(cls, hash): -## hash = to_unicode(hash, "latin-1", "hash") -## return len(hash) == 65 and cls._hash_regex.match(hash) is not None -## -## @classmethod -## def hash(cls, secret, config=None): -## if config is not None and not cls.identify(config): -## raise uh.exc.InvalidHashError(cls) -## return lmhash.hash(secret) + ":" + nthash.hash(secret) -## -## @classmethod -## def verify(cls, secret, hash): -## hash = to_unicode(hash, "ascii", "hash") -## m = cls._hash_regex.match(hash) -## if not m: -## raise uh.exc.InvalidHashError(cls) -## lm, nt = m.group("lm", "nt") -## # NOTE: verify against both in case encoding issue -## # causes one not to match. -## return lmhash.verify(secret, lm) or nthash.verify(secret, nt) - -#============================================================================= -# msdcc v1 -#============================================================================= -class msdcc(uh.HasUserContext, uh.StaticHandler): - """This class implements Microsoft's Domain Cached Credentials password hash, - and follows the :ref:`password-hash-api`. - - It has a fixed number of rounds, and uses the associated - username as the salt. - - The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods - have the following optional keywords: - - :type user: str - :param user: - String containing name of user account this password is associated with. - This is required to properly calculate the hash. - - This keyword is case-insensitive, and should contain just the username - (e.g. ``Administrator``, not ``SOMEDOMAIN\\Administrator``). - - Note that while this class outputs lower-case hexadecimal digests, - it will accept upper-case digests as well. - """ - name = "msdcc" - checksum_chars = uh.HEX_CHARS - checksum_size = 32 - - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - return hexlify(self.raw(secret, self.user)).decode("ascii") - - @classmethod - def raw(cls, secret, user): - """encode password using mscash v1 algorithm - - :arg secret: secret as unicode or utf-8 encoded bytes - :arg user: username to use as salt - - :returns: returns string of raw bytes - """ - secret = to_unicode(secret, "utf-8", param="secret").encode("utf-16-le") - user = to_unicode(user, "utf-8", param="user").lower().encode("utf-16-le") - return md4(md4(secret).digest() + user).digest() - -#============================================================================= -# msdcc2 aka mscash2 -#============================================================================= -class msdcc2(uh.HasUserContext, uh.StaticHandler): - """This class implements version 2 of Microsoft's Domain Cached Credentials - password hash, and follows the :ref:`password-hash-api`. - - It has a fixed number of rounds, and uses the associated - username as the salt. - - The :meth:`~passlib.ifc.PasswordHash.hash`, :meth:`~passlib.ifc.PasswordHash.genhash`, and :meth:`~passlib.ifc.PasswordHash.verify` methods - have the following extra keyword: - - :type user: str - :param user: - String containing name of user account this password is associated with. - This is required to properly calculate the hash. - - This keyword is case-insensitive, and should contain just the username - (e.g. ``Administrator``, not ``SOMEDOMAIN\\Administrator``). - """ - name = "msdcc2" - checksum_chars = uh.HEX_CHARS - checksum_size = 32 - - @classmethod - def _norm_hash(cls, hash): - return hash.lower() - - def _calc_checksum(self, secret): - return hexlify(self.raw(secret, self.user)).decode("ascii") - - @classmethod - def raw(cls, secret, user): - """encode password using msdcc v2 algorithm - - :type secret: unicode or utf-8 bytes - :arg secret: secret - - :type user: str - :arg user: username to use as salt - - :returns: returns string of raw bytes - """ - from passlib.crypto.digest import pbkdf2_hmac - secret = to_unicode(secret, "utf-8", param="secret").encode("utf-16-le") - user = to_unicode(user, "utf-8", param="user").lower().encode("utf-16-le") - tmp = md4(md4(secret).digest() + user).digest() - return pbkdf2_hmac("sha1", tmp, user, 10240, 16) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/hash.py b/.venv/Lib/site-packages/passlib/hash.py deleted file mode 100644 index 2cc0628..0000000 --- a/.venv/Lib/site-packages/passlib/hash.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -passlib.hash - proxy object mapping hash scheme names -> handlers - -================== -***** NOTICE ***** -================== - -This module does not actually contain any hashes. This file -is a stub that replaces itself with a proxy object. - -This proxy object (passlib.registry._PasslibRegistryProxy) -handles lazy-loading hashes as they are requested. - -The actual implementation of the various hashes is store elsewhere, -mainly in the submodules of the ``passlib.handlers`` subpackage. -""" - -#============================================================================= -# import proxy object and replace this module -#============================================================================= - -# XXX: if any platform has problem w/ lazy modules, could support 'non-lazy' -# version which just imports all schemes known to list_crypt_handlers() - -from passlib.registry import _proxy -import sys -sys.modules[__name__] = _proxy - -#============================================================================= -# HACK: the following bit of code is unreachable, but it's presence seems to -# help make autocomplete work for certain IDEs such as PyCharm. -# this list is automatically regenerated using $SOURCE/admin/regen.py -#============================================================================= - -#---------------------------------------------------- -# begin autocomplete hack (autogenerated 2016-11-10) -#---------------------------------------------------- -if False: - from passlib.handlers.argon2 import argon2 - from passlib.handlers.bcrypt import bcrypt, bcrypt_sha256 - from passlib.handlers.cisco import cisco_asa, cisco_pix, cisco_type7 - from passlib.handlers.des_crypt import bigcrypt, bsdi_crypt, crypt16, des_crypt - from passlib.handlers.digests import hex_md4, hex_md5, hex_sha1, hex_sha256, hex_sha512, htdigest - from passlib.handlers.django import django_bcrypt, django_bcrypt_sha256, django_des_crypt, django_disabled, django_pbkdf2_sha1, django_pbkdf2_sha256, django_salted_md5, django_salted_sha1 - from passlib.handlers.fshp import fshp - from passlib.handlers.ldap_digests import ldap_bcrypt, ldap_bsdi_crypt, ldap_des_crypt, ldap_md5, ldap_md5_crypt, ldap_plaintext, ldap_salted_md5, ldap_salted_sha1, ldap_salted_sha256, ldap_salted_sha512, ldap_sha1, ldap_sha1_crypt, ldap_sha256_crypt, ldap_sha512_crypt - from passlib.handlers.md5_crypt import apr_md5_crypt, md5_crypt - from passlib.handlers.misc import plaintext, unix_disabled, unix_fallback - from passlib.handlers.mssql import mssql2000, mssql2005 - from passlib.handlers.mysql import mysql323, mysql41 - from passlib.handlers.oracle import oracle10, oracle11 - from passlib.handlers.pbkdf2 import atlassian_pbkdf2_sha1, cta_pbkdf2_sha1, dlitz_pbkdf2_sha1, grub_pbkdf2_sha512, ldap_pbkdf2_sha1, ldap_pbkdf2_sha256, ldap_pbkdf2_sha512, pbkdf2_sha1, pbkdf2_sha256, pbkdf2_sha512 - from passlib.handlers.phpass import phpass - from passlib.handlers.postgres import postgres_md5 - from passlib.handlers.roundup import ldap_hex_md5, ldap_hex_sha1, roundup_plaintext - from passlib.handlers.scram import scram - from passlib.handlers.scrypt import scrypt - from passlib.handlers.sha1_crypt import sha1_crypt - from passlib.handlers.sha2_crypt import sha256_crypt, sha512_crypt - from passlib.handlers.sun_md5_crypt import sun_md5_crypt - from passlib.handlers.windows import bsd_nthash, lmhash, msdcc, msdcc2, nthash -#---------------------------------------------------- -# end autocomplete hack -#---------------------------------------------------- - -#============================================================================= -# eoc -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/hosts.py b/.venv/Lib/site-packages/passlib/hosts.py deleted file mode 100644 index 1f137a2..0000000 --- a/.venv/Lib/site-packages/passlib/hosts.py +++ /dev/null @@ -1,106 +0,0 @@ -"""passlib.hosts""" -#============================================================================= -# imports -#============================================================================= -# core -from warnings import warn -# pkg -from passlib.context import LazyCryptContext -from passlib.exc import PasslibRuntimeWarning -from passlib import registry -from passlib.utils import has_crypt, unix_crypt_schemes -# local -__all__ = [ - "linux_context", "linux2_context", - "openbsd_context", - "netbsd_context", - "freebsd_context", - "host_context", -] - -#============================================================================= -# linux support -#============================================================================= - -# known platform names - linux2 - -linux_context = linux2_context = LazyCryptContext( - schemes = [ "sha512_crypt", "sha256_crypt", "md5_crypt", - "des_crypt", "unix_disabled" ], - deprecated = [ "des_crypt" ], - ) - -#============================================================================= -# bsd support -#============================================================================= - -# known platform names - -# freebsd2 -# freebsd3 -# freebsd4 -# freebsd5 -# freebsd6 -# freebsd7 -# -# netbsd1 - -# referencing source via -http://fxr.googlebit.com -# freebsd 6,7,8 - des, md5, bcrypt, bsd_nthash -# netbsd - des, ext, md5, bcrypt, sha1 -# openbsd - des, ext, md5, bcrypt - -freebsd_context = LazyCryptContext(["bcrypt", "md5_crypt", "bsd_nthash", - "des_crypt", "unix_disabled"]) - -openbsd_context = LazyCryptContext(["bcrypt", "md5_crypt", "bsdi_crypt", - "des_crypt", "unix_disabled"]) - -netbsd_context = LazyCryptContext(["bcrypt", "sha1_crypt", "md5_crypt", - "bsdi_crypt", "des_crypt", "unix_disabled"]) - -# XXX: include darwin in this list? it's got a BSD crypt variant, -# but that's not what it uses for user passwords. - -#============================================================================= -# current host -#============================================================================= -if registry.os_crypt_present: - # NOTE: this is basically mimicing the output of os crypt(), - # except that it uses passlib's (usually stronger) defaults settings, - # and can be inspected and used much more flexibly. - - def _iter_os_crypt_schemes(): - """helper which iterates over supported os_crypt schemes""" - out = registry.get_supported_os_crypt_schemes() - if out: - # only offer disabled handler if there's another scheme in front, - # as this can't actually hash any passwords - out += ("unix_disabled",) - return out - - host_context = LazyCryptContext(_iter_os_crypt_schemes()) - -#============================================================================= -# other platforms -#============================================================================= - -# known platform strings - -# aix3 -# aix4 -# atheos -# beos5 -# darwin -# generic -# hp-ux11 -# irix5 -# irix6 -# mac -# next3 -# os2emx -# riscos -# sunos5 -# unixware7 - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/ifc.py b/.venv/Lib/site-packages/passlib/ifc.py deleted file mode 100644 index 559d256..0000000 --- a/.venv/Lib/site-packages/passlib/ifc.py +++ /dev/null @@ -1,353 +0,0 @@ -"""passlib.ifc - abstract interfaces used by Passlib""" -#============================================================================= -# imports -#============================================================================= -# core -import logging; log = logging.getLogger(__name__) -import sys -# site -# pkg -from passlib.utils.decor import deprecated_method -# local -__all__ = [ - "PasswordHash", -] - -#============================================================================= -# 2/3 compatibility helpers -#============================================================================= -def recreate_with_metaclass(meta): - """class decorator that re-creates class using metaclass""" - def builder(cls): - if meta is type(cls): - return cls - return meta(cls.__name__, cls.__bases__, cls.__dict__.copy()) - return builder - -#============================================================================= -# PasswordHash interface -#============================================================================= -from abc import ABCMeta, abstractmethod, abstractproperty - -# TODO: make this actually use abstractproperty(), -# now that we dropped py25, 'abc' is always available. - -# XXX: rename to PasswordHasher? - -@recreate_with_metaclass(ABCMeta) -class PasswordHash(object): - """This class describes an abstract interface which all password hashes - in Passlib adhere to. Under Python 2.6 and up, this is an actual - Abstract Base Class built using the :mod:`!abc` module. - - See the Passlib docs for full documentation. - """ - #=================================================================== - # class attributes - #=================================================================== - - #--------------------------------------------------------------- - # general information - #--------------------------------------------------------------- - ##name - ##setting_kwds - ##context_kwds - - #: flag which indicates this hasher matches a "disabled" hash - #: (e.g. unix_disabled, or django_disabled); and doesn't actually - #: depend on the provided password. - is_disabled = False - - #: Should be None, or a positive integer indicating hash - #: doesn't support secrets larger than this value. - #: Whether hash throws error or silently truncates secret - #: depends on .truncate_error and .truncate_verify_reject flags below. - #: NOTE: calls may treat as boolean, since value will never be 0. - #: .. versionadded:: 1.7 - #: .. TODO: passlib 1.8: deprecate/rename this attr to "max_secret_size"? - truncate_size = None - - # NOTE: these next two default to the optimistic "ideal", - # most hashes in passlib have to default to False - # for backward compat and/or expected behavior with existing hashes. - - #: If True, .hash() should throw a :exc:`~passlib.exc.PasswordSizeError` for - #: any secrets larger than .truncate_size. Many hashers default to False - #: for historical / compatibility purposes, indicating they will silently - #: truncate instead. All such hashers SHOULD support changing - #: the policy via ``.using(truncate_error=True)``. - #: .. versionadded:: 1.7 - #: .. TODO: passlib 1.8: deprecate/rename this attr to "truncate_hash_error"? - truncate_error = True - - #: If True, .verify() should reject secrets larger than max_password_size. - #: Many hashers default to False for historical / compatibility purposes, - #: indicating they will match on the truncated portion instead. - #: .. versionadded:: 1.7.1 - truncate_verify_reject = True - - #--------------------------------------------------------------- - # salt information -- if 'salt' in setting_kwds - #--------------------------------------------------------------- - ##min_salt_size - ##max_salt_size - ##default_salt_size - ##salt_chars - ##default_salt_chars - - #--------------------------------------------------------------- - # rounds information -- if 'rounds' in setting_kwds - #--------------------------------------------------------------- - ##min_rounds - ##max_rounds - ##default_rounds - ##rounds_cost - - #--------------------------------------------------------------- - # encoding info -- if 'encoding' in context_kwds - #--------------------------------------------------------------- - ##default_encoding - - #=================================================================== - # primary methods - #=================================================================== - @classmethod - @abstractmethod - def hash(cls, secret, # * - **setting_and_context_kwds): # pragma: no cover -- abstract method - r""" - Hash secret, returning result. - Should handle generating salt, etc, and should return string - containing identifier, salt & other configuration, as well as digest. - - :param \\*\\*settings_kwds: - - Pass in settings to customize configuration of resulting hash. - - .. deprecated:: 1.7 - - Starting with Passlib 1.7, callers should no longer pass settings keywords - (e.g. ``rounds`` or ``salt`` directly to :meth:`!hash`); should use - ``.using(**settings).hash(secret)`` construction instead. - - Support will be removed in Passlib 2.0. - - :param \\*\\*context_kwds: - - Specific algorithms may require context-specific information (such as the user login). - """ - # FIXME: need stub for classes that define .encrypt() instead ... - # this should call .encrypt(), and check for recursion back to here. - raise NotImplementedError("must be implemented by subclass") - - @deprecated_method(deprecated="1.7", removed="2.0", replacement=".hash()") - @classmethod - def encrypt(cls, *args, **kwds): - """ - Legacy alias for :meth:`hash`. - - .. deprecated:: 1.7 - This method was renamed to :meth:`!hash` in version 1.7. - This alias will be removed in version 2.0, and should only - be used for compatibility with Passlib 1.3 - 1.6. - """ - return cls.hash(*args, **kwds) - - # XXX: could provide default implementation which hands value to - # hash(), and then does constant-time comparision on the result - # (after making both are same string type) - @classmethod - @abstractmethod - def verify(cls, secret, hash, **context_kwds): # pragma: no cover -- abstract method - """verify secret against hash, returns True/False""" - raise NotImplementedError("must be implemented by subclass") - - #=================================================================== - # configuration - #=================================================================== - @classmethod - @abstractmethod - def using(cls, relaxed=False, **kwds): - """ - Return another hasher object (typically a subclass of the current one), - which integrates the configuration options specified by ``kwds``. - This should *always* return a new object, even if no configuration options are changed. - - .. todo:: - - document which options are accepted. - - :returns: - typically returns a subclass for most hasher implementations. - - .. todo:: - - add this method to main documentation. - """ - raise NotImplementedError("must be implemented by subclass") - - #=================================================================== - # migration - #=================================================================== - @classmethod - def needs_update(cls, hash, secret=None): - """ - check if hash's configuration is outside desired bounds, - or contains some other internal option which requires - updating the password hash. - - :param hash: - hash string to examine - - :param secret: - optional secret known to have verified against the provided hash. - (this is used by some hashes to detect legacy algorithm mistakes). - - :return: - whether secret needs re-hashing. - - .. versionadded:: 1.7 - """ - # by default, always report that we don't need update - return False - - #=================================================================== - # additional methods - #=================================================================== - @classmethod - @abstractmethod - def identify(cls, hash): # pragma: no cover -- abstract method - """check if hash belongs to this scheme, returns True/False""" - raise NotImplementedError("must be implemented by subclass") - - @deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genconfig(cls, **setting_kwds): # pragma: no cover -- abstract method - """ - compile settings into a configuration string for genhash() - - .. deprecated:: 1.7 - - As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0. - - For all known real-world uses, hashing a constant string - should provide equivalent functionality. - - This deprecation may be reversed if a use-case presents itself in the mean time. - """ - # NOTE: this fallback runs full hash alg, w/ whatever cost param is passed along. - # implementations (esp ones w/ variable cost) will want to subclass this - # with a constant-time implementation that just renders a config string. - if cls.context_kwds: - raise NotImplementedError("must be implemented by subclass") - return cls.using(**setting_kwds).hash("") - - @deprecated_method(deprecated="1.7", removed="2.0") - @classmethod - def genhash(cls, secret, config, **context): - """ - generated hash for secret, using settings from config/hash string - - .. deprecated:: 1.7 - - As of 1.7, this method is deprecated, and slated for complete removal in Passlib 2.0. - - This deprecation may be reversed if a use-case presents itself in the mean time. - """ - # XXX: if hashes reliably offered a .parse() method, could make a fallback for this. - raise NotImplementedError("must be implemented by subclass") - - #=================================================================== - # undocumented methods / attributes - #=================================================================== - # the following entry points are used internally by passlib, - # and aren't documented as part of the exposed interface. - # they are subject to change between releases, - # but are documented here so there's a list of them *somewhere*. - - #--------------------------------------------------------------- - # extra metdata - #--------------------------------------------------------------- - - #: this attribute shouldn't be used by hashers themselves, - #: it's reserved for the CryptContext to track which hashers are deprecated. - #: Note the context will only set this on objects it owns (and generated by .using()), - #: and WONT set it on global objects. - #: [added in 1.7] - #: TODO: document this, or at least the use of testing for - #: 'CryptContext().handler().deprecated' - deprecated = False - - #: optionally present if hasher corresponds to format built into Django. - #: this attribute (if not None) should be the Django 'algorithm' name. - #: also indicates to passlib.ext.django that (when installed in django), - #: django's native hasher should be used in preference to this one. - ## django_name - - #--------------------------------------------------------------- - # checksum information - defined for many hashes - #--------------------------------------------------------------- - ## checksum_chars - ## checksum_size - - #--------------------------------------------------------------- - # experimental methods - #--------------------------------------------------------------- - - ##@classmethod - ##def normhash(cls, hash): - ## """helper to clean up non-canonic instances of hash. - ## currently only provided by bcrypt() to fix an historical passlib issue. - ## """ - - # experimental helper to parse hash into components. - ##@classmethod - ##def parsehash(cls, hash, checksum=True, sanitize=False): - ## """helper to parse hash into components, returns dict""" - - # experiment helper to estimate bitsize of different hashes, - # implement for GenericHandler, but may be currently be off for some hashes. - # want to expand this into a way to programmatically compare - # "strengths" of different hashes and hash algorithms. - # still needs to have some factor for estimate relative cost per round, - # ala in the style of the scrypt whitepaper. - ##@classmethod - ##def bitsize(cls, **kwds): - ## """returns dict mapping component -> bits contributed. - ## components currently include checksum, salt, rounds. - ## """ - - #=================================================================== - # eoc - #=================================================================== - -class DisabledHash(PasswordHash): - """ - extended disabled-hash methods; only need be present if .disabled = True - """ - - is_disabled = True - - @classmethod - def disable(cls, hash=None): - """ - return string representing a 'disabled' hash; - optionally including previously enabled hash - (this is up to the individual scheme). - """ - # default behavior: ignore original hash, return standalone marker - return cls.hash("") - - @classmethod - def enable(cls, hash): - """ - given a disabled-hash string, - extract previously-enabled hash if one is present, - otherwise raises ValueError - """ - # default behavior: no way to restore original hash - raise ValueError("cannot restore original hash") - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/pwd.py b/.venv/Lib/site-packages/passlib/pwd.py deleted file mode 100644 index 27ed228..0000000 --- a/.venv/Lib/site-packages/passlib/pwd.py +++ /dev/null @@ -1,809 +0,0 @@ -"""passlib.pwd -- password generation helpers""" -#============================================================================= -# imports -#============================================================================= -from __future__ import absolute_import, division, print_function, unicode_literals -# core -import codecs -from collections import defaultdict -try: - from collections.abc import MutableMapping -except ImportError: - # py2 compat - from collections import MutableMapping -from math import ceil, log as logf -import logging; log = logging.getLogger(__name__) -import pkg_resources -import os -# site -# pkg -from passlib import exc -from passlib.utils.compat import PY2, irange, itervalues, int_types -from passlib.utils import rng, getrandstr, to_unicode -from passlib.utils.decor import memoized_property -# local -__all__ = [ - "genword", "default_charsets", - "genphrase", "default_wordsets", -] - -#============================================================================= -# constants -#============================================================================= - -# XXX: rename / publically document this map? -entropy_aliases = dict( - # barest protection from throttled online attack - unsafe=12, - - # some protection from unthrottled online attack - weak=24, - - # some protection from offline attacks - fair=36, - - # reasonable protection from offline attacks - strong=48, - - # very good protection from offline attacks - secure=60, -) - -#============================================================================= -# internal helpers -#============================================================================= - -def _superclasses(obj, cls): - """return remaining classes in object's MRO after cls""" - mro = type(obj).__mro__ - return mro[mro.index(cls)+1:] - - -def _self_info_rate(source): - """ - returns 'rate of self-information' -- - i.e. average (per-symbol) entropy of the sequence **source**, - where probability of a given symbol occurring is calculated based on - the number of occurrences within the sequence itself. - - if all elements of the source are unique, this should equal ``log(len(source), 2)``. - - :arg source: - iterable containing 0+ symbols - (e.g. list of strings or ints, string of characters, etc). - - :returns: - float bits of entropy - """ - try: - size = len(source) - except TypeError: - # if len() doesn't work, calculate size by summing counts later - size = None - counts = defaultdict(int) - for char in source: - counts[char] += 1 - if size is None: - values = counts.values() - size = sum(values) - else: - values = itervalues(counts) - if not size: - return 0 - # NOTE: the following performs ``- sum(value / size * logf(value / size, 2) for value in values)``, - # it just does so with as much pulled out of the sum() loop as possible... - return logf(size, 2) - sum(value * logf(value, 2) for value in values) / size - - -# def _total_self_info(source): -# """ -# return total self-entropy of a sequence -# (the average entropy per symbol * size of sequence) -# """ -# return _self_info_rate(source) * len(source) - - -def _open_asset_path(path, encoding=None): - """ - :param asset_path: - string containing absolute path to file, - or package-relative path using format - ``"python.module:relative/file/path"``. - - :returns: - filehandle opened in 'rb' mode - (unless encoding explicitly specified) - """ - if encoding: - return codecs.getreader(encoding)(_open_asset_path(path)) - if os.path.isabs(path): - return open(path, "rb") - package, sep, subpath = path.partition(":") - if not sep: - raise ValueError("asset path must be absolute file path " - "or use 'pkg.name:sub/path' format: %r" % (path,)) - return pkg_resources.resource_stream(package, subpath) - - -#: type aliases -_sequence_types = (list, tuple) -_set_types = (set, frozenset) - -#: set of elements that ensure_unique() has validated already. -_ensure_unique_cache = set() - - -def _ensure_unique(source, param="source"): - """ - helper for generators -- - Throws ValueError if source elements aren't unique. - Error message will display (abbreviated) repr of the duplicates in a string/list - """ - # check cache to speed things up for frozensets / tuples / strings - cache = _ensure_unique_cache - hashable = True - try: - if source in cache: - return True - except TypeError: - hashable = False - - # check if it has dup elements - if isinstance(source, _set_types) or len(set(source)) == len(source): - if hashable: - try: - cache.add(source) - except TypeError: - # XXX: under pypy, "list() in set()" above doesn't throw TypeError, - # but trying to add unhashable it to a set *does*. - pass - return True - - # build list of duplicate values - seen = set() - dups = set() - for elem in source: - (dups if elem in seen else seen).add(elem) - dups = sorted(dups) - trunc = 8 - if len(dups) > trunc: - trunc = 5 - dup_repr = ", ".join(repr(str(word)) for word in dups[:trunc]) - if len(dups) > trunc: - dup_repr += ", ... plus %d others" % (len(dups) - trunc) - - # throw error - raise ValueError("`%s` cannot contain duplicate elements: %s" % - (param, dup_repr)) - -#============================================================================= -# base generator class -#============================================================================= -class SequenceGenerator(object): - """ - Base class used by word & phrase generators. - - These objects take a series of options, corresponding - to those of the :func:`generate` function. - They act as callables which can be used to generate a password - or a list of 1+ passwords. They also expose some read-only - informational attributes. - - Parameters - ---------- - :param entropy: - Optionally specify the amount of entropy the resulting passwords - should contain (as measured with respect to the generator itself). - This will be used to auto-calculate the required password size. - - :param length: - Optionally specify the length of password to generate, - measured as count of whatever symbols the subclass uses (characters or words). - Note if ``entropy`` requires a larger minimum length, - that will be used instead. - - :param rng: - Optionally provide a custom RNG source to use. - Should be an instance of :class:`random.Random`, - defaults to :class:`random.SystemRandom`. - - Attributes - ---------- - .. autoattribute:: length - .. autoattribute:: symbol_count - .. autoattribute:: entropy_per_symbol - .. autoattribute:: entropy - - Subclassing - ----------- - Subclasses must implement the ``.__next__()`` method, - and set ``.symbol_count`` before calling base ``__init__`` method. - """ - #============================================================================= - # instance attrs - #============================================================================= - - #: requested size of final password - length = None - - #: requested entropy of final password - requested_entropy = "strong" - - #: random number source to use - rng = rng - - #: number of potential symbols (must be filled in by subclass) - symbol_count = None - - #============================================================================= - # init - #============================================================================= - def __init__(self, entropy=None, length=None, rng=None, **kwds): - - # make sure subclass set things up correctly - assert self.symbol_count is not None, "subclass must set .symbol_count" - - # init length & requested entropy - if entropy is not None or length is None: - if entropy is None: - entropy = self.requested_entropy - entropy = entropy_aliases.get(entropy, entropy) - if entropy <= 0: - raise ValueError("`entropy` must be positive number") - min_length = int(ceil(entropy / self.entropy_per_symbol)) - if length is None or length < min_length: - length = min_length - - self.requested_entropy = entropy - - if length < 1: - raise ValueError("`length` must be positive integer") - self.length = length - - # init other common options - if rng is not None: - self.rng = rng - - # hand off to parent - if kwds and _superclasses(self, SequenceGenerator) == (object,): - raise TypeError("Unexpected keyword(s): %s" % ", ".join(kwds.keys())) - super(SequenceGenerator, self).__init__(**kwds) - - #============================================================================= - # informational helpers - #============================================================================= - - @memoized_property - def entropy_per_symbol(self): - """ - Average entropy per symbol (assuming all symbols have equal probability) - """ - return logf(self.symbol_count, 2) - - @memoized_property - def entropy(self): - """ - Effective entropy of generated passwords. - - This value will always be a multiple of :attr:`entropy_per_symbol`. - If entropy is specified in constructor, :attr:`length` will be chosen so - so that this value is the smallest multiple >= :attr:`requested_entropy`. - """ - return self.length * self.entropy_per_symbol - - #============================================================================= - # generation - #============================================================================= - def __next__(self): - """main generation function, should create one password/phrase""" - raise NotImplementedError("implement in subclass") - - def __call__(self, returns=None): - """ - frontend used by genword() / genphrase() to create passwords - """ - if returns is None: - return next(self) - elif isinstance(returns, int_types): - return [next(self) for _ in irange(returns)] - elif returns is iter: - return self - else: - raise exc.ExpectedTypeError(returns, ", int, or ", "returns") - - def __iter__(self): - return self - - if PY2: - def next(self): - return self.__next__() - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# default charsets -#============================================================================= - -#: global dict of predefined characters sets -default_charsets = dict( - # ascii letters, digits, and some punctuation - ascii_72='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*?/', - - # ascii letters and digits - ascii_62='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', - - # ascii_50, without visually similar '1IiLl', '0Oo', '5S', '8B' - ascii_50='234679abcdefghjkmnpqrstuvwxyzACDEFGHJKMNPQRTUVWXYZ', - - # lower case hexadecimal - hex='0123456789abcdef', -) - -#============================================================================= -# password generator -#============================================================================= - -class WordGenerator(SequenceGenerator): - """ - Class which generates passwords by randomly choosing from a string of unique characters. - - Parameters - ---------- - :param chars: - custom character string to draw from. - - :param charset: - predefined charset to draw from. - - :param \\*\\*kwds: - all other keywords passed to the :class:`SequenceGenerator` parent class. - - Attributes - ---------- - .. autoattribute:: chars - .. autoattribute:: charset - .. autoattribute:: default_charsets - """ - #============================================================================= - # instance attrs - #============================================================================= - - #: Predefined character set in use (set to None for instances using custom 'chars') - charset = "ascii_62" - - #: string of chars to draw from -- usually filled in from charset - chars = None - - #============================================================================= - # init - #============================================================================= - def __init__(self, chars=None, charset=None, **kwds): - - # init chars and charset - if chars: - if charset: - raise TypeError("`chars` and `charset` are mutually exclusive") - else: - if not charset: - charset = self.charset - assert charset - chars = default_charsets[charset] - self.charset = charset - chars = to_unicode(chars, param="chars") - _ensure_unique(chars, param="chars") - self.chars = chars - - # hand off to parent - super(WordGenerator, self).__init__(**kwds) - # log.debug("WordGenerator(): entropy/char=%r", self.entropy_per_symbol) - - #============================================================================= - # informational helpers - #============================================================================= - - @memoized_property - def symbol_count(self): - return len(self.chars) - - #============================================================================= - # generation - #============================================================================= - - def __next__(self): - # XXX: could do things like optionally ensure certain character groups - # (e.g. letters & punctuation) are included - return getrandstr(self.rng, self.chars, self.length) - - #============================================================================= - # eoc - #============================================================================= - - -def genword(entropy=None, length=None, returns=None, **kwds): - """Generate one or more random passwords. - - This function uses :mod:`random.SystemRandom` to generate - one or more passwords using various character sets. - The complexity of the password can be specified - by size, or by the desired amount of entropy. - - Usage Example:: - - >>> # generate a random alphanumeric string with 48 bits of entropy (the default) - >>> from passlib import pwd - >>> pwd.genword() - 'DnBHvDjMK6' - - >>> # generate a random hexadecimal string with 52 bits of entropy - >>> pwd.genword(entropy=52, charset="hex") - '310f1a7ac793f' - - :param entropy: - Strength of resulting password, measured in 'guessing entropy' bits. - An appropriate **length** value will be calculated - based on the requested entropy amount, and the size of the character set. - - This can be a positive integer, or one of the following preset - strings: ``"weak"`` (24), ``"fair"`` (36), - ``"strong"`` (48), and ``"secure"`` (56). - - If neither this or **length** is specified, **entropy** will default - to ``"strong"`` (48). - - :param length: - Size of resulting password, measured in characters. - If omitted, the size is auto-calculated based on the **entropy** parameter. - - If both **entropy** and **length** are specified, - the stronger value will be used. - - :param returns: - Controls what this function returns: - - * If ``None`` (the default), this function will generate a single password. - * If an integer, this function will return a list containing that many passwords. - * If the ``iter`` constant, will return an iterator that yields passwords. - - :param chars: - - Optionally specify custom string of characters to use when randomly - generating a password. This option cannot be combined with **charset**. - - :param charset: - - The predefined character set to draw from (if not specified by **chars**). - There are currently four presets available: - - * ``"ascii_62"`` (the default) -- all digits and ascii upper & lowercase letters. - Provides ~5.95 entropy per character. - - * ``"ascii_50"`` -- subset which excludes visually similar characters - (``1IiLl0Oo5S8B``). Provides ~5.64 entropy per character. - - * ``"ascii_72"`` -- all digits and ascii upper & lowercase letters, - as well as some punctuation. Provides ~6.17 entropy per character. - - * ``"hex"`` -- Lower case hexadecimal. Providers 4 bits of entropy per character. - - :returns: - :class:`!unicode` string containing randomly generated password; - or list of 1+ passwords if :samp:`returns={int}` is specified. - """ - gen = WordGenerator(length=length, entropy=entropy, **kwds) - return gen(returns) - -#============================================================================= -# default wordsets -#============================================================================= - -def _load_wordset(asset_path): - """ - load wordset from compressed datafile within package data. - file should be utf-8 encoded - - :param asset_path: - string containing absolute path to wordset file, - or "python.module:relative/file/path". - - :returns: - tuple of words, as loaded from specified words file. - """ - # open resource file, convert to tuple of words (strip blank lines & ws) - with _open_asset_path(asset_path, "utf-8") as fh: - gen = (word.strip() for word in fh) - words = tuple(word for word in gen if word) - - # NOTE: works but not used - # # detect if file uses " " format, and strip numeric prefix - # def extract(row): - # idx, word = row.replace("\t", " ").split(" ", 1) - # if not idx.isdigit(): - # raise ValueError("row is not dice index + word") - # return word - # try: - # extract(words[-1]) - # except ValueError: - # pass - # else: - # words = tuple(extract(word) for word in words) - - log.debug("loaded %d-element wordset from %r", len(words), asset_path) - return words - - -class WordsetDict(MutableMapping): - """ - Special mapping used to store dictionary of wordsets. - Different from a regular dict in that some wordsets - may be lazy-loaded from an asset path. - """ - - #: dict of key -> asset path - paths = None - - #: dict of key -> value - _loaded = None - - def __init__(self, *args, **kwds): - self.paths = {} - self._loaded = {} - super(WordsetDict, self).__init__(*args, **kwds) - - def __getitem__(self, key): - try: - return self._loaded[key] - except KeyError: - pass - path = self.paths[key] - value = self._loaded[key] = _load_wordset(path) - return value - - def set_path(self, key, path): - """ - set asset path to lazy-load wordset from. - """ - self.paths[key] = path - - def __setitem__(self, key, value): - self._loaded[key] = value - - def __delitem__(self, key): - if key in self: - del self._loaded[key] - self.paths.pop(key, None) - else: - del self.paths[key] - - @property - def _keyset(self): - keys = set(self._loaded) - keys.update(self.paths) - return keys - - def __iter__(self): - return iter(self._keyset) - - def __len__(self): - return len(self._keyset) - - # NOTE: speeds things up, and prevents contains from lazy-loading - def __contains__(self, key): - return key in self._loaded or key in self.paths - - -#: dict of predefined word sets. -#: key is name of wordset, value should be sequence of words. -default_wordsets = WordsetDict() - -# register the wordsets built into passlib -for name in "eff_long eff_short eff_prefixed bip39".split(): - default_wordsets.set_path(name, "passlib:_data/wordsets/%s.txt" % name) - -#============================================================================= -# passphrase generator -#============================================================================= -class PhraseGenerator(SequenceGenerator): - """class which generates passphrases by randomly choosing - from a list of unique words. - - :param wordset: - wordset to draw from. - :param preset: - name of preset wordlist to use instead of ``wordset``. - :param spaces: - whether to insert spaces between words in output (defaults to ``True``). - :param \\*\\*kwds: - all other keywords passed to the :class:`SequenceGenerator` parent class. - - .. autoattribute:: wordset - """ - #============================================================================= - # instance attrs - #============================================================================= - - #: predefined wordset to use - wordset = "eff_long" - - #: list of words to draw from - words = None - - #: separator to use when joining words - sep = " " - - #============================================================================= - # init - #============================================================================= - def __init__(self, wordset=None, words=None, sep=None, **kwds): - - # load wordset - if words is not None: - if wordset is not None: - raise TypeError("`words` and `wordset` are mutually exclusive") - else: - if wordset is None: - wordset = self.wordset - assert wordset - words = default_wordsets[wordset] - self.wordset = wordset - - # init words - if not isinstance(words, _sequence_types): - words = tuple(words) - _ensure_unique(words, param="words") - self.words = words - - # init separator - if sep is None: - sep = self.sep - sep = to_unicode(sep, param="sep") - self.sep = sep - - # hand off to parent - super(PhraseGenerator, self).__init__(**kwds) - ##log.debug("PhraseGenerator(): entropy/word=%r entropy/char=%r min_chars=%r", - ## self.entropy_per_symbol, self.entropy_per_char, self.min_chars) - - #============================================================================= - # informational helpers - #============================================================================= - - @memoized_property - def symbol_count(self): - return len(self.words) - - #============================================================================= - # generation - #============================================================================= - - def __next__(self): - words = (self.rng.choice(self.words) for _ in irange(self.length)) - return self.sep.join(words) - - #============================================================================= - # eoc - #============================================================================= - - -def genphrase(entropy=None, length=None, returns=None, **kwds): - """Generate one or more random password / passphrases. - - This function uses :mod:`random.SystemRandom` to generate - one or more passwords; it can be configured to generate - alphanumeric passwords, or full english phrases. - The complexity of the password can be specified - by size, or by the desired amount of entropy. - - Usage Example:: - - >>> # generate random phrase with 48 bits of entropy - >>> from passlib import pwd - >>> pwd.genphrase() - 'gangly robbing salt shove' - - >>> # generate a random phrase with 52 bits of entropy - >>> # using a particular wordset - >>> pwd.genword(entropy=52, wordset="bip39") - 'wheat dilemma reward rescue diary' - - :param entropy: - Strength of resulting password, measured in 'guessing entropy' bits. - An appropriate **length** value will be calculated - based on the requested entropy amount, and the size of the word set. - - This can be a positive integer, or one of the following preset - strings: ``"weak"`` (24), ``"fair"`` (36), - ``"strong"`` (48), and ``"secure"`` (56). - - If neither this or **length** is specified, **entropy** will default - to ``"strong"`` (48). - - :param length: - Length of resulting password, measured in words. - If omitted, the size is auto-calculated based on the **entropy** parameter. - - If both **entropy** and **length** are specified, - the stronger value will be used. - - :param returns: - Controls what this function returns: - - * If ``None`` (the default), this function will generate a single password. - * If an integer, this function will return a list containing that many passwords. - * If the ``iter`` builtin, will return an iterator that yields passwords. - - :param words: - - Optionally specifies a list/set of words to use when randomly generating a passphrase. - This option cannot be combined with **wordset**. - - :param wordset: - - The predefined word set to draw from (if not specified by **words**). - There are currently four presets available: - - ``"eff_long"`` (the default) - - Wordset containing 7776 english words of ~7 letters. - Constructed by the EFF, it offers ~12.9 bits of entropy per word. - - This wordset (and the other ``"eff_"`` wordsets) - were `created by the EFF `_ - to aid in generating passwords. See their announcement page - for more details about the design & properties of these wordsets. - - ``"eff_short"`` - - Wordset containing 1296 english words of ~4.5 letters. - Constructed by the EFF, it offers ~10.3 bits of entropy per word. - - ``"eff_prefixed"`` - - Wordset containing 1296 english words of ~8 letters, - selected so that they each have a unique 3-character prefix. - Constructed by the EFF, it offers ~10.3 bits of entropy per word. - - ``"bip39"`` - - Wordset of 2048 english words of ~5 letters, - selected so that they each have a unique 4-character prefix. - Published as part of Bitcoin's `BIP 39 `_, - this wordset has exactly 11 bits of entropy per word. - - This list offers words that are typically shorter than ``"eff_long"`` - (at the cost of slightly less entropy); and much shorter than - ``"eff_prefixed"`` (at the cost of a longer unique prefix). - - :param sep: - Optional separator to use when joining words. - Defaults to ``" "`` (a space), but can be an empty string, a hyphen, etc. - - :returns: - :class:`!unicode` string containing randomly generated passphrase; - or list of 1+ passphrases if :samp:`returns={int}` is specified. - """ - gen = PhraseGenerator(entropy=entropy, length=length, **kwds) - return gen(returns) - -#============================================================================= -# strength measurement -# -# NOTE: -# for a little while, had rough draft of password strength measurement alg here. -# but not sure if there's value in yet another measurement algorithm, -# that's not just duplicating the effort of libraries like zxcbn. -# may revive it later, but for now, leaving some refs to others out there: -# * NIST 800-63 has simple alg -# * zxcvbn (https://tech.dropbox.com/2012/04/zxcvbn-realistic-password-strength-estimation/) -# might also be good, and has approach similar to composite approach i was already thinking about, -# but much more well thought out. -# * passfault (https://github.com/c-a-m/passfault) looks thorough, -# but may have licensing issues, plus porting to python looks like very big job :( -# * give a look at running things through zlib - might be able to cheaply -# catch extra redundancies. -#============================================================================= - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/registry.py b/.venv/Lib/site-packages/passlib/registry.py deleted file mode 100644 index 9964b25..0000000 --- a/.venv/Lib/site-packages/passlib/registry.py +++ /dev/null @@ -1,547 +0,0 @@ -"""passlib.registry - registry for password hash handlers""" -#============================================================================= -# imports -#============================================================================= -# core -import re -import logging; log = logging.getLogger(__name__) -from warnings import warn -# pkg -from passlib import exc -from passlib.exc import ExpectedTypeError, PasslibWarning -from passlib.ifc import PasswordHash -from passlib.utils import ( - is_crypt_handler, has_crypt as os_crypt_present, - unix_crypt_schemes as os_crypt_schemes, -) -from passlib.utils.compat import unicode_or_str -from passlib.utils.decor import memoize_single_value -# local -__all__ = [ - "register_crypt_handler_path", - "register_crypt_handler", - "get_crypt_handler", - "list_crypt_handlers", -] - -#============================================================================= -# proxy object used in place of 'passlib.hash' module -#============================================================================= -class _PasslibRegistryProxy(object): - """proxy module passlib.hash - - this module is in fact an object which lazy-loads - the requested password hash algorithm from wherever it has been stored. - it acts as a thin wrapper around :func:`passlib.registry.get_crypt_handler`. - """ - __name__ = "passlib.hash" - __package__ = None - - def __getattr__(self, attr): - if attr.startswith("_"): - raise AttributeError("missing attribute: %r" % (attr,)) - handler = get_crypt_handler(attr, None) - if handler: - return handler - else: - raise AttributeError("unknown password hash: %r" % (attr,)) - - def __setattr__(self, attr, value): - if attr.startswith("_"): - # writing to private attributes should behave normally. - # (required so GAE can write to the __loader__ attribute). - object.__setattr__(self, attr, value) - else: - # writing to public attributes should be treated - # as attempting to register a handler. - register_crypt_handler(value, _attr=attr) - - def __repr__(self): - return "" - - def __dir__(self): - # this adds in lazy-loaded handler names, - # otherwise this is the standard dir() implementation. - attrs = set(dir(self.__class__)) - attrs.update(self.__dict__) - attrs.update(_locations) - return sorted(attrs) - -# create single instance - available publically as 'passlib.hash' -_proxy = _PasslibRegistryProxy() - -#============================================================================= -# internal registry state -#============================================================================= - -# singleton uses to detect omitted keywords -_UNSET = object() - -# dict mapping name -> loaded handlers (just uses proxy object's internal dict) -_handlers = _proxy.__dict__ - -# dict mapping names -> import path for lazy loading. -# * import path should be "module.path" or "module.path:attr" -# * if attr omitted, "name" used as default. -_locations = dict( - # NOTE: this is a hardcoded list of the handlers built into passlib, - # applications should call register_crypt_handler_path() - apr_md5_crypt = "passlib.handlers.md5_crypt", - argon2 = "passlib.handlers.argon2", - atlassian_pbkdf2_sha1 = "passlib.handlers.pbkdf2", - bcrypt = "passlib.handlers.bcrypt", - bcrypt_sha256 = "passlib.handlers.bcrypt", - bigcrypt = "passlib.handlers.des_crypt", - bsd_nthash = "passlib.handlers.windows", - bsdi_crypt = "passlib.handlers.des_crypt", - cisco_pix = "passlib.handlers.cisco", - cisco_asa = "passlib.handlers.cisco", - cisco_type7 = "passlib.handlers.cisco", - cta_pbkdf2_sha1 = "passlib.handlers.pbkdf2", - crypt16 = "passlib.handlers.des_crypt", - des_crypt = "passlib.handlers.des_crypt", - django_argon2 = "passlib.handlers.django", - django_bcrypt = "passlib.handlers.django", - django_bcrypt_sha256 = "passlib.handlers.django", - django_pbkdf2_sha256 = "passlib.handlers.django", - django_pbkdf2_sha1 = "passlib.handlers.django", - django_salted_sha1 = "passlib.handlers.django", - django_salted_md5 = "passlib.handlers.django", - django_des_crypt = "passlib.handlers.django", - django_disabled = "passlib.handlers.django", - dlitz_pbkdf2_sha1 = "passlib.handlers.pbkdf2", - fshp = "passlib.handlers.fshp", - grub_pbkdf2_sha512 = "passlib.handlers.pbkdf2", - hex_md4 = "passlib.handlers.digests", - hex_md5 = "passlib.handlers.digests", - hex_sha1 = "passlib.handlers.digests", - hex_sha256 = "passlib.handlers.digests", - hex_sha512 = "passlib.handlers.digests", - htdigest = "passlib.handlers.digests", - ldap_plaintext = "passlib.handlers.ldap_digests", - ldap_md5 = "passlib.handlers.ldap_digests", - ldap_sha1 = "passlib.handlers.ldap_digests", - ldap_hex_md5 = "passlib.handlers.roundup", - ldap_hex_sha1 = "passlib.handlers.roundup", - ldap_salted_md5 = "passlib.handlers.ldap_digests", - ldap_salted_sha1 = "passlib.handlers.ldap_digests", - ldap_salted_sha256 = "passlib.handlers.ldap_digests", - ldap_salted_sha512 = "passlib.handlers.ldap_digests", - ldap_des_crypt = "passlib.handlers.ldap_digests", - ldap_bsdi_crypt = "passlib.handlers.ldap_digests", - ldap_md5_crypt = "passlib.handlers.ldap_digests", - ldap_bcrypt = "passlib.handlers.ldap_digests", - ldap_sha1_crypt = "passlib.handlers.ldap_digests", - ldap_sha256_crypt = "passlib.handlers.ldap_digests", - ldap_sha512_crypt = "passlib.handlers.ldap_digests", - ldap_pbkdf2_sha1 = "passlib.handlers.pbkdf2", - ldap_pbkdf2_sha256 = "passlib.handlers.pbkdf2", - ldap_pbkdf2_sha512 = "passlib.handlers.pbkdf2", - lmhash = "passlib.handlers.windows", - md5_crypt = "passlib.handlers.md5_crypt", - msdcc = "passlib.handlers.windows", - msdcc2 = "passlib.handlers.windows", - mssql2000 = "passlib.handlers.mssql", - mssql2005 = "passlib.handlers.mssql", - mysql323 = "passlib.handlers.mysql", - mysql41 = "passlib.handlers.mysql", - nthash = "passlib.handlers.windows", - oracle10 = "passlib.handlers.oracle", - oracle11 = "passlib.handlers.oracle", - pbkdf2_sha1 = "passlib.handlers.pbkdf2", - pbkdf2_sha256 = "passlib.handlers.pbkdf2", - pbkdf2_sha512 = "passlib.handlers.pbkdf2", - phpass = "passlib.handlers.phpass", - plaintext = "passlib.handlers.misc", - postgres_md5 = "passlib.handlers.postgres", - roundup_plaintext = "passlib.handlers.roundup", - scram = "passlib.handlers.scram", - scrypt = "passlib.handlers.scrypt", - sha1_crypt = "passlib.handlers.sha1_crypt", - sha256_crypt = "passlib.handlers.sha2_crypt", - sha512_crypt = "passlib.handlers.sha2_crypt", - sun_md5_crypt = "passlib.handlers.sun_md5_crypt", - unix_disabled = "passlib.handlers.misc", - unix_fallback = "passlib.handlers.misc", -) - -# master regexp for detecting valid handler names -_name_re = re.compile("^[a-z][a-z0-9_]+[a-z0-9]$") - -# names which aren't allowed for various reasons -# (mainly keyword conflicts in CryptContext) -_forbidden_names = frozenset(["onload", "policy", "context", "all", - "default", "none", "auto"]) - -#============================================================================= -# registry frontend functions -#============================================================================= -def _validate_handler_name(name): - """helper to validate handler name - - :raises ValueError: - * if empty name - * if name not lower case - * if name contains double underscores - * if name is reserved (e.g. ``context``, ``all``). - """ - if not name: - raise ValueError("handler name cannot be empty: %r" % (name,)) - if name.lower() != name: - raise ValueError("name must be lower-case: %r" % (name,)) - if not _name_re.match(name): - raise ValueError("invalid name (must be 3+ characters, " - " begin with a-z, and contain only underscore, a-z, " - "0-9): %r" % (name,)) - if '__' in name: - raise ValueError("name may not contain double-underscores: %r" % - (name,)) - if name in _forbidden_names: - raise ValueError("that name is not allowed: %r" % (name,)) - return True - -def register_crypt_handler_path(name, path): - """register location to lazy-load handler when requested. - - custom hashes may be registered via :func:`register_crypt_handler`, - or they may be registered by this function, - which will delay actually importing and loading the handler - until a call to :func:`get_crypt_handler` is made for the specified name. - - :arg name: name of handler - :arg path: module import path - - the specified module path should contain a password hash handler - called :samp:`{name}`, or the path may contain a colon, - specifying the module and module attribute to use. - for example, the following would cause ``get_handler("myhash")`` to look - for a class named ``myhash`` within the ``myapp.helpers`` module:: - - >>> from passlib.registry import registry_crypt_handler_path - >>> registry_crypt_handler_path("myhash", "myapp.helpers") - - ...while this form would cause ``get_handler("myhash")`` to look - for a class name ``MyHash`` within the ``myapp.helpers`` module:: - - >>> from passlib.registry import registry_crypt_handler_path - >>> registry_crypt_handler_path("myhash", "myapp.helpers:MyHash") - """ - # validate name - _validate_handler_name(name) - - # validate path - if path.startswith("."): - raise ValueError("path cannot start with '.'") - if ':' in path: - if path.count(':') > 1: - raise ValueError("path cannot have more than one ':'") - if path.find('.', path.index(':')) > -1: - raise ValueError("path cannot have '.' to right of ':'") - - # store location - _locations[name] = path - log.debug("registered path to %r handler: %r", name, path) - -def register_crypt_handler(handler, force=False, _attr=None): - """register password hash handler. - - this method immediately registers a handler with the internal passlib registry, - so that it will be returned by :func:`get_crypt_handler` when requested. - - :arg handler: the password hash handler to register - :param force: force override of existing handler (defaults to False) - :param _attr: - [internal kwd] if specified, ensures ``handler.name`` - matches this value, or raises :exc:`ValueError`. - - :raises TypeError: - if the specified object does not appear to be a valid handler. - - :raises ValueError: - if the specified object's name (or other required attributes) - contain invalid values. - - :raises KeyError: - if a (different) handler was already registered with - the same name, and ``force=True`` was not specified. - """ - # validate handler - if not is_crypt_handler(handler): - raise ExpectedTypeError(handler, "password hash handler", "handler") - if not handler: - raise AssertionError("``bool(handler)`` must be True") - - # validate name - name = handler.name - _validate_handler_name(name) - if _attr and _attr != name: - raise ValueError("handlers must be stored only under their own name (%r != %r)" % - (_attr, name)) - - # check for existing handler - other = _handlers.get(name) - if other: - if other is handler: - log.debug("same %r handler already registered: %r", name, handler) - return - elif force: - log.warning("overriding previously registered %r handler: %r", - name, other) - else: - raise KeyError("another %r handler has already been registered: %r" % - (name, other)) - - # register handler - _handlers[name] = handler - log.debug("registered %r handler: %r", name, handler) - -def get_crypt_handler(name, default=_UNSET): - """return handler for specified password hash scheme. - - this method looks up a handler for the specified scheme. - if the handler is not already loaded, - it checks if the location is known, and loads it first. - - :arg name: name of handler to return - :param default: optional default value to return if no handler with specified name is found. - - :raises KeyError: if no handler matching that name is found, and no default specified, a KeyError will be raised. - - :returns: handler attached to name, or default value (if specified). - """ - # catch invalid names before we check _handlers, - # since it's a module dict, and exposes things like __package__, etc. - if name.startswith("_"): - if default is _UNSET: - raise KeyError("invalid handler name: %r" % (name,)) - else: - return default - - # check if handler is already loaded - try: - return _handlers[name] - except KeyError: - pass - - # normalize name (and if changed, check dict again) - assert isinstance(name, unicode_or_str), "name must be string instance" - alt = name.replace("-","_").lower() - if alt != name: - warn("handler names should be lower-case, and use underscores instead " - "of hyphens: %r => %r" % (name, alt), PasslibWarning, - stacklevel=2) - name = alt - - # try to load using new name - try: - return _handlers[name] - except KeyError: - pass - - # check if lazy load mapping has been specified for this driver - path = _locations.get(name) - if path: - if ':' in path: - modname, modattr = path.split(":") - else: - modname, modattr = path, name - ##log.debug("loading %r handler from path: '%s:%s'", name, modname, modattr) - - # try to load the module - any import errors indicate runtime config, usually - # either missing package, or bad path provided to register_crypt_handler_path() - mod = __import__(modname, fromlist=[modattr], level=0) - - # first check if importing module triggered register_crypt_handler(), - # (this is discouraged due to its magical implicitness) - handler = _handlers.get(name) - if handler: - # XXX: issue deprecation warning here? - assert is_crypt_handler(handler), "unexpected object: name=%r object=%r" % (name, handler) - return handler - - # then get real handler & register it - handler = getattr(mod, modattr) - register_crypt_handler(handler, _attr=name) - return handler - - # fail! - if default is _UNSET: - raise KeyError("no crypt handler found for algorithm: %r" % (name,)) - else: - return default - -def list_crypt_handlers(loaded_only=False): - """return sorted list of all known crypt handler names. - - :param loaded_only: if ``True``, only returns names of handlers which have actually been loaded. - - :returns: list of names of all known handlers - """ - names = set(_handlers) - if not loaded_only: - names.update(_locations) - # strip private attrs out of namespace and sort. - # TODO: make _handlers a separate list, so we don't have module namespace mixed in. - return sorted(name for name in names if not name.startswith("_")) - -# NOTE: these two functions mainly exist just for the unittests... - -def _has_crypt_handler(name, loaded_only=False): - """check if handler name is known. - - this is only useful for two cases: - - * quickly checking if handler has already been loaded - * checking if handler exists, without actually loading it - - :arg name: name of handler - :param loaded_only: if ``True``, returns False if handler exists but hasn't been loaded - """ - return (name in _handlers) or (not loaded_only and name in _locations) - -def _unload_handler_name(name, locations=True): - """unloads a handler from the registry. - - .. warning:: - - this is an internal function, - used only by the unittests. - - if loaded handler is found with specified name, it's removed. - if path to lazy load handler is found, it's removed. - - missing names are a noop. - - :arg name: name of handler to unload - :param locations: if False, won't purge registered handler locations (default True) - """ - if name in _handlers: - del _handlers[name] - if locations and name in _locations: - del _locations[name] - -#============================================================================= -# inspection helpers -#============================================================================= - -#------------------------------------------------------------------ -# general -#------------------------------------------------------------------ - -# TODO: needs UTs -def _resolve(hasher, param="value"): - """ - internal helper to resolve argument to hasher object - """ - if is_crypt_handler(hasher): - return hasher - elif isinstance(hasher, unicode_or_str): - return get_crypt_handler(hasher) - else: - raise exc.ExpectedTypeError(hasher, unicode_or_str, param) - - -#: backend aliases -ANY = "any" -BUILTIN = "builtin" -OS_CRYPT = "os_crypt" - -# TODO: needs UTs -def has_backend(hasher, backend=ANY, safe=False): - """ - Test if specified backend is available for hasher. - - :param hasher: - Hasher name or object. - - :param backend: - Name of backend, or ``"any"`` if any backend will do. - For hashers without multiple backends, will pretend - they have a single backend named ``"builtin"``. - - :param safe: - By default, throws error if backend is unknown. - If ``safe=True``, will just return false value. - - :raises ValueError: - * if hasher name is unknown. - * if backend is unknown to hasher, and safe=False. - - :return: - True if backend available, False if not available, - and None if unknown + safe=True. - """ - hasher = _resolve(hasher) - - if backend == ANY: - if not hasattr(hasher, "get_backend"): - # single backend, assume it's loaded - return True - - # multiple backends, check at least one is loadable - try: - hasher.get_backend() - return True - except exc.MissingBackendError: - return False - - # test for specific backend - if hasattr(hasher, "has_backend"): - # multiple backends - if safe and backend not in hasher.backends: - return None - return hasher.has_backend(backend) - - # single builtin backend - if backend == BUILTIN: - return True - elif safe: - return None - else: - raise exc.UnknownBackendError(hasher, backend) - -#------------------------------------------------------------------ -# os crypt -#------------------------------------------------------------------ - -# TODO: move unix_crypt_schemes list to here. -# os_crypt_schemes -- alias for unix_crypt_schemes above - - -# TODO: needs UTs -@memoize_single_value -def get_supported_os_crypt_schemes(): - """ - return tuple of schemes which :func:`crypt.crypt` natively supports. - """ - if not os_crypt_present: - return () - cache = tuple(name for name in os_crypt_schemes - if get_crypt_handler(name).has_backend(OS_CRYPT)) - if not cache: # pragma: no cover -- sanity check - # no idea what OS this could happen on... - import platform - warn("crypt.crypt() function is present, but doesn't support any " - "formats known to passlib! (system=%r release=%r)" % - (platform.system(), platform.release()), - exc.PasslibRuntimeWarning) - return cache - - -# TODO: needs UTs -def has_os_crypt_support(hasher): - """ - check if hash is supported by native :func:`crypt.crypt` function. - if :func:`crypt.crypt` is not present, will always return False. - - :param hasher: - name or hasher object. - - :returns bool: - True if hash format is supported by OS, else False. - """ - return os_crypt_present and has_backend(hasher, OS_CRYPT, safe=True) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/__init__.py b/.venv/Lib/site-packages/passlib/tests/__init__.py deleted file mode 100644 index 389da76..0000000 --- a/.venv/Lib/site-packages/passlib/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""passlib tests""" diff --git a/.venv/Lib/site-packages/passlib/tests/__main__.py b/.venv/Lib/site-packages/passlib/tests/__main__.py deleted file mode 100644 index 2424576..0000000 --- a/.venv/Lib/site-packages/passlib/tests/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os -from nose import run -run( - defaultTest=os.path.dirname(__file__), -) - diff --git a/.venv/Lib/site-packages/passlib/tests/_test_bad_register.py b/.venv/Lib/site-packages/passlib/tests/_test_bad_register.py deleted file mode 100644 index f0683fc..0000000 --- a/.venv/Lib/site-packages/passlib/tests/_test_bad_register.py +++ /dev/null @@ -1,15 +0,0 @@ -"""helper for method in test_registry.py""" -from passlib.registry import register_crypt_handler -import passlib.utils.handlers as uh - -class dummy_bad(uh.StaticHandler): - name = "dummy_bad" - -class alt_dummy_bad(uh.StaticHandler): - name = "dummy_bad" - -# NOTE: if passlib.tests is being run from symlink (e.g. via gaeunit), -# this module may be imported a second time as test._test_bad_registry. -# we don't want it to do anything in that case. -if __name__.startswith("passlib.tests"): - register_crypt_handler(alt_dummy_bad) diff --git a/.venv/Lib/site-packages/passlib/tests/backports.py b/.venv/Lib/site-packages/passlib/tests/backports.py deleted file mode 100644 index 5058cec..0000000 --- a/.venv/Lib/site-packages/passlib/tests/backports.py +++ /dev/null @@ -1,67 +0,0 @@ -"""backports of needed unittest2 features""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -import re -import sys -##from warnings import warn -# site -# pkg -from passlib.utils.compat import PY26 -# local -__all__ = [ - "TestCase", - "unittest", - # TODO: deprecate these exports in favor of "unittest.XXX" - "skip", "skipIf", "skipUnless", -] - -#============================================================================= -# import latest unittest module available -#============================================================================= -try: - import unittest2 as unittest -except ImportError: - if PY26: - raise ImportError("Passlib's tests require 'unittest2' under Python 2.6 (as of Passlib 1.7)") - # python 2.7 and python 3.2 both have unittest2 features (at least, the ones we use) - import unittest - -#============================================================================= -# unittest aliases -#============================================================================= -skip = unittest.skip -skipIf = unittest.skipIf -skipUnless = unittest.skipUnless -SkipTest = unittest.SkipTest - -#============================================================================= -# custom test harness -#============================================================================= -class TestCase(unittest.TestCase): - """backports a number of unittest2 features in TestCase""" - - #=================================================================== - # backport some unittest2 names - #=================================================================== - - #--------------------------------------------------------------- - # backport assertRegex() alias from 3.2 to 2.7 - # was present in 2.7 under an alternate name - #--------------------------------------------------------------- - if not hasattr(unittest.TestCase, "assertRegex"): - assertRegex = unittest.TestCase.assertRegexpMatches - - if not hasattr(unittest.TestCase, "assertRaisesRegex"): - assertRaisesRegex = unittest.TestCase.assertRaisesRegexp - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/sample1.cfg b/.venv/Lib/site-packages/passlib/tests/sample1.cfg deleted file mode 100644 index 56e3ae8..0000000 --- a/.venv/Lib/site-packages/passlib/tests/sample1.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[passlib] -schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt -default = md5_crypt -all__vary_rounds = 0.1 -bsdi_crypt__default_rounds = 25001 -bsdi_crypt__max_rounds = 30001 -sha512_crypt__max_rounds = 50000 -sha512_crypt__min_rounds = 40000 - diff --git a/.venv/Lib/site-packages/passlib/tests/sample1b.cfg b/.venv/Lib/site-packages/passlib/tests/sample1b.cfg deleted file mode 100644 index 56e3ae8..0000000 --- a/.venv/Lib/site-packages/passlib/tests/sample1b.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[passlib] -schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt -default = md5_crypt -all__vary_rounds = 0.1 -bsdi_crypt__default_rounds = 25001 -bsdi_crypt__max_rounds = 30001 -sha512_crypt__max_rounds = 50000 -sha512_crypt__min_rounds = 40000 - diff --git a/.venv/Lib/site-packages/passlib/tests/sample1c.cfg b/.venv/Lib/site-packages/passlib/tests/sample1c.cfg deleted file mode 100644 index a5033eb90ead55436ce1e5727a33fc6fa9c0842a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 490 zcmaKp-3o$05QWe6K1B}@VOEztNrY4y37U}>_43s>s}b26gUexN&(FBe$4aH{I;m2j zTs!buPBrMDj9CUAX~~y*oG>|iMx!y^lKw*d?iN;xUcX0G^*yr-IhxM# zSKD!;pc3i|wj>E>1@DN)J8Pr~9!{Yg#E=s)w^mOZqy$") - - #=================================================================== - # modifiers - #=================================================================== - def test_10_load(self): - """test load() / load_path() method""" - # NOTE: load() is the workhorse that handles all policy parsing, - # compilation, and validation. most of its features are tested - # elsewhere, since all the constructors and modifiers are just - # wrappers for it. - - # source_type 'auto' - ctx = CryptContext() - - # detect dict - ctx.load(self.sample_1_dict) - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - - # detect unicode string - ctx.load(self.sample_1_unicode) - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - - # detect bytes string - ctx.load(self.sample_1_unicode.encode("utf-8")) - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - - # anything else - TypeError - self.assertRaises(TypeError, ctx.load, None) - - # NOTE: load_path() tested by from_path() - # NOTE: additional string tests done by from_string() - - # update flag - tested by update() method tests - # encoding keyword - tested by from_string() & from_path() - # section keyword - tested by from_string() & from_path() - - # test load empty - ctx = CryptContext(**self.sample_1_dict) - ctx.load({}, update=True) - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - - # multiple loads should clear the state - ctx = CryptContext() - ctx.load(self.sample_1_dict) - ctx.load(self.sample_2_dict) - self.assertEqual(ctx.to_dict(), self.sample_2_dict) - - def test_11_load_rollback(self): - """test load() errors restore old state""" - # create initial context - cc = CryptContext(["des_crypt", "sha256_crypt"], - sha256_crypt__default_rounds=5000, - all__vary_rounds=0.1, - ) - result = cc.to_string() - - # do an update operation that should fail during parsing - # XXX: not sure what the right error type is here. - self.assertRaises(TypeError, cc.update, too__many__key__parts=True) - self.assertEqual(cc.to_string(), result) - - # do an update operation that should fail during extraction - # FIXME: this isn't failing even in broken case, need to figure out - # way to ensure some keys come after this one. - self.assertRaises(KeyError, cc.update, fake_context_option=True) - self.assertEqual(cc.to_string(), result) - - # do an update operation that should fail during compilation - self.assertRaises(ValueError, cc.update, sha256_crypt__min_rounds=10000) - self.assertEqual(cc.to_string(), result) - - def test_12_update(self): - """test update() method""" - - # empty overlay - ctx = CryptContext(**self.sample_1_dict) - ctx.update() - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - - # test basic overlay - ctx = CryptContext(**self.sample_1_dict) - ctx.update(**self.sample_2_dict) - self.assertEqual(ctx.to_dict(), self.sample_12_dict) - - # ... and again - ctx.update(**self.sample_3_dict) - self.assertEqual(ctx.to_dict(), self.sample_123_dict) - - # overlay w/ dict arg - ctx = CryptContext(**self.sample_1_dict) - ctx.update(self.sample_2_dict) - self.assertEqual(ctx.to_dict(), self.sample_12_dict) - - # overlay w/ string - ctx = CryptContext(**self.sample_1_dict) - ctx.update(self.sample_2_unicode) - self.assertEqual(ctx.to_dict(), self.sample_12_dict) - - # too many args - self.assertRaises(TypeError, ctx.update, {}, {}) - self.assertRaises(TypeError, ctx.update, {}, schemes=['des_crypt']) - - # wrong arg type - self.assertRaises(TypeError, ctx.update, None) - - #=================================================================== - # option parsing - #=================================================================== - def test_20_options(self): - """test basic option parsing""" - def parse(**kwds): - return CryptContext(**kwds).to_dict() - - # - # common option parsing tests - # - - # test keys with blank fields are rejected - # blank option - self.assertRaises(TypeError, CryptContext, __=0.1) - self.assertRaises(TypeError, CryptContext, default__scheme__='x') - - # blank scheme - self.assertRaises(TypeError, CryptContext, __option='x') - self.assertRaises(TypeError, CryptContext, default____option='x') - - # blank category - self.assertRaises(TypeError, CryptContext, __scheme__option='x') - - # test keys with too many field are rejected - self.assertRaises(TypeError, CryptContext, - category__scheme__option__invalid = 30000) - - # keys with mixed separators should be handled correctly. - # (testing actual data, not to_dict(), since re-render hid original bug) - self.assertRaises(KeyError, parse, - **{"admin.context__schemes":"md5_crypt"}) - ctx = CryptContext(**{"schemes":"md5_crypt,des_crypt", - "admin.context__default":"des_crypt"}) - self.assertEqual(ctx.default_scheme("admin"), "des_crypt") - - # - # context option -specific tests - # - - # test context option key parsing - result = dict(default="md5_crypt") - self.assertEqual(parse(default="md5_crypt"), result) - self.assertEqual(parse(context__default="md5_crypt"), result) - self.assertEqual(parse(default__context__default="md5_crypt"), result) - self.assertEqual(parse(**{"context.default":"md5_crypt"}), result) - self.assertEqual(parse(**{"default.context.default":"md5_crypt"}), result) - - # test context option key parsing w/ category - result = dict(admin__context__default="md5_crypt") - self.assertEqual(parse(admin__context__default="md5_crypt"), result) - self.assertEqual(parse(**{"admin.context.default":"md5_crypt"}), result) - - # - # hash option -specific tests - # - - # test hash option key parsing - result = dict(all__vary_rounds=0.1) - self.assertEqual(parse(all__vary_rounds=0.1), result) - self.assertEqual(parse(default__all__vary_rounds=0.1), result) - self.assertEqual(parse(**{"all.vary_rounds":0.1}), result) - self.assertEqual(parse(**{"default.all.vary_rounds":0.1}), result) - - # test hash option key parsing w/ category - result = dict(admin__all__vary_rounds=0.1) - self.assertEqual(parse(admin__all__vary_rounds=0.1), result) - self.assertEqual(parse(**{"admin.all.vary_rounds":0.1}), result) - - # settings not allowed if not in hash.setting_kwds - ctx = CryptContext(["phpass", "md5_crypt"], phpass__ident="P") - self.assertRaises(KeyError, ctx.copy, md5_crypt__ident="P") - - # hash options 'salt' and 'rounds' not allowed - self.assertRaises(KeyError, CryptContext, schemes=["des_crypt"], - des_crypt__salt="xx") - self.assertRaises(KeyError, CryptContext, schemes=["des_crypt"], - all__salt="xx") - - def test_21_schemes(self): - """test 'schemes' context option parsing""" - - # schemes can be empty - cc = CryptContext(schemes=None) - self.assertEqual(cc.schemes(), ()) - - # schemes can be list of names - cc = CryptContext(schemes=["des_crypt", "md5_crypt"]) - self.assertEqual(cc.schemes(), ("des_crypt", "md5_crypt")) - - # schemes can be comma-sep string - cc = CryptContext(schemes=" des_crypt, md5_crypt, ") - self.assertEqual(cc.schemes(), ("des_crypt", "md5_crypt")) - - # schemes can be list of handlers - cc = CryptContext(schemes=[hash.des_crypt, hash.md5_crypt]) - self.assertEqual(cc.schemes(), ("des_crypt", "md5_crypt")) - - # scheme must be name or handler - self.assertRaises(TypeError, CryptContext, schemes=[uh.StaticHandler]) - - # handlers must have a name - class nameless(uh.StaticHandler): - name = None - self.assertRaises(ValueError, CryptContext, schemes=[nameless]) - - # names must be unique - class dummy_1(uh.StaticHandler): - name = 'dummy_1' - self.assertRaises(KeyError, CryptContext, schemes=[dummy_1, dummy_1]) - - # schemes not allowed per-category - self.assertRaises(KeyError, CryptContext, - admin__context__schemes=["md5_crypt"]) - - def test_22_deprecated(self): - """test 'deprecated' context option parsing""" - def getdep(ctx, category=None): - return [name for name in ctx.schemes() - if ctx.handler(name, category).deprecated] - - # no schemes - all deprecated values allowed - cc = CryptContext(deprecated=["md5_crypt"]) - cc.update(schemes=["md5_crypt", "des_crypt"]) - self.assertEqual(getdep(cc),["md5_crypt"]) - - # deprecated values allowed if subset of schemes - cc = CryptContext(deprecated=["md5_crypt"], schemes=["md5_crypt", "des_crypt"]) - self.assertEqual(getdep(cc), ["md5_crypt"]) - - # can be handler - # XXX: allow handlers in deprecated list? not for now. - self.assertRaises(TypeError, CryptContext, deprecated=[hash.md5_crypt], - schemes=["md5_crypt", "des_crypt"]) -## cc = CryptContext(deprecated=[hash.md5_crypt], schemes=["md5_crypt", "des_crypt"]) -## self.assertEqual(getdep(cc), ["md5_crypt"]) - - # comma sep list - cc = CryptContext(deprecated="md5_crypt,des_crypt", schemes=["md5_crypt", "des_crypt", "sha256_crypt"]) - self.assertEqual(getdep(cc), ["md5_crypt", "des_crypt"]) - - # values outside of schemes not allowed - self.assertRaises(KeyError, CryptContext, schemes=['des_crypt'], - deprecated=['md5_crypt']) - - # deprecating ALL schemes should cause ValueError - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt'], - deprecated=['des_crypt']) - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt', 'md5_crypt'], - admin__context__deprecated=['des_crypt', 'md5_crypt']) - - # deprecating explicit default scheme should cause ValueError - - # ... default listed as deprecated - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt', 'md5_crypt'], - default="md5_crypt", - deprecated="md5_crypt") - - # ... global default deprecated per-category - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt', 'md5_crypt'], - default="md5_crypt", - admin__context__deprecated="md5_crypt") - - # ... category default deprecated globally - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt', 'md5_crypt'], - admin__context__default="md5_crypt", - deprecated="md5_crypt") - - # ... category default deprecated in category - self.assertRaises(ValueError, CryptContext, - schemes=['des_crypt', 'md5_crypt'], - admin__context__default="md5_crypt", - admin__context__deprecated="md5_crypt") - - # category deplist should shadow default deplist - CryptContext( - schemes=['des_crypt', 'md5_crypt'], - deprecated="md5_crypt", - admin__context__default="md5_crypt", - admin__context__deprecated=[]) - - # wrong type - self.assertRaises(TypeError, CryptContext, deprecated=123) - - # deprecated per-category - cc = CryptContext(deprecated=["md5_crypt"], - schemes=["md5_crypt", "des_crypt"], - admin__context__deprecated=["des_crypt"], - ) - self.assertEqual(getdep(cc), ["md5_crypt"]) - self.assertEqual(getdep(cc, "user"), ["md5_crypt"]) - self.assertEqual(getdep(cc, "admin"), ["des_crypt"]) - - # blank per-category deprecated list, shadowing default list - cc = CryptContext(deprecated=["md5_crypt"], - schemes=["md5_crypt", "des_crypt"], - admin__context__deprecated=[], - ) - self.assertEqual(getdep(cc), ["md5_crypt"]) - self.assertEqual(getdep(cc, "user"), ["md5_crypt"]) - self.assertEqual(getdep(cc, "admin"), []) - - def test_23_default(self): - """test 'default' context option parsing""" - - # anything allowed if no schemes - self.assertEqual(CryptContext(default="md5_crypt").to_dict(), - dict(default="md5_crypt")) - - # default allowed if in scheme list - ctx = CryptContext(default="md5_crypt", schemes=["des_crypt", "md5_crypt"]) - self.assertEqual(ctx.default_scheme(), "md5_crypt") - - # default can be handler - # XXX: sure we want to allow this ? maybe deprecate in future. - ctx = CryptContext(default=hash.md5_crypt, schemes=["des_crypt", "md5_crypt"]) - self.assertEqual(ctx.default_scheme(), "md5_crypt") - - # implicit default should be first non-deprecated scheme - ctx = CryptContext(schemes=["des_crypt", "md5_crypt"]) - self.assertEqual(ctx.default_scheme(), "des_crypt") - ctx.update(deprecated="des_crypt") - self.assertEqual(ctx.default_scheme(), "md5_crypt") - - # error if not in scheme list - self.assertRaises(KeyError, CryptContext, schemes=['des_crypt'], - default='md5_crypt') - - # wrong type - self.assertRaises(TypeError, CryptContext, default=1) - - # per-category - ctx = CryptContext(default="des_crypt", - schemes=["des_crypt", "md5_crypt"], - admin__context__default="md5_crypt") - self.assertEqual(ctx.default_scheme(), "des_crypt") - self.assertEqual(ctx.default_scheme("user"), "des_crypt") - self.assertEqual(ctx.default_scheme("admin"), "md5_crypt") - - def test_24_vary_rounds(self): - """test 'vary_rounds' hash option parsing""" - def parse(v): - return CryptContext(all__vary_rounds=v).to_dict()['all__vary_rounds'] - - # floats should be preserved - self.assertEqual(parse(0.1), 0.1) - self.assertEqual(parse('0.1'), 0.1) - - # 'xx%' should be converted to float - self.assertEqual(parse('10%'), 0.1) - - # ints should be preserved - self.assertEqual(parse(1000), 1000) - self.assertEqual(parse('1000'), 1000) - - #=================================================================== - # inspection & serialization - #=================================================================== - - def assertHandlerDerivedFrom(self, handler, base, msg=None): - self.assertTrue(handler_derived_from(handler, base), msg=msg) - - def test_30_schemes(self): - """test schemes() method""" - # NOTE: also checked under test_21 - - # test empty - ctx = CryptContext() - self.assertEqual(ctx.schemes(), ()) - self.assertEqual(ctx.schemes(resolve=True), ()) - - # test sample 1 - ctx = CryptContext(**self.sample_1_dict) - self.assertEqual(ctx.schemes(), tuple(self.sample_1_schemes)) - self.assertEqual(ctx.schemes(resolve=True, unconfigured=True), tuple(self.sample_1_handlers)) - for result, correct in zip(ctx.schemes(resolve=True), self.sample_1_handlers): - self.assertTrue(handler_derived_from(result, correct)) - - # test sample 2 - ctx = CryptContext(**self.sample_2_dict) - self.assertEqual(ctx.schemes(), ()) - - def test_31_default_scheme(self): - """test default_scheme() method""" - # NOTE: also checked under test_23 - - # test empty - ctx = CryptContext() - self.assertRaises(KeyError, ctx.default_scheme) - - # test sample 1 - ctx = CryptContext(**self.sample_1_dict) - self.assertEqual(ctx.default_scheme(), "md5_crypt") - self.assertEqual(ctx.default_scheme(resolve=True, unconfigured=True), hash.md5_crypt) - self.assertHandlerDerivedFrom(ctx.default_scheme(resolve=True), hash.md5_crypt) - - # test sample 2 - ctx = CryptContext(**self.sample_2_dict) - self.assertRaises(KeyError, ctx.default_scheme) - - # test defaults to first in scheme - ctx = CryptContext(schemes=self.sample_1_schemes) - self.assertEqual(ctx.default_scheme(), "des_crypt") - - # categories tested under test_23 - - def test_32_handler(self): - """test handler() method""" - - # default for empty - ctx = CryptContext() - self.assertRaises(KeyError, ctx.handler) - self.assertRaises(KeyError, ctx.handler, "md5_crypt") - - # default for sample 1 - ctx = CryptContext(**self.sample_1_dict) - self.assertEqual(ctx.handler(unconfigured=True), hash.md5_crypt) - self.assertHandlerDerivedFrom(ctx.handler(), hash.md5_crypt) - - # by name - self.assertEqual(ctx.handler("des_crypt", unconfigured=True), hash.des_crypt) - self.assertHandlerDerivedFrom(ctx.handler("des_crypt"), hash.des_crypt) - - # name not in schemes - self.assertRaises(KeyError, ctx.handler, "mysql323") - - # check handler() honors category default - ctx = CryptContext("sha256_crypt,md5_crypt", admin__context__default="md5_crypt") - self.assertEqual(ctx.handler(unconfigured=True), hash.sha256_crypt) - self.assertHandlerDerivedFrom(ctx.handler(), hash.sha256_crypt) - - self.assertEqual(ctx.handler(category="staff", unconfigured=True), hash.sha256_crypt) - self.assertHandlerDerivedFrom(ctx.handler(category="staff"), hash.sha256_crypt) - - self.assertEqual(ctx.handler(category="admin", unconfigured=True), hash.md5_crypt) - self.assertHandlerDerivedFrom(ctx.handler(category="staff"), hash.sha256_crypt) - - # test unicode category strings are accepted under py2 - if PY2: - self.assertEqual(ctx.handler(category=u("staff"), unconfigured=True), hash.sha256_crypt) - self.assertEqual(ctx.handler(category=u("admin"), unconfigured=True), hash.md5_crypt) - - def test_33_options(self): - """test internal _get_record_options() method""" - - def options(ctx, scheme, category=None): - return ctx._config._get_record_options_with_flag(scheme, category)[0] - - # this checks that (3 schemes, 3 categories) inherit options correctly. - # the 'user' category is not present in the options. - cc4 = CryptContext( - truncate_error=True, - schemes = [ "sha512_crypt", "des_crypt", "bsdi_crypt"], - deprecated = ["sha512_crypt", "des_crypt"], - all__vary_rounds = 0.1, - bsdi_crypt__vary_rounds=0.2, - sha512_crypt__max_rounds = 20000, - admin__context__deprecated = [ "des_crypt", "bsdi_crypt" ], - admin__all__vary_rounds = 0.05, - admin__bsdi_crypt__vary_rounds=0.3, - admin__sha512_crypt__max_rounds = 40000, - ) - self.assertEqual(cc4._config.categories, ("admin",)) - - # - # sha512_crypt - # NOTE: 'truncate_error' shouldn't be passed along... - # - self.assertEqual(options(cc4, "sha512_crypt"), dict( - deprecated=True, - vary_rounds=0.1, # inherited from all__ - max_rounds=20000, - )) - - self.assertEqual(options(cc4, "sha512_crypt", "user"), dict( - deprecated=True, # unconfigured category inherits from default - vary_rounds=0.1, - max_rounds=20000, - )) - - self.assertEqual(options(cc4, "sha512_crypt", "admin"), dict( - # NOT deprecated - context option overridden per-category - vary_rounds=0.05, # global overridden per-cateogry - max_rounds=40000, # overridden per-category - )) - - # - # des_crypt - # NOTE: vary_rounds shouldn't be passed along... - # - self.assertEqual(options(cc4, "des_crypt"), dict( - deprecated=True, - truncate_error=True, - )) - - self.assertEqual(options(cc4, "des_crypt", "user"), dict( - deprecated=True, # unconfigured category inherits from default - truncate_error=True, - )) - - self.assertEqual(options(cc4, "des_crypt", "admin"), dict( - deprecated=True, # unchanged though overidden - truncate_error=True, - )) - - # - # bsdi_crypt - # - self.assertEqual(options(cc4, "bsdi_crypt"), dict( - vary_rounds=0.2, # overridden from all__vary_rounds - )) - - self.assertEqual(options(cc4, "bsdi_crypt", "user"), dict( - vary_rounds=0.2, # unconfigured category inherits from default - )) - - self.assertEqual(options(cc4, "bsdi_crypt", "admin"), dict( - vary_rounds=0.3, - deprecated=True, # deprecation set per-category - )) - - def test_34_to_dict(self): - """test to_dict() method""" - # NOTE: this is tested all throughout this test case. - ctx = CryptContext(**self.sample_1_dict) - self.assertEqual(ctx.to_dict(), self.sample_1_dict) - self.assertEqual(ctx.to_dict(resolve=True), self.sample_1_resolved_dict) - - def test_35_to_string(self): - """test to_string() method""" - - # create ctx and serialize - ctx = CryptContext(**self.sample_1_dict) - dump = ctx.to_string() - - # check ctx->string returns canonical format. - # NOTE: ConfigParser for PY26 doesn't use OrderedDict, - # making to_string()'s ordering unpredictable... - # so we skip this test under PY26. - if not PY26: - self.assertEqual(dump, self.sample_1_unicode) - - # check ctx->string->ctx->dict returns original - ctx2 = CryptContext.from_string(dump) - self.assertEqual(ctx2.to_dict(), self.sample_1_dict) - - # test section kwd is honored - other = ctx.to_string(section="password-security") - self.assertEqual(other, dump.replace("[passlib]","[password-security]")) - - # test unmanaged handler warning - from passlib.tests.test_utils_handlers import UnsaltedHash - ctx3 = CryptContext([UnsaltedHash, "md5_crypt"]) - dump = ctx3.to_string() - self.assertRegex(dump, r"# NOTE: the 'unsalted_test_hash' handler\(s\)" - r" are not registered with Passlib") - - #=================================================================== - # password hash api - #=================================================================== - nonstring_vectors = [ - (None, {}), - (None, {"scheme": "des_crypt"}), - (1, {}), - ((), {}), - ] - - def test_40_basic(self): - """test basic hash/identify/verify functionality""" - handlers = [hash.md5_crypt, hash.des_crypt, hash.bsdi_crypt] - cc = CryptContext(handlers, bsdi_crypt__default_rounds=5) - - # run through handlers - for crypt in handlers: - h = cc.hash("test", scheme=crypt.name) - self.assertEqual(cc.identify(h), crypt.name) - self.assertEqual(cc.identify(h, resolve=True, unconfigured=True), crypt) - self.assertHandlerDerivedFrom(cc.identify(h, resolve=True), crypt) - self.assertTrue(cc.verify('test', h)) - self.assertFalse(cc.verify('notest', h)) - - # test default - h = cc.hash("test") - self.assertEqual(cc.identify(h), "md5_crypt") - - # test genhash - h = cc.genhash('secret', cc.genconfig()) - self.assertEqual(cc.identify(h), 'md5_crypt') - - h = cc.genhash('secret', cc.genconfig(), scheme='md5_crypt') - self.assertEqual(cc.identify(h), 'md5_crypt') - - self.assertRaises(ValueError, cc.genhash, 'secret', cc.genconfig(), scheme="des_crypt") - - def test_41_genconfig(self): - """test genconfig() method""" - cc = CryptContext(schemes=["md5_crypt", "phpass"], - phpass__ident="H", - phpass__default_rounds=7, - admin__phpass__ident="P", - ) - - # uses default scheme - self.assertTrue(cc.genconfig().startswith("$1$")) - - # override scheme - self.assertTrue(cc.genconfig(scheme="phpass").startswith("$H$5")) - - # category override - self.assertTrue(cc.genconfig(scheme="phpass", category="admin").startswith("$P$5")) - self.assertTrue(cc.genconfig(scheme="phpass", category="staff").startswith("$H$5")) - - # override scheme & custom settings - self.assertEqual( - cc.genconfig(scheme="phpass", salt='.'*8, rounds=8, ident='P'), - '$P$6........22zGEuacuPOqEpYPDeR0R/', # NOTE: config string generated w/ rounds=1 - ) - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # test unicode category strings are accepted under py2 - # this tests basic _get_record() used by hash/genhash/verify. - # we have to omit scheme=xxx so codepath is tested fully - if PY2: - c2 = cc.copy(default="phpass") - self.assertTrue(c2.genconfig(category=u("admin")).startswith("$P$5")) - self.assertTrue(c2.genconfig(category=u("staff")).startswith("$H$5")) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().genconfig) - self.assertRaises(KeyError, CryptContext().genconfig, scheme='md5_crypt') - - # bad scheme values - self.assertRaises(KeyError, cc.genconfig, scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.genconfig, scheme=1, category='staff') - self.assertRaises(TypeError, cc.genconfig, scheme=1) - - # bad category values - self.assertRaises(TypeError, cc.genconfig, category=1) - - - def test_42_genhash(self): - """test genhash() method""" - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # rejects non-string secrets - cc = CryptContext(["des_crypt"]) - hash = cc.hash('stub') - for secret, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.genhash, secret, hash, **kwds) - - # rejects non-string config strings - cc = CryptContext(["des_crypt"]) - for config, kwds in self.nonstring_vectors: - if hash is None: - # NOTE: as of 1.7, genhash is just wrapper for hash(), - # and handles genhash(secret, None) fine. - continue - self.assertRaises(TypeError, cc.genhash, 'secret', config, **kwds) - - # rejects config=None, even if default scheme lacks config string - cc = CryptContext(["mysql323"]) - self.assertRaises(TypeError, cc.genhash, "stub", None) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().genhash, 'secret', 'hash') - - # bad scheme values - self.assertRaises(KeyError, cc.genhash, 'secret', hash, scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.genhash, 'secret', hash, scheme=1) - - # bad category values - self.assertRaises(TypeError, cc.genconfig, 'secret', hash, category=1) - - def test_43_hash(self,): - """test hash() method""" - # XXX: what more can we test here that isn't deprecated - # or handled under another test (e.g. context kwds?) - - # respects rounds - cc = CryptContext(**self.sample_4_dict) - hash = cc.hash("password") - self.assertTrue(hash.startswith("$5$rounds=3000$")) - self.assertTrue(cc.verify("password", hash)) - self.assertFalse(cc.verify("passwordx", hash)) - - # make default > max throws error if attempted - # XXX: move this to copy() test? - self.assertRaises(ValueError, cc.copy, - sha256_crypt__default_rounds=4000) - - # rejects non-string secrets - cc = CryptContext(["des_crypt"]) - for secret, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.hash, secret, **kwds) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().hash, 'secret') - - # bad category values - self.assertRaises(TypeError, cc.hash, 'secret', category=1) - - def test_43_hash_legacy(self, use_16_legacy=False): - """test hash() method -- legacy 'scheme' and settings keywords""" - cc = CryptContext(**self.sample_4_dict) - - # TODO: should migrate these tests elsewhere, or remove them. - # can be replaced with following equivalent: - # - # def wrapper(secret, scheme=None, category=None, **kwds): - # handler = cc.handler(scheme, category) - # if kwds: - # handler = handler.using(**kwds) - # return handler.hash(secret) - # - # need to make sure bits being tested here are tested - # under the tests for the equivalent methods called above, - # and then discard the rest of these under 2.0. - - # hash specific settings - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertEqual( - cc.hash("password", scheme="phpass", salt='.'*8), - '$H$5........De04R5Egz0aq8Tf.1eVhY/', - ) - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertEqual( - cc.hash("password", scheme="phpass", salt='.'*8, ident="P"), - '$P$5........De04R5Egz0aq8Tf.1eVhY/', - ) - - # NOTE: more thorough job of rounds limits done below. - - # min rounds - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertEqual( - cc.hash("password", rounds=1999, salt="nacl"), - '$5$rounds=1999$nacl$nmfwJIxqj0csloAAvSER0B8LU0ERCAbhmMug4Twl609', - ) - - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertEqual( - cc.hash("password", rounds=2001, salt="nacl"), - '$5$rounds=2001$nacl$8PdeoPL4aXQnJ0woHhqgIw/efyfCKC2WHneOpnvF.31' - ) - # NOTE: max rounds, etc tested in genconfig() - - # bad scheme values - self.assertRaises(KeyError, cc.hash, 'secret', scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.hash, 'secret', scheme=1) - - def test_44_identify(self): - """test identify() border cases""" - handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"] - cc = CryptContext(handlers, bsdi_crypt__default_rounds=5) - - # check unknown hash - self.assertEqual(cc.identify('$9$232323123$1287319827'), None) - self.assertRaises(ValueError, cc.identify, '$9$232323123$1287319827', required=True) - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # rejects non-string hashes - cc = CryptContext(["des_crypt"]) - for hash, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.identify, hash, **kwds) - - # throws error without schemes - cc = CryptContext() - self.assertIs(cc.identify('hash'), None) - self.assertRaises(KeyError, cc.identify, 'hash', required=True) - - # bad category values - self.assertRaises(TypeError, cc.identify, None, category=1) - - def test_45_verify(self): - """test verify() scheme kwd""" - handlers = ["md5_crypt", "des_crypt", "bsdi_crypt"] - cc = CryptContext(handlers, bsdi_crypt__default_rounds=5) - - h = hash.md5_crypt.hash("test") - - # check base verify - self.assertTrue(cc.verify("test", h)) - self.assertTrue(not cc.verify("notest", h)) - - # check verify using right alg - self.assertTrue(cc.verify('test', h, scheme='md5_crypt')) - self.assertTrue(not cc.verify('notest', h, scheme='md5_crypt')) - - # check verify using wrong alg - self.assertRaises(ValueError, cc.verify, 'test', h, scheme='bsdi_crypt') - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # unknown hash should throw error - self.assertRaises(ValueError, cc.verify, 'stub', '$6$232323123$1287319827') - - # rejects non-string secrets - cc = CryptContext(["des_crypt"]) - h = refhash = cc.hash('stub') - for secret, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.verify, secret, h, **kwds) - - # always treat hash=None as False - self.assertFalse(cc.verify(secret, None)) - - # rejects non-string hashes - cc = CryptContext(["des_crypt"]) - for h, kwds in self.nonstring_vectors: - if h is None: - continue - self.assertRaises(TypeError, cc.verify, 'secret', h, **kwds) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().verify, 'secret', 'hash') - - # bad scheme values - self.assertRaises(KeyError, cc.verify, 'secret', refhash, scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.verify, 'secret', refhash, scheme=1) - - # bad category values - self.assertRaises(TypeError, cc.verify, 'secret', refhash, category=1) - - def test_46_needs_update(self): - """test needs_update() method""" - cc = CryptContext(**self.sample_4_dict) - - # check deprecated scheme - self.assertTrue(cc.needs_update('9XXD4trGYeGJA')) - self.assertFalse(cc.needs_update('$1$J8HC2RCr$HcmM.7NxB2weSvlw2FgzU0')) - - # check min rounds - self.assertTrue(cc.needs_update('$5$rounds=1999$jD81UCoo.zI.UETs$Y7qSTQ6mTiU9qZB4fRr43wRgQq4V.5AAf7F97Pzxey/')) - self.assertFalse(cc.needs_update('$5$rounds=2000$228SSRje04cnNCaQ$YGV4RYu.5sNiBvorQDlO0WWQjyJVGKBcJXz3OtyQ2u8')) - - # check max rounds - self.assertFalse(cc.needs_update('$5$rounds=3000$fS9iazEwTKi7QPW4$VasgBC8FqlOvD7x2HhABaMXCTh9jwHclPA9j5YQdns.')) - self.assertTrue(cc.needs_update('$5$rounds=3001$QlFHHifXvpFX4PLs$/0ekt7lSs/lOikSerQ0M/1porEHxYq7W/2hdFpxA3fA')) - - #-------------------------------------------------------------- - # test hash.needs_update() interface - #-------------------------------------------------------------- - check_state = [] - class dummy(uh.StaticHandler): - name = 'dummy' - _hash_prefix = '@' - - @classmethod - def needs_update(cls, hash, secret=None): - check_state.append((hash, secret)) - return secret == "nu" - - def _calc_checksum(self, secret): - from hashlib import md5 - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(md5(secret).hexdigest()) - - # calling needs_update should query callback - ctx = CryptContext([dummy]) - hash = refhash = dummy.hash("test") - self.assertFalse(ctx.needs_update(hash)) - self.assertEqual(check_state, [(hash,None)]) - del check_state[:] - - # now with a password - self.assertFalse(ctx.needs_update(hash, secret='bob')) - self.assertEqual(check_state, [(hash,'bob')]) - del check_state[:] - - # now when it returns True - self.assertTrue(ctx.needs_update(hash, secret='nu')) - self.assertEqual(check_state, [(hash,'nu')]) - del check_state[:] - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # rejects non-string hashes - cc = CryptContext(["des_crypt"]) - for hash, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.needs_update, hash, **kwds) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().needs_update, 'hash') - - # bad scheme values - self.assertRaises(KeyError, cc.needs_update, refhash, scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.needs_update, refhash, scheme=1) - - # bad category values - self.assertRaises(TypeError, cc.needs_update, refhash, category=1) - - def test_47_verify_and_update(self): - """test verify_and_update()""" - cc = CryptContext(**self.sample_4_dict) - - # create some hashes - h1 = cc.handler("des_crypt").hash("password") - h2 = cc.handler("sha256_crypt").hash("password") - - # check bad password, deprecated hash - ok, new_hash = cc.verify_and_update("wrongpass", h1) - self.assertFalse(ok) - self.assertIs(new_hash, None) - - # check bad password, good hash - ok, new_hash = cc.verify_and_update("wrongpass", h2) - self.assertFalse(ok) - self.assertIs(new_hash, None) - - # check right password, deprecated hash - ok, new_hash = cc.verify_and_update("password", h1) - self.assertTrue(ok) - self.assertTrue(cc.identify(new_hash), "sha256_crypt") - - # check right password, good hash - ok, new_hash = cc.verify_and_update("password", h2) - self.assertTrue(ok) - self.assertIs(new_hash, None) - - #-------------------------------------------------------------- - # border cases - #-------------------------------------------------------------- - - # rejects non-string secrets - cc = CryptContext(["des_crypt"]) - hash = refhash = cc.hash('stub') - for secret, kwds in self.nonstring_vectors: - self.assertRaises(TypeError, cc.verify_and_update, secret, hash, **kwds) - - # always treat hash=None as False - self.assertEqual(cc.verify_and_update(secret, None), (False, None)) - - # rejects non-string hashes - cc = CryptContext(["des_crypt"]) - for hash, kwds in self.nonstring_vectors: - if hash is None: - continue - self.assertRaises(TypeError, cc.verify_and_update, 'secret', hash, **kwds) - - # throws error without schemes - self.assertRaises(KeyError, CryptContext().verify_and_update, 'secret', 'hash') - - # bad scheme values - self.assertRaises(KeyError, cc.verify_and_update, 'secret', refhash, scheme="fake") # XXX: should this be ValueError? - self.assertRaises(TypeError, cc.verify_and_update, 'secret', refhash, scheme=1) - - # bad category values - self.assertRaises(TypeError, cc.verify_and_update, 'secret', refhash, category=1) - - def test_48_context_kwds(self): - """hash(), verify(), and verify_and_update() -- discard unused context keywords""" - - # setup test case - # NOTE: postgres_md5 hash supports 'user' context kwd, which is used for this test. - from passlib.hash import des_crypt, md5_crypt, postgres_md5 - des_hash = des_crypt.hash("stub") - pg_root_hash = postgres_md5.hash("stub", user="root") - pg_admin_hash = postgres_md5.hash("stub", user="admin") - - #------------------------------------------------------------ - # case 1: contextual kwds not supported by any hash in CryptContext - #------------------------------------------------------------ - cc1 = CryptContext([des_crypt, md5_crypt]) - self.assertEqual(cc1.context_kwds, set()) - - # des_scrypt should work w/o any contextual kwds - self.assertTrue(des_crypt.identify(cc1.hash("stub")), "des_crypt") - self.assertTrue(cc1.verify("stub", des_hash)) - self.assertEqual(cc1.verify_and_update("stub", des_hash), (True, None)) - - # des_crypt should throw error due to unknown context keyword - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertRaises(TypeError, cc1.hash, "stub", user="root") - self.assertRaises(TypeError, cc1.verify, "stub", des_hash, user="root") - self.assertRaises(TypeError, cc1.verify_and_update, "stub", des_hash, user="root") - - #------------------------------------------------------------ - # case 2: at least one contextual kwd supported by non-default hash - #------------------------------------------------------------ - cc2 = CryptContext([des_crypt, postgres_md5]) - self.assertEqual(cc2.context_kwds, set(["user"])) - - # verify des_crypt works w/o "user" kwd - self.assertTrue(des_crypt.identify(cc2.hash("stub")), "des_crypt") - self.assertTrue(cc2.verify("stub", des_hash)) - self.assertEqual(cc2.verify_and_update("stub", des_hash), (True, None)) - - # verify des_crypt ignores "user" kwd - self.assertTrue(des_crypt.identify(cc2.hash("stub", user="root")), "des_crypt") - self.assertTrue(cc2.verify("stub", des_hash, user="root")) - self.assertEqual(cc2.verify_and_update("stub", des_hash, user="root"), (True, None)) - - # verify error with unknown kwd - with self.assertWarningList(["passing settings to.*is deprecated"]): - self.assertRaises(TypeError, cc2.hash, "stub", badkwd="root") - self.assertRaises(TypeError, cc2.verify, "stub", des_hash, badkwd="root") - self.assertRaises(TypeError, cc2.verify_and_update, "stub", des_hash, badkwd="root") - - #------------------------------------------------------------ - # case 3: at least one contextual kwd supported by default hash - #------------------------------------------------------------ - cc3 = CryptContext([postgres_md5, des_crypt], deprecated="auto") - self.assertEqual(cc3.context_kwds, set(["user"])) - - # postgres_md5 should have error w/o context kwd - self.assertRaises(TypeError, cc3.hash, "stub") - self.assertRaises(TypeError, cc3.verify, "stub", pg_root_hash) - self.assertRaises(TypeError, cc3.verify_and_update, "stub", pg_root_hash) - - # postgres_md5 should work w/ context kwd - self.assertEqual(cc3.hash("stub", user="root"), pg_root_hash) - self.assertTrue(cc3.verify("stub", pg_root_hash, user="root")) - self.assertEqual(cc3.verify_and_update("stub", pg_root_hash, user="root"), (True, None)) - - # verify_and_update() should fail against wrong user - self.assertEqual(cc3.verify_and_update("stub", pg_root_hash, user="admin"), (False, None)) - - # verify_and_update() should pass all context kwds through when rehashing - self.assertEqual(cc3.verify_and_update("stub", des_hash, user="root"), - (True, pg_root_hash)) - - #=================================================================== - # rounds options - #=================================================================== - - # TODO: now that rounds generation has moved out of _CryptRecord to HasRounds, - # this should just test that we're passing right options to handler.using(), - # and that resulting handler has right settings. - # Can then just let HasRounds tests (which are a copy of this) deal with things. - - # NOTE: the follow tests check how _CryptRecord handles - # the min/max/default/vary_rounds options, via the output of - # genconfig(). it's assumed hash() takes the same codepath. - - def test_50_rounds_limits(self): - """test rounds limits""" - cc = CryptContext(schemes=["sha256_crypt"], - sha256_crypt__min_rounds=2000, - sha256_crypt__max_rounds=3000, - sha256_crypt__default_rounds=2500, - ) - - # stub digest returned by sha256_crypt's genconfig calls.. - STUB = '...........................................' - - #-------------------------------------------------- - # settings should have been applied to custom handler, - # it should take care of the rest - #-------------------------------------------------- - custom_handler = cc._get_record("sha256_crypt", None) - self.assertEqual(custom_handler.min_desired_rounds, 2000) - self.assertEqual(custom_handler.max_desired_rounds, 3000) - self.assertEqual(custom_handler.default_rounds, 2500) - - #-------------------------------------------------- - # min_rounds - #-------------------------------------------------- - - # set below handler minimum - with self.assertWarningList([PasslibHashWarning]*2): - c2 = cc.copy(sha256_crypt__min_rounds=500, sha256_crypt__max_rounds=None, - sha256_crypt__default_rounds=500) - self.assertEqual(c2.genconfig(salt="nacl"), "$5$rounds=1000$nacl$" + STUB) - - # below policy minimum - # NOTE: formerly issued a warning in passlib 1.6, now just a wrapper for .replace() - with self.assertWarningList([]): - self.assertEqual( - cc.genconfig(rounds=1999, salt="nacl"), '$5$rounds=1999$nacl$' + STUB) - - # equal to policy minimum - self.assertEqual( - cc.genconfig(rounds=2000, salt="nacl"), '$5$rounds=2000$nacl$' + STUB) - - # above policy minimum - self.assertEqual( - cc.genconfig(rounds=2001, salt="nacl"), '$5$rounds=2001$nacl$' + STUB) - - #-------------------------------------------------- - # max rounds - #-------------------------------------------------- - - # set above handler max - with self.assertWarningList([PasslibHashWarning]*2): - c2 = cc.copy(sha256_crypt__max_rounds=int(1e9)+500, sha256_crypt__min_rounds=None, - sha256_crypt__default_rounds=int(1e9)+500) - - self.assertEqual(c2.genconfig(salt="nacl"), "$5$rounds=999999999$nacl$" + STUB) - - # above policy max - # NOTE: formerly issued a warning in passlib 1.6, now just a wrapper for .using() - with self.assertWarningList([]): - self.assertEqual( - cc.genconfig(rounds=3001, salt="nacl"), '$5$rounds=3001$nacl$' + STUB) - - # equal policy max - self.assertEqual( - cc.genconfig(rounds=3000, salt="nacl"), '$5$rounds=3000$nacl$' + STUB) - - # below policy max - self.assertEqual( - cc.genconfig(rounds=2999, salt="nacl"), '$5$rounds=2999$nacl$' + STUB) - - #-------------------------------------------------- - # default_rounds - #-------------------------------------------------- - - # explicit default rounds - self.assertEqual(cc.genconfig(salt="nacl"), '$5$rounds=2500$nacl$' + STUB) - - # fallback default rounds - use handler's - df = hash.sha256_crypt.default_rounds - c2 = cc.copy(sha256_crypt__default_rounds=None, sha256_crypt__max_rounds=df<<1) - self.assertEqual(c2.genconfig(salt="nacl"), '$5$rounds=%d$nacl$%s' % (df, STUB)) - - # fallback default rounds - use handler's, but clipped to max rounds - c2 = cc.copy(sha256_crypt__default_rounds=None, sha256_crypt__max_rounds=3000) - self.assertEqual(c2.genconfig(salt="nacl"), '$5$rounds=3000$nacl$' + STUB) - - # TODO: test default falls back to mx / mn if handler has no default. - - # default rounds - out of bounds - self.assertRaises(ValueError, cc.copy, sha256_crypt__default_rounds=1999) - cc.copy(sha256_crypt__default_rounds=2000) - cc.copy(sha256_crypt__default_rounds=3000) - self.assertRaises(ValueError, cc.copy, sha256_crypt__default_rounds=3001) - - #-------------------------------------------------- - # border cases - #-------------------------------------------------- - - # invalid min/max bounds - c2 = CryptContext(schemes=["sha256_crypt"]) - # NOTE: as of v1.7, these are clipped w/ a warning instead... - # self.assertRaises(ValueError, c2.copy, sha256_crypt__min_rounds=-1) - # self.assertRaises(ValueError, c2.copy, sha256_crypt__max_rounds=-1) - self.assertRaises(ValueError, c2.copy, sha256_crypt__min_rounds=2000, - sha256_crypt__max_rounds=1999) - - # test bad values - self.assertRaises(ValueError, CryptContext, sha256_crypt__min_rounds='x') - self.assertRaises(ValueError, CryptContext, sha256_crypt__max_rounds='x') - self.assertRaises(ValueError, CryptContext, all__vary_rounds='x') - self.assertRaises(ValueError, CryptContext, sha256_crypt__default_rounds='x') - - # test bad types rejected - bad = datetime.datetime.now() # picked cause can't be compared to int - self.assertRaises(TypeError, CryptContext, "sha256_crypt", sha256_crypt__min_rounds=bad) - self.assertRaises(TypeError, CryptContext, "sha256_crypt", sha256_crypt__max_rounds=bad) - self.assertRaises(TypeError, CryptContext, "sha256_crypt", all__vary_rounds=bad) - self.assertRaises(TypeError, CryptContext, "sha256_crypt", sha256_crypt__default_rounds=bad) - - def test_51_linear_vary_rounds(self): - """test linear vary rounds""" - cc = CryptContext(schemes=["sha256_crypt"], - sha256_crypt__min_rounds=1995, - sha256_crypt__max_rounds=2005, - sha256_crypt__default_rounds=2000, - ) - - # test negative - self.assertRaises(ValueError, cc.copy, all__vary_rounds=-1) - self.assertRaises(ValueError, cc.copy, all__vary_rounds="-1%") - self.assertRaises(ValueError, cc.copy, all__vary_rounds="101%") - - # test static - c2 = cc.copy(all__vary_rounds=0) - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 0) - self.assert_rounds_range(c2, "sha256_crypt", 2000, 2000) - - c2 = cc.copy(all__vary_rounds="0%") - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 0) - self.assert_rounds_range(c2, "sha256_crypt", 2000, 2000) - - # test absolute - c2 = cc.copy(all__vary_rounds=1) - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 1) - self.assert_rounds_range(c2, "sha256_crypt", 1999, 2001) - c2 = cc.copy(all__vary_rounds=100) - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 100) - self.assert_rounds_range(c2, "sha256_crypt", 1995, 2005) - - # test relative - c2 = cc.copy(all__vary_rounds="0.1%") - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 0.001) - self.assert_rounds_range(c2, "sha256_crypt", 1998, 2002) - c2 = cc.copy(all__vary_rounds="100%") - self.assertEqual(c2._get_record("sha256_crypt", None).vary_rounds, 1.0) - self.assert_rounds_range(c2, "sha256_crypt", 1995, 2005) - - def test_52_log2_vary_rounds(self): - """test log2 vary rounds""" - cc = CryptContext(schemes=["bcrypt"], - bcrypt__min_rounds=15, - bcrypt__max_rounds=25, - bcrypt__default_rounds=20, - ) - - # test negative - self.assertRaises(ValueError, cc.copy, all__vary_rounds=-1) - self.assertRaises(ValueError, cc.copy, all__vary_rounds="-1%") - self.assertRaises(ValueError, cc.copy, all__vary_rounds="101%") - - # test static - c2 = cc.copy(all__vary_rounds=0) - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 0) - self.assert_rounds_range(c2, "bcrypt", 20, 20) - - c2 = cc.copy(all__vary_rounds="0%") - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 0) - self.assert_rounds_range(c2, "bcrypt", 20, 20) - - # test absolute - c2 = cc.copy(all__vary_rounds=1) - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 1) - self.assert_rounds_range(c2, "bcrypt", 19, 21) - c2 = cc.copy(all__vary_rounds=100) - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 100) - self.assert_rounds_range(c2, "bcrypt", 15, 25) - - # test relative - should shift over at 50% mark - c2 = cc.copy(all__vary_rounds="1%") - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 0.01) - self.assert_rounds_range(c2, "bcrypt", 20, 20) - - c2 = cc.copy(all__vary_rounds="49%") - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 0.49) - self.assert_rounds_range(c2, "bcrypt", 20, 20) - - c2 = cc.copy(all__vary_rounds="50%") - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 0.5) - self.assert_rounds_range(c2, "bcrypt", 19, 20) - - c2 = cc.copy(all__vary_rounds="100%") - self.assertEqual(c2._get_record("bcrypt", None).vary_rounds, 1.0) - self.assert_rounds_range(c2, "bcrypt", 15, 21) - - def assert_rounds_range(self, context, scheme, lower, upper): - """helper to check vary_rounds covers specified range""" - # NOTE: this runs enough times the min and max *should* be hit, - # though there's a faint chance it will randomly fail. - handler = context.handler(scheme) - salt = handler.default_salt_chars[0:1] * handler.max_salt_size - seen = set() - for i in irange(300): - h = context.genconfig(scheme, salt=salt) - r = handler.from_string(h).rounds - seen.add(r) - self.assertEqual(min(seen), lower, "vary_rounds had wrong lower limit:") - self.assertEqual(max(seen), upper, "vary_rounds had wrong upper limit:") - - #=================================================================== - # harden_verify / min_verify_time - #=================================================================== - def test_harden_verify_parsing(self): - """harden_verify -- parsing""" - warnings.filterwarnings("ignore", ".*harden_verify.*", - category=DeprecationWarning) - - # valid values - ctx = CryptContext(schemes=["sha256_crypt"]) - self.assertEqual(ctx.harden_verify, None) - self.assertEqual(ctx.using(harden_verify="").harden_verify, None) - self.assertEqual(ctx.using(harden_verify="true").harden_verify, None) - self.assertEqual(ctx.using(harden_verify="false").harden_verify, None) - - def test_dummy_verify(self): - """ - dummy_verify() method - """ - # check dummy_verify() takes expected time - expected = 0.05 - accuracy = 0.2 - handler = DelayHash.using() - handler.delay = expected - ctx = CryptContext(schemes=[handler]) - ctx.dummy_verify() # prime the memoized helpers - elapsed, _ = time_call(ctx.dummy_verify) - self.assertAlmostEqual(elapsed, expected, delta=expected * accuracy) - - # TODO: test dummy_verify() invoked by .verify() when hash is None, - # and same for .verify_and_update() - - #=================================================================== - # feature tests - #=================================================================== - def test_61_autodeprecate(self): - """test deprecated='auto' is handled correctly""" - - def getstate(ctx, category=None): - return [ctx.handler(scheme, category).deprecated for scheme in ctx.schemes()] - - # correctly reports default - ctx = CryptContext("sha256_crypt,md5_crypt,des_crypt", deprecated="auto") - self.assertEqual(getstate(ctx, None), [False, True, True]) - self.assertEqual(getstate(ctx, "admin"), [False, True, True]) - - # correctly reports changed default - ctx.update(default="md5_crypt") - self.assertEqual(getstate(ctx, None), [True, False, True]) - self.assertEqual(getstate(ctx, "admin"), [True, False, True]) - - # category default is handled correctly - ctx.update(admin__context__default="des_crypt") - self.assertEqual(getstate(ctx, None), [True, False, True]) - self.assertEqual(getstate(ctx, "admin"), [True, True, False]) - - # handles 1 scheme - ctx = CryptContext(["sha256_crypt"], deprecated="auto") - self.assertEqual(getstate(ctx, None), [False]) - self.assertEqual(getstate(ctx, "admin"), [False]) - - # disallow auto & other deprecated schemes at same time. - self.assertRaises(ValueError, CryptContext, "sha256_crypt,md5_crypt", - deprecated="auto,md5_crypt") - self.assertRaises(ValueError, CryptContext, "sha256_crypt,md5_crypt", - deprecated="md5_crypt,auto") - - def test_disabled_hashes(self): - """disabled hash support""" - # - # init ref info - # - from passlib.exc import UnknownHashError - from passlib.hash import md5_crypt, unix_disabled - - ctx = CryptContext(["des_crypt"]) - ctx2 = CryptContext(["des_crypt", "unix_disabled"]) - h_ref = ctx.hash("foo") - h_other = md5_crypt.hash('foo') - - # - # ctx.disable() - # - - # test w/o disabled hash support - self.assertRaisesRegex(RuntimeError, "no disabled hasher present", - ctx.disable) - self.assertRaisesRegex(RuntimeError, "no disabled hasher present", - ctx.disable, h_ref) - self.assertRaisesRegex(RuntimeError, "no disabled hasher present", - ctx.disable, h_other) - - # test w/ disabled hash support - h_dis = ctx2.disable() - self.assertEqual(h_dis, unix_disabled.default_marker) - h_dis_ref = ctx2.disable(h_ref) - self.assertEqual(h_dis_ref, unix_disabled.default_marker + h_ref) - - h_dis_other = ctx2.disable(h_other) - self.assertEqual(h_dis_other, unix_disabled.default_marker + h_other) - - # don't double-wrap existing disabled hash - self.assertEqual(ctx2.disable(h_dis_ref), h_dis_ref) - - # - # ctx.is_enabled() - # - - # test w/o disabled hash support - self.assertTrue(ctx.is_enabled(h_ref)) - self.assertRaises(UnknownHashError, ctx.is_enabled, h_other) - self.assertRaises(UnknownHashError, ctx.is_enabled, h_dis) - self.assertRaises(UnknownHashError, ctx.is_enabled, h_dis_ref) - - # test w/ disabled hash support - self.assertTrue(ctx2.is_enabled(h_ref)) - self.assertRaises(UnknownHashError, ctx.is_enabled, h_other) - self.assertFalse(ctx2.is_enabled(h_dis)) - self.assertFalse(ctx2.is_enabled(h_dis_ref)) - - # - # ctx.enable() - # - - # test w/o disabled hash support - self.assertRaises(UnknownHashError, ctx.enable, "") - self.assertRaises(TypeError, ctx.enable, None) - self.assertEqual(ctx.enable(h_ref), h_ref) - self.assertRaises(UnknownHashError, ctx.enable, h_other) - self.assertRaises(UnknownHashError, ctx.enable, h_dis) - self.assertRaises(UnknownHashError, ctx.enable, h_dis_ref) - - # test w/ disabled hash support - self.assertRaises(UnknownHashError, ctx.enable, "") - self.assertRaises(TypeError, ctx2.enable, None) - self.assertEqual(ctx2.enable(h_ref), h_ref) - self.assertRaises(UnknownHashError, ctx2.enable, h_other) - self.assertRaisesRegex(ValueError, "cannot restore original hash", - ctx2.enable, h_dis) - self.assertEqual(ctx2.enable(h_dis_ref), h_ref) - - #=================================================================== - # eoc - #=================================================================== - -import hashlib, time - -class DelayHash(uh.StaticHandler): - """dummy hasher which delays by specified amount""" - name = "delay_hash" - checksum_chars = uh.LOWER_HEX_CHARS - checksum_size = 40 - delay = 0 - _hash_prefix = u("$x$") - - def _calc_checksum(self, secret): - time.sleep(self.delay) - if isinstance(secret, unicode): - secret = secret.encode("utf-8") - return str_to_uascii(hashlib.sha1(b"prefix" + secret).hexdigest()) - -#============================================================================= -# LazyCryptContext -#============================================================================= -class dummy_2(uh.StaticHandler): - name = "dummy_2" - -class LazyCryptContextTest(TestCase): - descriptionPrefix = "LazyCryptContext" - - def setUp(self): - # make sure this isn't registered before OR after - unload_handler_name("dummy_2") - self.addCleanup(unload_handler_name, "dummy_2") - - def test_kwd_constructor(self): - """test plain kwds""" - self.assertFalse(has_crypt_handler("dummy_2")) - register_crypt_handler_path("dummy_2", "passlib.tests.test_context") - - cc = LazyCryptContext(iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"]) - - self.assertFalse(has_crypt_handler("dummy_2", True)) - - self.assertEqual(cc.schemes(), ("dummy_2", "des_crypt")) - self.assertTrue(cc.handler("des_crypt").deprecated) - - self.assertTrue(has_crypt_handler("dummy_2", True)) - - def test_callable_constructor(self): - self.assertFalse(has_crypt_handler("dummy_2")) - register_crypt_handler_path("dummy_2", "passlib.tests.test_context") - - def onload(flag=False): - self.assertTrue(flag) - return dict(schemes=iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"]) - - cc = LazyCryptContext(onload=onload, flag=True) - - self.assertFalse(has_crypt_handler("dummy_2", True)) - - self.assertEqual(cc.schemes(), ("dummy_2", "des_crypt")) - self.assertTrue(cc.handler("des_crypt").deprecated) - - self.assertTrue(has_crypt_handler("dummy_2", True)) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_context_deprecated.py b/.venv/Lib/site-packages/passlib/tests/test_context_deprecated.py deleted file mode 100644 index 0f76624..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_context_deprecated.py +++ /dev/null @@ -1,743 +0,0 @@ -"""tests for passlib.context - -this file is a clone of the 1.5 test_context.py, -containing the tests using the legacy CryptPolicy api. -it's being preserved here to ensure the old api doesn't break -(until Passlib 1.8, when this and the legacy api will be removed). -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -from logging import getLogger -import os -import warnings -# site -try: - from pkg_resources import resource_filename -except ImportError: - resource_filename = None -# pkg -from passlib import hash -from passlib.context import CryptContext, CryptPolicy, LazyCryptContext -from passlib.utils import to_bytes, to_unicode -import passlib.utils.handlers as uh -from passlib.tests.utils import TestCase, set_file -from passlib.registry import (register_crypt_handler_path, - _has_crypt_handler as has_crypt_handler, - _unload_handler_name as unload_handler_name, - ) -# module -log = getLogger(__name__) - -#============================================================================= -# -#============================================================================= -class CryptPolicyTest(TestCase): - """test CryptPolicy object""" - - # TODO: need to test user categories w/in all this - - descriptionPrefix = "CryptPolicy" - - #=================================================================== - # sample crypt policies used for testing - #=================================================================== - - #--------------------------------------------------------------- - # sample 1 - average config file - #--------------------------------------------------------------- - # NOTE: copy of this is stored in file passlib/tests/sample_config_1s.cfg - sample_config_1s = """\ -[passlib] -schemes = des_crypt, md5_crypt, bsdi_crypt, sha512_crypt -default = md5_crypt -all.vary_rounds = 10%% -bsdi_crypt.max_rounds = 30000 -bsdi_crypt.default_rounds = 25000 -sha512_crypt.max_rounds = 50000 -sha512_crypt.min_rounds = 40000 -""" - sample_config_1s_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), "sample_config_1s.cfg")) - if not os.path.exists(sample_config_1s_path) and resource_filename: - # in case we're zipped up in an egg. - sample_config_1s_path = resource_filename("passlib.tests", - "sample_config_1s.cfg") - - # make sure sample_config_1s uses \n linesep - tests rely on this - assert sample_config_1s.startswith("[passlib]\nschemes") - - sample_config_1pd = dict( - schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"], - default = "md5_crypt", - # NOTE: not maintaining backwards compat for rendering to "10%" - all__vary_rounds = 0.1, - bsdi_crypt__max_rounds = 30000, - bsdi_crypt__default_rounds = 25000, - sha512_crypt__max_rounds = 50000, - sha512_crypt__min_rounds = 40000, - ) - - sample_config_1pid = { - "schemes": "des_crypt, md5_crypt, bsdi_crypt, sha512_crypt", - "default": "md5_crypt", - # NOTE: not maintaining backwards compat for rendering to "10%" - "all.vary_rounds": 0.1, - "bsdi_crypt.max_rounds": 30000, - "bsdi_crypt.default_rounds": 25000, - "sha512_crypt.max_rounds": 50000, - "sha512_crypt.min_rounds": 40000, - } - - sample_config_1prd = dict( - schemes = [ hash.des_crypt, hash.md5_crypt, hash.bsdi_crypt, hash.sha512_crypt], - default = "md5_crypt", # NOTE: passlib <= 1.5 was handler obj. - # NOTE: not maintaining backwards compat for rendering to "10%" - all__vary_rounds = 0.1, - bsdi_crypt__max_rounds = 30000, - bsdi_crypt__default_rounds = 25000, - sha512_crypt__max_rounds = 50000, - sha512_crypt__min_rounds = 40000, - ) - - #--------------------------------------------------------------- - # sample 2 - partial policy & result of overlay on sample 1 - #--------------------------------------------------------------- - sample_config_2s = """\ -[passlib] -bsdi_crypt.min_rounds = 29000 -bsdi_crypt.max_rounds = 35000 -bsdi_crypt.default_rounds = 31000 -sha512_crypt.min_rounds = 45000 -""" - - sample_config_2pd = dict( - # using this to test full replacement of existing options - bsdi_crypt__min_rounds = 29000, - bsdi_crypt__max_rounds = 35000, - bsdi_crypt__default_rounds = 31000, - # using this to test partial replacement of existing options - sha512_crypt__min_rounds=45000, - ) - - sample_config_12pd = dict( - schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"], - default = "md5_crypt", - # NOTE: not maintaining backwards compat for rendering to "10%" - all__vary_rounds = 0.1, - bsdi_crypt__min_rounds = 29000, - bsdi_crypt__max_rounds = 35000, - bsdi_crypt__default_rounds = 31000, - sha512_crypt__max_rounds = 50000, - sha512_crypt__min_rounds=45000, - ) - - #--------------------------------------------------------------- - # sample 3 - just changing default - #--------------------------------------------------------------- - sample_config_3pd = dict( - default="sha512_crypt", - ) - - sample_config_123pd = dict( - schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"], - default = "sha512_crypt", - # NOTE: not maintaining backwards compat for rendering to "10%" - all__vary_rounds = 0.1, - bsdi_crypt__min_rounds = 29000, - bsdi_crypt__max_rounds = 35000, - bsdi_crypt__default_rounds = 31000, - sha512_crypt__max_rounds = 50000, - sha512_crypt__min_rounds=45000, - ) - - #--------------------------------------------------------------- - # sample 4 - category specific - #--------------------------------------------------------------- - sample_config_4s = """ -[passlib] -schemes = sha512_crypt -all.vary_rounds = 10%% -default.sha512_crypt.max_rounds = 20000 -admin.all.vary_rounds = 5%% -admin.sha512_crypt.max_rounds = 40000 -""" - - sample_config_4pd = dict( - schemes = [ "sha512_crypt" ], - # NOTE: not maintaining backwards compat for rendering to "10%" - all__vary_rounds = 0.1, - sha512_crypt__max_rounds = 20000, - # NOTE: not maintaining backwards compat for rendering to "5%" - admin__all__vary_rounds = 0.05, - admin__sha512_crypt__max_rounds = 40000, - ) - - #--------------------------------------------------------------- - # sample 5 - to_string & deprecation testing - #--------------------------------------------------------------- - sample_config_5s = sample_config_1s + """\ -deprecated = des_crypt -admin__context__deprecated = des_crypt, bsdi_crypt -""" - - sample_config_5pd = sample_config_1pd.copy() - sample_config_5pd.update( - deprecated = [ "des_crypt" ], - admin__context__deprecated = [ "des_crypt", "bsdi_crypt" ], - ) - - sample_config_5pid = sample_config_1pid.copy() - sample_config_5pid.update({ - "deprecated": "des_crypt", - "admin.context.deprecated": "des_crypt, bsdi_crypt", - }) - - sample_config_5prd = sample_config_1prd.copy() - sample_config_5prd.update({ - # XXX: should deprecated return the actual handlers in this case? - # would have to modify how policy stores info, for one. - "deprecated": ["des_crypt"], - "admin__context__deprecated": ["des_crypt", "bsdi_crypt"], - }) - - #=================================================================== - # constructors - #=================================================================== - def setUp(self): - TestCase.setUp(self) - warnings.filterwarnings("ignore", - r"The CryptPolicy class has been deprecated") - warnings.filterwarnings("ignore", - r"the method.*hash_needs_update.*is deprecated") - warnings.filterwarnings("ignore", "The 'all' scheme is deprecated.*") - warnings.filterwarnings("ignore", "bsdi_crypt rounds should be odd") - - def test_00_constructor(self): - """test CryptPolicy() constructor""" - policy = CryptPolicy(**self.sample_config_1pd) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - policy = CryptPolicy(self.sample_config_1pd) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - self.assertRaises(TypeError, CryptPolicy, {}, {}) - self.assertRaises(TypeError, CryptPolicy, {}, dummy=1) - - # check key with too many separators is rejected - self.assertRaises(TypeError, CryptPolicy, - schemes = [ "des_crypt", "md5_crypt", "bsdi_crypt", "sha512_crypt"], - bad__key__bsdi_crypt__max_rounds = 30000, - ) - - # check nameless handler rejected - class nameless(uh.StaticHandler): - name = None - self.assertRaises(ValueError, CryptPolicy, schemes=[nameless]) - - # check scheme must be name or crypt handler - self.assertRaises(TypeError, CryptPolicy, schemes=[uh.StaticHandler]) - - # check name conflicts are rejected - class dummy_1(uh.StaticHandler): - name = 'dummy_1' - self.assertRaises(KeyError, CryptPolicy, schemes=[dummy_1, dummy_1]) - - # with unknown deprecated value - self.assertRaises(KeyError, CryptPolicy, - schemes=['des_crypt'], - deprecated=['md5_crypt']) - - # with unknown default value - self.assertRaises(KeyError, CryptPolicy, - schemes=['des_crypt'], - default='md5_crypt') - - def test_01_from_path_simple(self): - """test CryptPolicy.from_path() constructor""" - # NOTE: this is separate so it can also run under GAE - - # test preset stored in existing file - path = self.sample_config_1s_path - policy = CryptPolicy.from_path(path) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test if path missing - self.assertRaises(EnvironmentError, CryptPolicy.from_path, path + 'xxx') - - def test_01_from_path(self): - """test CryptPolicy.from_path() constructor with encodings""" - path = self.mktemp() - - # test "\n" linesep - set_file(path, self.sample_config_1s) - policy = CryptPolicy.from_path(path) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test "\r\n" linesep - set_file(path, self.sample_config_1s.replace("\n","\r\n")) - policy = CryptPolicy.from_path(path) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test with custom encoding - uc2 = to_bytes(self.sample_config_1s, "utf-16", source_encoding="utf-8") - set_file(path, uc2) - policy = CryptPolicy.from_path(path, encoding="utf-16") - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - def test_02_from_string(self): - """test CryptPolicy.from_string() constructor""" - # test "\n" linesep - policy = CryptPolicy.from_string(self.sample_config_1s) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test "\r\n" linesep - policy = CryptPolicy.from_string( - self.sample_config_1s.replace("\n","\r\n")) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test with unicode - data = to_unicode(self.sample_config_1s) - policy = CryptPolicy.from_string(data) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test with non-ascii-compatible encoding - uc2 = to_bytes(self.sample_config_1s, "utf-16", source_encoding="utf-8") - policy = CryptPolicy.from_string(uc2, encoding="utf-16") - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # test category specific options - policy = CryptPolicy.from_string(self.sample_config_4s) - self.assertEqual(policy.to_dict(), self.sample_config_4pd) - - def test_03_from_source(self): - """test CryptPolicy.from_source() constructor""" - # pass it a path - policy = CryptPolicy.from_source(self.sample_config_1s_path) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # pass it a string - policy = CryptPolicy.from_source(self.sample_config_1s) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # pass it a dict (NOTE: make a copy to detect in-place modifications) - policy = CryptPolicy.from_source(self.sample_config_1pd.copy()) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # pass it existing policy - p2 = CryptPolicy.from_source(policy) - self.assertIs(policy, p2) - - # pass it something wrong - self.assertRaises(TypeError, CryptPolicy.from_source, 1) - self.assertRaises(TypeError, CryptPolicy.from_source, []) - - def test_04_from_sources(self): - """test CryptPolicy.from_sources() constructor""" - - # pass it empty list - self.assertRaises(ValueError, CryptPolicy.from_sources, []) - - # pass it one-element list - policy = CryptPolicy.from_sources([self.sample_config_1s]) - self.assertEqual(policy.to_dict(), self.sample_config_1pd) - - # pass multiple sources - policy = CryptPolicy.from_sources( - [ - self.sample_config_1s_path, - self.sample_config_2s, - self.sample_config_3pd, - ]) - self.assertEqual(policy.to_dict(), self.sample_config_123pd) - - def test_05_replace(self): - """test CryptPolicy.replace() constructor""" - - p1 = CryptPolicy(**self.sample_config_1pd) - - # check overlaying sample 2 - p2 = p1.replace(**self.sample_config_2pd) - self.assertEqual(p2.to_dict(), self.sample_config_12pd) - - # check repeating overlay makes no change - p2b = p2.replace(**self.sample_config_2pd) - self.assertEqual(p2b.to_dict(), self.sample_config_12pd) - - # check overlaying sample 3 - p3 = p2.replace(self.sample_config_3pd) - self.assertEqual(p3.to_dict(), self.sample_config_123pd) - - def test_06_forbidden(self): - """test CryptPolicy() forbidden kwds""" - - # salt not allowed to be set - self.assertRaises(KeyError, CryptPolicy, - schemes=["des_crypt"], - des_crypt__salt="xx", - ) - self.assertRaises(KeyError, CryptPolicy, - schemes=["des_crypt"], - all__salt="xx", - ) - - # schemes not allowed for category - self.assertRaises(KeyError, CryptPolicy, - schemes=["des_crypt"], - user__context__schemes=["md5_crypt"], - ) - - #=================================================================== - # reading - #=================================================================== - def test_10_has_schemes(self): - """test has_schemes() method""" - - p1 = CryptPolicy(**self.sample_config_1pd) - self.assertTrue(p1.has_schemes()) - - p3 = CryptPolicy(**self.sample_config_3pd) - self.assertTrue(not p3.has_schemes()) - - def test_11_iter_handlers(self): - """test iter_handlers() method""" - - p1 = CryptPolicy(**self.sample_config_1pd) - s = self.sample_config_1prd['schemes'] - self.assertEqual(list(p1.iter_handlers()), s) - - p3 = CryptPolicy(**self.sample_config_3pd) - self.assertEqual(list(p3.iter_handlers()), []) - - def test_12_get_handler(self): - """test get_handler() method""" - - p1 = CryptPolicy(**self.sample_config_1pd) - - # check by name - self.assertIs(p1.get_handler("bsdi_crypt"), hash.bsdi_crypt) - - # check by missing name - self.assertIs(p1.get_handler("sha256_crypt"), None) - self.assertRaises(KeyError, p1.get_handler, "sha256_crypt", required=True) - - # check default - self.assertIs(p1.get_handler(), hash.md5_crypt) - - def test_13_get_options(self): - """test get_options() method""" - - p12 = CryptPolicy(**self.sample_config_12pd) - - self.assertEqual(p12.get_options("bsdi_crypt"),dict( - # NOTE: not maintaining backwards compat for rendering to "10%" - vary_rounds = 0.1, - min_rounds = 29000, - max_rounds = 35000, - default_rounds = 31000, - )) - - self.assertEqual(p12.get_options("sha512_crypt"),dict( - # NOTE: not maintaining backwards compat for rendering to "10%" - vary_rounds = 0.1, - min_rounds = 45000, - max_rounds = 50000, - )) - - p4 = CryptPolicy.from_string(self.sample_config_4s) - self.assertEqual(p4.get_options("sha512_crypt"), dict( - # NOTE: not maintaining backwards compat for rendering to "10%" - vary_rounds=0.1, - max_rounds=20000, - )) - - self.assertEqual(p4.get_options("sha512_crypt", "user"), dict( - # NOTE: not maintaining backwards compat for rendering to "10%" - vary_rounds=0.1, - max_rounds=20000, - )) - - self.assertEqual(p4.get_options("sha512_crypt", "admin"), dict( - # NOTE: not maintaining backwards compat for rendering to "5%" - vary_rounds=0.05, - max_rounds=40000, - )) - - def test_14_handler_is_deprecated(self): - """test handler_is_deprecated() method""" - pa = CryptPolicy(**self.sample_config_1pd) - pb = CryptPolicy(**self.sample_config_5pd) - - self.assertFalse(pa.handler_is_deprecated("des_crypt")) - self.assertFalse(pa.handler_is_deprecated(hash.bsdi_crypt)) - self.assertFalse(pa.handler_is_deprecated("sha512_crypt")) - - self.assertTrue(pb.handler_is_deprecated("des_crypt")) - self.assertFalse(pb.handler_is_deprecated(hash.bsdi_crypt)) - self.assertFalse(pb.handler_is_deprecated("sha512_crypt")) - - # check categories as well - self.assertTrue(pb.handler_is_deprecated("des_crypt", "user")) - self.assertFalse(pb.handler_is_deprecated("bsdi_crypt", "user")) - self.assertTrue(pb.handler_is_deprecated("des_crypt", "admin")) - self.assertTrue(pb.handler_is_deprecated("bsdi_crypt", "admin")) - - # check deprecation is overridden per category - pc = CryptPolicy( - schemes=["md5_crypt", "des_crypt"], - deprecated=["md5_crypt"], - user__context__deprecated=["des_crypt"], - ) - self.assertTrue(pc.handler_is_deprecated("md5_crypt")) - self.assertFalse(pc.handler_is_deprecated("des_crypt")) - self.assertFalse(pc.handler_is_deprecated("md5_crypt", "user")) - self.assertTrue(pc.handler_is_deprecated("des_crypt", "user")) - - def test_15_min_verify_time(self): - """test get_min_verify_time() method""" - # silence deprecation warnings for min verify time - warnings.filterwarnings("ignore", category=DeprecationWarning) - - pa = CryptPolicy() - self.assertEqual(pa.get_min_verify_time(), 0) - self.assertEqual(pa.get_min_verify_time('admin'), 0) - - pb = pa.replace(min_verify_time=.1) - self.assertEqual(pb.get_min_verify_time(), 0) - self.assertEqual(pb.get_min_verify_time('admin'), 0) - - #=================================================================== - # serialization - #=================================================================== - def test_20_iter_config(self): - """test iter_config() method""" - p5 = CryptPolicy(**self.sample_config_5pd) - self.assertEqual(dict(p5.iter_config()), self.sample_config_5pd) - self.assertEqual(dict(p5.iter_config(resolve=True)), self.sample_config_5prd) - self.assertEqual(dict(p5.iter_config(ini=True)), self.sample_config_5pid) - - def test_21_to_dict(self): - """test to_dict() method""" - p5 = CryptPolicy(**self.sample_config_5pd) - self.assertEqual(p5.to_dict(), self.sample_config_5pd) - self.assertEqual(p5.to_dict(resolve=True), self.sample_config_5prd) - - def test_22_to_string(self): - """test to_string() method""" - pa = CryptPolicy(**self.sample_config_5pd) - s = pa.to_string() # NOTE: can't compare string directly, ordering etc may not match - pb = CryptPolicy.from_string(s) - self.assertEqual(pb.to_dict(), self.sample_config_5pd) - - s = pa.to_string(encoding="latin-1") - self.assertIsInstance(s, bytes) - - #=================================================================== - # - #=================================================================== - -#============================================================================= -# CryptContext -#============================================================================= -class CryptContextTest(TestCase): - """test CryptContext class""" - descriptionPrefix = "CryptContext" - - def setUp(self): - TestCase.setUp(self) - warnings.filterwarnings("ignore", - r"CryptContext\(\)\.replace\(\) has been deprecated.*") - warnings.filterwarnings("ignore", - r"The CryptContext ``policy`` keyword has been deprecated.*") - warnings.filterwarnings("ignore", ".*(CryptPolicy|context\.policy).*(has|have) been deprecated.*") - warnings.filterwarnings("ignore", - r"the method.*hash_needs_update.*is deprecated") - - #=================================================================== - # constructor - #=================================================================== - def test_00_constructor(self): - """test constructor""" - # create crypt context using handlers - cc = CryptContext([hash.md5_crypt, hash.bsdi_crypt, hash.des_crypt]) - c,b,a = cc.policy.iter_handlers() - self.assertIs(a, hash.des_crypt) - self.assertIs(b, hash.bsdi_crypt) - self.assertIs(c, hash.md5_crypt) - - # create context using names - cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"]) - c,b,a = cc.policy.iter_handlers() - self.assertIs(a, hash.des_crypt) - self.assertIs(b, hash.bsdi_crypt) - self.assertIs(c, hash.md5_crypt) - - # policy kwd - policy = cc.policy - cc = CryptContext(policy=policy) - self.assertEqual(cc.to_dict(), policy.to_dict()) - - cc = CryptContext(policy=policy, default="bsdi_crypt") - self.assertNotEqual(cc.to_dict(), policy.to_dict()) - self.assertEqual(cc.to_dict(), dict(schemes=["md5_crypt","bsdi_crypt","des_crypt"], - default="bsdi_crypt")) - - self.assertRaises(TypeError, setattr, cc, 'policy', None) - self.assertRaises(TypeError, CryptContext, policy='x') - - def test_01_replace(self): - """test replace()""" - - cc = CryptContext(["md5_crypt", "bsdi_crypt", "des_crypt"]) - self.assertIs(cc.policy.get_handler(), hash.md5_crypt) - - cc2 = cc.replace() - self.assertIsNot(cc2, cc) - # NOTE: was not able to maintain backward compatibility with this... - ##self.assertIs(cc2.policy, cc.policy) - - cc3 = cc.replace(default="bsdi_crypt") - self.assertIsNot(cc3, cc) - # NOTE: was not able to maintain backward compatibility with this... - ##self.assertIs(cc3.policy, cc.policy) - self.assertIs(cc3.policy.get_handler(), hash.bsdi_crypt) - - def test_02_no_handlers(self): - """test no handlers""" - - # check constructor... - cc = CryptContext() - self.assertRaises(KeyError, cc.identify, 'hash', required=True) - self.assertRaises(KeyError, cc.hash, 'secret') - self.assertRaises(KeyError, cc.verify, 'secret', 'hash') - - # check updating policy after the fact... - cc = CryptContext(['md5_crypt']) - p = CryptPolicy(schemes=[]) - cc.policy = p - - self.assertRaises(KeyError, cc.identify, 'hash', required=True) - self.assertRaises(KeyError, cc.hash, 'secret') - self.assertRaises(KeyError, cc.verify, 'secret', 'hash') - - #=================================================================== - # policy adaptation - #=================================================================== - sample_policy_1 = dict( - schemes = [ "des_crypt", "md5_crypt", "phpass", "bsdi_crypt", - "sha256_crypt"], - deprecated = [ "des_crypt", ], - default = "sha256_crypt", - bsdi_crypt__max_rounds = 30, - bsdi_crypt__default_rounds = 25, - bsdi_crypt__vary_rounds = 0, - sha256_crypt__max_rounds = 3000, - sha256_crypt__min_rounds = 2000, - sha256_crypt__default_rounds = 3000, - phpass__ident = "H", - phpass__default_rounds = 7, - ) - - def test_12_hash_needs_update(self): - """test hash_needs_update() method""" - cc = CryptContext(**self.sample_policy_1) - - # check deprecated scheme - self.assertTrue(cc.hash_needs_update('9XXD4trGYeGJA')) - self.assertFalse(cc.hash_needs_update('$1$J8HC2RCr$HcmM.7NxB2weSvlw2FgzU0')) - - # check min rounds - self.assertTrue(cc.hash_needs_update('$5$rounds=1999$jD81UCoo.zI.UETs$Y7qSTQ6mTiU9qZB4fRr43wRgQq4V.5AAf7F97Pzxey/')) - self.assertFalse(cc.hash_needs_update('$5$rounds=2000$228SSRje04cnNCaQ$YGV4RYu.5sNiBvorQDlO0WWQjyJVGKBcJXz3OtyQ2u8')) - - # check max rounds - self.assertFalse(cc.hash_needs_update('$5$rounds=3000$fS9iazEwTKi7QPW4$VasgBC8FqlOvD7x2HhABaMXCTh9jwHclPA9j5YQdns.')) - self.assertTrue(cc.hash_needs_update('$5$rounds=3001$QlFHHifXvpFX4PLs$/0ekt7lSs/lOikSerQ0M/1porEHxYq7W/2hdFpxA3fA')) - - #=================================================================== - # border cases - #=================================================================== - def test_30_nonstring_hash(self): - """test non-string hash values cause error""" - warnings.filterwarnings("ignore", ".*needs_update.*'scheme' keyword is deprecated.*") - - # - # test hash=None or some other non-string causes TypeError - # and that explicit-scheme code path behaves the same. - # - cc = CryptContext(["des_crypt"]) - for hash, kwds in [ - (None, {}), - # NOTE: 'scheme' kwd is deprecated... - (None, {"scheme": "des_crypt"}), - (1, {}), - ((), {}), - ]: - - self.assertRaises(TypeError, cc.hash_needs_update, hash, **kwds) - - cc2 = CryptContext(["mysql323"]) - self.assertRaises(TypeError, cc2.hash_needs_update, None) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# LazyCryptContext -#============================================================================= -class dummy_2(uh.StaticHandler): - name = "dummy_2" - -class LazyCryptContextTest(TestCase): - descriptionPrefix = "LazyCryptContext" - - def setUp(self): - TestCase.setUp(self) - - # make sure this isn't registered before OR after - unload_handler_name("dummy_2") - self.addCleanup(unload_handler_name, "dummy_2") - - # silence some warnings - warnings.filterwarnings("ignore", - r"CryptContext\(\)\.replace\(\) has been deprecated") - warnings.filterwarnings("ignore", ".*(CryptPolicy|context\.policy).*(has|have) been deprecated.*") - - def test_kwd_constructor(self): - """test plain kwds""" - self.assertFalse(has_crypt_handler("dummy_2")) - register_crypt_handler_path("dummy_2", "passlib.tests.test_context") - - cc = LazyCryptContext(iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"]) - - self.assertFalse(has_crypt_handler("dummy_2", True)) - - self.assertTrue(cc.policy.handler_is_deprecated("des_crypt")) - self.assertEqual(cc.policy.schemes(), ["dummy_2", "des_crypt"]) - - self.assertTrue(has_crypt_handler("dummy_2", True)) - - def test_callable_constructor(self): - """test create_policy() hook, returning CryptPolicy""" - self.assertFalse(has_crypt_handler("dummy_2")) - register_crypt_handler_path("dummy_2", "passlib.tests.test_context") - - def create_policy(flag=False): - self.assertTrue(flag) - return CryptPolicy(schemes=iter(["dummy_2", "des_crypt"]), deprecated=["des_crypt"]) - - cc = LazyCryptContext(create_policy=create_policy, flag=True) - - self.assertFalse(has_crypt_handler("dummy_2", True)) - - self.assertTrue(cc.policy.handler_is_deprecated("des_crypt")) - self.assertEqual(cc.policy.schemes(), ["dummy_2", "des_crypt"]) - - self.assertTrue(has_crypt_handler("dummy_2", True)) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_crypto_builtin_md4.py b/.venv/Lib/site-packages/passlib/tests/test_crypto_builtin_md4.py deleted file mode 100644 index 0aca1eb..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_crypto_builtin_md4.py +++ /dev/null @@ -1,160 +0,0 @@ -"""passlib.tests -- unittests for passlib.crypto._md4""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, division -# core -from binascii import hexlify -import hashlib -# site -# pkg -# module -from passlib.utils.compat import bascii_to_str, PY3, u -from passlib.crypto.digest import lookup_hash -from passlib.tests.utils import TestCase, skipUnless -# local -__all__ = [ - "_Common_MD4_Test", - "MD4_Builtin_Test", - "MD4_SSL_Test", -] -#============================================================================= -# test pure-python MD4 implementation -#============================================================================= -class _Common_MD4_Test(TestCase): - """common code for testing md4 backends""" - - vectors = [ - # input -> hex digest - # test vectors from http://www.faqs.org/rfcs/rfc1320.html - A.5 - (b"", "31d6cfe0d16ae931b73c59d7e0c089c0"), - (b"a", "bde52cb31de33e46245e05fbdbd6fb24"), - (b"abc", "a448017aaf21d8525fc10ae87aa6729d"), - (b"message digest", "d9130a8164549fe818874806e1c7014b"), - (b"abcdefghijklmnopqrstuvwxyz", "d79e1c308aa5bbcdeea8ed63df412da9"), - (b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "043f8582f241db351ce627e153e7f0e4"), - (b"12345678901234567890123456789012345678901234567890123456789012345678901234567890", "e33b4ddc9c38f2199c3e7b164fcc0536"), - ] - - def get_md4_const(self): - """ - get md4 constructor -- - overridden by subclasses to use alternate backends. - """ - return lookup_hash("md4").const - - def test_attrs(self): - """informational attributes""" - h = self.get_md4_const()() - self.assertEqual(h.name, "md4") - self.assertEqual(h.digest_size, 16) - self.assertEqual(h.block_size, 64) - - def test_md4_update(self): - """update() method""" - md4 = self.get_md4_const() - h = md4(b'') - self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0") - - h.update(b'a') - self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24") - - h.update(b'bcdefghijklmnopqrstuvwxyz') - self.assertEqual(h.hexdigest(), "d79e1c308aa5bbcdeea8ed63df412da9") - - if PY3: - # reject unicode, hash should return digest of b'' - h = md4() - self.assertRaises(TypeError, h.update, u('a')) - self.assertEqual(h.hexdigest(), "31d6cfe0d16ae931b73c59d7e0c089c0") - else: - # coerce unicode to ascii, hash should return digest of b'a' - h = md4() - h.update(u('a')) - self.assertEqual(h.hexdigest(), "bde52cb31de33e46245e05fbdbd6fb24") - - def test_md4_hexdigest(self): - """hexdigest() method""" - md4 = self.get_md4_const() - for input, hex in self.vectors: - out = md4(input).hexdigest() - self.assertEqual(out, hex) - - def test_md4_digest(self): - """digest() method""" - md4 = self.get_md4_const() - for input, hex in self.vectors: - out = bascii_to_str(hexlify(md4(input).digest())) - self.assertEqual(out, hex) - - def test_md4_copy(self): - """copy() method""" - md4 = self.get_md4_const() - h = md4(b'abc') - - h2 = h.copy() - h2.update(b'def') - self.assertEqual(h2.hexdigest(), '804e7f1c2586e50b49ac65db5b645131') - - h.update(b'ghi') - self.assertEqual(h.hexdigest(), 'c5225580bfe176f6deeee33dee98732c') - - -#------------------------------------------------------------------------ -# create subclasses to test various backends -#------------------------------------------------------------------------ - -def has_native_md4(): # pragma: no cover -- runtime detection - """ - check if hashlib natively supports md4. - """ - try: - hashlib.new("md4") - return True - except ValueError: - # not supported - ssl probably missing (e.g. ironpython) - return False - - -@skipUnless(has_native_md4(), "hashlib lacks ssl/md4 support") -class MD4_SSL_Test(_Common_MD4_Test): - descriptionPrefix = "hashlib.new('md4')" - - # NOTE: we trust ssl got md4 implementation right, - # this is more to test our test is correct :) - - def setUp(self): - super(MD4_SSL_Test, self).setUp() - - # make sure we're using right constructor. - self.assertEqual(self.get_md4_const().__module__, "hashlib") - - -class MD4_Builtin_Test(_Common_MD4_Test): - descriptionPrefix = "passlib.crypto._md4.md4()" - - def setUp(self): - super(MD4_Builtin_Test, self).setUp() - - if has_native_md4(): - - # Temporarily make lookup_hash() use builtin pure-python implementation, - # by monkeypatching hashlib.new() to ensure we fall back to passlib's md4 class. - orig = hashlib.new - def wrapper(name, *args): - if name == "md4": - raise ValueError("md4 disabled for testing") - return orig(name, *args) - self.patchAttr(hashlib, "new", wrapper) - - # flush cache before & after test, since we're mucking with it. - lookup_hash.clear_cache() - self.addCleanup(lookup_hash.clear_cache) - - # make sure we're using right constructor. - self.assertEqual(self.get_md4_const().__module__, "passlib.crypto._md4") - - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_crypto_des.py b/.venv/Lib/site-packages/passlib/tests/test_crypto_des.py deleted file mode 100644 index ab31845..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_crypto_des.py +++ /dev/null @@ -1,194 +0,0 @@ -"""passlib.tests -- unittests for passlib.crypto.des""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, division -# core -from functools import partial -# site -# pkg -# module -from passlib.utils import getrandbytes -from passlib.tests.utils import TestCase - -#============================================================================= -# test DES routines -#============================================================================= -class DesTest(TestCase): - descriptionPrefix = "passlib.crypto.des" - - # test vectors taken from http://www.skepticfiles.org/faq/testdes.htm - des_test_vectors = [ - # key, plaintext, ciphertext - (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7), - (0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x7359B2163E4EDC58), - (0x3000000000000000, 0x1000000000000001, 0x958E6E627A05557B), - (0x1111111111111111, 0x1111111111111111, 0xF40379AB9E0EC533), - (0x0123456789ABCDEF, 0x1111111111111111, 0x17668DFC7292532D), - (0x1111111111111111, 0x0123456789ABCDEF, 0x8A5AE1F81AB8F2DD), - (0x0000000000000000, 0x0000000000000000, 0x8CA64DE9C1B123A7), - (0xFEDCBA9876543210, 0x0123456789ABCDEF, 0xED39D950FA74BCC4), - (0x7CA110454A1A6E57, 0x01A1D6D039776742, 0x690F5B0D9A26939B), - (0x0131D9619DC1376E, 0x5CD54CA83DEF57DA, 0x7A389D10354BD271), - (0x07A1133E4A0B2686, 0x0248D43806F67172, 0x868EBB51CAB4599A), - (0x3849674C2602319E, 0x51454B582DDF440A, 0x7178876E01F19B2A), - (0x04B915BA43FEB5B6, 0x42FD443059577FA2, 0xAF37FB421F8C4095), - (0x0113B970FD34F2CE, 0x059B5E0851CF143A, 0x86A560F10EC6D85B), - (0x0170F175468FB5E6, 0x0756D8E0774761D2, 0x0CD3DA020021DC09), - (0x43297FAD38E373FE, 0x762514B829BF486A, 0xEA676B2CB7DB2B7A), - (0x07A7137045DA2A16, 0x3BDD119049372802, 0xDFD64A815CAF1A0F), - (0x04689104C2FD3B2F, 0x26955F6835AF609A, 0x5C513C9C4886C088), - (0x37D06BB516CB7546, 0x164D5E404F275232, 0x0A2AEEAE3FF4AB77), - (0x1F08260D1AC2465E, 0x6B056E18759F5CCA, 0xEF1BF03E5DFA575A), - (0x584023641ABA6176, 0x004BD6EF09176062, 0x88BF0DB6D70DEE56), - (0x025816164629B007, 0x480D39006EE762F2, 0xA1F9915541020B56), - (0x49793EBC79B3258F, 0x437540C8698F3CFA, 0x6FBF1CAFCFFD0556), - (0x4FB05E1515AB73A7, 0x072D43A077075292, 0x2F22E49BAB7CA1AC), - (0x49E95D6D4CA229BF, 0x02FE55778117F12A, 0x5A6B612CC26CCE4A), - (0x018310DC409B26D6, 0x1D9D5C5018F728C2, 0x5F4C038ED12B2E41), - (0x1C587F1C13924FEF, 0x305532286D6F295A, 0x63FAC0D034D9F793), - (0x0101010101010101, 0x0123456789ABCDEF, 0x617B3A0CE8F07100), - (0x1F1F1F1F0E0E0E0E, 0x0123456789ABCDEF, 0xDB958605F8C8C606), - (0xE0FEE0FEF1FEF1FE, 0x0123456789ABCDEF, 0xEDBFD1C66C29CCC7), - (0x0000000000000000, 0xFFFFFFFFFFFFFFFF, 0x355550B2150E2451), - (0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0xCAAAAF4DEAF1DBAE), - (0x0123456789ABCDEF, 0x0000000000000000, 0xD5D44FF720683D0D), - (0xFEDCBA9876543210, 0xFFFFFFFFFFFFFFFF, 0x2A2BB008DF97C2F2), - ] - - def test_01_expand(self): - """expand_des_key()""" - from passlib.crypto.des import expand_des_key, shrink_des_key, \ - _KDATA_MASK, INT_56_MASK - - # make sure test vectors are preserved (sans parity bits) - # uses ints, bytes are tested under # 02 - for key1, _, _ in self.des_test_vectors: - key2 = shrink_des_key(key1) - key3 = expand_des_key(key2) - # NOTE: this assumes expand_des_key() sets parity bits to 0 - self.assertEqual(key3, key1 & _KDATA_MASK) - - # type checks - self.assertRaises(TypeError, expand_des_key, 1.0) - - # too large - self.assertRaises(ValueError, expand_des_key, INT_56_MASK+1) - self.assertRaises(ValueError, expand_des_key, b"\x00"*8) - - # too small - self.assertRaises(ValueError, expand_des_key, -1) - self.assertRaises(ValueError, expand_des_key, b"\x00"*6) - - def test_02_shrink(self): - """shrink_des_key()""" - from passlib.crypto.des import expand_des_key, shrink_des_key, INT_64_MASK - rng = self.getRandom() - - # make sure reverse works for some random keys - # uses bytes, ints are tested under # 01 - for i in range(20): - key1 = getrandbytes(rng, 7) - key2 = expand_des_key(key1) - key3 = shrink_des_key(key2) - self.assertEqual(key3, key1) - - # type checks - self.assertRaises(TypeError, shrink_des_key, 1.0) - - # too large - self.assertRaises(ValueError, shrink_des_key, INT_64_MASK+1) - self.assertRaises(ValueError, shrink_des_key, b"\x00"*9) - - # too small - self.assertRaises(ValueError, shrink_des_key, -1) - self.assertRaises(ValueError, shrink_des_key, b"\x00"*7) - - def _random_parity(self, key): - """randomize parity bits""" - from passlib.crypto.des import _KDATA_MASK, _KPARITY_MASK, INT_64_MASK - rng = self.getRandom() - return (key & _KDATA_MASK) | (rng.randint(0,INT_64_MASK) & _KPARITY_MASK) - - def test_03_encrypt_bytes(self): - """des_encrypt_block()""" - from passlib.crypto.des import (des_encrypt_block, shrink_des_key, - _pack64, _unpack64) - - # run through test vectors - for key, plaintext, correct in self.des_test_vectors: - # convert to bytes - key = _pack64(key) - plaintext = _pack64(plaintext) - correct = _pack64(correct) - - # test 64-bit key - result = des_encrypt_block(key, plaintext) - self.assertEqual(result, correct, "key=%r plaintext=%r:" % - (key, plaintext)) - - # test 56-bit version - key2 = shrink_des_key(key) - result = des_encrypt_block(key2, plaintext) - self.assertEqual(result, correct, "key=%r shrink(key)=%r plaintext=%r:" % - (key, key2, plaintext)) - - # test with random parity bits - for _ in range(20): - key3 = _pack64(self._random_parity(_unpack64(key))) - result = des_encrypt_block(key3, plaintext) - self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" % - (key, key3, plaintext)) - - # check invalid keys - stub = b'\x00' * 8 - self.assertRaises(TypeError, des_encrypt_block, 0, stub) - self.assertRaises(ValueError, des_encrypt_block, b'\x00'*6, stub) - - # check invalid input - self.assertRaises(TypeError, des_encrypt_block, stub, 0) - self.assertRaises(ValueError, des_encrypt_block, stub, b'\x00'*7) - - # check invalid salts - self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=-1) - self.assertRaises(ValueError, des_encrypt_block, stub, stub, salt=1<<24) - - # check invalid rounds - self.assertRaises(ValueError, des_encrypt_block, stub, stub, 0, rounds=0) - - def test_04_encrypt_ints(self): - """des_encrypt_int_block()""" - from passlib.crypto.des import des_encrypt_int_block - - # run through test vectors - for key, plaintext, correct in self.des_test_vectors: - # test 64-bit key - result = des_encrypt_int_block(key, plaintext) - self.assertEqual(result, correct, "key=%r plaintext=%r:" % - (key, plaintext)) - - # test with random parity bits - for _ in range(20): - key3 = self._random_parity(key) - result = des_encrypt_int_block(key3, plaintext) - self.assertEqual(result, correct, "key=%r rndparity(key)=%r plaintext=%r:" % - (key, key3, plaintext)) - - # check invalid keys - self.assertRaises(TypeError, des_encrypt_int_block, b'\x00', 0) - self.assertRaises(ValueError, des_encrypt_int_block, -1, 0) - - # check invalid input - self.assertRaises(TypeError, des_encrypt_int_block, 0, b'\x00') - self.assertRaises(ValueError, des_encrypt_int_block, 0, -1) - - # check invalid salts - self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=-1) - self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, salt=1<<24) - - # check invalid rounds - self.assertRaises(ValueError, des_encrypt_int_block, 0, 0, 0, rounds=0) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_crypto_digest.py b/.venv/Lib/site-packages/passlib/tests/test_crypto_digest.py deleted file mode 100644 index 461d209..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_crypto_digest.py +++ /dev/null @@ -1,544 +0,0 @@ -"""tests for passlib.utils.(des|pbkdf2|md4)""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement, division -# core -from binascii import hexlify -import hashlib -import warnings -# site -# pkg -# module -from passlib.exc import UnknownHashError -from passlib.utils.compat import PY3, u, JYTHON -from passlib.tests.utils import TestCase, TEST_MODE, skipUnless, hb - -#============================================================================= -# test assorted crypto helpers -#============================================================================= -class HashInfoTest(TestCase): - """test various crypto functions""" - descriptionPrefix = "passlib.crypto.digest" - - #: list of formats norm_hash_name() should support - norm_hash_formats = ["hashlib", "iana"] - - #: test cases for norm_hash_name() - #: each row contains (iana name, hashlib name, ... 0+ unnormalized names) - norm_hash_samples = [ - # real hashes - ("md5", "md5", "SCRAM-MD5-PLUS", "MD-5"), - ("sha1", "sha-1", "SCRAM-SHA-1", "SHA1"), - ("sha256", "sha-256", "SHA_256", "sha2-256"), - ("ripemd160", "ripemd-160", "SCRAM-RIPEMD-160", "RIPEmd160", - # NOTE: there was an older "RIPEMD" & "RIPEMD-128", but python treates "RIPEMD" - # as alias for "RIPEMD-160" - "ripemd", "SCRAM-RIPEMD"), - - # fake hashes (to check if fallback normalization behaves sanely) - ("sha4_256", "sha4-256", "SHA4-256", "SHA-4-256"), - ("test128", "test-128", "TEST128"), - ("test2", "test2", "TEST-2"), - ("test3_128", "test3-128", "TEST-3-128"), - ] - - def test_norm_hash_name(self): - """norm_hash_name()""" - from itertools import chain - from passlib.crypto.digest import norm_hash_name, _known_hash_names - - # snapshot warning state, ignore unknown hash warnings - ctx = warnings.catch_warnings() - ctx.__enter__() - self.addCleanup(ctx.__exit__) - warnings.filterwarnings("ignore", '.*unknown hash') - warnings.filterwarnings("ignore", '.*unsupported hash') - - # test string types - self.assertEqual(norm_hash_name(u("MD4")), "md4") - self.assertEqual(norm_hash_name(b"MD4"), "md4") - self.assertRaises(TypeError, norm_hash_name, None) - - # test selected results - for row in chain(_known_hash_names, self.norm_hash_samples): - for idx, format in enumerate(self.norm_hash_formats): - correct = row[idx] - for value in row: - result = norm_hash_name(value, format) - self.assertEqual(result, correct, - "name=%r, format=%r:" % (value, - format)) - - def test_lookup_hash_ctor(self): - """lookup_hash() -- constructor""" - from passlib.crypto.digest import lookup_hash - - # invalid/unknown names should be rejected - self.assertRaises(ValueError, lookup_hash, "new") - self.assertRaises(ValueError, lookup_hash, "__name__") - self.assertRaises(ValueError, lookup_hash, "sha4") - - # 1. should return hashlib builtin if found - self.assertEqual(lookup_hash("md5"), (hashlib.md5, 16, 64)) - - # 2. should return wrapper around hashlib.new() if found - try: - hashlib.new("sha") - has_sha = True - except ValueError: - has_sha = False - if has_sha: - record = lookup_hash("sha") - const = record[0] - self.assertEqual(record, (const, 20, 64)) - self.assertEqual(hexlify(const(b"abc").digest()), - b"0164b8a914cd2a5e74c4f7ff082c4d97f1edf880") - - else: - self.assertRaises(ValueError, lookup_hash, "sha") - - # 3. should fall back to builtin md4 - try: - hashlib.new("md4") - has_md4 = True - except ValueError: - has_md4 = False - record = lookup_hash("md4") - const = record[0] - if not has_md4: - from passlib.crypto._md4 import md4 - self.assertIs(const, md4) - self.assertEqual(record, (const, 16, 64)) - self.assertEqual(hexlify(const(b"abc").digest()), - b"a448017aaf21d8525fc10ae87aa6729d") - - # should memoize records - self.assertIs(lookup_hash("md5"), lookup_hash("md5")) - - def test_lookup_hash_w_unknown_name(self): - """lookup_hash() -- unknown hash name""" - from passlib.crypto.digest import lookup_hash - - # unknown names should be rejected by default - self.assertRaises(UnknownHashError, lookup_hash, "xxx256") - - # required=False should return stub record instead - info = lookup_hash("xxx256", required=False) - self.assertFalse(info.supported) - self.assertRaisesRegex(UnknownHashError, "unknown hash: 'xxx256'", info.const) - self.assertEqual(info.name, "xxx256") - self.assertEqual(info.digest_size, None) - self.assertEqual(info.block_size, None) - - # should cache stub records - info2 = lookup_hash("xxx256", required=False) - self.assertIs(info2, info) - - def test_mock_fips_mode(self): - """ - lookup_hash() -- test set_mock_fips_mode() - """ - from passlib.crypto.digest import lookup_hash, _set_mock_fips_mode - - # check if md5 is available so we can test mock helper - if not lookup_hash("md5", required=False).supported: - raise self.skipTest("md5 not supported") - - # enable monkeypatch to mock up fips mode - _set_mock_fips_mode() - self.addCleanup(_set_mock_fips_mode, False) - - pat = "'md5' hash disabled for fips" - self.assertRaisesRegex(UnknownHashError, pat, lookup_hash, "md5") - - info = lookup_hash("md5", required=False) - self.assertRegex(info.error_text, pat) - self.assertRaisesRegex(UnknownHashError, pat, info.const) - - # should use hardcoded fallback info - self.assertEqual(info.digest_size, 16) - self.assertEqual(info.block_size, 64) - - def test_lookup_hash_metadata(self): - """lookup_hash() -- metadata""" - - from passlib.crypto.digest import lookup_hash - - # quick test of metadata using known reference - sha256 - info = lookup_hash("sha256") - self.assertEqual(info.name, "sha256") - self.assertEqual(info.iana_name, "sha-256") - self.assertEqual(info.block_size, 64) - self.assertEqual(info.digest_size, 32) - self.assertIs(lookup_hash("SHA2-256"), info) - - # quick test of metadata using known reference - md5 - info = lookup_hash("md5") - self.assertEqual(info.name, "md5") - self.assertEqual(info.iana_name, "md5") - self.assertEqual(info.block_size, 64) - self.assertEqual(info.digest_size, 16) - - def test_lookup_hash_alt_types(self): - """lookup_hash() -- alternate types""" - - from passlib.crypto.digest import lookup_hash - - info = lookup_hash("sha256") - self.assertIs(lookup_hash(info), info) - self.assertIs(lookup_hash(info.const), info) - - self.assertRaises(TypeError, lookup_hash, 123) - - # TODO: write full test of compile_hmac() -- currently relying on pbkdf2_hmac() tests - -#============================================================================= -# test PBKDF1 support -#============================================================================= -class Pbkdf1_Test(TestCase): - """test kdf helpers""" - descriptionPrefix = "passlib.crypto.digest.pbkdf1" - - pbkdf1_tests = [ - # (password, salt, rounds, keylen, hash, result) - - # - # from http://www.di-mgt.com.au/cryptoKDFs.html - # - (b'password', hb('78578E5A5D63CB06'), 1000, 16, 'sha1', hb('dc19847e05c64d2faf10ebfb4a3d2a20')), - - # - # custom - # - (b'password', b'salt', 1000, 0, 'md5', b''), - (b'password', b'salt', 1000, 1, 'md5', hb('84')), - (b'password', b'salt', 1000, 8, 'md5', hb('8475c6a8531a5d27')), - (b'password', b'salt', 1000, 16, 'md5', hb('8475c6a8531a5d27e386cd496457812c')), - (b'password', b'salt', 1000, None, 'md5', hb('8475c6a8531a5d27e386cd496457812c')), - (b'password', b'salt', 1000, None, 'sha1', hb('4a8fd48e426ed081b535be5769892fa396293efb')), - ] - if not JYTHON: # FIXME: find out why not jython, or reenable this. - pbkdf1_tests.append( - (b'password', b'salt', 1000, None, 'md4', hb('f7f2e91100a8f96190f2dd177cb26453')) - ) - - def test_known(self): - """test reference vectors""" - from passlib.crypto.digest import pbkdf1 - for secret, salt, rounds, keylen, digest, correct in self.pbkdf1_tests: - result = pbkdf1(digest, secret, salt, rounds, keylen) - self.assertEqual(result, correct) - - def test_border(self): - """test border cases""" - from passlib.crypto.digest import pbkdf1 - def helper(secret=b'secret', salt=b'salt', rounds=1, keylen=1, hash='md5'): - return pbkdf1(hash, secret, salt, rounds, keylen) - helper() - - # salt/secret wrong type - self.assertRaises(TypeError, helper, secret=1) - self.assertRaises(TypeError, helper, salt=1) - - # non-existent hashes - self.assertRaises(ValueError, helper, hash='missing') - - # rounds < 1 and wrong type - self.assertRaises(ValueError, helper, rounds=0) - self.assertRaises(TypeError, helper, rounds='1') - - # keylen < 0, keylen > block_size, and wrong type - self.assertRaises(ValueError, helper, keylen=-1) - self.assertRaises(ValueError, helper, keylen=17, hash='md5') - self.assertRaises(TypeError, helper, keylen='1') - -#============================================================================= -# test PBKDF2-HMAC support -#============================================================================= - -# import the test subject -from passlib.crypto.digest import pbkdf2_hmac, PBKDF2_BACKENDS - -# NOTE: relying on tox to verify this works under all the various backends. -class Pbkdf2Test(TestCase): - """test pbkdf2() support""" - descriptionPrefix = "passlib.crypto.digest.pbkdf2_hmac() " % ", ".join(PBKDF2_BACKENDS) - - pbkdf2_test_vectors = [ - # (result, secret, salt, rounds, keylen, digest="sha1") - - # - # from rfc 3962 - # - - # test case 1 / 128 bit - ( - hb("cdedb5281bb2f801565a1122b2563515"), - b"password", b"ATHENA.MIT.EDUraeburn", 1, 16 - ), - - # test case 2 / 128 bit - ( - hb("01dbee7f4a9e243e988b62c73cda935d"), - b"password", b"ATHENA.MIT.EDUraeburn", 2, 16 - ), - - # test case 2 / 256 bit - ( - hb("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"), - b"password", b"ATHENA.MIT.EDUraeburn", 2, 32 - ), - - # test case 3 / 256 bit - ( - hb("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"), - b"password", b"ATHENA.MIT.EDUraeburn", 1200, 32 - ), - - # test case 4 / 256 bit - ( - hb("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"), - b"password", b'\x12\x34\x56\x78\x78\x56\x34\x12', 5, 32 - ), - - # test case 5 / 256 bit - ( - hb("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"), - b"X"*64, b"pass phrase equals block size", 1200, 32 - ), - - # test case 6 / 256 bit - ( - hb("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"), - b"X"*65, b"pass phrase exceeds block size", 1200, 32 - ), - - # - # from rfc 6070 - # - ( - hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"), - b"password", b"salt", 1, 20, - ), - - ( - hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), - b"password", b"salt", 2, 20, - ), - - ( - hb("4b007901b765489abead49d926f721d065a429c1"), - b"password", b"salt", 4096, 20, - ), - - # just runs too long - could enable if ALL option is set - ##( - ## - ## hb("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"), - ## "password", "salt", 16777216, 20, - ##), - - ( - hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), - b"passwordPASSWORDpassword", - b"saltSALTsaltSALTsaltSALTsaltSALTsalt", - 4096, 25, - ), - - ( - hb("56fa6aa75548099dcc37d7f03425e0c3"), - b"pass\00word", b"sa\00lt", 4096, 16, - ), - - # - # from example in http://grub.enbug.org/Authentication - # - ( - hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED" - "97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC" - "6C29E293F0A0"), - b"hello", - hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71" - "784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073" - "994D79080136"), - 10000, 64, "sha512" - ), - - # - # test vectors from fastpbkdf2 - # - ( - hb('55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc' - '49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783'), - b'passwd', b'salt', 1, 64, 'sha256', - ), - - ( - hb('4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56' - 'a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d'), - b'Password', b'NaCl', 80000, 64, 'sha256', - ), - - ( - hb('120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b'), - b'password', b'salt', 1, 32, 'sha256', - ), - - ( - hb('ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43'), - b'password', b'salt', 2, 32, 'sha256', - ), - - ( - hb('c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a'), - b'password', b'salt', 4096, 32, 'sha256', - ), - - ( - hb('348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1c' - '635518c7dac47e9'), - b'passwordPASSWORDpassword', b'saltSALTsaltSALTsaltSALTsaltSALTsalt', - 4096, 40, 'sha256', - ), - - ( - hb('9e83f279c040f2a11aa4a02b24c418f2d3cb39560c9627fa4f47e3bcc2897c3d'), - b'', b'salt', 1024, 32, 'sha256', - ), - - ( - hb('ea5808411eb0c7e830deab55096cee582761e22a9bc034e3ece925225b07bf46'), - b'password', b'', 1024, 32, 'sha256', - ), - - ( - hb('89b69d0516f829893c696226650a8687'), - b'pass\x00word', b'sa\x00lt', 4096, 16, 'sha256', - ), - - ( - hb('867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252'), - b'password', b'salt', 1, 32, 'sha512', - ), - - ( - hb('e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53c'), - b'password', b'salt', 2, 32, 'sha512', - ), - - ( - hb('d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5'), - b'password', b'salt', 4096, 32, 'sha512', - ), - - ( - hb('6e23f27638084b0f7ea1734e0d9841f55dd29ea60a834466f3396bac801fac1eeb' - '63802f03a0b4acd7603e3699c8b74437be83ff01ad7f55dac1ef60f4d56480c35e' - 'e68fd52c6936'), - b'passwordPASSWORDpassword', b'saltSALTsaltSALTsaltSALTsaltSALTsalt', - 1, 72, 'sha512', - ), - - ( - hb('0c60c80f961f0e71f3a9b524af6012062fe037a6'), - b'password', b'salt', 1, 20, 'sha1', - ), - - # - # custom tests - # - ( - hb('e248fb6b13365146f8ac6307cc222812'), - b"secret", b"salt", 10, 16, "sha1", - ), - ( - hb('e248fb6b13365146f8ac6307cc2228127872da6d'), - b"secret", b"salt", 10, None, "sha1", - ), - ( - hb('b1d5485772e6f76d5ebdc11b38d3eff0a5b2bd50dc11f937e86ecacd0cd40d1b' - '9113e0734e3b76a3'), - b"secret", b"salt", 62, 40, "md5", - ), - ( - hb('ea014cc01f78d3883cac364bb5d054e2be238fb0b6081795a9d84512126e3129' - '062104d2183464c4'), - b"secret", b"salt", 62, 40, "md4", - ), - ] - - def test_known(self): - """test reference vectors""" - for row in self.pbkdf2_test_vectors: - correct, secret, salt, rounds, keylen = row[:5] - digest = row[5] if len(row) == 6 else "sha1" - result = pbkdf2_hmac(digest, secret, salt, rounds, keylen) - self.assertEqual(result, correct) - - def test_backends(self): - """verify expected backends are present""" - from passlib.crypto.digest import PBKDF2_BACKENDS - - # check for fastpbkdf2 - try: - import fastpbkdf2 - has_fastpbkdf2 = True - except ImportError: - has_fastpbkdf2 = False - self.assertEqual("fastpbkdf2" in PBKDF2_BACKENDS, has_fastpbkdf2) - - # check for hashlib - try: - from hashlib import pbkdf2_hmac - has_hashlib_ssl = pbkdf2_hmac.__module__ != "hashlib" - except ImportError: - has_hashlib_ssl = False - self.assertEqual("hashlib-ssl" in PBKDF2_BACKENDS, has_hashlib_ssl) - - # check for appropriate builtin - from passlib.utils.compat import PY3 - if PY3: - self.assertIn("builtin-from-bytes", PBKDF2_BACKENDS) - else: - # XXX: only true as long as this is preferred over hexlify - self.assertIn("builtin-unpack", PBKDF2_BACKENDS) - - def test_border(self): - """test border cases""" - def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, digest="sha1"): - return pbkdf2_hmac(digest, secret, salt, rounds, keylen) - helper() - - # invalid rounds - self.assertRaises(ValueError, helper, rounds=-1) - self.assertRaises(ValueError, helper, rounds=0) - self.assertRaises(TypeError, helper, rounds='x') - - # invalid keylen - helper(keylen=1) - self.assertRaises(ValueError, helper, keylen=-1) - self.assertRaises(ValueError, helper, keylen=0) - # NOTE: hashlib actually throws error for keylen>=MAX_SINT32, - # but pbkdf2 forbids anything > MAX_UINT32 * digest_size - self.assertRaises(OverflowError, helper, keylen=20*(2**32-1)+1) - self.assertRaises(TypeError, helper, keylen='x') - - # invalid secret/salt type - self.assertRaises(TypeError, helper, salt=5) - self.assertRaises(TypeError, helper, secret=5) - - # invalid hash - self.assertRaises(ValueError, helper, digest='foo') - self.assertRaises(TypeError, helper, digest=5) - - def test_default_keylen(self): - """test keylen==None""" - def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, digest="sha1"): - return pbkdf2_hmac(digest, secret, salt, rounds, keylen) - self.assertEqual(len(helper(digest='sha1')), 20) - self.assertEqual(len(helper(digest='sha256')), 32) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py b/.venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py deleted file mode 100644 index 73ff1fa..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_crypto_scrypt.py +++ /dev/null @@ -1,634 +0,0 @@ -"""tests for passlib.utils.scrypt""" -#============================================================================= -# imports -#============================================================================= -# core -from binascii import hexlify -import hashlib -import logging; log = logging.getLogger(__name__) -import struct -import warnings -warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*") -# site -# pkg -from passlib import exc -from passlib.utils import getrandbytes -from passlib.utils.compat import PYPY, u, bascii_to_str -from passlib.utils.decor import classproperty -from passlib.tests.utils import TestCase, skipUnless, TEST_MODE, hb -# subject -from passlib.crypto import scrypt as scrypt_mod -# local -__all__ = [ - "ScryptEngineTest", - "BuiltinScryptTest", - "FastScryptTest", -] - -#============================================================================= -# support functions -#============================================================================= -def hexstr(data): - """return bytes as hex str""" - return bascii_to_str(hexlify(data)) - -def unpack_uint32_list(data, check_count=None): - """unpack bytes as list of uint32 values""" - count = len(data) // 4 - assert check_count is None or check_count == count - return struct.unpack("<%dI" % count, data) - -def seed_bytes(seed, count): - """ - generate random reference bytes from specified seed. - used to generate some predictable test vectors. - """ - if hasattr(seed, "encode"): - seed = seed.encode("ascii") - buf = b'' - i = 0 - while len(buf) < count: - buf += hashlib.sha256(seed + struct.pack("" % cls.backend - backend = None - - #============================================================================= - # setup - #============================================================================= - def setUp(self): - assert self.backend - scrypt_mod._set_backend(self.backend) - super(_CommonScryptTest, self).setUp() - - #============================================================================= - # reference vectors - #============================================================================= - - reference_vectors = [ - # entry format: (secret, salt, n, r, p, keylen, result) - - #------------------------------------------------------------------------ - # test vectors from scrypt whitepaper -- - # http://www.tarsnap.com/scrypt/scrypt.pdf, appendix b - # - # also present in (expired) scrypt rfc draft -- - # https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01, section 11 - #------------------------------------------------------------------------ - ("", "", 16, 1, 1, 64, hb(""" - 77 d6 57 62 38 65 7b 20 3b 19 ca 42 c1 8a 04 97 - f1 6b 48 44 e3 07 4a e8 df df fa 3f ed e2 14 42 - fc d0 06 9d ed 09 48 f8 32 6a 75 3a 0f c8 1f 17 - e8 d3 e0 fb 2e 0d 36 28 cf 35 e2 0c 38 d1 89 06 - """)), - - ("password", "NaCl", 1024, 8, 16, 64, hb(""" - fd ba be 1c 9d 34 72 00 78 56 e7 19 0d 01 e9 fe - 7c 6a d7 cb c8 23 78 30 e7 73 76 63 4b 37 31 62 - 2e af 30 d9 2e 22 a3 88 6f f1 09 27 9d 98 30 da - c7 27 af b9 4a 83 ee 6d 83 60 cb df a2 cc 06 40 - """)), - - # NOTE: the following are skipped for all backends unless TEST_MODE="full" - - ("pleaseletmein", "SodiumChloride", 16384, 8, 1, 64, hb(""" - 70 23 bd cb 3a fd 73 48 46 1c 06 cd 81 fd 38 eb - fd a8 fb ba 90 4f 8e 3e a9 b5 43 f6 54 5d a1 f2 - d5 43 29 55 61 3f 0f cf 62 d4 97 05 24 2a 9a f9 - e6 1e 85 dc 0d 65 1e 40 df cf 01 7b 45 57 58 87 - """)), - - # NOTE: the following are always skipped for the builtin backend, - # (just takes too long to be worth it) - - ("pleaseletmein", "SodiumChloride", 1048576, 8, 1, 64, hb(""" - 21 01 cb 9b 6a 51 1a ae ad db be 09 cf 70 f8 81 - ec 56 8d 57 4a 2f fd 4d ab e5 ee 98 20 ad aa 47 - 8e 56 fd 8f 4b a5 d0 9f fa 1c 6d 92 7c 40 f4 c3 - 37 30 40 49 e8 a9 52 fb cb f4 5c 6f a7 7a 41 a4 - """)), - ] - - def test_reference_vectors(self): - """reference vectors""" - for secret, salt, n, r, p, keylen, result in self.reference_vectors: - if n >= 1024 and TEST_MODE(max="default"): - # skip large values unless we're running full test suite - continue - if n > 16384 and self.backend == "builtin": - # skip largest vector for builtin, takes WAAY too long - # (46s under pypy, ~5m under cpython) - continue - log.debug("scrypt reference vector: %r %r n=%r r=%r p=%r", secret, salt, n, r, p) - self.assertEqual(scrypt_mod.scrypt(secret, salt, n, r, p, keylen), result) - - #============================================================================= - # fuzz testing - #============================================================================= - - _already_tested_others = None - - def test_other_backends(self): - """compare output to other backends""" - # only run once, since test is symetric. - # maybe this means it should go somewhere else? - if self._already_tested_others: - raise self.skipTest("already run under %r backend test" % self._already_tested_others) - self._already_tested_others = self.backend - rng = self.getRandom() - - # get available backends - orig = scrypt_mod.backend - available = set(name for name in scrypt_mod.backend_values - if scrypt_mod._has_backend(name)) - scrypt_mod._set_backend(orig) - available.discard(self.backend) - if not available: - raise self.skipTest("no other backends found") - - warnings.filterwarnings("ignore", "(?i)using builtin scrypt backend", - category=exc.PasslibSecurityWarning) - - # generate some random options, and cross-check output - for _ in range(10): - # NOTE: keeping values low due to builtin test - secret = getrandbytes(rng, rng.randint(0, 64)) - salt = getrandbytes(rng, rng.randint(0, 64)) - n = 1 << rng.randint(1, 10) - r = rng.randint(1, 8) - p = rng.randint(1, 3) - ks = rng.randint(1, 64) - previous = None - backends = set() - for name in available: - scrypt_mod._set_backend(name) - self.assertNotIn(scrypt_mod._scrypt, backends) - backends.add(scrypt_mod._scrypt) - result = hexstr(scrypt_mod.scrypt(secret, salt, n, r, p, ks)) - self.assertEqual(len(result), 2*ks) - if previous is not None: - self.assertEqual(result, previous, - msg="%r output differs from others %r: %r" % - (name, available, [secret, salt, n, r, p, ks])) - - #============================================================================= - # test input types - #============================================================================= - def test_backend(self): - """backend management""" - # clobber backend - scrypt_mod.backend = None - scrypt_mod._scrypt = None - self.assertRaises(TypeError, scrypt_mod.scrypt, 's', 's', 2, 2, 2, 16) - - # reload backend - scrypt_mod._set_backend(self.backend) - self.assertEqual(scrypt_mod.backend, self.backend) - scrypt_mod.scrypt('s', 's', 2, 2, 2, 16) - - # throw error for unknown backend - self.assertRaises(ValueError, scrypt_mod._set_backend, 'xxx') - self.assertEqual(scrypt_mod.backend, self.backend) - - def test_secret_param(self): - """'secret' parameter""" - - def run_scrypt(secret): - return hexstr(scrypt_mod.scrypt(secret, "salt", 2, 2, 2, 16)) - - # unicode - TEXT = u("abc\u00defg") - self.assertEqual(run_scrypt(TEXT), '05717106997bfe0da42cf4779a2f8bd8') - - # utf8 bytes - TEXT_UTF8 = b'abc\xc3\x9efg' - self.assertEqual(run_scrypt(TEXT_UTF8), '05717106997bfe0da42cf4779a2f8bd8') - - # latin1 bytes - TEXT_LATIN1 = b'abc\xdefg' - self.assertEqual(run_scrypt(TEXT_LATIN1), '770825d10eeaaeaf98e8a3c40f9f441d') - - # accept empty string - self.assertEqual(run_scrypt(""), 'ca1399e5fae5d3b9578dcd2b1faff6e2') - - # reject other types - self.assertRaises(TypeError, run_scrypt, None) - self.assertRaises(TypeError, run_scrypt, 1) - - def test_salt_param(self): - """'salt' parameter""" - - def run_scrypt(salt): - return hexstr(scrypt_mod.scrypt("secret", salt, 2, 2, 2, 16)) - - # unicode - TEXT = u("abc\u00defg") - self.assertEqual(run_scrypt(TEXT), 'a748ec0f4613929e9e5f03d1ab741d88') - - # utf8 bytes - TEXT_UTF8 = b'abc\xc3\x9efg' - self.assertEqual(run_scrypt(TEXT_UTF8), 'a748ec0f4613929e9e5f03d1ab741d88') - - # latin1 bytes - TEXT_LATIN1 = b'abc\xdefg' - self.assertEqual(run_scrypt(TEXT_LATIN1), '91d056fb76fb6e9a7d1cdfffc0a16cd1') - - # reject other types - self.assertRaises(TypeError, run_scrypt, None) - self.assertRaises(TypeError, run_scrypt, 1) - - def test_n_param(self): - """'n' (rounds) parameter""" - - def run_scrypt(n): - return hexstr(scrypt_mod.scrypt("secret", "salt", n, 2, 2, 16)) - - # must be > 1, and a power of 2 - self.assertRaises(ValueError, run_scrypt, -1) - self.assertRaises(ValueError, run_scrypt, 0) - self.assertRaises(ValueError, run_scrypt, 1) - self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66') - self.assertRaises(ValueError, run_scrypt, 3) - self.assertRaises(ValueError, run_scrypt, 15) - self.assertEqual(run_scrypt(16), '0272b8fc72bc54b1159340ed99425233') - - def test_r_param(self): - """'r' (block size) parameter""" - def run_scrypt(r, n=2, p=2): - return hexstr(scrypt_mod.scrypt("secret", "salt", n, r, p, 16)) - - # must be > 1 - self.assertRaises(ValueError, run_scrypt, -1) - self.assertRaises(ValueError, run_scrypt, 0) - self.assertEqual(run_scrypt(1), '3d630447d9f065363b8a79b0b3670251') - self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66') - self.assertEqual(run_scrypt(5), '114f05e985a903c27237b5578e763736') - - # reject r*p >= 2**30 - self.assertRaises(ValueError, run_scrypt, (1<<30), p=1) - self.assertRaises(ValueError, run_scrypt, (1<<30) / 2, p=2) - - def test_p_param(self): - """'p' (parallelism) parameter""" - def run_scrypt(p, n=2, r=2): - return hexstr(scrypt_mod.scrypt("secret", "salt", n, r, p, 16)) - - # must be > 1 - self.assertRaises(ValueError, run_scrypt, -1) - self.assertRaises(ValueError, run_scrypt, 0) - self.assertEqual(run_scrypt(1), 'f2960ea8b7d48231fcec1b89b784a6fa') - self.assertEqual(run_scrypt(2), 'dacf2bca255e2870e6636fa8c8957a66') - self.assertEqual(run_scrypt(5), '848a0eeb2b3543e7f543844d6ca79782') - - # reject r*p >= 2**30 - self.assertRaises(ValueError, run_scrypt, (1<<30), r=1) - self.assertRaises(ValueError, run_scrypt, (1<<30) / 2, r=2) - - def test_keylen_param(self): - """'keylen' parameter""" - rng = self.getRandom() - - def run_scrypt(keylen): - return hexstr(scrypt_mod.scrypt("secret", "salt", 2, 2, 2, keylen)) - - # must be > 0 - self.assertRaises(ValueError, run_scrypt, -1) - self.assertRaises(ValueError, run_scrypt, 0) - self.assertEqual(run_scrypt(1), 'da') - - # pick random value - ksize = rng.randint(1, 1 << 10) - self.assertEqual(len(run_scrypt(ksize)), 2*ksize) # 2 hex chars per output - - # one more than upper bound - self.assertRaises(ValueError, run_scrypt, ((2**32) - 1) * 32 + 1) - - #============================================================================= - # eoc - #============================================================================= - - -#----------------------------------------------------------------------- -# check what backends 'should' be available -#----------------------------------------------------------------------- - -def _can_import_cffi_scrypt(): - try: - import scrypt - except ImportError as err: - if "scrypt" in str(err): - return False - raise - return True - -has_cffi_scrypt = _can_import_cffi_scrypt() - - -def _can_import_stdlib_scrypt(): - try: - from hashlib import scrypt - return True - except ImportError: - return False - -has_stdlib_scrypt = _can_import_stdlib_scrypt() - -#----------------------------------------------------------------------- -# test individual backends -#----------------------------------------------------------------------- - -# NOTE: builtin version runs VERY slow (except under PyPy, where it's only 11x slower), -# so skipping under quick test mode. -@skipUnless(PYPY or TEST_MODE(min="default"), "skipped under current test mode") -class BuiltinScryptTest(_CommonScryptTest): - backend = "builtin" - - def setUp(self): - super(BuiltinScryptTest, self).setUp() - warnings.filterwarnings("ignore", "(?i)using builtin scrypt backend", - category=exc.PasslibSecurityWarning) - - def test_missing_backend(self): - """backend management -- missing backend""" - if has_stdlib_scrypt or has_cffi_scrypt: - raise self.skipTest("non-builtin backend is present") - self.assertRaises(exc.MissingBackendError, scrypt_mod._set_backend, 'scrypt') - - -@skipUnless(has_cffi_scrypt, "'scrypt' package not found") -class ScryptPackageTest(_CommonScryptTest): - backend = "scrypt" - - def test_default_backend(self): - """backend management -- default backend""" - if has_stdlib_scrypt: - raise self.skipTest("higher priority backend present") - scrypt_mod._set_backend("default") - self.assertEqual(scrypt_mod.backend, "scrypt") - - -@skipUnless(has_stdlib_scrypt, "'hashlib.scrypt()' not found") -class StdlibScryptTest(_CommonScryptTest): - backend = "stdlib" - - def test_default_backend(self): - """backend management -- default backend""" - scrypt_mod._set_backend("default") - self.assertEqual(scrypt_mod.backend, "stdlib") - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_ext_django.py b/.venv/Lib/site-packages/passlib/tests/test_ext_django.py deleted file mode 100644 index 2a0b418..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_ext_django.py +++ /dev/null @@ -1,1080 +0,0 @@ -"""test passlib.ext.django""" -#============================================================================= -# imports -#============================================================================= -# core -from __future__ import absolute_import, division, print_function -import logging; log = logging.getLogger(__name__) -import sys -import re -# site -# pkg -from passlib import apps as _apps, exc, registry -from passlib.apps import django10_context, django14_context, django16_context -from passlib.context import CryptContext -from passlib.ext.django.utils import ( - DJANGO_VERSION, MIN_DJANGO_VERSION, DjangoTranslator, quirks, -) -from passlib.utils.compat import iteritems, get_method_function, u -from passlib.utils.decor import memoized_property -# tests -from passlib.tests.utils import TestCase, TEST_MODE, handler_derived_from -from passlib.tests.test_handlers import get_handler_case -# local -__all__ = [ - "DjangoBehaviorTest", - "ExtensionBehaviorTest", - "DjangoExtensionTest", - - "_ExtensionSupport", - "_ExtensionTest", -] -#============================================================================= -# configure django settings for testcases -#============================================================================= - -# whether we have supported django version -has_min_django = DJANGO_VERSION >= MIN_DJANGO_VERSION - -# import and configure empty django settings -# NOTE: we don't want to set up entirety of django, so not using django.setup() directly. -# instead, manually configuring the settings, and setting it up w/ no apps installed. -# in future, may need to alter this so we call django.setup() after setting -# DJANGO_SETTINGS_MODULE to a custom settings module w/ a dummy django app. -if has_min_django: - # - # initialize django settings manually - # - from django.conf import settings, LazySettings - - if not isinstance(settings, LazySettings): - # this probably means django globals have been configured already, - # which we don't want, since test cases reset and manipulate settings. - raise RuntimeError("expected django.conf.settings to be LazySettings: %r" % (settings,)) - - # else configure a blank settings instance for the unittests - if not settings.configured: - settings.configure() - - # - # init django apps w/ NO installed apps. - # NOTE: required for django >= 1.9 - # - from django.apps import apps - apps.populate(["django.contrib.contenttypes", "django.contrib.auth"]) - -# log a warning if tested w/ newer version. -# NOTE: this is mainly here as place to mark what version it was run against before release. -if DJANGO_VERSION >= (3, 2): - log.info("this release hasn't been tested against Django %r", DJANGO_VERSION) - -#============================================================================= -# support funcs -#============================================================================= - -# flag for update_settings() to remove specified key entirely -UNSET = object() - -def update_settings(**kwds): - """helper to update django settings from kwds""" - for k,v in iteritems(kwds): - if v is UNSET: - if hasattr(settings, k): - delattr(settings, k) - else: - setattr(settings, k, v) - -if has_min_django: - from django.contrib.auth.models import User - - class FakeUser(User): - """mock user object for use in testing""" - # NOTE: this mainly just overrides .save() to test commit behavior. - - # NOTE: .Meta.app_label required for django >= 1.9 - class Meta: - app_label = __name__ - - @memoized_property - def saved_passwords(self): - return [] - - def pop_saved_passwords(self): - try: - return self.saved_passwords[:] - finally: - del self.saved_passwords[:] - - def save(self, update_fields=None): - # NOTE: ignoring update_fields for test purposes - self.saved_passwords.append(self.password) - -def create_mock_setter(): - state = [] - def setter(password): - state.append(password) - def popstate(): - try: - return state[:] - finally: - del state[:] - setter.popstate = popstate - return setter - - -def check_django_hasher_has_backend(name): - """ - check whether django hasher is available; - or if it should be skipped because django lacks third-party library. - """ - assert name - from django.contrib.auth.hashers import make_password - try: - make_password("", hasher=name) - return True - except ValueError as err: - if re.match("Couldn't load '.*?' algorithm .* No module named .*", str(err)): - return False - raise - -#============================================================================= -# work up stock django config -#============================================================================= - -def _modify_django_config(kwds, sha_rounds=None): - """ - helper to build django CryptContext config matching expected setup for stock django deploy. - :param kwds: - :param sha_rounds: - :return: - """ - # make sure we have dict - if hasattr(kwds, "to_dict"): - # type: CryptContext - kwds = kwds.to_dict() - - # update defaults - kwds.update( - # TODO: push this to passlib.apps django contexts - deprecated="auto", - ) - - # fill in default rounds for current django version, so our sample hashes come back - # unchanged, instead of being upgraded in-place by check_password(). - if sha_rounds is None and has_min_django: - from django.contrib.auth.hashers import PBKDF2PasswordHasher - sha_rounds = PBKDF2PasswordHasher.iterations - - # modify rounds - if sha_rounds: - kwds.update( - django_pbkdf2_sha1__default_rounds=sha_rounds, - django_pbkdf2_sha256__default_rounds=sha_rounds, - ) - - return kwds - -#---------------------------------------------------- -# build config dict that matches stock django -#---------------------------------------------------- - -# XXX: replace this with code that interrogates default django config directly? -# could then separate out "validation of djangoXX_context objects" -# and "validation that individual hashers match django". -# or maybe add a "get_django_context(django_version)" helper to passlib.apps? -if DJANGO_VERSION >= (2, 1): - stock_config = _modify_django_config(_apps.django21_context) -elif DJANGO_VERSION >= (1, 10): - stock_config = _modify_django_config(_apps.django110_context) -else: - # assert DJANGO_VERSION >= (1, 8) - stock_config = _modify_django_config(_apps.django16_context) - -#---------------------------------------------------- -# override sample hashes used in test cases -#---------------------------------------------------- -from passlib.hash import django_pbkdf2_sha256 -sample_hashes = dict( - django_pbkdf2_sha256=("not a password", django_pbkdf2_sha256 - .using(rounds=stock_config.get("django_pbkdf2_sha256__default_rounds")) - .hash("not a password")) -) - -#============================================================================= -# test utils -#============================================================================= - -class _ExtensionSupport(object): - """ - test support funcs for loading/unloading extension. - this class is mixed in to various TestCase subclasses. - """ - #=================================================================== - # support funcs - #=================================================================== - - @classmethod - def _iter_patch_candidates(cls): - """helper to scan for monkeypatches. - - returns tuple containing: - * object (module or class) - * attribute of object - * value of attribute - * whether it should or should not be patched - """ - # XXX: this and assert_unpatched() could probably be refactored to use - # the PatchManager class to do the heavy lifting. - from django.contrib.auth import models, hashers - user_attrs = ["check_password", "set_password"] - model_attrs = ["check_password", "make_password"] - hasher_attrs = ["check_password", "make_password", "get_hasher", "identify_hasher", - "get_hashers"] - objs = [(models, model_attrs), - (models.User, user_attrs), - (hashers, hasher_attrs), - ] - for obj, patched in objs: - for attr in dir(obj): - if attr.startswith("_"): - continue - value = obj.__dict__.get(attr, UNSET) # can't use getattr() due to GAE - if value is UNSET and attr not in patched: - continue - value = get_method_function(value) - source = getattr(value, "__module__", None) - if source: - yield obj, attr, source, (attr in patched) - - #=================================================================== - # verify current patch state - #=================================================================== - - def assert_unpatched(self): - """ - test that django is in unpatched state - """ - # make sure we aren't currently patched - mod = sys.modules.get("passlib.ext.django.models") - self.assertFalse(mod and mod.adapter.patched, "patch should not be enabled") - - # make sure no objects have been replaced, by checking __module__ - for obj, attr, source, patched in self._iter_patch_candidates(): - if patched: - self.assertTrue(source.startswith("django.contrib.auth."), - "obj=%r attr=%r was not reverted: %r" % - (obj, attr, source)) - else: - self.assertFalse(source.startswith("passlib."), - "obj=%r attr=%r should not have been patched: %r" % - (obj, attr, source)) - - def assert_patched(self, context=None): - """ - helper to ensure django HAS been patched, and is using specified config - """ - # make sure we're currently patched - mod = sys.modules.get("passlib.ext.django.models") - self.assertTrue(mod and mod.adapter.patched, "patch should have been enabled") - - # make sure only the expected objects have been patched - for obj, attr, source, patched in self._iter_patch_candidates(): - if patched: - self.assertTrue(source == "passlib.ext.django.utils", - "obj=%r attr=%r should have been patched: %r" % - (obj, attr, source)) - else: - self.assertFalse(source.startswith("passlib."), - "obj=%r attr=%r should not have been patched: %r" % - (obj, attr, source)) - - # check context matches - if context is not None: - context = CryptContext._norm_source(context) - self.assertEqual(mod.password_context.to_dict(resolve=True), - context.to_dict(resolve=True)) - - #=================================================================== - # load / unload the extension (and verify it worked) - #=================================================================== - - _config_keys = ["PASSLIB_CONFIG", "PASSLIB_CONTEXT", "PASSLIB_GET_CATEGORY"] - - def load_extension(self, check=True, **kwds): - """ - helper to load extension with specified config & patch django - """ - self.unload_extension() - if check: - config = kwds.get("PASSLIB_CONFIG") or kwds.get("PASSLIB_CONTEXT") - for key in self._config_keys: - kwds.setdefault(key, UNSET) - update_settings(**kwds) - import passlib.ext.django.models - if check: - self.assert_patched(context=config) - - def unload_extension(self): - """ - helper to remove patches and unload extension - """ - # remove patches and unload module - mod = sys.modules.get("passlib.ext.django.models") - if mod: - mod.adapter.remove_patch() - del sys.modules["passlib.ext.django.models"] - # wipe config from django settings - update_settings(**dict((key, UNSET) for key in self._config_keys)) - # check everything's gone - self.assert_unpatched() - - #=================================================================== - # eoc - #=================================================================== - - -# XXX: rename to ExtensionFixture? -# NOTE: would roll this into _ExtensionSupport class; -# but we have to mix that one into django's TestCase classes as well; -# and our TestCase class (and this setUp() method) would foul things up. -class _ExtensionTest(TestCase, _ExtensionSupport): - """ - TestCase mixin which makes sure extension is unloaded before test; - and make sure it's unloaded after test as well. - """ - #============================================================================= - # setup - #============================================================================= - - def setUp(self): - super(_ExtensionTest, self).setUp() - - self.require_TEST_MODE("default") - - if not DJANGO_VERSION: - raise self.skipTest("Django not installed") - elif not has_min_django: - raise self.skipTest("Django version too old") - - # reset to baseline, and verify it worked - self.unload_extension() - - # and do the same when the test exits - self.addCleanup(self.unload_extension) - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# extension tests -#============================================================================= - -#: static passwords used by DjangoBehaviorTest methods -PASS1 = "toomanysecrets" -WRONG1 = "letmein" - - -class DjangoBehaviorTest(_ExtensionTest): - """ - tests model to verify it matches django's behavior. - - running this class verifies the tests correctly assert what Django itself does. - - running the ExtensionBehaviorTest subclass below verifies "passlib.ext.django" - matches what the tests assert. - """ - #============================================================================= - # class attrs - #============================================================================= - - descriptionPrefix = "verify django behavior" - - #: tracks whether tests should assume "passlib.ext.django" monkeypatch is applied. - #: (set to True by ExtensionBehaviorTest subclass) - patched = False - - #: dict containing CryptContext() config which should match current django deploy. - #: used by tests to verify expected behavior. - config = stock_config - - # NOTE: if this test fails, it means we're not accounting for - # some part of django's hashing logic, or that this is - # running against an untested version of django with a new - # hashing policy. - - #============================================================================= - # test helpers - #============================================================================= - - @memoized_property - def context(self): - """ - per-test CryptContext() created from .config. - """ - return CryptContext._norm_source(self.config) - - def assert_unusable_password(self, user): - """ - check that user object is set to 'unusable password' constant - """ - self.assertTrue(user.password.startswith("!")) - self.assertFalse(user.has_usable_password()) - self.assertEqual(user.pop_saved_passwords(), []) - - def assert_valid_password(self, user, hash=UNSET, saved=None): - """ - check that user object has a usable password hash. - :param hash: optionally check it has this exact hash - :param saved: check that mock commit history for user.password matches this list - """ - if hash is UNSET: - self.assertNotEqual(user.password, "!") - self.assertNotEqual(user.password, None) - else: - self.assertEqual(user.password, hash) - self.assertTrue(user.has_usable_password(), - "hash should be usable: %r" % (user.password,)) - self.assertEqual(user.pop_saved_passwords(), - [] if saved is None else [saved]) - - #============================================================================= - # test hashing interface - #----------------------------------------------------------------------------- - # these functions are run against both the actual django code, - # to verify the assumptions of the unittests are correct; - # and run against the passlib extension, to verify it matches those assumptions. - # - # these tests check the following django methods: - # User.set_password() - # User.check_password() - # make_password() -- 1.4 only - # check_password() - # identify_hasher() - # User.has_usable_password() - # User.set_unusable_password() - # - # XXX: this take a while to run. what could be trimmed? - # - # TODO: add get_hasher() checks where appropriate in tests below. - #============================================================================= - - def test_extension_config(self): - """ - test extension config is loaded correctly - """ - if not self.patched: - raise self.skipTest("extension not loaded") - - ctx = self.context - - # contexts should match - from django.contrib.auth.hashers import check_password - from passlib.ext.django.models import password_context - self.assertEqual(password_context.to_dict(resolve=True), ctx.to_dict(resolve=True)) - - # should have patched both places - from django.contrib.auth.models import check_password as check_password2 - self.assertEqual(check_password2, check_password) - - def test_default_algorithm(self): - """ - test django's default algorithm - """ - ctx = self.context - - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import make_password - - # User.set_password() should use default alg - user = FakeUser() - user.set_password(PASS1) - self.assertTrue(ctx.handler().verify(PASS1, user.password)) - self.assert_valid_password(user) - - # User.check_password() - n/a - - # make_password() should use default alg - hash = make_password(PASS1) - self.assertTrue(ctx.handler().verify(PASS1, hash)) - - # check_password() - n/a - - def test_empty_password(self): - """ - test how methods handle empty string as password - """ - ctx = self.context - - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - # User.set_password() should use default alg - user = FakeUser() - user.set_password('') - hash = user.password - self.assertTrue(ctx.handler().verify('', hash)) - self.assert_valid_password(user, hash) - - # User.check_password() should return True - self.assertTrue(user.check_password("")) - self.assert_valid_password(user, hash) - - # XXX: test make_password() ? - - # TODO: is_password_usable() - - # identify_hasher() -- na - - # check_password() should return True - self.assertTrue(check_password("", hash)) - - def test_unusable_flag(self): - """ - test how methods handle 'unusable flag' in hash - """ - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - # sanity check via user.set_unusable_password() - user = FakeUser() - user.set_unusable_password() - self.assert_unusable_password(user) - - # ensure User.set_password() sets unusable flag - user = FakeUser() - user.set_password(None) - self.assert_unusable_password(user) - - # User.check_password() should always fail - self.assertFalse(user.check_password(None)) - self.assertFalse(user.check_password('None')) - self.assertFalse(user.check_password('')) - self.assertFalse(user.check_password(PASS1)) - self.assertFalse(user.check_password(WRONG1)) - self.assert_unusable_password(user) - - # make_password() should also set flag - self.assertTrue(make_password(None).startswith("!")) - - # check_password() should return False (didn't handle disabled under 1.3) - self.assertFalse(check_password(PASS1, '!')) - - # identify_hasher() and is_password_usable() should reject it - self.assertFalse(is_password_usable(user.password)) - self.assertRaises(ValueError, identify_hasher, user.password) - - def test_none_hash_value(self): - """ - test how methods handle None as hash value - """ - patched = self.patched - - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - # User.set_password() - n/a - - # User.check_password() - returns False - user = FakeUser() - user.password = None - if quirks.none_causes_check_password_error and not patched: - # django 2.1+ - self.assertRaises(TypeError, user.check_password, PASS1) - else: - self.assertFalse(user.check_password(PASS1)) - - self.assertEqual(user.has_usable_password(), - quirks.empty_is_usable_password) - - # TODO: is_password_usable() - - # make_password() - n/a - - # check_password() - error - if quirks.none_causes_check_password_error and not patched: - self.assertRaises(TypeError, check_password, PASS1, None) - else: - self.assertFalse(check_password(PASS1, None)) - - # identify_hasher() - error - self.assertRaises(TypeError, identify_hasher, None) - - def test_empty_hash_value(self): - """ - test how methods handle empty string as hash value - """ - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - # User.set_password() - n/a - - # User.check_password() - # As of django 1.5, blank hash returns False (django issue 18453) - user = FakeUser() - user.password = "" - self.assertFalse(user.check_password(PASS1)) - - # verify hash wasn't changed/upgraded during check_password() call - self.assertEqual(user.password, "") - self.assertEqual(user.pop_saved_passwords(), []) - - # User.has_usable_password() - self.assertEqual(user.has_usable_password(), quirks.empty_is_usable_password) - - # TODO: is_password_usable() - - # make_password() - n/a - - # check_password() - self.assertFalse(check_password(PASS1, "")) - - # identify_hasher() - throws error - self.assertRaises(ValueError, identify_hasher, "") - - def test_invalid_hash_values(self): - """ - test how methods handle invalid hash values. - """ - for hash in [ - "$789$foo", # empty identifier - ]: - with self.subTest(hash=hash): - self._do_test_invalid_hash_value(hash) - - def _do_test_invalid_hash_value(self, hash): - - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - # User.set_password() - n/a - - # User.check_password() - # As of django 1.5, invalid hash returns False (side effect of django issue 18453) - user = FakeUser() - user.password = hash - self.assertFalse(user.check_password(PASS1)) - - # verify hash wasn't changed/upgraded during check_password() call - self.assertEqual(user.password, hash) - self.assertEqual(user.pop_saved_passwords(), []) - - # User.has_usable_password() - self.assertEqual(user.has_usable_password(), quirks.invalid_is_usable_password) - - # TODO: is_password_usable() - - # make_password() - n/a - - # check_password() - self.assertFalse(check_password(PASS1, hash)) - - # identify_hasher() - throws error - self.assertRaises(ValueError, identify_hasher, hash) - - def test_available_schemes(self): - """ - run a bunch of subtests for each hasher available in the default django setup - (as determined by reading self.context) - """ - for scheme in self.context.schemes(): - with self.subTest(scheme=scheme): - self._do_test_available_scheme(scheme) - - def _do_test_available_scheme(self, scheme): - """ - helper to test how specific hasher behaves. - :param scheme: *passlib* name of hasher (e.g. "django_pbkdf2_sha256") - """ - log = self.getLogger() - ctx = self.context - patched = self.patched - setter = create_mock_setter() - - # NOTE: import has to be done w/in method, in case monkeypatching is applied by setUp() - from django.contrib.auth.hashers import ( - check_password, - make_password, - is_password_usable, - identify_hasher, - ) - - #------------------------------------------------------- - # setup constants & imports, pick a sample secret/hash combo - #------------------------------------------------------- - handler = ctx.handler(scheme) - log.debug("testing scheme: %r => %r", scheme, handler) - deprecated = ctx.handler(scheme).deprecated - assert not deprecated or scheme != ctx.default_scheme() - try: - testcase = get_handler_case(scheme) - except exc.MissingBackendError: - raise self.skipTest("backend not available") - assert handler_derived_from(handler, testcase.handler) - if handler.is_disabled: - raise self.skipTest("skip disabled hasher") - - # verify that django has a backend available - # (since our hasher may use different set of backends, - # get_handler_case() above may work, but django will have nothing) - if not patched and not check_django_hasher_has_backend(handler.django_name): - assert scheme in ["django_bcrypt", "django_bcrypt_sha256", "django_argon2"], \ - "%r scheme should always have active backend" % scheme - log.warning("skipping scheme %r due to missing django dependency", scheme) - raise self.skipTest("skip due to missing dependency") - - # find a sample (secret, hash) pair to test with - try: - secret, hash = sample_hashes[scheme] - except KeyError: - get_sample_hash = testcase("setUp").get_sample_hash - while True: - secret, hash = get_sample_hash() - if secret: # don't select blank passwords - break - other = 'dontletmein' - - #------------------------------------------------------- - # User.set_password() - not tested here - #------------------------------------------------------- - - #------------------------------------------------------- - # User.check_password()+migration against known hash - #------------------------------------------------------- - user = FakeUser() - user.password = hash - - # check against invalid password - self.assertFalse(user.check_password(None)) - ##self.assertFalse(user.check_password('')) - self.assertFalse(user.check_password(other)) - self.assert_valid_password(user, hash) - - # check against valid password - self.assertTrue(user.check_password(secret)) - - # check if it upgraded the hash - # NOTE: needs_update kept separate in case we need to test rounds. - needs_update = deprecated - if needs_update: - self.assertNotEqual(user.password, hash) - self.assertFalse(handler.identify(user.password)) - self.assertTrue(ctx.handler().verify(secret, user.password)) - self.assert_valid_password(user, saved=user.password) - else: - self.assert_valid_password(user, hash) - - # don't need to check rest for most deployments - if TEST_MODE(max="default"): - return - - #------------------------------------------------------- - # make_password() correctly selects algorithm - #------------------------------------------------------- - alg = DjangoTranslator().passlib_to_django_name(scheme) - hash2 = make_password(secret, hasher=alg) - self.assertTrue(handler.verify(secret, hash2)) - - #------------------------------------------------------- - # check_password()+setter against known hash - #------------------------------------------------------- - # should call setter only if it needs_update - self.assertTrue(check_password(secret, hash, setter=setter)) - self.assertEqual(setter.popstate(), [secret] if needs_update else []) - - # should not call setter - self.assertFalse(check_password(other, hash, setter=setter)) - self.assertEqual(setter.popstate(), []) - - ### check preferred kwd is ignored (feature we don't currently support fully) - ##self.assertTrue(check_password(secret, hash, setter=setter, preferred='fooey')) - ##self.assertEqual(setter.popstate(), [secret]) - - # TODO: get_hasher() - - #------------------------------------------------------- - # identify_hasher() recognizes known hash - #------------------------------------------------------- - self.assertTrue(is_password_usable(hash)) - name = DjangoTranslator().django_to_passlib_name(identify_hasher(hash).algorithm) - self.assertEqual(name, scheme) - - #=================================================================== - # eoc - #=================================================================== - -#=================================================================== -# extension fidelity tests -#=================================================================== - -class ExtensionBehaviorTest(DjangoBehaviorTest): - """ - test that "passlib.ext.django" conforms to behavioral assertions in DjangoBehaviorTest - """ - descriptionPrefix = "verify extension behavior" - - config = dict( - schemes="sha256_crypt,md5_crypt,des_crypt", - deprecated="des_crypt", - ) - - def setUp(self): - super(ExtensionBehaviorTest, self).setUp() - - # always load extension before each test - self.load_extension(PASSLIB_CONFIG=self.config) - self.patched = True - -#=================================================================== -# extension internal tests -#=================================================================== - -class DjangoExtensionTest(_ExtensionTest): - """ - test the ``passlib.ext.django`` plugin - """ - #=================================================================== - # class attrs - #=================================================================== - - descriptionPrefix = "passlib.ext.django plugin" - - #=================================================================== - # monkeypatch testing - #=================================================================== - - def test_00_patch_control(self): - """test set_django_password_context patch/unpatch""" - - # check config="disabled" - self.load_extension(PASSLIB_CONFIG="disabled", check=False) - self.assert_unpatched() - - # check legacy config=None - with self.assertWarningList("PASSLIB_CONFIG=None is deprecated"): - self.load_extension(PASSLIB_CONFIG=None, check=False) - self.assert_unpatched() - - # try stock django 1.0 context - self.load_extension(PASSLIB_CONFIG="django-1.0", check=False) - self.assert_patched(context=django10_context) - - # try to remove patch - self.unload_extension() - - # patch to use stock django 1.4 context - self.load_extension(PASSLIB_CONFIG="django-1.4", check=False) - self.assert_patched(context=django14_context) - - # try to remove patch again - self.unload_extension() - - def test_01_overwrite_detection(self): - """test detection of foreign monkeypatching""" - # NOTE: this sets things up, and spot checks two methods, - # this should be enough to verify patch manager is working. - # TODO: test unpatch behavior honors flag. - - # configure plugin to use sample context - config = "[passlib]\nschemes=des_crypt\n" - self.load_extension(PASSLIB_CONFIG=config) - - # setup helpers - import django.contrib.auth.models as models - from passlib.ext.django.models import adapter - def dummy(): - pass - - # mess with User.set_password, make sure it's detected - orig = models.User.set_password - models.User.set_password = dummy - with self.assertWarningList("another library has patched.*User\.set_password"): - adapter._manager.check_all() - models.User.set_password = orig - - # mess with models.check_password, make sure it's detected - orig = models.check_password - models.check_password = dummy - with self.assertWarningList("another library has patched.*models:check_password"): - adapter._manager.check_all() - models.check_password = orig - - def test_02_handler_wrapper(self): - """test Hasher-compatible handler wrappers""" - from django.contrib.auth import hashers - - passlib_to_django = DjangoTranslator().passlib_to_django - - # should return native django hasher if available - if DJANGO_VERSION > (1, 10): - self.assertRaises(ValueError, passlib_to_django, "hex_md5") - else: - hasher = passlib_to_django("hex_md5") - self.assertIsInstance(hasher, hashers.UnsaltedMD5PasswordHasher) - - # should return native django hasher - # NOTE: present but not enabled by default in django as of 2.1 - # (see _builtin_django_hashers) - hasher = passlib_to_django("django_bcrypt") - self.assertIsInstance(hasher, hashers.BCryptPasswordHasher) - - # otherwise should return wrapper - from passlib.hash import sha256_crypt - hasher = passlib_to_django("sha256_crypt") - self.assertEqual(hasher.algorithm, "passlib_sha256_crypt") - - # and wrapper should return correct hash - encoded = hasher.encode("stub") - self.assertTrue(sha256_crypt.verify("stub", encoded)) - self.assertTrue(hasher.verify("stub", encoded)) - self.assertFalse(hasher.verify("xxxx", encoded)) - - # test wrapper accepts options - encoded = hasher.encode("stub", "abcd"*4, rounds=1234) - self.assertEqual(encoded, "$5$rounds=1234$abcdabcdabcdabcd$" - "v2RWkZQzctPdejyRqmmTDQpZN6wTh7.RUy9zF2LftT6") - self.assertEqual(hasher.safe_summary(encoded), - {'algorithm': 'sha256_crypt', - 'salt': u('abcdab**********'), - 'rounds': 1234, - 'hash': u('v2RWkZ*************************************'), - }) - - # made up name should throw error - # XXX: should this throw ValueError instead, to match django? - self.assertRaises(KeyError, passlib_to_django, "does_not_exist") - - #=================================================================== - # PASSLIB_CONFIG settings - #=================================================================== - def test_11_config_disabled(self): - """test PASSLIB_CONFIG='disabled'""" - # test config=None (deprecated) - with self.assertWarningList("PASSLIB_CONFIG=None is deprecated"): - self.load_extension(PASSLIB_CONFIG=None, check=False) - self.assert_unpatched() - - # test disabled config - self.load_extension(PASSLIB_CONFIG="disabled", check=False) - self.assert_unpatched() - - def test_12_config_presets(self): - """test PASSLIB_CONFIG=''""" - # test django presets - self.load_extension(PASSLIB_CONTEXT="django-default", check=False) - ctx = django16_context - self.assert_patched(ctx) - - self.load_extension(PASSLIB_CONFIG="django-1.0", check=False) - self.assert_patched(django10_context) - - self.load_extension(PASSLIB_CONFIG="django-1.4", check=False) - self.assert_patched(django14_context) - - def test_13_config_defaults(self): - """test PASSLIB_CONFIG default behavior""" - # check implicit default - from passlib.ext.django.utils import PASSLIB_DEFAULT - default = CryptContext.from_string(PASSLIB_DEFAULT) - self.load_extension() - self.assert_patched(PASSLIB_DEFAULT) - - # check default preset - self.load_extension(PASSLIB_CONTEXT="passlib-default", check=False) - self.assert_patched(PASSLIB_DEFAULT) - - # check explicit string - self.load_extension(PASSLIB_CONTEXT=PASSLIB_DEFAULT, check=False) - self.assert_patched(PASSLIB_DEFAULT) - - def test_14_config_invalid(self): - """test PASSLIB_CONFIG type checks""" - update_settings(PASSLIB_CONTEXT=123, PASSLIB_CONFIG=UNSET) - self.assertRaises(TypeError, __import__, 'passlib.ext.django.models') - - self.unload_extension() - update_settings(PASSLIB_CONFIG="missing-preset", PASSLIB_CONTEXT=UNSET) - self.assertRaises(ValueError, __import__, 'passlib.ext.django.models') - - #=================================================================== - # PASSLIB_GET_CATEGORY setting - #=================================================================== - def test_21_category_setting(self): - """test PASSLIB_GET_CATEGORY parameter""" - # define config where rounds can be used to detect category - config = dict( - schemes = ["sha256_crypt"], - sha256_crypt__default_rounds = 1000, - staff__sha256_crypt__default_rounds = 2000, - superuser__sha256_crypt__default_rounds = 3000, - ) - from passlib.hash import sha256_crypt - - def run(**kwds): - """helper to take in user opts, return rounds used in password""" - user = FakeUser(**kwds) - user.set_password("stub") - return sha256_crypt.from_string(user.password).rounds - - # test default get_category - self.load_extension(PASSLIB_CONFIG=config) - self.assertEqual(run(), 1000) - self.assertEqual(run(is_staff=True), 2000) - self.assertEqual(run(is_superuser=True), 3000) - - # test patch uses explicit get_category function - def get_category(user): - return user.first_name or None - self.load_extension(PASSLIB_CONTEXT=config, - PASSLIB_GET_CATEGORY=get_category) - self.assertEqual(run(), 1000) - self.assertEqual(run(first_name='other'), 1000) - self.assertEqual(run(first_name='staff'), 2000) - self.assertEqual(run(first_name='superuser'), 3000) - - # test patch can disable get_category entirely - def get_category(user): - return None - self.load_extension(PASSLIB_CONTEXT=config, - PASSLIB_GET_CATEGORY=get_category) - self.assertEqual(run(), 1000) - self.assertEqual(run(first_name='other'), 1000) - self.assertEqual(run(first_name='staff', is_staff=True), 1000) - self.assertEqual(run(first_name='superuser', is_superuser=True), 1000) - - # test bad value - self.assertRaises(TypeError, self.load_extension, PASSLIB_CONTEXT=config, - PASSLIB_GET_CATEGORY='x') - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_ext_django_source.py b/.venv/Lib/site-packages/passlib/tests/test_ext_django_source.py deleted file mode 100644 index 4b42e59..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_ext_django_source.py +++ /dev/null @@ -1,250 +0,0 @@ -""" -test passlib.ext.django against django source tests -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import absolute_import, division, print_function -# core -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils.compat import suppress_cause -from passlib.ext.django.utils import DJANGO_VERSION, DjangoTranslator, _PasslibHasherWrapper -# tests -from passlib.tests.utils import TestCase, TEST_MODE -from .test_ext_django import ( - has_min_django, stock_config, _ExtensionSupport, -) -if has_min_django: - from .test_ext_django import settings -# local -__all__ = [ - "HashersTest", -] -#============================================================================= -# HashersTest -- -# hack up the some of the real django tests to run w/ extension loaded, -# to ensure we mimic their behavior. -# however, the django tests were moved out of the package, and into a source-only location -# as of django 1.7. so we disable tests from that point on unless test-runner specifies -#============================================================================= - -#: ref to django unittest root module (if found) -test_hashers_mod = None - -#: message about why test module isn't present (if not found) -hashers_skip_msg = None - -#---------------------------------------------------------------------- -# try to load django's tests/auth_tests/test_hasher.py module, -# or note why we failed. -#---------------------------------------------------------------------- -if TEST_MODE(max="quick"): - hashers_skip_msg = "requires >= 'default' test mode" - -elif has_min_django: - import os - import sys - source_path = os.environ.get("PASSLIB_TESTS_DJANGO_SOURCE_PATH") - - if source_path: - if not os.path.exists(source_path): - raise EnvironmentError("django source path not found: %r" % source_path) - if not all(os.path.exists(os.path.join(source_path, name)) - for name in ["django", "tests"]): - raise EnvironmentError("invalid django source path: %r" % source_path) - log.info("using django tests from source path: %r", source_path) - tests_path = os.path.join(source_path, "tests") - sys.path.insert(0, tests_path) - try: - from auth_tests import test_hashers as test_hashers_mod - except ImportError as err: - raise suppress_cause( - EnvironmentError("error trying to import django tests " - "from source path (%r): %r" % - (source_path, err))) - finally: - sys.path.remove(tests_path) - - else: - hashers_skip_msg = "requires PASSLIB_TESTS_DJANGO_SOURCE_PATH to be set" - - if TEST_MODE("full"): - # print warning so user knows what's happening - sys.stderr.write("\nWARNING: $PASSLIB_TESTS_DJANGO_SOURCE_PATH is not set; " - "can't run Django's own unittests against passlib.ext.django\n") - -elif DJANGO_VERSION: - hashers_skip_msg = "django version too old" - -else: - hashers_skip_msg = "django not installed" - -#---------------------------------------------------------------------- -# if found module, create wrapper to run django's own tests, -# but with passlib monkeypatched in. -#---------------------------------------------------------------------- -if test_hashers_mod: - from django.core.signals import setting_changed - from django.dispatch import receiver - from django.utils.module_loading import import_string - from passlib.utils.compat import get_unbound_method_function - - class HashersTest(test_hashers_mod.TestUtilsHashPass, _ExtensionSupport): - """ - Run django's hasher unittests against passlib's extension - and workalike implementations - """ - - #================================================================== - # helpers - #================================================================== - - # port patchAttr() helper method from passlib.tests.utils.TestCase - patchAttr = get_unbound_method_function(TestCase.patchAttr) - - #================================================================== - # custom setup - #================================================================== - def setUp(self): - #--------------------------------------------------------- - # install passlib.ext.django adapter, and get context - #--------------------------------------------------------- - self.load_extension(PASSLIB_CONTEXT=stock_config, check=False) - from passlib.ext.django.models import adapter - context = adapter.context - - #--------------------------------------------------------- - # patch tests module to use our versions of patched funcs - # (which should be installed in hashers module) - #--------------------------------------------------------- - from django.contrib.auth import hashers - for attr in ["make_password", - "check_password", - "identify_hasher", - "is_password_usable", - "get_hasher"]: - self.patchAttr(test_hashers_mod, attr, getattr(hashers, attr)) - - #--------------------------------------------------------- - # django tests expect empty django_des_crypt salt field - #--------------------------------------------------------- - from passlib.hash import django_des_crypt - self.patchAttr(django_des_crypt, "use_duplicate_salt", False) - - #--------------------------------------------------------- - # install receiver to update scheme list if test changes settings - #--------------------------------------------------------- - django_to_passlib_name = DjangoTranslator().django_to_passlib_name - - @receiver(setting_changed, weak=False) - def update_schemes(**kwds): - if kwds and kwds['setting'] != 'PASSWORD_HASHERS': - return - assert context is adapter.context - schemes = [ - django_to_passlib_name(import_string(hash_path)()) - for hash_path in settings.PASSWORD_HASHERS - ] - # workaround for a few tests that only specify hex_md5, - # but test for django_salted_md5 format. - if "hex_md5" in schemes and "django_salted_md5" not in schemes: - schemes.append("django_salted_md5") - schemes.append("django_disabled") - context.update(schemes=schemes, deprecated="auto") - adapter.reset_hashers() - - self.addCleanup(setting_changed.disconnect, update_schemes) - - update_schemes() - - #--------------------------------------------------------- - # need password_context to keep up to date with django_hasher.iterations, - # which is frequently patched by django tests. - # - # HACK: to fix this, inserting wrapper around a bunch of context - # methods so that any time adapter calls them, - # attrs are resynced first. - #--------------------------------------------------------- - - def update_rounds(): - """ - sync django hasher config -> passlib hashers - """ - for handler in context.schemes(resolve=True): - if 'rounds' not in handler.setting_kwds: - continue - hasher = adapter.passlib_to_django(handler) - if isinstance(hasher, _PasslibHasherWrapper): - continue - rounds = getattr(hasher, "rounds", None) or \ - getattr(hasher, "iterations", None) - if rounds is None: - continue - # XXX: this doesn't modify the context, which would - # cause other weirdness (since it would replace handler factories completely, - # instead of just updating their state) - handler.min_desired_rounds = handler.max_desired_rounds = handler.default_rounds = rounds - - _in_update = [False] - - def update_wrapper(wrapped, *args, **kwds): - """ - wrapper around arbitrary func, that first triggers sync - """ - if not _in_update[0]: - _in_update[0] = True - try: - update_rounds() - finally: - _in_update[0] = False - return wrapped(*args, **kwds) - - # sync before any context call - for attr in ["schemes", "handler", "default_scheme", "hash", - "verify", "needs_update", "verify_and_update"]: - self.patchAttr(context, attr, update_wrapper, wrap=True) - - # sync whenever adapter tries to resolve passlib hasher - self.patchAttr(adapter, "django_to_passlib", update_wrapper, wrap=True) - - def tearDown(self): - # NOTE: could rely on addCleanup() instead, but need py26 compat - self.unload_extension() - super(HashersTest, self).tearDown() - - #================================================================== - # skip a few methods that can't be replicated properly - # *want to minimize these as much as possible* - #================================================================== - - _OMIT = lambda self: self.skipTest("omitted by passlib") - - # XXX: this test registers two classes w/ same algorithm id, - # something we don't support -- how does django sanely handle - # that anyways? get_hashers_by_algorithm() should throw KeyError, right? - test_pbkdf2_upgrade_new_hasher = _OMIT - - # TODO: support wrapping django's harden-runtime feature? - # would help pass their tests. - test_check_password_calls_harden_runtime = _OMIT - test_bcrypt_harden_runtime = _OMIT - test_pbkdf2_harden_runtime = _OMIT - - #================================================================== - # eoc - #================================================================== - -else: - # otherwise leave a stub so test log tells why test was skipped. - - class HashersTest(TestCase): - - def test_external_django_hasher_tests(self): - """external django hasher tests""" - raise self.skipTest(hashers_skip_msg) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers.py b/.venv/Lib/site-packages/passlib/tests/test_handlers.py deleted file mode 100644 index cad5ef9..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers.py +++ /dev/null @@ -1,1819 +0,0 @@ -"""passlib.tests.test_handlers - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -import os -import sys -import warnings -# site -# pkg -from passlib import exc, hash -from passlib.utils import repeat_string -from passlib.utils.compat import irange, PY3, u, get_method_function -from passlib.tests.utils import TestCase, HandlerCase, skipUnless, \ - TEST_MODE, UserHandlerMixin, EncodingHandlerMixin -# module - -#============================================================================= -# constants & support -#============================================================================= - -# some common unicode passwords which used as test cases -UPASS_WAV = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2') -UPASS_USD = u("\u20AC\u00A5$") -UPASS_TABLE = u("t\u00e1\u0411\u2113\u0259") - -PASS_TABLE_UTF8 = b't\xc3\xa1\xd0\x91\xe2\x84\x93\xc9\x99' # utf-8 - -# handlers which support multiple backends, but don't have multi-backend tests. -_omitted_backend_tests = ["django_bcrypt", "django_bcrypt_sha256", "django_argon2"] - -#: modules where get_handler_case() should search for test cases. -_handler_test_modules = [ - "test_handlers", - "test_handlers_argon2", - "test_handlers_bcrypt", - "test_handlers_cisco", - "test_handlers_django", - "test_handlers_pbkdf2", - "test_handlers_scrypt", -] - -def get_handler_case(scheme): - """ - return HandlerCase instance for scheme, used by other tests. - - :param scheme: name of hasher to locate test for (e.g. "bcrypt") - - :raises KeyError: - if scheme isn't known hasher. - - :raises MissingBackendError: - if hasher doesn't have any available backends. - - :returns: - HandlerCase subclass (which derives from TestCase) - """ - from passlib.registry import get_crypt_handler - handler = get_crypt_handler(scheme) - if hasattr(handler, "backends") and scheme not in _omitted_backend_tests: - # XXX: if no backends available, could proceed to pick first backend for test lookup; - # should investigate if that would be useful to callers. - try: - backend = handler.get_backend() - except exc.MissingBackendError: - assert scheme in conditionally_available_hashes - raise - name = "%s_%s_test" % (scheme, backend) - else: - name = "%s_test" % scheme - for module in _handler_test_modules: - modname = "passlib.tests." + module - __import__(modname) - mod = sys.modules[modname] - try: - return getattr(mod, name) - except AttributeError: - pass - # every hasher should have test suite, so if we get here, means test is either missing, - # misnamed, or _handler_test_modules list is out of date. - raise RuntimeError("can't find test case named %r for %r" % (name, scheme)) - -#: hashes which there may not be a backend available for, -#: and get_handler_case() may (correctly) throw a MissingBackendError -conditionally_available_hashes = ["argon2", "bcrypt", "bcrypt_sha256"] - -#============================================================================= -# apr md5 crypt -#============================================================================= -class apr_md5_crypt_test(HandlerCase): - handler = hash.apr_md5_crypt - - known_correct_hashes = [ - # - # http://httpd.apache.org/docs/2.2/misc/password_encryptions.html - # - ('myPassword', '$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/'), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, '$apr1$bzYrOHUx$a1FcpXuQDJV3vPY20CS6N1'), - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash ----\/ - '$apr1$r31.....$HqJZimcKQFAMYayBlzkrA!' - ] - -#============================================================================= -# bigcrypt -#============================================================================= -class bigcrypt_test(HandlerCase): - handler = hash.bigcrypt - - # TODO: find an authoritative source of test vectors - known_correct_hashes = [ - - # - # various docs & messages on the web. - # - ("passphrase", "qiyh4XPJGsOZ2MEAyLkfWqeQ"), - ("This is very long passwd", "f8.SVpL2fvwjkAnxn8/rgTkwvrif6bjYB5c"), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, 'SEChBAyMbMNhgGLyP7kD1HZU'), - ] - - known_unidentified_hashes = [ - # one char short (10 % 11) - "qiyh4XPJGsOZ2MEAyLkfWqe" - - # one char too many (1 % 11) - "f8.SVpL2fvwjkAnxn8/rgTkwvrif6bjYB5cd" - ] - - # omit des_crypt from known_other since it's a valid bigcrypt hash too. - known_other_hashes = [row for row in HandlerCase.known_other_hashes - if row[0] != "des_crypt"] - - def test_90_internal(self): - # check that _norm_checksum() also validates checksum size. - # (current code uses regex in parser) - self.assertRaises(ValueError, hash.bigcrypt, use_defaults=True, - checksum=u('yh4XPJGsOZ')) - -#============================================================================= -# bsdi crypt -#============================================================================= -class _bsdi_crypt_test(HandlerCase): - """test BSDiCrypt algorithm""" - handler = hash.bsdi_crypt - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', '_J9..CCCCXBrJUJV154M'), - ('U*U***U', '_J9..CCCCXUhOBTXzaiE'), - ('U*U***U*', '_J9..CCCC4gQ.mB/PffM'), - ('*U*U*U*U', '_J9..XXXXvlzQGqpPPdk'), - ('*U*U*U*U*', '_J9..XXXXsqM/YSSP..Y'), - ('*U*U*U*U*U*U*U*U', '_J9..XXXXVL7qJCnku0I'), - ('*U*U*U*U*U*U*U*U*', '_J9..XXXXAj8cFbP5scI'), - ('ab1234567', '_J9..SDizh.vll5VED9g'), - ('cr1234567', '_J9..SDizRjWQ/zePPHc'), - ('zxyDPWgydbQjgq', '_J9..SDizxmRI1GjnQuE'), - ('726 even', '_K9..SaltNrQgIYUAeoY'), - ('', '_J9..SDSD5YGyRCr4W4c'), - - # - # custom - # - (" ", "_K1..crsmZxOLzfJH8iw"), - ("my", '_KR/.crsmykRplHbAvwA'), # <-- to detect old 12-bit rounds bug - ("my socra", "_K1..crsmf/9NzZr1fLM"), - ("my socrates", '_K1..crsmOv1rbde9A9o'), - ("my socrates note", "_K1..crsm/2qeAhdISMA"), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '_7C/.ABw0WIKy0ILVqo2'), - ] - known_unidentified_hashes = [ - # bad char in otherwise correctly formatted hash - # \/ - "_K1.!crsmZxOLzfJH8iw" - ] - - platform_crypt_support = [ - # openbsd 5.8 dropped everything except bcrypt - ("openbsd[6789]", False), - ("openbsd5", None), - ("openbsd", True), - - ("freebsd|netbsd|darwin", True), - ("solaris", False), - ("linux", None), # may be present if libxcrypt is in use - ] - - def test_77_fuzz_input(self, **kwds): - # we want to generate even rounds to verify it's correct, but want to ignore warnings - warnings.filterwarnings("ignore", "bsdi_crypt rounds should be odd.*") - super(_bsdi_crypt_test, self).test_77_fuzz_input(**kwds) - - def test_needs_update_w_even_rounds(self): - """needs_update() should flag even rounds""" - handler = self.handler - even_hash = '_Y/../cG0zkJa6LY6k4c' - odd_hash = '_Z/..TgFg0/ptQtpAgws' - secret = 'test' - - # don't issue warning - self.assertTrue(handler.verify(secret, even_hash)) - self.assertTrue(handler.verify(secret, odd_hash)) - - # *do* signal as needing updates - self.assertTrue(handler.needs_update(even_hash)) - self.assertFalse(handler.needs_update(odd_hash)) - - # new hashes shouldn't have even rounds - new_hash = handler.hash("stub") - self.assertFalse(handler.needs_update(new_hash)) - -# create test cases for specific backends -bsdi_crypt_os_crypt_test = _bsdi_crypt_test.create_backend_case("os_crypt") -bsdi_crypt_builtin_test = _bsdi_crypt_test.create_backend_case("builtin") - -#============================================================================= -# crypt16 -#============================================================================= -class crypt16_test(HandlerCase): - handler = hash.crypt16 - - # TODO: find an authortative source of test vectors - known_correct_hashes = [ - # - # from messages around the web, including - # http://seclists.org/bugtraq/1999/Mar/76 - # - ("passphrase", "qi8H8R7OM4xMUNMPuRAZxlY."), - ("printf", "aaCjFz4Sh8Eg2QSqAReePlq6"), - ("printf", "AA/xje2RyeiSU0iBY3PDwjYo"), - ("LOLOAQICI82QB4IP", "/.FcK3mad6JwYt8LVmDqz9Lc"), - ("LOLOAQICI", "/.FcK3mad6JwYSaRHJoTPzY2"), - ("LOLOAQIC", "/.FcK3mad6JwYelhbtlysKy6"), - ("L", "/.CIu/PzYCkl6elhbtlysKy6"), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, 'YeDc9tKkkmDvwP7buzpwhoqQ'), - ] - -#============================================================================= -# des crypt -#============================================================================= -class _des_crypt_test(HandlerCase): - """test des-crypt algorithm""" - handler = hash.des_crypt - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', 'CCNf8Sbh3HDfQ'), - ('U*U***U', 'CCX.K.MFy4Ois'), - ('U*U***U*', 'CC4rMpbg9AMZ.'), - ('*U*U*U*U', 'XXxzOu6maQKqQ'), - ('', 'SDbsugeBiC58A'), - - # - # custom - # - ('', 'OgAwTx2l6NADI'), - (' ', '/Hk.VPuwQTXbc'), - ('test', 'N1tQbOFcM5fpg'), - ('Compl3X AlphaNu3meric', 'um.Wguz3eVCx2'), - ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', 'sNYqfOyauIyic'), - ('AlOtBsOl', 'cEpWz5IUCShqM'), - - # ensures utf-8 used for unicode - (u('hell\u00D6'), 'saykDgk3BPZ9E'), - ] - known_unidentified_hashes = [ - # bad char in otherwise correctly formatted hash - #\/ - '!gAwTx2l6NADI', - - # wrong size - 'OgAwTx2l6NAD', - 'OgAwTx2l6NADIj', - ] - - platform_crypt_support = [ - # openbsd 5.8 dropped everything except bcrypt - ("openbsd[6789]", False), - ("openbsd5", None), - ("openbsd", True), - - ("freebsd|netbsd|linux|solaris|darwin", True), - ] - -# create test cases for specific backends -des_crypt_os_crypt_test = _des_crypt_test.create_backend_case("os_crypt") -des_crypt_builtin_test = _des_crypt_test.create_backend_case("builtin") - -#============================================================================= -# fshp -#============================================================================= -class fshp_test(HandlerCase): - """test fshp algorithm""" - handler = hash.fshp - - known_correct_hashes = [ - # - # test vectors from FSHP reference implementation - # https://github.com/bdd/fshp-is-not-secure-anymore/blob/master/python/test.py - # - ('test', '{FSHP0|0|1}qUqP5cyxm6YcTAhz05Hph5gvu9M='), - - ('test', - '{FSHP1|8|4096}MTIzNDU2NzjTdHcmoXwNc0f' - 'f9+ArUHoN0CvlbPZpxFi1C6RDM/MHSA==' - ), - - ('OrpheanBeholderScryDoubt', - '{FSHP1|8|4096}GVSUFDAjdh0vBosn1GUhz' - 'GLHP7BmkbCZVH/3TQqGIjADXpc+6NCg3g==' - ), - ('ExecuteOrder66', - '{FSHP3|16|8192}0aY7rZQ+/PR+Rd5/I9ss' - 'RM7cjguyT8ibypNaSp/U1uziNO3BVlg5qPU' - 'ng+zHUDQC3ao/JbzOnIBUtAeWHEy7a2vZeZ' - '7jAwyJJa2EqOsq4Io=' - ), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, '{FSHP1|16|16384}9v6/l3Lu/d9by5nznpOS' - 'cqQo8eKu/b/CKli3RCkgYg4nRTgZu5y659YV8cCZ68UL'), - ] - - known_unidentified_hashes = [ - # incorrect header - '{FSHX0|0|1}qUqP5cyxm6YcTAhz05Hph5gvu9M=', - 'FSHP0|0|1}qUqP5cyxm6YcTAhz05Hph5gvu9M=', - ] - - known_malformed_hashes = [ - # bad base64 padding - '{FSHP0|0|1}qUqP5cyxm6YcTAhz05Hph5gvu9M', - - # wrong salt size - '{FSHP0|1|1}qUqP5cyxm6YcTAhz05Hph5gvu9M=', - - # bad rounds - '{FSHP0|0|A}qUqP5cyxm6YcTAhz05Hph5gvu9M=', - ] - - def test_90_variant(self): - """test variant keyword""" - handler = self.handler - kwds = dict(salt=b'a', rounds=1) - - # accepts ints - handler(variant=1, **kwds) - - # accepts bytes or unicode - handler(variant=u('1'), **kwds) - handler(variant=b'1', **kwds) - - # aliases - handler(variant=u('sha256'), **kwds) - handler(variant=b'sha256', **kwds) - - # rejects None - self.assertRaises(TypeError, handler, variant=None, **kwds) - - # rejects other types - self.assertRaises(TypeError, handler, variant=complex(1,1), **kwds) - - # invalid variant - self.assertRaises(ValueError, handler, variant='9', **kwds) - self.assertRaises(ValueError, handler, variant=9, **kwds) - -#============================================================================= -# hex digests -#============================================================================= -class hex_md4_test(HandlerCase): - handler = hash.hex_md4 - known_correct_hashes = [ - ("password", '8a9d093f14f8701df17732b2bb182c74'), - (UPASS_TABLE, '876078368c47817ce5f9115f3a42cf74'), - ] - -class hex_md5_test(HandlerCase): - handler = hash.hex_md5 - known_correct_hashes = [ - ("password", '5f4dcc3b5aa765d61d8327deb882cf99'), - (UPASS_TABLE, '05473f8a19f66815e737b33264a0d0b0'), - ] - - # XXX: should test this for ALL the create_hex_md5() hashers. - def test_mock_fips_mode(self): - """ - if md5 isn't available, a dummy instance should be created. - (helps on FIPS systems). - """ - from passlib.exc import UnknownHashError - from passlib.crypto.digest import lookup_hash, _set_mock_fips_mode - - # check if md5 is available so we can test mock helper - supported = lookup_hash("md5", required=False).supported - self.assertEqual(self.handler.supported, supported) - if supported: - _set_mock_fips_mode() - self.addCleanup(_set_mock_fips_mode, False) - - # HACK: have to recreate hasher, since underlying HashInfo has changed. - # could reload module and re-import, but this should be good enough. - from passlib.handlers.digests import create_hex_hash - hasher = create_hex_hash("md5", required=False) - self.assertFalse(hasher.supported) - - # can identify hashes even if disabled - ref1 = '5f4dcc3b5aa765d61d8327deb882cf99' - ref2 = 'xxx' - self.assertTrue(hasher.identify(ref1)) - self.assertFalse(hasher.identify(ref2)) - - # throw error if try to use it - pat = "'md5' hash disabled for fips" - self.assertRaisesRegex(UnknownHashError, pat, hasher.hash, "password") - self.assertRaisesRegex(UnknownHashError, pat, hasher.verify, "password", ref1) - - -class hex_sha1_test(HandlerCase): - handler = hash.hex_sha1 - known_correct_hashes = [ - ("password", '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'), - (UPASS_TABLE, 'e059b2628e3a3e2de095679de9822c1d1466e0f0'), - ] - -class hex_sha256_test(HandlerCase): - handler = hash.hex_sha256 - known_correct_hashes = [ - ("password", '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8'), - (UPASS_TABLE, '6ed729e19bf24d3d20f564375820819932029df05547116cfc2cc868a27b4493'), - ] - -class hex_sha512_test(HandlerCase): - handler = hash.hex_sha512 - known_correct_hashes = [ - ("password", 'b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c' - '706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cac' - 'bc86'), - (UPASS_TABLE, 'd91bb0a23d66dca07a1781fd63ae6a05f6919ee5fc368049f350c9f' - '293b078a18165d66097cf0d89fdfbeed1ad6e7dba2344e57348cd6d51308c843a06f' - '29caf'), - ] - -#============================================================================= -# htdigest hash -#============================================================================= -class htdigest_test(UserHandlerMixin, HandlerCase): - handler = hash.htdigest - - known_correct_hashes = [ - # secret, user, realm - - # from RFC 2617 - (("Circle Of Life", "Mufasa", "testrealm@host.com"), - '939e7578ed9e3c518a452acee763bce9'), - - # custom - ((UPASS_TABLE, UPASS_USD, UPASS_WAV), - '4dabed2727d583178777fab468dd1f17'), - ] - - known_unidentified_hashes = [ - # bad char \/ - currently rejecting upper hex chars, may change - '939e7578edAe3c518a452acee763bce9', - - # bad char \/ - '939e7578edxe3c518a452acee763bce9', - ] - - def test_80_user(self): - raise self.skipTest("test case doesn't support 'realm' keyword") - - def populate_context(self, secret, kwds): - """insert username into kwds""" - if isinstance(secret, tuple): - secret, user, realm = secret - else: - user, realm = "user", "realm" - kwds.setdefault("user", user) - kwds.setdefault("realm", realm) - return secret - -#============================================================================= -# ldap hashes -#============================================================================= -class ldap_md5_test(HandlerCase): - handler = hash.ldap_md5 - known_correct_hashes = [ - ("helloworld", '{MD5}/F4DjTilcDIIVEHn/nAQsA=='), - (UPASS_TABLE, '{MD5}BUc/ihn2aBXnN7MyZKDQsA=='), - ] - -class ldap_sha1_test(HandlerCase): - handler = hash.ldap_sha1 - known_correct_hashes = [ - ("helloworld", '{SHA}at+xg6SiyUovktq1redipHiJpaE='), - (UPASS_TABLE, '{SHA}4FmyYo46Pi3glWed6YIsHRRm4PA='), - ] - -class ldap_salted_md5_test(HandlerCase): - handler = hash.ldap_salted_md5 - known_correct_hashes = [ - ("testing1234", '{SMD5}UjFY34os/pnZQ3oQOzjqGu4yeXE='), - (UPASS_TABLE, '{SMD5}Z0ioJ58LlzUeRxm3K6JPGAvBGIM='), - - # alternate salt sizes (8, 15, 16) - ('test', '{SMD5}LnuZPJhiaY95/4lmVFpg548xBsD4P4cw'), - ('test', '{SMD5}XRlncfRzvGi0FDzgR98tUgBg7B3jXOs9p9S615qTkg=='), - ('test', '{SMD5}FbAkzOMOxRbMp6Nn4hnZuel9j9Gas7a2lvI+x5hT6j0='), - ] - - known_malformed_hashes = [ - # salt too small (3) - '{SMD5}IGVhwK+anvspmfDt2t0vgGjt/Q==', - - # incorrect base64 encoding - '{SMD5}LnuZPJhiaY95/4lmVFpg548xBsD4P4c', - '{SMD5}LnuZPJhiaY95/4lmVFpg548xBsD4P4cw' - '{SMD5}LnuZPJhiaY95/4lmVFpg548xBsD4P4cw=', - '{SMD5}LnuZPJhiaY95/4lmV=pg548xBsD4P4cw', - '{SMD5}LnuZPJhiaY95/4lmVFpg548xBsD4P===', - ] - -class ldap_salted_sha1_test(HandlerCase): - handler = hash.ldap_salted_sha1 - known_correct_hashes = [ - ("testing123", '{SSHA}0c0blFTXXNuAMHECS4uxrj3ZieMoWImr'), - ("secret", "{SSHA}0H+zTv8o4MR4H43n03eCsvw1luG8LdB7"), - (UPASS_TABLE, '{SSHA}3yCSD1nLZXznra4N8XzZgAL+s1sQYsx5'), - - # alternate salt sizes (8, 15, 16) - ('test', '{SSHA}P90+qijSp8MJ1tN25j5o1PflUvlqjXHOGeOckw=='), - ('test', '{SSHA}/ZMF5KymNM+uEOjW+9STKlfCFj51bg3BmBNCiPHeW2ttbU0='), - ('test', '{SSHA}Pfx6Vf48AT9x3FVv8znbo8WQkEVSipHSWovxXmvNWUvp/d/7'), - ] - - known_malformed_hashes = [ - # salt too small (3) - '{SSHA}ZQK3Yvtvl6wtIRoISgMGPkcWU7Nfq5U=', - - # incorrect base64 encoding - '{SSHA}P90+qijSp8MJ1tN25j5o1PflUvlqjXHOGeOck', - '{SSHA}P90+qijSp8MJ1tN25j5o1PflUvlqjXHOGeOckw=', - '{SSHA}P90+qijSp8MJ1tN25j5o1Pf=UvlqjXHOGeOckw==', - '{SSHA}P90+qijSp8MJ1tN25j5o1PflUvlqjXHOGeOck===', - ] - - -class ldap_salted_sha256_test(HandlerCase): - handler = hash.ldap_salted_sha256 - known_correct_hashes = [ - # generated locally - # salt size = 8 - ("password", '{SSHA256}x1tymSTVjozxQ2PtT46ysrzhZxbcskK0o2f8hEFx7fAQQmhtDSEkJA=='), - ("test", '{SSHA256}xfqc9aOR6z15YaEk3/Ufd7UL9+JozB/1EPmCDTizL0GkdA7BuNda6w=='), - ("toomanysecrets", '{SSHA256}RrTKrg6HFXcjJ+eDAq4UtbODxOr9RLeG+I69FoJvutcbY0zpfU+p1Q=='), - (u('letm\xe8\xefn'), '{SSHA256}km7UjUTBZN8a+gf1ND2/qn15N7LsO/jmGYJXvyTfJKAbI0RoLWWslQ=='), - - # alternate salt sizes (4, 15, 16) - # generated locally - ('test', '{SSHA256}TFv2RpwyO0U9mA0Hk8FsXRa1I+4dNUtv27Qa8dzGVLinlDIm'), - ('test', '{SSHA256}J6MFQdkfjdmXz9UyUPb773kekJdm4dgSL4y8WQEQW11VipHSundOKaV0LsV4L6U='), - ('test', '{SSHA256}uBLazLaiBaPb6Cpnvq2XTYDkvXbYIuqRW1anMKk85d1/j1GqFQIgpHSOMUYIIcS4'), - ] - - known_malformed_hashes = [ - # salt too small (3) - '{SSHA256}Lpdyr1+lR+rtxgp3SpQnUuNw33ENivTl28nzF2ZI4Gm41/o=', - - # incorrect base64 encoding - '{SSHA256}TFv2RpwyO0U9mA0Hk8FsXRa1I+4dNUtv27Qa8dzGVLinlDI@', - '{SSHA256}TFv2RpwyO0U9mA0Hk8FsXRa1I+4dNUtv27Qa8dzGVLinlDI', - '{SSHA256}TFv2RpwyO0U9mA0Hk8FsXRa1I+4dNUtv27Qa8dzGVLinlDIm===', - ] - - - -class ldap_salted_sha512_test(HandlerCase): - handler = hash.ldap_salted_sha512 - known_correct_hashes = [ - # generated by testing ldap server web interface (see issue 124 comments) - # salt size = 8 - ("toomanysecrets", '{SSHA512}wExp4xjiCHS0zidJDC4UJq9EEeIebAQPJ1PWSwfhxWjfutI9XiiKuHm2AE41cEFfK+8HyI8bh+ztbczUGsvVFIgICWWPt7qu'), - (u('letm\xe8\xefn'), '{SSHA512}mpNUSmZc3TNx+RnPwkIAVMf7ocEKLPrIoQNsg4Eu8dHvyCeb2xzHp5A6n4tF7ntknSvfvRZaJII4ImvNJlYsgiwAm0FMqR+3'), - - # generated locally - # salt size = 8 - ("password", '{SSHA512}f/lFQskkl7PdMsTGJxHZq8LDt/l+UqRMm6/pj4pV7/xZkcOaKCgvQqp+KCeXc/Vd4RY6vEHWn4y0DnFcQ6wgyv9fyxk='), - ("test", '{SSHA512}Tgx/uhHnlM9/GgQvI31dN7cheDXg7WypZwaaIkyRsgV/BKIzBG3G/wUd9o1dpi06p3SYzMedg0lvTc3b6CtdO0Xo/f9/L+Uc'), - - # alternate salt sizes (4, 15, 16) - # generated locally - ('test', '{SSHA512}Yg9DQ2wURCFGwobu7R2O6cq7nVbnGMPrFCX0aPQ9kj/y1hd6k9PEzkgWCB5aXdPwPzNrVb0PkiHiBnG1CxFiT+B8L8U='), - ('test', '{SSHA512}5ecDGWs5RY4xLszUO6hAcl90W3wAozGQoI4Gqj8xSZdcfU1lVEM4aY8s+4xVeLitcn7BO8i7xkzMFWLoxas7SeHc23sP4dx77937PyeE0A=='), - ('test', '{SSHA512}6FQv5W47HGg2MFBFZofoiIbO8KRW75Pm51NKoInpthYQQ5ujazHGhVGzrj3JXgA7j0k+UNmkHdbJjdY5xcUHPzynFEII4fwfIySEcG5NKSU='), - ] - - known_malformed_hashes = [ - # salt too small (3) - '{SSHA512}zFnn4/8x8GveUaMqgrYWyIWqFQ0Irt6gADPtRk4Uv3nUC6uR5cD8+YdQni/0ZNij9etm6p17kSFuww3M6l+d6AbAeA==', - - # incorrect base64 encoding - '{SSHA512}Tgx/uhHnlM9/GgQvI31dN7cheDXg7WypZwaaIkyRsgV/BKIzBG3G/wUd9o1dpi06p3SYzMedg0lvTc3b6CtdO0Xo/f9/L+U', - '{SSHA512}Tgx/uhHnlM9/GgQvI31dN7cheDXg7WypZwaaIkyRsgV/BKIzBG3G/wUd9o1dpi06p3SYzMedg0lvTc3b6CtdO0Xo/f9/L+U@', - '{SSHA512}Tgx/uhHnlM9/GgQvI31dN7cheDXg7WypZwaaIkyRsgV/BKIzBG3G/wUd9o1dpi06p3SYzMedg0lvTc3b6CtdO0Xo/f9/L+U===', - ] - - -class ldap_plaintext_test(HandlerCase): - # TODO: integrate EncodingHandlerMixin - handler = hash.ldap_plaintext - known_correct_hashes = [ - ("password", 'password'), - (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8), - (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8), - ] - known_unidentified_hashes = [ - "{FOO}bar", - - # NOTE: this hash currently rejects the empty string. - "", - ] - - known_other_hashes = [ - ("ldap_md5", "{MD5}/F4DjTilcDIIVEHn/nAQsA==") - ] - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_password(self): - # NOTE: this hash currently rejects the empty string. - while True: - pwd = super(ldap_plaintext_test.FuzzHashGenerator, self).random_password() - if pwd: - return pwd - -class _ldap_md5_crypt_test(HandlerCase): - # NOTE: since the ldap_{crypt} handlers are all wrappers, don't need - # separate test; this is just to test the codebase end-to-end - handler = hash.ldap_md5_crypt - - known_correct_hashes = [ - # - # custom - # - ('', '{CRYPT}$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.'), - (' ', '{CRYPT}$1$m/5ee7ol$bZn0kIBFipq39e.KDXX8I0'), - ('test', '{CRYPT}$1$ec6XvcoW$ghEtNK2U1MC5l.Dwgi3020'), - ('Compl3X AlphaNu3meric', '{CRYPT}$1$nX1e7EeI$ljQn72ZUgt6Wxd9hfvHdV0'), - ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '{CRYPT}$1$jQS7o98J$V6iTcr71CGgwW2laf17pi1'), - ('test', '{CRYPT}$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '{CRYPT}$1$d6/Ky1lU$/xpf8m7ftmWLF.TjHCqel0'), - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash - '{CRYPT}$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o!', - ] - -# create test cases for specific backends -ldap_md5_crypt_os_crypt_test =_ldap_md5_crypt_test.create_backend_case("os_crypt") -ldap_md5_crypt_builtin_test =_ldap_md5_crypt_test.create_backend_case("builtin") - -class _ldap_sha1_crypt_test(HandlerCase): - # NOTE: this isn't for testing the hash (see ldap_md5_crypt note) - # but as a self-test of the os_crypt patching code in HandlerCase. - handler = hash.ldap_sha1_crypt - - known_correct_hashes = [ - ('password', '{CRYPT}$sha1$10$c.mcTzCw$gF8UeYst9yXX7WNZKc5Fjkq0.au7'), - (UPASS_TABLE, '{CRYPT}$sha1$10$rnqXlOsF$aGJf.cdRPewJAXo1Rn1BkbaYh0fP'), - ] - - def populate_settings(self, kwds): - kwds.setdefault("rounds", 10) - super(_ldap_sha1_crypt_test, self).populate_settings(kwds) - - def test_77_fuzz_input(self, **ignored): - raise self.skipTest("unneeded") - -# create test cases for specific backends -ldap_sha1_crypt_os_crypt_test = _ldap_sha1_crypt_test.create_backend_case("os_crypt") - -#============================================================================= -# lanman -#============================================================================= -class lmhash_test(EncodingHandlerMixin, HandlerCase): - handler = hash.lmhash - secret_case_insensitive = True - - known_correct_hashes = [ - # - # http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx - # - ("OLDPASSWORD", "c9b81d939d6fd80cd408e6b105741864"), - ("NEWPASSWORD", '09eeab5aa415d6e4d408e6b105741864'), - ("welcome", "c23413a8a1e7665faad3b435b51404ee"), - - # - # custom - # - ('', 'aad3b435b51404eeaad3b435b51404ee'), - ('zzZZZzz', 'a5e6066de61c3e35aad3b435b51404ee'), - ('passphrase', '855c3697d9979e78ac404c4ba2c66533'), - ('Yokohama', '5ecd9236d21095ce7584248b8d2c9f9e'), - - # ensures cp437 used for unicode - (u('ENCYCLOP\xC6DIA'), 'fed6416bffc9750d48462b9d7aaac065'), - (u('encyclop\xE6dia'), 'fed6416bffc9750d48462b9d7aaac065'), - - # test various encoding values - ((u("\xC6"), None), '25d8ab4a0659c97aaad3b435b51404ee'), - ((u("\xC6"), "cp437"), '25d8ab4a0659c97aaad3b435b51404ee'), - ((u("\xC6"), "latin-1"), '184eecbbe9991b44aad3b435b51404ee'), - ((u("\xC6"), "utf-8"), '00dd240fcfab20b8aad3b435b51404ee'), - ] - - known_unidentified_hashes = [ - # bad char in otherwise correct hash - '855c3697d9979e78ac404c4ba2c6653X', - ] - - def test_90_raw(self): - """test lmhash.raw() method""" - from binascii import unhexlify - from passlib.utils.compat import str_to_bascii - lmhash = self.handler - for secret, hash in self.known_correct_hashes: - kwds = {} - secret = self.populate_context(secret, kwds) - data = unhexlify(str_to_bascii(hash)) - self.assertEqual(lmhash.raw(secret, **kwds), data) - self.assertRaises(TypeError, lmhash.raw, 1) - -#============================================================================= -# md5 crypt -#============================================================================= -class _md5_crypt_test(HandlerCase): - handler = hash.md5_crypt - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', '$1$dXc3I7Rw$ctlgjDdWJLMT.qwHsWhXR1'), - ('U*U***U', '$1$dXc3I7Rw$94JPyQc/eAgQ3MFMCoMF.0'), - ('U*U***U*', '$1$dXc3I7Rw$is1mVIAEtAhIzSdfn5JOO0'), - ('*U*U*U*U', '$1$eQT9Hwbt$XtuElNJD.eW5MN5UCWyTQ0'), - ('', '$1$Eu.GHtia$CFkL/nE1BYTlEPiVx1VWX0'), - - # - # custom - # - - # NOTE: would need to patch HandlerCase to coerce hashes - # to native str for this first one to work under py3. -## ('', b('$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.')), - ('', '$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.'), - (' ', '$1$m/5ee7ol$bZn0kIBFipq39e.KDXX8I0'), - ('test', '$1$ec6XvcoW$ghEtNK2U1MC5l.Dwgi3020'), - ('Compl3X AlphaNu3meric', '$1$nX1e7EeI$ljQn72ZUgt6Wxd9hfvHdV0'), - ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$1$jQS7o98J$V6iTcr71CGgwW2laf17pi1'), - ('test', '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'), - (b'test', '$1$SuMrG47N$ymvzYjr7QcEQjaK5m1PGx1'), - (u('s'), '$1$ssssssss$YgmLTApYTv12qgTwBoj8i/'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '$1$d6/Ky1lU$/xpf8m7ftmWLF.TjHCqel0'), - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash \/ - '$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o!', - - # too many fields - '$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.$', - ] - - platform_crypt_support = [ - # openbsd 5.8 dropped everything except bcrypt - ("openbsd[6789]", False), - ("openbsd5", None), - ("openbsd", True), - - ("freebsd|netbsd|linux|solaris", True), - ("darwin", False), - ] - -# create test cases for specific backends -md5_crypt_os_crypt_test = _md5_crypt_test.create_backend_case("os_crypt") -md5_crypt_builtin_test = _md5_crypt_test.create_backend_case("builtin") - -#============================================================================= -# msdcc 1 & 2 -#============================================================================= -class msdcc_test(UserHandlerMixin, HandlerCase): - handler = hash.msdcc - user_case_insensitive = True - - known_correct_hashes = [ - - # - # http://www.jedge.com/wordpress/windows-password-cache/ - # - (("Asdf999", "sevans"), "b1176c2587478785ec1037e5abc916d0"), - - # - # http://infosecisland.com/blogview/12156-Cachedump-for-Meterpreter-in-Action.html - # - (("ASDqwe123", "jdoe"), "592cdfbc3f1ef77ae95c75f851e37166"), - - # - # http://comments.gmane.org/gmane.comp.security.openwall.john.user/1917 - # - (("test1", "test1"), "64cd29e36a8431a2b111378564a10631"), - (("test2", "test2"), "ab60bdb4493822b175486810ac2abe63"), - (("test3", "test3"), "14dd041848e12fc48c0aa7a416a4a00c"), - (("test4", "test4"), "b945d24866af4b01a6d89b9d932a153c"), - - # - # http://ciscoit.wordpress.com/2011/04/13/metasploit-hashdump-vs-cachedump/ - # - (("1234qwer!@#$", "Administrator"), "7b69d06ef494621e3f47b9802fe7776d"), - - # - # http://www.securiteam.com/tools/5JP0I2KFPA.html - # - (("password", "user"), "2d9f0b052932ad18b87f315641921cda"), - - # - # from JTR 1.7.9 - # - (("", "root"), "176a4c2bd45ac73687676c2f09045353"), - (("test1", "TEST1"), "64cd29e36a8431a2b111378564a10631"), - (("okolada", "nineteen_characters"), "290efa10307e36a79b3eebf2a6b29455"), - ((u("\u00FC"), u("\u00FC")), "48f84e6f73d6d5305f6558a33fa2c9bb"), - ((u("\u00FC\u00FC"), u("\u00FC\u00FC")), "593246a8335cf0261799bda2a2a9c623"), - ((u("\u20AC\u20AC"), "user"), "9121790702dda0fa5d353014c334c2ce"), - - # - # custom - # - - # ensures utf-8 used for unicode - ((UPASS_TABLE, 'bob'), 'fcb82eb4212865c7ac3503156ca3f349'), - ] - - known_alternate_hashes = [ - # check uppercase accepted. - ("B1176C2587478785EC1037E5ABC916D0", ("Asdf999", "sevans"), - "b1176c2587478785ec1037e5abc916d0"), - ] - -class msdcc2_test(UserHandlerMixin, HandlerCase): - handler = hash.msdcc2 - user_case_insensitive = True - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - (("test1", "test1"), "607bbe89611e37446e736f7856515bf8"), - (("qerwt", "Joe"), "e09b38f84ab0be586b730baf61781e30"), - (("12345", "Joe"), "6432f517a900b3fc34ffe57f0f346e16"), - (("", "bin"), "c0cbe0313a861062e29f92ede58f9b36"), - (("w00t", "nineteen_characters"), "87136ae0a18b2dafe4a41d555425b2ed"), - (("w00t", "eighteencharacters"), "fc5df74eca97afd7cd5abb0032496223"), - (("longpassword", "twentyXXX_characters"), "cfc6a1e33eb36c3d4f84e4c2606623d2"), - (("longpassword", "twentyoneX_characters"), "99ff74cea552799da8769d30b2684bee"), - (("longpassword", "twentytwoXX_characters"), "0a721bdc92f27d7fb23b87a445ec562f"), - (("test2", "TEST2"), "c6758e5be7fc943d00b97972a8a97620"), - (("test3", "test3"), "360e51304a2d383ea33467ab0b639cc4"), - (("test4", "test4"), "6f79ee93518306f071c47185998566ae"), - ((u("\u00FC"), "joe"), "bdb80f2c4656a8b8591bd27d39064a54"), - ((u("\u20AC\u20AC"), "joe"), "1e1e20f482ff748038e47d801d0d1bda"), - ((u("\u00FC\u00FC"), "admin"), "0839e4a07c00f18a8c65cf5b985b9e73"), - - # - # custom - # - - # custom unicode test - ((UPASS_TABLE, 'bob'), 'cad511dc9edefcf69201da72efb6bb55'), - ] - -#============================================================================= -# mssql 2000 & 2005 -#============================================================================= -class mssql2000_test(HandlerCase): - handler = hash.mssql2000 - secret_case_insensitive = "verify-only" - # FIXME: fix UT framework - this hash is sensitive to password case, but verify() is not - - known_correct_hashes = [ - # - # http://hkashfi.blogspot.com/2007/08/breaking-sql-server-2005-hashes.html - # - ('Test', '0x010034767D5C0CFA5FDCA28C4A56085E65E882E71CB0ED2503412FD54D6119FFF04129A1D72E7C3194F7284A7F3A'), - ('TEST', '0x010034767D5C2FD54D6119FFF04129A1D72E7C3194F7284A7F3A2FD54D6119FFF04129A1D72E7C3194F7284A7F3A'), - - # - # http://www.sqlmag.com/forums/aft/68438 - # - ('x', '0x010086489146C46DD7318D2514D1AC706457CBF6CD3DF8407F071DB4BBC213939D484BF7A766E974F03C96524794'), - - # - # http://stackoverflow.com/questions/173329/how-to-decrypt-a-password-from-sql-server - # - ('AAAA', '0x0100CF465B7B12625EF019E157120D58DD46569AC7BF4118455D12625EF019E157120D58DD46569AC7BF4118455D'), - - # - # http://msmvps.com/blogs/gladchenko/archive/2005/04/06/41083.aspx - # - ('123', '0x01002D60BA07FE612C8DE537DF3BFCFA49CD9968324481C1A8A8FE612C8DE537DF3BFCFA49CD9968324481C1A8A8'), - - # - # http://www.simple-talk.com/sql/t-sql-programming/temporarily-changing-an-unknown-password-of-the-sa-account-/ - # - ('12345', '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'), - - # - # XXX: sample is incomplete, password unknown - # https://anthonystechblog.wordpress.com/2011/04/20/password-encryption-in-sql-server-how-to-tell-if-a-user-is-using-a-weak-password/ - # (????, '0x0100813F782D66EF15E40B1A3FDF7AB88B322F51401A87D8D3E3A8483C4351A3D96FC38499E6CDD2B6F?????????'), - # - - # - # from JTR 1.7.9 - # - ('foo', '0x0100A607BA7C54A24D17B565C59F1743776A10250F581D482DA8B6D6261460D3F53B279CC6913CE747006A2E3254'), - ('bar', '0x01000508513EADDF6DB7DDD270CCA288BF097F2FF69CC2DB74FBB9644D6901764F999BAB9ECB80DE578D92E3F80D'), - ('canard', '0x01008408C523CF06DCB237835D701C165E68F9460580132E28ED8BC558D22CEDF8801F4503468A80F9C52A12C0A3'), - ('lapin', '0x0100BF088517935FC9183FE39FDEC77539FD5CB52BA5F5761881E5B9638641A79DBF0F1501647EC941F3355440A2'), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_USD, '0x0100624C0961B28E39FEE13FD0C35F57B4523F0DA1861C11D5A5B28E39FEE13FD0C35F57B4523F0DA1861C11D5A5'), - (UPASS_TABLE, '0x010083104228FAD559BE52477F2131E538BE9734E5C4B0ADEFD7F6D784B03C98585DC634FE2B8CA3A6DFFEC729B4'), - - ] - - known_alternate_hashes = [ - # lower case hex - ('0x01005b20054332752e1bc2e7c5df0f9ebfe486e9bee063e8d3b332752e1bc2e7c5df0f9ebfe486e9bee063e8d3b3', - '12345', '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'), - ] - - known_unidentified_hashes = [ - # malformed start - '0X01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3', - - # wrong magic value - '0x02005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3', - - # wrong size - '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3', - '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3AF', - - # mssql2005 - '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3', - ] - - known_malformed_hashes = [ - # non-hex char -----\/ - b'0x01005B200543327G2E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3', - u('0x01005B200543327G2E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'), - ] - -class mssql2005_test(HandlerCase): - handler = hash.mssql2005 - - known_correct_hashes = [ - # - # http://hkashfi.blogspot.com/2007/08/breaking-sql-server-2005-hashes.html - # - ('TEST', '0x010034767D5C2FD54D6119FFF04129A1D72E7C3194F7284A7F3A'), - - # - # http://www.openwall.com/lists/john-users/2009/07/14/2 - # - ('toto', '0x01004086CEB6BF932BC4151A1AF1F13CD17301D70816A8886908'), - - # - # http://msmvps.com/blogs/gladchenko/archive/2005/04/06/41083.aspx - # - ('123', '0x01004A335DCEDB366D99F564D460B1965B146D6184E4E1025195'), - ('123', '0x0100E11D573F359629B344990DCD3D53DE82CF8AD6BBA7B638B6'), - - # - # XXX: password unknown - # http://www.simple-talk.com/sql/t-sql-programming/temporarily-changing-an-unknown-password-of-the-sa-account-/ - # (???, '0x01004086CEB6301EEC0A994E49E30DA235880057410264030797'), - # - - # - # http://therelentlessfrontend.com/2010/03/26/encrypting-and-decrypting-passwords-in-sql-server/ - # - ('AAAA', '0x010036D726AE86834E97F20B198ACD219D60B446AC5E48C54F30'), - - # - # from JTR 1.7.9 - # - ("toto", "0x01004086CEB6BF932BC4151A1AF1F13CD17301D70816A8886908"), - ("titi", "0x01004086CEB60ED526885801C23B366965586A43D3DEAC6DD3FD"), - ("foo", "0x0100A607BA7C54A24D17B565C59F1743776A10250F581D482DA8"), - ("bar", "0x01000508513EADDF6DB7DDD270CCA288BF097F2FF69CC2DB74FB"), - ("canard", "0x01008408C523CF06DCB237835D701C165E68F9460580132E28ED"), - ("lapin", "0x0100BF088517935FC9183FE39FDEC77539FD5CB52BA5F5761881"), - - # - # adapted from mssql2000.known_correct_hashes (above) - # - ('Test', '0x010034767D5C0CFA5FDCA28C4A56085E65E882E71CB0ED250341'), - ('Test', '0x0100993BF2315F36CC441485B35C4D84687DC02C78B0E680411F'), - ('x', '0x010086489146C46DD7318D2514D1AC706457CBF6CD3DF8407F07'), - ('AAAA', '0x0100CF465B7B12625EF019E157120D58DD46569AC7BF4118455D'), - ('123', '0x01002D60BA07FE612C8DE537DF3BFCFA49CD9968324481C1A8A8'), - ('12345', '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_USD, '0x0100624C0961B28E39FEE13FD0C35F57B4523F0DA1861C11D5A5'), - (UPASS_TABLE, '0x010083104228FAD559BE52477F2131E538BE9734E5C4B0ADEFD7'), - ] - - known_alternate_hashes = [ - # lower case hex - ('0x01005b20054332752e1bc2e7c5df0f9ebfe486e9bee063e8d3b3', - '12345', '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3'), - ] - - known_unidentified_hashes = [ - # malformed start - '0X010036D726AE86834E97F20B198ACD219D60B446AC5E48C54F30', - - # wrong magic value - '0x020036D726AE86834E97F20B198ACD219D60B446AC5E48C54F30', - - # wrong size - '0x010036D726AE86834E97F20B198ACD219D60B446AC5E48C54F', - '0x010036D726AE86834E97F20B198ACD219D60B446AC5E48C54F3012', - - # mssql2000 - '0x01005B20054332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B332752E1BC2E7C5DF0F9EBFE486E9BEE063E8D3B3', - ] - - known_malformed_hashes = [ - # non-hex char --\/ - '0x010036D726AE86G34E97F20B198ACD219D60B446AC5E48C54F30', - ] - -#============================================================================= -# mysql 323 & 41 -#============================================================================= -class mysql323_test(HandlerCase): - handler = hash.mysql323 - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('drew', '697a7de87c5390b2'), - ('password', "5d2e19393cc5ef67"), - - # - # custom - # - ('mypass', '6f8c114b58f2ce9e'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '4ef327ca5491c8d7'), - ] - - known_unidentified_hashes = [ - # bad char in otherwise correct hash - '6z8c114b58f2ce9e', - ] - - def test_90_whitespace(self): - """check whitespace is ignored per spec""" - h = self.do_encrypt("mypass") - h2 = self.do_encrypt("my pass") - self.assertEqual(h, h2) - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def accept_password_pair(self, secret, other): - # override to handle whitespace - return secret.replace(" ","") != other.replace(" ","") - -class mysql41_test(HandlerCase): - handler = hash.mysql41 - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('verysecretpassword', '*2C905879F74F28F8570989947D06A8429FB943E6'), - ('12345678123456781234567812345678', '*F9F1470004E888963FB466A5452C9CBD9DF6239C'), - ("' OR 1 /*'", '*97CF7A3ACBE0CA58D5391AC8377B5D9AC11D46D9'), - - # - # custom - # - ('mypass', '*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '*E7AFE21A9CFA2FC9D15D942AE8FB5C240FE5837B'), - ] - known_unidentified_hashes = [ - # bad char in otherwise correct hash - '*6Z8989366EAF75BB670AD8EA7A7FC1176A95CEF4', - ] - -#============================================================================= -# NTHASH -#============================================================================= -class nthash_test(HandlerCase): - handler = hash.nthash - - known_correct_hashes = [ - # - # http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx - # - ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")), - ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")), - - # - # from JTR 1.7.9 - # - - # ascii - ('', '31d6cfe0d16ae931b73c59d7e0c089c0'), - ('tigger', 'b7e0ea9fbffcf6dd83086e905089effd'), - - # utf-8 - (b'\xC3\xBC', '8bd6e4fb88e01009818749c5443ea712'), - (b'\xC3\xBC\xC3\xBC', 'cc1260adb6985ca749f150c7e0b22063'), - (b'\xE2\x82\xAC', '030926b781938db4365d46adc7cfbcb8'), - (b'\xE2\x82\xAC\xE2\x82\xAC','682467b963bb4e61943e170a04f7db46'), - - # - # custom - # - ('passphrase', '7f8fe03093cc84b267b109625f6bbf4b'), - ] - - known_unidentified_hashes = [ - # bad char in otherwise correct hash - '7f8fe03093cc84b267b109625f6bbfxb', - ] - -class bsd_nthash_test(HandlerCase): - handler = hash.bsd_nthash - - known_correct_hashes = [ - ('passphrase', '$3$$7f8fe03093cc84b267b109625f6bbf4b'), - (b'\xC3\xBC', '$3$$8bd6e4fb88e01009818749c5443ea712'), - ] - - known_unidentified_hashes = [ - # bad char in otherwise correct hash --\/ - '$3$$7f8fe03093cc84b267b109625f6bbfxb', - ] - -#============================================================================= -# oracle 10 & 11 -#============================================================================= -class oracle10_test(UserHandlerMixin, HandlerCase): - handler = hash.oracle10 - secret_case_insensitive = True - user_case_insensitive = True - - # TODO: get more test vectors (especially ones which properly test unicode) - known_correct_hashes = [ - # ((secret,user),hash) - - # - # http://www.petefinnigan.com/default/default_password_list.htm - # - (('tiger', 'scott'), 'F894844C34402B67'), - ((u('ttTiGGeR'), u('ScO')), '7AA1A84E31ED7771'), - (("d_syspw", "SYSTEM"), '1B9F1F9A5CB9EB31'), - (("strat_passwd", "strat_user"), 'AEBEDBB4EFB5225B'), - - # - # http://openwall.info/wiki/john/sample-hashes - # - (('#95LWEIGHTS', 'USER'), '000EA4D72A142E29'), - (('CIAO2010', 'ALFREDO'), 'EB026A76F0650F7B'), - - # - # from JTR 1.7.9 - # - (('GLOUGlou', 'Bob'), 'CDC6B483874B875B'), - (('GLOUGLOUTER', 'bOB'), 'EF1F9139DB2D5279'), - (('LONG_MOT_DE_PASSE_OUI', 'BOB'), 'EC8147ABB3373D53'), - - # - # custom - # - ((UPASS_TABLE, 'System'), 'B915A853F297B281'), - ] - - known_unidentified_hashes = [ - # bad char in hash --\ - 'F894844C34402B6Z', - ] - -class oracle11_test(HandlerCase): - handler = hash.oracle11 - # TODO: find more test vectors (especially ones which properly test unicode) - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ("abc123", "S:5FDAB69F543563582BA57894FE1C1361FB8ED57B903603F2C52ED1B4D642"), - ("SyStEm123!@#", "S:450F957ECBE075D2FA009BA822A9E28709FBC3DA82B44D284DDABEC14C42"), - ("oracle", "S:3437FF72BD69E3FB4D10C750B92B8FB90B155E26227B9AB62D94F54E5951"), - ("11g", "S:61CE616647A4F7980AFD7C7245261AF25E0AFE9C9763FCF0D54DA667D4E6"), - ("11g", "S:B9E7556F53500C8C78A58F50F24439D79962DE68117654B6700CE7CC71CF"), - - # - # source? - # - ("SHAlala", "S:2BFCFDF5895014EE9BB2B9BA067B01E0389BB5711B7B5F82B7235E9E182C"), - - # - # custom - # - (UPASS_TABLE, 'S:51586343E429A6DF024B8F242F2E9F8507B1096FACD422E29142AA4974B0'), - ] - -#============================================================================= -# PHPass Portable Crypt -#============================================================================= -class phpass_test(HandlerCase): - handler = hash.phpass - - known_correct_hashes = [ - # - # from official 0.3 implementation - # http://www.openwall.com/phpass/ - # - ('test12345', '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r.L0'), # from the source - - # - # from JTR 1.7.9 - # - ('test1', '$H$9aaaaaSXBjgypwqm.JsMssPLiS8YQ00'), - ('123456', '$H$9PE8jEklgZhgLmZl5.HYJAzfGCQtzi1'), - ('123456', '$H$9pdx7dbOW3Nnt32sikrjAxYFjX8XoK1'), - ('thisisalongertestPW', '$P$912345678LIjjb6PhecupozNBmDndU0'), - ('JohnRipper', '$P$612345678si5M0DDyPpmRCmcltU/YW/'), - ('JohnRipper', '$H$712345678WhEyvy1YWzT4647jzeOmo0'), - ('JohnRipper', '$P$B12345678L6Lpt4BxNotVIMILOa9u81'), - - # - # custom - # - ('', '$P$7JaFQsPzJSuenezefD/3jHgt5hVfNH0'), - ('compL3X!', '$P$FiS0N5L672xzQx1rt1vgdJQRYKnQM9/'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '$P$7SMy8VxnfsIy2Sxm7fJxDSdil.h7TW.'), - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash - # ---\/ - '$P$9IQRaTwmfeRo7ud9Fh4E2PdI0S3r!L0', - ] - -#============================================================================= -# plaintext -#============================================================================= -class plaintext_test(HandlerCase): - # TODO: integrate EncodingHandlerMixin - handler = hash.plaintext - accepts_all_hashes = True - - known_correct_hashes = [ - ('',''), - ('password', 'password'), - - # ensure unicode uses utf-8 - (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8), - (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8), - ] - -#============================================================================= -# postgres_md5 -#============================================================================= -class postgres_md5_test(UserHandlerMixin, HandlerCase): - handler = hash.postgres_md5 - known_correct_hashes = [ - # ((secret,user),hash) - - # - # generated using postgres 8.1 - # - (('mypass', 'postgres'), 'md55fba2ea04fd36069d2574ea71c8efe9d'), - (('mypass', 'root'), 'md540c31989b20437833f697e485811254b'), - (("testpassword",'testuser'), 'md5d4fc5129cc2c25465a5370113ae9835f'), - - # - # custom - # - - # verify unicode->utf8 - ((UPASS_TABLE, 'postgres'), 'md5cb9f11283265811ce076db86d18a22d2'), - ] - known_unidentified_hashes = [ - # bad 'z' char in otherwise correct hash - 'md54zc31989b20437833f697e485811254b', - ] - -#============================================================================= -# (netbsd's) sha1 crypt -#============================================================================= -class _sha1_crypt_test(HandlerCase): - handler = hash.sha1_crypt - - known_correct_hashes = [ - # - # custom - # - ("password", "$sha1$19703$iVdJqfSE$v4qYKl1zqYThwpjJAoKX6UvlHq/a"), - ("password", "$sha1$21773$uV7PTeux$I9oHnvwPZHMO0Nq6/WgyGV/tDJIH"), - (UPASS_TABLE, '$sha1$40000$uJ3Sp7LE$.VEmLO5xntyRFYihC7ggd3297T/D'), - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash - '$sha1$21773$u!7PTeux$I9oHnvwPZHMO0Nq6/WgyGV/tDJIH', - - # zero padded rounds - '$sha1$01773$uV7PTeux$I9oHnvwPZHMO0Nq6/WgyGV/tDJIH', - - # too many fields - '$sha1$21773$uV7PTeux$I9oHnvwPZHMO0Nq6/WgyGV/tDJIH$', - - # empty rounds field - '$sha1$$uV7PTeux$I9oHnvwPZHMO0Nq6/WgyGV/tDJIH$', - ] - - platform_crypt_support = [ - ("netbsd", True), - ("freebsd|openbsd|solaris|darwin", False), - ("linux", None), # may be present if libxcrypt is in use - ] - -# create test cases for specific backends -sha1_crypt_os_crypt_test = _sha1_crypt_test.create_backend_case("os_crypt") -sha1_crypt_builtin_test = _sha1_crypt_test.create_backend_case("builtin") - -#============================================================================= -# roundup -#============================================================================= - -# NOTE: all roundup hashes use PrefixWrapper, -# so there's nothing natively to test. -# so we just have a few quick cases... - -class RoundupTest(TestCase): - - def _test_pair(self, h, secret, hash): - self.assertTrue(h.verify(secret, hash)) - self.assertFalse(h.verify('x'+secret, hash)) - - def test_pairs(self): - self._test_pair( - hash.ldap_hex_sha1, - "sekrit", - '{SHA}8d42e738c7adee551324955458b5e2c0b49ee655') - - self._test_pair( - hash.ldap_hex_md5, - "sekrit", - '{MD5}ccbc53f4464604e714f69dd11138d8b5') - - self._test_pair( - hash.ldap_des_crypt, - "sekrit", - '{CRYPT}nFia0rj2TT59A') - - self._test_pair( - hash.roundup_plaintext, - "sekrit", - '{plaintext}sekrit') - - self._test_pair( - hash.ldap_pbkdf2_sha1, - "sekrit", - '{PBKDF2}5000$7BvbBq.EZzz/O0HuwX3iP.nAG3s$g3oPnFFaga2BJaX5PoPRljl4XIE') - -#============================================================================= -# sha256-crypt -#============================================================================= -class _sha256_crypt_test(HandlerCase): - handler = hash.sha256_crypt - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', '$5$LKO/Ute40T3FNF95$U0prpBQd4PloSGU0pnpM4z9wKn4vZ1.jsrzQfPqxph9'), - ('U*U***U', '$5$LKO/Ute40T3FNF95$fdgfoJEBoMajNxCv3Ru9LyQ0xZgv0OBMQoq80LQ/Qd.'), - ('U*U***U*', '$5$LKO/Ute40T3FNF95$8Ry82xGnnPI/6HtFYnvPBTYgOL23sdMXn8C29aO.x/A'), - ('*U*U*U*U', '$5$9mx1HkCz7G1xho50$O7V7YgleJKLUhcfk9pgzdh3RapEaWqMtEp9UUBAKIPA'), - ('', '$5$kc7lRD1fpYg0g.IP$d7CMTcEqJyTXyeq8hTdu/jB/I6DGkoo62NXbHIR7S43'), - - # - # custom tests - # - ('', '$5$rounds=10428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3'), - (' ', '$5$rounds=10376$I5lNtXtRmf.OoMd8$Ko3AI1VvTANdyKhBPavaRjJzNpSatKU6QVN9uwS9MH.'), - ('test', '$5$rounds=11858$WH1ABM5sKhxbkgCK$aTQsjPkz0rBsH3lQlJxw9HDTDXPKBxC0LlVeV69P.t1'), - ('Compl3X AlphaNu3meric', '$5$rounds=10350$o.pwkySLCzwTdmQX$nCMVsnF3TXWcBPOympBUUSQi6LGGloZoOsVJMGJ09UB'), - ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$5$rounds=11944$9dhlu07dQMRWvTId$LyUI5VWkGFwASlzntk1RLurxX54LUhgAcJZIt0pYGT7'), - (u('with unic\u00D6de'), '$5$rounds=1000$IbG0EuGQXw5EkMdP$LQ5AfPf13KufFsKtmazqnzSGZ4pxtUNw3woQ.ELRDF4'), - ] - - if TEST_MODE("full"): - # builtin alg was changed in 1.6, and had possibility of fencepost - # errors near rounds that are multiples of 42. these hashes test rounds - # 1004..1012 (42*24=1008 +/- 4) to ensure no mistakes were made. - # (also relying on fuzz testing against os_crypt backend). - known_correct_hashes.extend([ - ("secret", '$5$rounds=1004$nacl$oiWPbm.kQ7.jTCZoOtdv7/tO5mWv/vxw5yTqlBagVR7'), - ("secret", '$5$rounds=1005$nacl$6Mo/TmGDrXxg.bMK9isRzyWH3a..6HnSVVsJMEX7ud/'), - ("secret", '$5$rounds=1006$nacl$I46VwuAiUBwmVkfPFakCtjVxYYaOJscsuIeuZLbfKID'), - ("secret", '$5$rounds=1007$nacl$9fY4j1AV3N/dV/YMUn1enRHKH.7nEL4xf1wWB6wfDD4'), - ("secret", '$5$rounds=1008$nacl$CiFWCfn8ODmWs0I1xAdXFo09tM8jr075CyP64bu3by9'), - ("secret", '$5$rounds=1009$nacl$QtpFX.CJHgVQ9oAjVYStxAeiU38OmFILWm684c6FyED'), - ("secret", '$5$rounds=1010$nacl$ktAwXuT5WbjBW/0ZU1eNMpqIWY1Sm4twfRE1zbZyo.B'), - ("secret", '$5$rounds=1011$nacl$QJWLBEhO9qQHyMx4IJojSN9sS41P1Yuz9REddxdO721'), - ("secret", '$5$rounds=1012$nacl$mmf/k2PkbBF4VCtERgky3bEVavmLZKFwAcvxD1p3kV2'), - ]) - - known_malformed_hashes = [ - # bad char in otherwise correct hash - '$5$rounds=10428$uy/:jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMeZGsGx2aBvxTvDFI613c3', - - # zero-padded rounds - '$5$rounds=010428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3', - - # extra "$" - '$5$rounds=10428$uy/jIAhCetNCTtb0$YWvUOXbkqlqhyoPMpN8BMe.ZGsGx2aBvxTvDFI613c3$', - ] - - known_correct_configs = [ - # config, secret, result - - # - # taken from official specification at http://www.akkadia.org/drepper/SHA-crypt.txt - # - ( "$5$saltstring", "Hello world!", - "$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5" ), - ( "$5$rounds=10000$saltstringsaltstring", "Hello world!", - "$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2." - "opqey6IcA" ), - ( "$5$rounds=5000$toolongsaltstring", "This is just a test", - "$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8" - "mGRcvxa5" ), - ( "$5$rounds=1400$anotherlongsaltstring", - "a very much longer text to encrypt. This one even stretches over more" - "than one line.", - "$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12" - "oP84Bnq1" ), - ( "$5$rounds=77777$short", - "we have a short salt string but not a short password", - "$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/" ), - ( "$5$rounds=123456$asaltof16chars..", "a short string", - "$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/" - "cZKmF/wJvD" ), - ( "$5$rounds=10$roundstoolow", "the minimum number is still observed", - "$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL97" - "2bIC" ), - ] - - filter_config_warnings = True # rounds too low, salt too small - - platform_crypt_support = [ - ("freebsd(9|1\d)|linux", True), - ("freebsd8", None), # added in freebsd 8.3 - ("freebsd|openbsd|netbsd|darwin", False), - ("solaris", None), # depends on policy - ] - -# create test cases for specific backends -sha256_crypt_os_crypt_test = _sha256_crypt_test.create_backend_case("os_crypt") -sha256_crypt_builtin_test = _sha256_crypt_test.create_backend_case("builtin") - -#============================================================================= -# test sha512-crypt -#============================================================================= -class _sha512_crypt_test(HandlerCase): - handler = hash.sha512_crypt - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', "$6$LKO/Ute40T3FNF95$6S/6T2YuOIHY0N3XpLKABJ3soYcXD9mB7uVbtEZDj/LNscVhZoZ9DEH.sBciDrMsHOWOoASbNLTypH/5X26gN0"), - ('U*U***U', "$6$LKO/Ute40T3FNF95$wK80cNqkiAUzFuVGxW6eFe8J.fSVI65MD5yEm8EjYMaJuDrhwe5XXpHDJpwF/kY.afsUs1LlgQAaOapVNbggZ1"), - ('U*U***U*', "$6$LKO/Ute40T3FNF95$YS81pp1uhOHTgKLhSMtQCr2cDiUiN03Ud3gyD4ameviK1Zqz.w3oXsMgO6LrqmIEcG3hiqaUqHi/WEE2zrZqa/"), - ('*U*U*U*U', "$6$OmBOuxFYBZCYAadG$WCckkSZok9xhp4U1shIZEV7CCVwQUwMVea7L3A77th6SaE9jOPupEMJB.z0vIWCDiN9WLh2m9Oszrj5G.gt330"), - ('', "$6$ojWH1AiTee9x1peC$QVEnTvRVlPRhcLQCk/HnHaZmlGAAjCfrAN0FtOsOnUk5K5Bn/9eLHHiRzrTzaIKjW9NTLNIBUCtNVOowWS2mN."), - - # - # custom tests - # - ('', '$6$rounds=11021$KsvQipYPWpr93wWP$v7xjI4X6vyVptJjB1Y02vZC5SaSijBkGmq1uJhPr3cvqvvkd42Xvo48yLVPFt8dvhCsnlUgpX.//Cxn91H4qy1'), - (' ', '$6$rounds=11104$ED9SA4qGmd57Fq2m$q/.PqACDM/JpAHKmr86nkPzzuR5.YpYa8ZJJvI8Zd89ZPUYTJExsFEIuTYbM7gAGcQtTkCEhBKmp1S1QZwaXx0'), - ('test', '$6$rounds=11531$G/gkPn17kHYo0gTF$Kq.uZBHlSBXyzsOJXtxJruOOH4yc0Is13uY7yK0PvAvXxbvc1w8DO1RzREMhKsc82K/Jh8OquV8FZUlreYPJk1'), - ('Compl3X AlphaNu3meric', '$6$rounds=10787$wakX8nGKEzgJ4Scy$X78uqaX1wYXcSCtS4BVYw2trWkvpa8p7lkAtS9O/6045fK4UB2/Jia0Uy/KzCpODlfVxVNZzCCoV9s2hoLfDs/'), - ('4lpHa N|_|M3r1K W/ Cur5Es: #$%(*)(*%#', '$6$rounds=11065$5KXQoE1bztkY5IZr$Jf6krQSUKKOlKca4hSW07MSerFFzVIZt/N3rOTsUgKqp7cUdHrwV8MoIVNCk9q9WL3ZRMsdbwNXpVk0gVxKtz1'), - - # ensures utf-8 used for unicode - (UPASS_TABLE, '$6$rounds=40000$PEZTJDiyzV28M3.m$GTlnzfzGB44DGd1XqlmC4erAJKCP.rhvLvrYxiT38htrNzVGBnplFOHjejUGVrCfusGWxLQCc3pFO0A/1jYYr0'), - ] - - known_malformed_hashes = [ - # zero-padded rounds - '$6$rounds=011021$KsvQipYPWpr93wWP$v7xjI4X6vyVptJjB1Y02vZC5SaSijBkGmq1uJhPr3cvqvvkd42Xvo48yLVPFt8dvhCsnlUgpX.//Cxn91H4qy1', - # bad char in otherwise correct hash - '$6$rounds=11021$KsvQipYPWpr9:wWP$v7xjI4X6vyVptJjB1Y02vZC5SaSijBkGmq1uJhPr3cvqvvkd42Xvo48yLVPFt8dvhCsnlUgpX.//Cxn91H4qy1', - ] - - known_correct_configs = [ - # config, secret, result - - # - # taken from official specification at http://www.akkadia.org/drepper/SHA-crypt.txt - # - ("$6$saltstring", "Hello world!", - "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" - "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" ), - - ( "$6$rounds=10000$saltstringsaltstring", "Hello world!", - "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sb" - "HbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v." ), - - ( "$6$rounds=5000$toolongsaltstring", "This is just a test", - "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQ" - "zQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0" ), - - ( "$6$rounds=1400$anotherlongsaltstring", - "a very much longer text to encrypt. This one even stretches over more" - "than one line.", - "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wP" - "vMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1" ), - - ( "$6$rounds=77777$short", - "we have a short salt string but not a short password", - "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0g" - "ge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0" ), - - ( "$6$rounds=123456$asaltof16chars..", "a short string", - "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwc" - "elCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1" ), - - ( "$6$rounds=10$roundstoolow", "the minimum number is still observed", - "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1x" - "hLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX." ), - ] - - filter_config_warnings = True # rounds too low, salt too small - - platform_crypt_support = _sha256_crypt_test.platform_crypt_support - -# create test cases for specific backends -sha512_crypt_os_crypt_test = _sha512_crypt_test.create_backend_case("os_crypt") -sha512_crypt_builtin_test = _sha512_crypt_test.create_backend_case("builtin") - -#============================================================================= -# sun md5 crypt -#============================================================================= -class sun_md5_crypt_test(HandlerCase): - handler = hash.sun_md5_crypt - - # TODO: this scheme needs some real test vectors, especially due to - # the "bare salt" issue which plagued the official parser. - known_correct_hashes = [ - # - # http://forums.halcyoninc.com/showthread.php?t=258 - # - ("Gpcs3_adm", "$md5$zrdhpMlZ$$wBvMOEqbSjU.hu5T2VEP01"), - - # - # http://www.c0t0d0s0.org/archives/4453-Less-known-Solaris-features-On-passwords-Part-2-Using-stronger-password-hashing.html - # - ("aa12345678", "$md5$vyy8.OVF$$FY4TWzuauRl4.VQNobqMY."), - - # - # http://www.cuddletech.com/blog/pivot/entry.php?id=778 - # - ("this", "$md5$3UqYqndY$$6P.aaWOoucxxq.l00SS9k0"), - - # - # http://compgroups.net/comp.unix.solaris/password-file-in-linux-and-solaris-8-9 - # - ("passwd", "$md5$RPgLF6IJ$WTvAlUJ7MqH5xak2FMEwS/"), - - # - # source: http://solaris-training.com/301_HTML/docs/deepdiv.pdf page 27 - # FIXME: password unknown - # "$md5,rounds=8000$kS9FT1JC$$mnUrRO618lLah5iazwJ9m1" - - # - # source: http://www.visualexams.com/310-303.htm - # XXX: this has 9 salt chars unlike all other hashes. is that valid? - # FIXME: password unknown - # "$md5,rounds=2006$2amXesSj5$$kCF48vfPsHDjlKNXeEw7V." - # - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, '$md5,rounds=5000$10VYDzAA$$1arAVtMA3trgE1qJ2V0Ez1'), - ] - - known_correct_configs = [ - # (config, secret, hash) - - #--------------------------- - # test salt string handling - # - # these tests attempt to verify that passlib is handling - # the "bare salt" issue (see sun md5 crypt docs) - # in a sane manner - #--------------------------- - - # config with "$" suffix, hash strings with "$$" suffix, - # should all be treated the same, with one "$" added to salt digest. - ("$md5$3UqYqndY$", - "this", "$md5$3UqYqndY$$6P.aaWOoucxxq.l00SS9k0"), - ("$md5$3UqYqndY$$.................DUMMY", - "this", "$md5$3UqYqndY$$6P.aaWOoucxxq.l00SS9k0"), - - # config with no suffix, hash strings with "$" suffix, - # should all be treated the same, and no suffix added to salt digest. - # NOTE: this is just a guess re: config w/ no suffix, - # but otherwise there's no sane way to encode bare_salt=False - # within config string. - ("$md5$3UqYqndY", - "this", "$md5$3UqYqndY$HIZVnfJNGCPbDZ9nIRSgP1"), - ("$md5$3UqYqndY$.................DUMMY", - "this", "$md5$3UqYqndY$HIZVnfJNGCPbDZ9nIRSgP1"), - ] - - known_malformed_hashes = [ - # unexpected end of hash - "$md5,rounds=5000", - - # bad rounds - "$md5,rounds=500A$xxxx", - "$md5,rounds=0500$xxxx", - "$md5,rounds=0$xxxx", - - # bad char in otherwise correct hash - "$md5$RPgL!6IJ$WTvAlUJ7MqH5xak2FMEwS/", - - # digest too short - "$md5$RPgLa6IJ$WTvAlUJ7MqH5xak2FMEwS", - - # digest too long - "$md5$RPgLa6IJ$WTvAlUJ7MqH5xak2FMEwS/.", - - # 2+ "$" at end of salt in config - # NOTE: not sure what correct behavior is, so forbidding format for now. - "$md5$3UqYqndY$$", - - # 3+ "$" at end of salt in hash - # NOTE: not sure what correct behavior is, so forbidding format for now. - "$md5$RPgLa6IJ$$$WTvAlUJ7MqH5xak2FMEwS/", - - ] - - platform_crypt_support = [ - ("solaris", True), - ("freebsd|openbsd|netbsd|linux|darwin", False), - ] - def do_verify(self, secret, hash): - # Override to fake error for "$..." hash string listed in known_correct_configs (above) - # These have to be hash strings, in order to test bare salt issue. - if isinstance(hash, str) and hash.endswith("$.................DUMMY"): - raise ValueError("pretending '$...' stub hash is config string") - return self.handler.verify(secret, hash) - -#============================================================================= -# unix disabled / fallback -#============================================================================= -class unix_disabled_test(HandlerCase): - handler = hash.unix_disabled -# accepts_all_hashes = True # TODO: turn this off. - - known_correct_hashes = [ - # everything should hash to "!" (or "*" on BSD), - # and nothing should verify against either string - ("password", "!"), - (UPASS_TABLE, "*"), - ] - - known_unidentified_hashes = [ - # should never identify anything crypt() could return... - "$1$xxx", - "abc", - "./az", - "{SHA}xxx", - ] - - def test_76_hash_border(self): - # so empty strings pass - self.accepts_all_hashes = True - super(unix_disabled_test, self).test_76_hash_border() - - def test_90_special(self): - """test marker option & special behavior""" - warnings.filterwarnings("ignore", "passing settings to .*.hash\(\) is deprecated") - handler = self.handler - - # preserve hash if provided - self.assertEqual(handler.genhash("stub", "!asd"), "!asd") - - # use marker if no hash - self.assertEqual(handler.genhash("stub", ""), handler.default_marker) - self.assertEqual(handler.hash("stub"), handler.default_marker) - self.assertEqual(handler.using().default_marker, handler.default_marker) - - # custom marker - self.assertEqual(handler.genhash("stub", "", marker="*xxx"), "*xxx") - self.assertEqual(handler.hash("stub", marker="*xxx"), "*xxx") - self.assertEqual(handler.using(marker="*xxx").hash("stub"), "*xxx") - - # reject invalid marker - self.assertRaises(ValueError, handler.genhash, 'stub', "", marker='abc') - self.assertRaises(ValueError, handler.hash, 'stub', marker='abc') - self.assertRaises(ValueError, handler.using, marker='abc') - -class unix_fallback_test(HandlerCase): - handler = hash.unix_fallback - accepts_all_hashes = True - - known_correct_hashes = [ - # *everything* should hash to "!", and nothing should verify - ("password", "!"), - (UPASS_TABLE, "!"), - ] - - # silence annoying deprecation warning - def setUp(self): - super(unix_fallback_test, self).setUp() - warnings.filterwarnings("ignore", "'unix_fallback' is deprecated") - - def test_90_wildcard(self): - """test enable_wildcard flag""" - h = self.handler - self.assertTrue(h.verify('password','', enable_wildcard=True)) - self.assertFalse(h.verify('password','')) - for c in "!*x": - self.assertFalse(h.verify('password',c, enable_wildcard=True)) - self.assertFalse(h.verify('password',c)) - - def test_91_preserves_existing(self): - """test preserves existing disabled hash""" - handler = self.handler - - # use marker if no hash - self.assertEqual(handler.genhash("stub", ""), "!") - self.assertEqual(handler.hash("stub"), "!") - - # use hash if provided and valid - self.assertEqual(handler.genhash("stub", "!asd"), "!asd") - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py deleted file mode 100644 index e771769..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_argon2.py +++ /dev/null @@ -1,507 +0,0 @@ -"""passlib.tests.test_handlers_argon2 - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -# core -import logging -log = logging.getLogger(__name__) -import re -import warnings -# site -# pkg -from passlib import hash -from passlib.utils.compat import unicode -from passlib.tests.utils import HandlerCase, TEST_MODE -from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8 -# module - -#============================================================================= -# a bunch of tests lifted nearlky verbatim from official argon2 UTs... -# https://github.com/P-H-C/phc-winner-argon2/blob/master/src/test.c -#============================================================================= -def hashtest(version, t, logM, p, secret, salt, hex_digest, hash): - return dict(version=version, rounds=t, logM=logM, memory_cost=1< max uint32 - "$argon2i$v=19$m=65536,t=8589934592,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY", - - # unexpected param - "$argon2i$v=19$m=65536,t=2,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY", - - # wrong param order - "$argon2i$v=19$t=2,m=65536,p=4,q=5$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY", - - # constraint violation: m < 8 * p - "$argon2i$v=19$m=127,t=2,p=16$c29tZXNhbHQ$IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4", - ] - - known_parsehash_results = [ - ('$argon2i$v=19$m=256,t=2,p=3$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A', - dict(type="i", memory_cost=256, rounds=2, parallelism=3, salt=b'somesalt', - checksum=b'\x00\x91H\xb0\xd6S0\xa4\xc0{\x00x\xf8D\xcd\xd4')), - ] - - def setUpWarnings(self): - super(_base_argon2_test, self).setUpWarnings() - warnings.filterwarnings("ignore", ".*Using argon2pure backend.*") - - def do_stub_encrypt(self, handler=None, **settings): - if self.backend == "argon2_cffi": - # overriding default since no way to get stub config from argon2._calc_hash() - # (otherwise test_21b_max_rounds blocks trying to do max rounds) - handler = (handler or self.handler).using(**settings) - self = handler(use_defaults=True) - self.checksum = self._stub_checksum - assert self.checksum - return self.to_string() - else: - return super(_base_argon2_test, self).do_stub_encrypt(handler, **settings) - - def test_03_legacy_hash_workflow(self): - # override base method - raise self.skipTest("legacy 1.6 workflow not supported") - - def test_keyid_parameter(self): - # NOTE: keyid parameter currently not supported by official argon2 hash parser, - # even though it's mentioned in the format spec. - # we're trying to be consistent w/ this, so hashes w/ keyid should - # always through a NotImplementedError. - self.assertRaises(NotImplementedError, self.handler.verify, 'password', - "$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD$c29tZXNhbHQ$" - "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4") - - def test_data_parameter(self): - # NOTE: argon2 c library doesn't support passing in a data parameter to argon2_hash(); - # but argon2_verify() appears to parse that info... but then discards it (!?). - # not sure what proper behavior is, filed issue -- https://github.com/P-H-C/phc-winner-argon2/issues/143 - # For now, replicating behavior we have for the two backends, to detect when things change. - handler = self.handler - - # ref hash of 'password' when 'data' is correctly passed into argon2() - sample1 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$KgHyCesFyyjkVkihZ5VNFw' - - # ref hash of 'password' when 'data' is silently discarded (same digest as w/o data) - sample2 = '$argon2i$v=19$m=512,t=2,p=2,data=c29tZWRhdGE$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w' - - # hash of 'password' w/o the data field - sample3 = '$argon2i$v=19$m=512,t=2,p=2$c29tZXNhbHQ$uEeXt1dxN1iFKGhklseW4w' - - # - # test sample 1 - # - - if self.backend == "argon2_cffi": - # argon2_cffi v16.1 would incorrectly return False here. - # but v16.2 patches so it throws error on data parameter. - # our code should detect that, and adapt it into a NotImplementedError - self.assertRaises(NotImplementedError, handler.verify, "password", sample1) - - # incorrectly returns sample3, dropping data parameter - self.assertEqual(handler.genhash("password", sample1), sample3) - - else: - assert self.backend == "argon2pure" - # should parse and verify - self.assertTrue(handler.verify("password", sample1)) - - # should preserve sample1 - self.assertEqual(handler.genhash("password", sample1), sample1) - - # - # test sample 2 - # - - if self.backend == "argon2_cffi": - # argon2_cffi v16.1 would incorrectly return True here. - # but v16.2 patches so it throws error on data parameter. - # our code should detect that, and adapt it into a NotImplementedError - self.assertRaises(NotImplementedError, handler.verify,"password", sample2) - - # incorrectly returns sample3, dropping data parameter - self.assertEqual(handler.genhash("password", sample1), sample3) - - else: - assert self.backend == "argon2pure" - # should parse, but fail to verify - self.assertFalse(self.handler.verify("password", sample2)) - - # should return sample1 (corrected digest) - self.assertEqual(handler.genhash("password", sample2), sample1) - - def test_keyid_and_data_parameters(self): - # test combination of the two, just in case - self.assertRaises(NotImplementedError, self.handler.verify, 'stub', - "$argon2i$v=19$m=65536,t=2,p=4,keyid=ABCD,data=EFGH$c29tZXNhbHQ$" - "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4") - - def test_type_kwd(self): - cls = self.handler - - # XXX: this mirrors test_30_HasManyIdents(); - # maybe switch argon2 class to use that mixin instead of "type" kwd? - - # check settings - self.assertTrue("type" in cls.setting_kwds) - - # check supported type_values - for value in cls.type_values: - self.assertIsInstance(value, unicode) - self.assertTrue("i" in cls.type_values) - self.assertTrue("d" in cls.type_values) - - # check default - self.assertTrue(cls.type in cls.type_values) - - # check constructor validates ident correctly. - handler = cls - hash = self.get_sample_hash()[1] - kwds = handler.parsehash(hash) - del kwds['type'] - - # ... accepts good type - handler(type=cls.type, **kwds) - - # XXX: this is policy "ident" uses, maybe switch to it? - # # ... requires type w/o defaults - # self.assertRaises(TypeError, handler, **kwds) - handler(**kwds) - - # ... supplies default type - handler(use_defaults=True, **kwds) - - # ... rejects bad type - self.assertRaises(ValueError, handler, type='xXx', **kwds) - - def test_type_using(self): - handler = self.handler - - # XXX: this mirrors test_has_many_idents_using(); - # maybe switch argon2 class to use that mixin instead of "type" kwd? - - orig_type = handler.type - for alt_type in handler.type_values: - if alt_type != orig_type: - break - else: - raise AssertionError("expected to find alternate type: default=%r values=%r" % - (orig_type, handler.type_values)) - - def effective_type(cls): - return cls(use_defaults=True).type - - # keep default if nothing else specified - subcls = handler.using() - self.assertEqual(subcls.type, orig_type) - - # accepts alt type - subcls = handler.using(type=alt_type) - self.assertEqual(subcls.type, alt_type) - self.assertEqual(handler.type, orig_type) - - # check subcls actually *generates* default type, - # and that we didn't affect orig handler - self.assertEqual(effective_type(subcls), alt_type) - self.assertEqual(effective_type(handler), orig_type) - - # rejects bad type - self.assertRaises(ValueError, handler.using, type='xXx') - - # honor 'type' alias - subcls = handler.using(type=alt_type) - self.assertEqual(subcls.type, alt_type) - self.assertEqual(handler.type, orig_type) - - # check type aliases are being honored - self.assertEqual(effective_type(handler.using(type="I")), "i") - - def test_needs_update_w_type(self): - handler = self.handler - - hash = handler.hash("stub") - self.assertFalse(handler.needs_update(hash)) - - hash2 = re.sub(r"\$argon2\w+\$", "$argon2d$", hash) - self.assertTrue(handler.needs_update(hash2)) - - def test_needs_update_w_version(self): - handler = self.handler.using(memory_cost=65536, time_cost=2, parallelism=4, - digest_size=32) - hash = ("$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$" - "QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY") - if handler.max_version == 0x10: - self.assertFalse(handler.needs_update(hash)) - else: - self.assertTrue(handler.needs_update(hash)) - - def test_argon_byte_encoding(self): - """verify we're using right base64 encoding for argon2""" - handler = self.handler - if handler.version != 0x13: - # TODO: make this fatal, and add refs for other version. - raise self.skipTest("handler uses wrong version for sample hashes") - - # 8 byte salt - salt = b'somesalt' - temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt, - checksum_size=32, type="i") - hash = temp.hash("password") - self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2" - "$c29tZXNhbHQ" - "$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E") - - # 16 byte salt - salt = b'somesalt\x00\x00\x00\x00\x00\x00\x00\x00' - temp = handler.using(memory_cost=256, time_cost=2, parallelism=2, salt=salt, - checksum_size=32, type="i") - hash = temp.hash("password") - self.assertEqual(hash, "$argon2i$v=19$m=256,t=2,p=2" - "$c29tZXNhbHQAAAAAAAAAAA" - "$rqnbEp1/jFDUEKZZmw+z14amDsFqMDC53dIe57ZHD38") - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - settings_map = HandlerCase.FuzzHashGenerator.settings_map.copy() - settings_map.update(memory_cost="random_memory_cost", type="random_type") - - def random_type(self): - return self.rng.choice(self.handler.type_values) - - def random_memory_cost(self): - if self.test.backend == "argon2pure": - return self.randintgauss(128, 384, 256, 128) - else: - return self.randintgauss(128, 32767, 16384, 4096) - - # TODO: fuzz parallelism, digest_size - -#----------------------------------------- -# test suites for specific backends -#----------------------------------------- - -class argon2_argon2_cffi_test(_base_argon2_test.create_backend_case("argon2_cffi")): - - # add some more test vectors that take too long under argon2pure - known_correct_hashes = _base_argon2_test.known_correct_hashes + [ - # - # sample hashes from argon2 cffi package's unittests, - # which in turn were generated by official argon2 cmdline tool. - # - - # v1.2, type I, w/o a version tag - ('password', "$argon2i$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$" - "QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY"), - - # v1.3, type I - ('password', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$" - "IMit9qkFULCMA/ViizL57cnTLOa5DiVM9eMwpAvPwr4"), - - # v1.3, type D - ('password', "$argon2d$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$" - "cZn5d+rFh+ZfuRhm2iGUGgcrW5YLeM6q7L3vBsdmFA0"), - - # v1.3, type ID - ('password', "$argon2id$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$" - "GpZ3sK/oH9p7VIiV56G/64Zo/8GaUw434IimaPqxwCo"), - - # - # custom - # - - # ensure trailing null bytes handled correctly - ('password\x00', "$argon2i$v=19$m=65536,t=2,p=4$c29tZXNhbHQ$" - "Vpzuc0v0SrP88LcVvmg+z5RoOYpMDKH/lt6O+CZabIQ"), - - ] - - # add reference hashes from argon2 clib tests - known_correct_hashes.extend( - (info['secret'], info['hash']) for info in reference_data - if info['logM'] <= (18 if TEST_MODE("full") else 16) - ) - -class argon2_argon2pure_test(_base_argon2_test.create_backend_case("argon2pure")): - - # XXX: setting max_threads at 1 to prevent argon2pure from using multiprocessing, - # which causes big problems when testing under pypy. - # would like a "pure_use_threads" option instead, to make it use multiprocessing.dummy instead. - handler = hash.argon2.using(memory_cost=32, parallelism=2) - - # don't use multiprocessing for unittests, makes it a lot harder to ctrl-c - # XXX: make this controlled by env var? - handler.pure_use_threads = True - - # add reference hashes from argon2 clib tests - known_correct_hashes = _base_argon2_test.known_correct_hashes[:] - - known_correct_hashes.extend( - (info['secret'], info['hash']) for info in reference_data - if info['logM'] < 16 - ) - - class FuzzHashGenerator(_base_argon2_test.FuzzHashGenerator): - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(1, 3, 2, 1) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py deleted file mode 100644 index 64fc8bf..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_bcrypt.py +++ /dev/null @@ -1,688 +0,0 @@ -"""passlib.tests.test_handlers - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -import os -import warnings -# site -# pkg -from passlib import hash -from passlib.handlers.bcrypt import IDENT_2, IDENT_2X -from passlib.utils import repeat_string, to_bytes, is_safe_crypt_input -from passlib.utils.compat import irange, PY3 -from passlib.tests.utils import HandlerCase, TEST_MODE -from passlib.tests.test_handlers import UPASS_TABLE -# module - -#============================================================================= -# bcrypt -#============================================================================= -class _bcrypt_test(HandlerCase): - """base for BCrypt test cases""" - handler = hash.bcrypt - reduce_default_rounds = True - fuzz_salts_need_bcrypt_repair = True - - known_correct_hashes = [ - # - # from JTR 1.7.9 - # - ('U*U*U*U*', '$2a$05$c92SVSfjeiCD6F2nAD6y0uBpJDjdRkt0EgeC4/31Rf2LUZbDRDE.O'), - ('U*U***U', '$2a$05$WY62Xk2TXZ7EvVDQ5fmjNu7b0GEzSzUXUh2cllxJwhtOeMtWV3Ujq'), - ('U*U***U*', '$2a$05$Fa0iKV3E2SYVUlMknirWU.CFYGvJ67UwVKI1E2FP6XeLiZGcH3MJi'), - ('*U*U*U*U', '$2a$05$.WRrXibc1zPgIdRXYfv.4uu6TD1KWf0VnHzq/0imhUhuxSxCyeBs2'), - ('', '$2a$05$Otz9agnajgrAe0.kFVF9V.tzaStZ2s1s4ZWi/LY4sw2k/MTVFj/IO'), - - # - # test vectors from http://www.openwall.com/crypt v1.2 - # note that this omits any hashes that depend on crypt_blowfish's - # various CVE-2011-2483 workarounds (hash 2a and \xff\xff in password, - # and any 2x hashes); and only contain hashes which are correct - # under both crypt_blowfish 1.2 AND OpenBSD. - # - ('U*U', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW'), - ('U*U*', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK'), - ('U*U*U', '$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a'), - ('', '$2a$05$CCCCCCCCCCCCCCCCCCCCC.7uG0VCzI2bS7j6ymqJi9CdcdxiRTWNy'), - ('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' - '0123456789chars after 72 are ignored', - '$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui'), - (b'\xa3', - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq'), - (b'\xff\xa3345', - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.nRht2l/HRhr6zmCp9vYUvvsqynflf9e'), - (b'\xa3ab', - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.6IflQkJytoRVc1yuaNtHfiuq.FRlSIS'), - (b'\xaa'*72 + b'chars after 72 are ignored as usual', - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6'), - (b'\xaa\x55'*36, - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.R9xrDjiycxMbQE2bp.vgqlYpW5wx2yy'), - (b'\x55\xaa\xff'*24, - '$2a$05$/OK.fbVrR/bpIqNJ5ianF.9tQZzcJfm3uj2NvJ/n5xkhpqLrMpWCe'), - - # keeping one of their 2y tests, because we are supporting that. - (b'\xa3', - '$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq'), - - # - # 8bit bug (fixed in 2y/2b) - # - - # NOTE: see assert_lacks_8bit_bug() for origins of this test vector. - (b"\xd1\x91", "$2y$05$6bNw2HLQYeqHYyBfLMsv/OUcZd0LKP39b87nBw3.S2tVZSqiQX6eu"), - - # - # bsd wraparound bug (fixed in 2b) - # - - # NOTE: if backend is vulnerable, password will hash the same as '0'*72 - # ("$2a$04$R1lJ2gkNaoPGdafE.H.16.nVyh2niHsGJhayOHLMiXlI45o8/DU.6"), - # rather than same as ("0123456789"*8)[:72] - # 255 should be sufficient, but checking - (("0123456789"*26)[:254], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'), - (("0123456789"*26)[:255], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'), - (("0123456789"*26)[:256], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'), - (("0123456789"*26)[:257], '$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi'), - - - # - # from py-bcrypt tests - # - ('', '$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'), - ('a', '$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u'), - ('abc', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi'), - ('abcdefghijklmnopqrstuvwxyz', - '$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'), - ('~!@#$%^&*() ~!@#$%^&*()PNBFRD', - '$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS'), - - # - # custom test vectors - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, - '$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'), - - # ensure 2b support - (UPASS_TABLE, - '$2b$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'), - - ] - - if TEST_MODE("full"): - # - # add some extra tests related to 2/2a - # - CONFIG_2 = '$2$05$' + '.'*22 - CONFIG_A = '$2a$05$' + '.'*22 - known_correct_hashes.extend([ - ("", CONFIG_2 + 'J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq'), - ("", CONFIG_A + 'J2ihDv8vVf7QZ9BsaRrKyqs2tkn55Yq'), - ("abc", CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ("abc", CONFIG_A + 'ev6gDwpVye3oMCUpLY85aTpfBNHD0Ga'), - ("abc"*23, CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ("abc"*23, CONFIG_A + '2kIdfSj/4/R/Q6n847VTvc68BXiRYZC'), - ("abc"*24, CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ("abc"*24, CONFIG_A + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ("abc"*24+'x', CONFIG_2 + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ("abc"*24+'x', CONFIG_A + 'XuQjdH.wPVNUZ/bOfstdW/FqB8QSjte'), - ]) - - known_correct_configs = [ - ('$2a$04$uM6csdM8R9SXTex/gbTaye', UPASS_TABLE, - '$2a$04$uM6csdM8R9SXTex/gbTayezuvzFEufYGd2uB6of7qScLjQ4GwcD4G'), - ] - - known_unidentified_hashes = [ - # invalid minor version - "$2f$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q", - "$2`$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q", - ] - - known_malformed_hashes = [ - # bad char in otherwise correct hash - # \/ - "$2a$12$EXRkfkdmXn!gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q", - - # unsupported (but recognized) minor version - "$2x$12$EXRkfkdmXnagzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q", - - # rounds not zero-padded (py-bcrypt rejects this, therefore so do we) - '$2a$6$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.' - - # NOTE: salts with padding bits set are technically malformed, - # but we can reliably correct & issue a warning for that. - ] - - platform_crypt_support = [ - ("freedbsd|openbsd|netbsd", True), - ("darwin", False), - ("linux", None), # may be present via addon, e.g. debian's libpam-unix2 - ("solaris", None), # depends on system policy - ] - - #=================================================================== - # override some methods - #=================================================================== - def setUp(self): - # ensure builtin is enabled for duration of test. - if TEST_MODE("full") and self.backend == "builtin": - key = "PASSLIB_BUILTIN_BCRYPT" - orig = os.environ.get(key) - if orig: - self.addCleanup(os.environ.__setitem__, key, orig) - else: - self.addCleanup(os.environ.__delitem__, key) - os.environ[key] = "true" - - super(_bcrypt_test, self).setUp() - - # silence this warning, will come up a bunch during testing of old 2a hashes. - warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*") - - def populate_settings(self, kwds): - # builtin is still just way too slow. - if self.backend == "builtin": - kwds.setdefault("rounds", 4) - super(_bcrypt_test, self).populate_settings(kwds) - - #=================================================================== - # fuzz testing - #=================================================================== - def crypt_supports_variant(self, hash): - """check if OS crypt is expected to support given ident""" - from passlib.handlers.bcrypt import bcrypt, IDENT_2X, IDENT_2Y - from passlib.utils import safe_crypt - ident = bcrypt.from_string(hash) - return (safe_crypt("test", ident + "04$5BJqKfqMQvV7nS.yUguNcu") or "").startswith(ident) - - fuzz_verifiers = HandlerCase.fuzz_verifiers + ( - "fuzz_verifier_bcrypt", - "fuzz_verifier_pybcrypt", - "fuzz_verifier_bcryptor", - ) - - def fuzz_verifier_bcrypt(self): - # test against bcrypt, if available - from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y, _detect_pybcrypt - from passlib.utils import to_native_str, to_bytes - try: - import bcrypt - except ImportError: - return - if _detect_pybcrypt(): - return - def check_bcrypt(secret, hash): - """bcrypt""" - secret = to_bytes(secret, self.FuzzHashGenerator.password_encoding) - if hash.startswith(IDENT_2B): - # bcrypt <1.1 lacks 2B support - hash = IDENT_2A + hash[4:] - elif hash.startswith(IDENT_2): - # bcrypt doesn't support $2$ hashes; but we can fake it - # using the $2a$ algorithm, by repeating the password until - # it's 72 chars in length. - hash = IDENT_2A + hash[3:] - if secret: - secret = repeat_string(secret, 72) - elif hash.startswith(IDENT_2Y) and bcrypt.__version__ == "3.0.0": - hash = IDENT_2B + hash[4:] - hash = to_bytes(hash) - try: - return bcrypt.hashpw(secret, hash) == hash - except ValueError: - raise ValueError("bcrypt rejected hash: %r (secret=%r)" % (hash, secret)) - return check_bcrypt - - def fuzz_verifier_pybcrypt(self): - # test against py-bcrypt, if available - from passlib.handlers.bcrypt import ( - IDENT_2, IDENT_2A, IDENT_2B, IDENT_2X, IDENT_2Y, - _PyBcryptBackend, - ) - from passlib.utils import to_native_str - - loaded = _PyBcryptBackend._load_backend_mixin("pybcrypt", False) - if not loaded: - return - - from passlib.handlers.bcrypt import _pybcrypt as bcrypt_mod - - lock = _PyBcryptBackend._calc_lock # reuse threadlock workaround for pybcrypt 0.2 - - def check_pybcrypt(secret, hash): - """pybcrypt""" - secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding) - if len(secret) > 200: # vulnerable to wraparound bug - secret = secret[:200] - if hash.startswith((IDENT_2B, IDENT_2Y)): - hash = IDENT_2A + hash[4:] - try: - if lock: - with lock: - return bcrypt_mod.hashpw(secret, hash) == hash - else: - return bcrypt_mod.hashpw(secret, hash) == hash - except ValueError: - raise ValueError("py-bcrypt rejected hash: %r" % (hash,)) - return check_pybcrypt - - def fuzz_verifier_bcryptor(self): - # test against bcryptor if available - from passlib.handlers.bcrypt import IDENT_2, IDENT_2A, IDENT_2Y, IDENT_2B - from passlib.utils import to_native_str - try: - from bcryptor.engine import Engine - except ImportError: - return - def check_bcryptor(secret, hash): - """bcryptor""" - secret = to_native_str(secret, self.FuzzHashGenerator.password_encoding) - if hash.startswith((IDENT_2B, IDENT_2Y)): - hash = IDENT_2A + hash[4:] - elif hash.startswith(IDENT_2): - # bcryptor doesn't support $2$ hashes; but we can fake it - # using the $2a$ algorithm, by repeating the password until - # it's 72 chars in length. - hash = IDENT_2A + hash[3:] - if secret: - secret = repeat_string(secret, 72) - return Engine(False).hash_key(secret, hash) == hash - return check_bcryptor - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def generate(self): - opts = super(_bcrypt_test.FuzzHashGenerator, self).generate() - - secret = opts['secret'] - other = opts['other'] - settings = opts['settings'] - ident = settings.get('ident') - - if ident == IDENT_2X: - # 2x is just recognized, not supported. don't test with it. - del settings['ident'] - - elif ident == IDENT_2 and other and repeat_string(to_bytes(other), len(to_bytes(secret))) == to_bytes(secret): - # avoid false failure due to flaw in 0-revision bcrypt: - # repeated strings like 'abc' and 'abcabc' hash identically. - opts['secret'], opts['other'] = self.random_password_pair() - - return opts - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(5, 8, 6, 1) - - #=================================================================== - # custom tests - #=================================================================== - known_incorrect_padding = [ - # password, bad hash, good hash - - # 2 bits of salt padding set -# ("loppux", # \/ -# "$2a$12$oaQbBqq8JnSM1NHRPQGXORm4GCUMqp7meTnkft4zgSnrbhoKdDV0C", -# "$2a$12$oaQbBqq8JnSM1NHRPQGXOOm4GCUMqp7meTnkft4zgSnrbhoKdDV0C"), - ("test", # \/ - '$2a$04$oaQbBqq8JnSM1NHRPQGXORY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO', - '$2a$04$oaQbBqq8JnSM1NHRPQGXOOY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO'), - - # all 4 bits of salt padding set -# ("Passlib11", # \/ -# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVcUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK", -# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVOUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK"), - ("test", # \/ - "$2a$04$yjDgE74RJkeqC0/1NheSScrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS", - "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"), - - # bad checksum padding - ("test", # \/ - "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIV", - "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"), - ] - - def test_90_bcrypt_padding(self): - """test passlib correctly handles bcrypt padding bits""" - self.require_TEST_MODE("full") - # - # prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25) - # were some unused bits were incorrectly set in bcrypt salt strings. - # (fixed since 1.5.3) - # - bcrypt = self.handler - corr_desc = ".*incorrectly set padding bits" - - # - # test hash() / genconfig() don't generate invalid salts anymore - # - def check_padding(hash): - assert hash.startswith(("$2a$", "$2b$")) and len(hash) >= 28, \ - "unexpectedly malformed hash: %r" % (hash,) - self.assertTrue(hash[28] in '.Oeu', - "unused bits incorrectly set in hash: %r" % (hash,)) - for i in irange(6): - check_padding(bcrypt.genconfig()) - for i in irange(3): - check_padding(bcrypt.using(rounds=bcrypt.min_rounds).hash("bob")) - - # - # test genconfig() corrects invalid salts & issues warning. - # - with self.assertWarningList(["salt too large", corr_desc]): - hash = bcrypt.genconfig(salt="."*21 + "A.", rounds=5, relaxed=True) - self.assertEqual(hash, "$2b$05$" + "." * (22 + 31)) - - # - # test public methods against good & bad hashes - # - samples = self.known_incorrect_padding - for pwd, bad, good in samples: - - # make sure genhash() corrects bad configs, leaves good unchanged - with self.assertWarningList([corr_desc]): - self.assertEqual(bcrypt.genhash(pwd, bad), good) - with self.assertWarningList([]): - self.assertEqual(bcrypt.genhash(pwd, good), good) - - # make sure verify() works correctly with good & bad hashes - with self.assertWarningList([corr_desc]): - self.assertTrue(bcrypt.verify(pwd, bad)) - with self.assertWarningList([]): - self.assertTrue(bcrypt.verify(pwd, good)) - - # make sure normhash() corrects bad hashes, leaves good unchanged - with self.assertWarningList([corr_desc]): - self.assertEqual(bcrypt.normhash(bad), good) - with self.assertWarningList([]): - self.assertEqual(bcrypt.normhash(good), good) - - # make sure normhash() leaves non-bcrypt hashes alone - self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc") - - def test_needs_update_w_padding(self): - """needs_update corrects bcrypt padding""" - # NOTE: see padding test above for details about issue this detects - bcrypt = self.handler.using(rounds=4) - - # PASS1 = "test" - # bad contains invalid 'c' char at end of salt: - # \/ - BAD1 = "$2a$04$yjDgE74RJkeqC0/1NheSScrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS" - GOOD1 = "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS" - - self.assertTrue(bcrypt.needs_update(BAD1)) - self.assertFalse(bcrypt.needs_update(GOOD1)) - - #=================================================================== - # eoc - #=================================================================== - -# create test cases for specific backends -bcrypt_bcrypt_test = _bcrypt_test.create_backend_case("bcrypt") -bcrypt_pybcrypt_test = _bcrypt_test.create_backend_case("pybcrypt") -bcrypt_bcryptor_test = _bcrypt_test.create_backend_case("bcryptor") - -class bcrypt_os_crypt_test(_bcrypt_test.create_backend_case("os_crypt")): - - # os crypt doesn't support non-utf8 secret bytes - known_correct_hashes = [row for row in _bcrypt_test.known_correct_hashes - if is_safe_crypt_input(row[0])] - - # os crypt backend doesn't currently implement a per-call fallback if it fails - has_os_crypt_fallback = False - -bcrypt_builtin_test = _bcrypt_test.create_backend_case("builtin") - -#============================================================================= -# bcrypt -#============================================================================= -class _bcrypt_sha256_test(HandlerCase): - "base for BCrypt-SHA256 test cases" - handler = hash.bcrypt_sha256 - reduce_default_rounds = True - forbidden_characters = None - fuzz_salts_need_bcrypt_repair = True - - known_correct_hashes = [ - #------------------------------------------------------------------- - # custom test vectors for old v1 format - #------------------------------------------------------------------- - - # empty - ("", - '$bcrypt-sha256$2a,5$E/e/2AOhqM5W/KJTFQzLce$F6dYSxOdAEoJZO2eoHUZWZljW/e0TXO'), - - # ascii - ("password", - '$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'), - - # unicode / utf8 - (UPASS_TABLE, - '$bcrypt-sha256$2a,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'), - (UPASS_TABLE.encode("utf-8"), - '$bcrypt-sha256$2a,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'), - - # ensure 2b support - ("password", - '$bcrypt-sha256$2b,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'), - (UPASS_TABLE, - '$bcrypt-sha256$2b,5$.US1fQ4TQS.ZTz/uJ5Kyn.$QNdPDOTKKT5/sovNz1iWg26quOU4Pje'), - - # test >72 chars is hashed correctly -- under bcrypt these hash the same. - # NOTE: test_60_truncate_size() handles this already, this is just for overkill :) - (repeat_string("abc123", 72), - '$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$r/hyEtqJ0teqPEmfTLoZ83ciAI1Q74.'), - (repeat_string("abc123", 72) + "qwr", - '$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$021KLEif6epjot5yoxk0m8I0929ohEa'), - (repeat_string("abc123", 72) + "xyz", - '$bcrypt-sha256$2b,5$X1g1nh3g0v4h6970O68cxe$7.1kgpHduMGEjvM3fX6e/QCvfn6OKja'), - - #------------------------------------------------------------------- - # custom test vectors for v2 format - # TODO: convert to v2 format - #------------------------------------------------------------------- - - # empty - ("", - '$bcrypt-sha256$v=2,t=2b,r=5$E/e/2AOhqM5W/KJTFQzLce$WFPIZKtDDTriqWwlmRFfHiOTeheAZWe'), - - # ascii - ("password", - '$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$wOK1VFFtS8IGTrGa7.h5fs0u84qyPbS'), - - # unicode / utf8 - (UPASS_TABLE, - '$bcrypt-sha256$v=2,t=2b,r=5$.US1fQ4TQS.ZTz/uJ5Kyn.$pzzgp40k8reM1CuQb03PvE0IDPQSdV6'), - (UPASS_TABLE.encode("utf-8"), - '$bcrypt-sha256$v=2,t=2b,r=5$.US1fQ4TQS.ZTz/uJ5Kyn.$pzzgp40k8reM1CuQb03PvE0IDPQSdV6'), - - # test >72 chars is hashed correctly -- under bcrypt these hash the same. - # NOTE: test_60_truncate_size() handles this already, this is just for overkill :) - (repeat_string("abc123", 72), - '$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$zu1cloESVFIOsUIo7fCEgkdHaI9SSue'), - (repeat_string("abc123", 72) + "qwr", - '$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$CBF9csfEdW68xv3DwE6xSULXMtqEFP.'), - (repeat_string("abc123", 72) + "xyz", - '$bcrypt-sha256$v=2,t=2b,r=5$X1g1nh3g0v4h6970O68cxe$zC/1UDUG2ofEXB6Onr2vvyFzfhEOS3S'), - ] - - known_correct_configs =[ - # v1 - ('$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe', - "password", '$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu'), - # v2 - ('$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe', - "password", '$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$wOK1VFFtS8IGTrGa7.h5fs0u84qyPbS'), - ] - - known_malformed_hashes = [ - #------------------------------------------------------------------- - # v1 format - #------------------------------------------------------------------- - - # bad char in otherwise correct hash - # \/ - '$bcrypt-sha256$2a,5$5Hg1DKF!PE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unrecognized bcrypt variant - '$bcrypt-sha256$2c,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unsupported bcrypt variant - '$bcrypt-sha256$2x,5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # rounds zero-padded - '$bcrypt-sha256$2a,05$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # config string w/ $ added - '$bcrypt-sha256$2a,5$5Hg1DKFqPE8C2aflZ5vVoe$', - - #------------------------------------------------------------------- - # v2 format - #------------------------------------------------------------------- - - # bad char in otherwise correct hash - # \/ - '$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKF!PE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unsupported version (for this format) - '$bcrypt-sha256$v=1,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unrecognized version - '$bcrypt-sha256$v=3,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unrecognized bcrypt variant - '$bcrypt-sha256$v=2,t=2c,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # unsupported bcrypt variant - '$bcrypt-sha256$v=2,t=2a,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - '$bcrypt-sha256$v=2,t=2x,r=5$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # rounds zero-padded - '$bcrypt-sha256$v=2,t=2b,r=05$5Hg1DKFqPE8C2aflZ5vVoe$12BjNE0p7axMg55.Y/mHsYiVuFBDQyu', - - # config string w/ $ added - '$bcrypt-sha256$v=2,t=2b,r=5$5Hg1DKFqPE8C2aflZ5vVoe$', - ] - - #=================================================================== - # override some methods -- cloned from bcrypt - #=================================================================== - def setUp(self): - # ensure builtin is enabled for duration of test. - if TEST_MODE("full") and self.backend == "builtin": - key = "PASSLIB_BUILTIN_BCRYPT" - orig = os.environ.get(key) - if orig: - self.addCleanup(os.environ.__setitem__, key, orig) - else: - self.addCleanup(os.environ.__delitem__, key) - os.environ[key] = "enabled" - super(_bcrypt_sha256_test, self).setUp() - warnings.filterwarnings("ignore", ".*backend is vulnerable to the bsd wraparound bug.*") - - def populate_settings(self, kwds): - # builtin is still just way too slow. - if self.backend == "builtin": - kwds.setdefault("rounds", 4) - super(_bcrypt_sha256_test, self).populate_settings(kwds) - - #=================================================================== - # override ident tests for now - #=================================================================== - - def require_many_idents(self): - raise self.skipTest("multiple idents not supported") - - def test_30_HasOneIdent(self): - # forbidding ident keyword, we only support "2b" for now - handler = self.handler - handler(use_defaults=True) - self.assertRaises(ValueError, handler, ident="$2y$", use_defaults=True) - - #=================================================================== - # fuzz testing -- cloned from bcrypt - #=================================================================== - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(5, 8, 6, 1) - - def random_ident(self): - return "2b" - - #=================================================================== - # custom tests - #=================================================================== - - def test_using_version(self): - # default to v2 - handler = self.handler - self.assertEqual(handler.version, 2) - - # allow v1 explicitly - subcls = handler.using(version=1) - self.assertEqual(subcls.version, 1) - - # forbid unknown ver - self.assertRaises(ValueError, handler.using, version=999) - - # allow '2a' only for v1 - subcls = handler.using(version=1, ident="2a") - self.assertRaises(ValueError, handler.using, ident="2a") - - def test_calc_digest_v2(self): - """ - test digest calc v2 matches bcrypt() - """ - from passlib.hash import bcrypt - from passlib.crypto.digest import compile_hmac - from passlib.utils.binary import b64encode - - # manually calc intermediary digest - salt = "nyKYxTAvjmy6lMDYMl11Uu" - secret = "test" - temp_digest = compile_hmac("sha256", salt.encode("ascii"))(secret.encode("ascii")) - temp_digest = b64encode(temp_digest).decode("ascii") - self.assertEqual(temp_digest, "J5TlyIDm+IcSWmKiDJm+MeICndBkFVPn4kKdJW8f+xY=") - - # manually final hash from intermediary - # XXX: genhash() could be useful here - bcrypt_digest = bcrypt(ident="2b", salt=salt, rounds=12)._calc_checksum(temp_digest) - self.assertEqual(bcrypt_digest, "M0wE0Ov/9LXoQFCe.jRHu3MSHPF54Ta") - self.assertTrue(bcrypt.verify(temp_digest, "$2b$12$" + salt + bcrypt_digest)) - - # confirm handler outputs same thing. - # XXX: genhash() could be useful here - result = self.handler(ident="2b", salt=salt, rounds=12)._calc_checksum(secret) - self.assertEqual(result, bcrypt_digest) - - #=================================================================== - # eoc - #=================================================================== - -# create test cases for specific backends -bcrypt_sha256_bcrypt_test = _bcrypt_sha256_test.create_backend_case("bcrypt") -bcrypt_sha256_pybcrypt_test = _bcrypt_sha256_test.create_backend_case("pybcrypt") -bcrypt_sha256_bcryptor_test = _bcrypt_sha256_test.create_backend_case("bcryptor") - -class bcrypt_sha256_os_crypt_test(_bcrypt_sha256_test.create_backend_case("os_crypt")): - - @classmethod - def _get_safe_crypt_handler_backend(cls): - return bcrypt_os_crypt_test._get_safe_crypt_handler_backend() - - has_os_crypt_fallback = False - -bcrypt_sha256_builtin_test = _bcrypt_sha256_test.create_backend_case("builtin") - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py deleted file mode 100644 index ea6594b..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_cisco.py +++ /dev/null @@ -1,457 +0,0 @@ -""" -passlib.tests.test_handlers_cisco - tests for Cisco-specific algorithms -""" -#============================================================================= -# imports -#============================================================================= -from __future__ import absolute_import, division, print_function -# core -import logging -log = logging.getLogger(__name__) -# site -# pkg -from passlib import hash, exc -from passlib.utils.compat import u -from .utils import UserHandlerMixin, HandlerCase, repeat_string -from .test_handlers import UPASS_TABLE -# module -__all__ = [ - "cisco_pix_test", - "cisco_asa_test", - "cisco_type7_test", -] -#============================================================================= -# shared code for cisco PIX & ASA -#============================================================================= - -class _PixAsaSharedTest(UserHandlerMixin, HandlerCase): - """ - class w/ shared info for PIX & ASA tests. - """ - __unittest_skip = True # for TestCase - requires_user = False # for UserHandlerMixin - - #: shared list of hashes which should be identical under pix & asa7 - #: (i.e. combined secret + user < 17 bytes) - pix_asa_shared_hashes = [ - # - # http://www.perlmonks.org/index.pl?node_id=797623 - # - (("cisco", ""), "2KFQnbNIdI.2KYOU"), # confirmed ASA 9.6 - - # - # http://www.hsc.fr/ressources/breves/pix_crack.html.en - # - (("hsc", ""), "YtT8/k6Np8F1yz2c"), # confirmed ASA 9.6 - - # - # www.freerainbowtables.com/phpBB3/viewtopic.php?f=2&t=1441 - # - (("", ""), "8Ry2YjIyt7RRXU24"), # confirmed ASA 9.6 - (("cisco", "john"), "hN7LzeyYjw12FSIU"), - (("cisco", "jack"), "7DrfeZ7cyOj/PslD"), - - # - # http://comments.gmane.org/gmane.comp.security.openwall.john.user/2529 - # - (("ripper", "alex"), "h3mJrcH0901pqX/m"), - (("cisco", "cisco"), "3USUcOPFUiMCO4Jk"), - (("cisco", "cisco1"), "3USUcOPFUiMCO4Jk"), - (("CscFw-ITC!", "admcom"), "lZt7HSIXw3.QP7.R"), - ("cangetin", "TynyB./ftknE77QP"), - (("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"), - - # - # http://openwall.info/wiki/john/sample-hashes - # - (("phonehome", "rharris"), "zyIIMSYjiPm0L7a6"), - - # - # http://www.openwall.com/lists/john-users/2010/08/08/3 - # - (("cangetin", ""), "TynyB./ftknE77QP"), - (("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"), - - # - # from JTR 1.7.9 - # - ("test1", "TRPEas6f/aa6JSPL"), - ("test2", "OMT6mXmAvGyzrCtp"), - ("test3", "gTC7RIy1XJzagmLm"), - ("test4", "oWC1WRwqlBlbpf/O"), - ("password", "NuLKvvWGg.x9HEKO"), - ("0123456789abcdef", ".7nfVBEIEu4KbF/1"), - - # - # http://www.cisco.com/en/US/docs/security/pix/pix50/configuration/guide/commands.html#wp5472 - # - (("1234567890123456", ""), "feCkwUGktTCAgIbD"), # canonical source - (("watag00s1am", ""), "jMorNbK0514fadBh"), # canonical source - - # - # custom - # - (("cisco1", "cisco1"), "jmINXNH6p1BxUppp"), - - # ensures utf-8 used for unicode - (UPASS_TABLE, 'CaiIvkLMu2TOHXGT'), - - # - # passlib reference vectors - # - # Some of these have been confirmed on various ASA firewalls, - # and the exact version is noted next to each hash. - # Would like to verify these under more PIX & ASA versions. - # - # Those without a note are generally an extrapolation, - # to ensure the code stays consistent, but for various reasons, - # hasn't been verified. - # - # * One such case is usernames w/ 1 & 2 digits -- - # ASA (9.6 at least) requires 3+ digits in username. - # - # The following hashes (below 13 chars) should be identical for PIX/ASA. - # Ones which differ are listed separately in the known_correct_hashes - # list for the two test classes. - # - - # 4 char password - (('1234', ''), 'RLPMUQ26KL4blgFN'), # confirmed ASA 9.6 - - # 8 char password - (('01234567', ''), '0T52THgnYdV1tlOF'), # confirmed ASA 9.6 - (('01234567', '3'), '.z0dT9Alkdc7EIGS'), - (('01234567', '36'), 'CC3Lam53t/mHhoE7'), - (('01234567', '365'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6 - (('01234567', '3333'), '.z0dT9Alkdc7EIGS'), # confirmed ASA 9.6 - (('01234567', '3636'), 'CC3Lam53t/mHhoE7'), # confirmed ASA 9.6 - (('01234567', '3653'), '8xPrWpNnBdD2DzdZ'), # confirmed ASA 9.6 - (('01234567', 'adm'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6 - (('01234567', 'adma'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6 - (('01234567', 'admad'), 'dfWs2qiao6KD/P2L'), # confirmed ASA 9.6 - (('01234567', 'user'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6 - (('01234567', 'user1234'), 'PNZ4ycbbZ0jp1.j1'), # confirmed ASA 9.6 - - # 12 char password - (('0123456789ab', ''), 'S31BxZOGlAigndcJ'), # confirmed ASA 9.6 - (('0123456789ab', '36'), 'wFqSX91X5.YaRKsi'), - (('0123456789ab', '365'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6 - (('0123456789ab', '3333'), 'mcXPL/vIZcIxLUQs'), # confirmed ASA 9.6 - (('0123456789ab', '3636'), 'wFqSX91X5.YaRKsi'), # confirmed ASA 9.6 - (('0123456789ab', '3653'), 'qjgo3kNgTVxExbno'), # confirmed ASA 9.6 - (('0123456789ab', 'user'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6 - (('0123456789ab', 'user1234'), 'f.T4BKdzdNkjxQl7'), # confirmed ASA 9.6 - - # NOTE: remaining reference vectors for 13+ char passwords - # are split up between cisco_pix & cisco_asa tests. - - # unicode passwords - # ASA supposedly uses utf-8 encoding, but entering non-ascii - # chars is error-prone, and while UTF-8 appears to be intended, - # observed behaviors include: - # * ssh cli stripping non-ascii chars entirely - # * ASDM web iface double-encoding utf-8 strings - ((u("t\xe1ble").encode("utf-8"), 'user'), 'Og8fB4NyF0m5Ed9c'), - ((u("t\xe1ble").encode("utf-8").decode("latin-1").encode("utf-8"), - 'user'), 'cMvFC2XVBmK/68yB'), # confirmed ASA 9.6 when typed into ASDM - ] - - def test_calc_digest_spoiler(self): - """ - _calc_checksum() -- spoil oversize passwords during verify - - for details, see 'spoil_digest' flag instead that function. - this helps cisco_pix/cisco_asa implement their policy of - ``.truncate_verify_reject=True``. - """ - def calc(secret, for_hash=False): - return self.handler(use_defaults=for_hash)._calc_checksum(secret) - - # short (non-truncated) password - short_secret = repeat_string("1234", self.handler.truncate_size) - short_hash = calc(short_secret) - - # longer password should have totally different hash, - # to prevent verify from matching (i.e. "spoiled"). - long_secret = short_secret + "X" - long_hash = calc(long_secret) - self.assertNotEqual(long_hash, short_hash) - - # spoiled hash should depend on whole secret, - # so that output isn't predictable - alt_long_secret = short_secret + "Y" - alt_long_hash = calc(alt_long_secret) - self.assertNotEqual(alt_long_hash, short_hash) - self.assertNotEqual(alt_long_hash, long_hash) - - # for hash(), should throw error if password too large - calc(short_secret, for_hash=True) - self.assertRaises(exc.PasswordSizeError, calc, long_secret, for_hash=True) - self.assertRaises(exc.PasswordSizeError, calc, alt_long_secret, for_hash=True) - -#============================================================================= -# cisco pix -#============================================================================= -class cisco_pix_test(_PixAsaSharedTest): - handler = hash.cisco_pix - - #: known correct pix hashes - known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [ - # - # passlib reference vectors (PIX-specific) - # - # NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors, - # and general notes about the 'passlib reference vectors' test set. - # - # All of the following are PIX-specific, as ASA starts - # to use a different padding size at 13 characters. - # - # TODO: these need confirming w/ an actual PIX system. - # - - # 13 char password - (('0123456789abc', ''), 'eacOpB7vE7ZDukSF'), - (('0123456789abc', '3'), 'ylJTd/qei66WZe3w'), - (('0123456789abc', '36'), 'hDx8QRlUhwd6bU8N'), - (('0123456789abc', '365'), 'vYOOtnkh1HXcMrM7'), - (('0123456789abc', '3333'), 'ylJTd/qei66WZe3w'), - (('0123456789abc', '3636'), 'hDx8QRlUhwd6bU8N'), - (('0123456789abc', '3653'), 'vYOOtnkh1HXcMrM7'), - (('0123456789abc', 'user'), 'f4/.SALxqDo59mfV'), - (('0123456789abc', 'user1234'), 'f4/.SALxqDo59mfV'), - - # 14 char password - (('0123456789abcd', ''), '6r8888iMxEoPdLp4'), - (('0123456789abcd', '3'), 'f5lvmqWYj9gJqkIH'), - (('0123456789abcd', '36'), 'OJJ1Khg5HeAYBH1c'), - (('0123456789abcd', '365'), 'OJJ1Khg5HeAYBH1c'), - (('0123456789abcd', '3333'), 'f5lvmqWYj9gJqkIH'), - (('0123456789abcd', '3636'), 'OJJ1Khg5HeAYBH1c'), - (('0123456789abcd', '3653'), 'OJJ1Khg5HeAYBH1c'), - (('0123456789abcd', 'adm'), 'DbPLCFIkHc2SiyDk'), - (('0123456789abcd', 'adma'), 'DbPLCFIkHc2SiyDk'), - (('0123456789abcd', 'user'), 'WfO2UiTapPkF/FSn'), - (('0123456789abcd', 'user1234'), 'WfO2UiTapPkF/FSn'), - - # 15 char password - (('0123456789abcde', ''), 'al1e0XFIugTYLai3'), - (('0123456789abcde', '3'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', '36'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', '365'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', '3333'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', '3636'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', '3653'), 'lYbwBu.f82OIApQB'), - (('0123456789abcde', 'adm'), 'KgKx1UQvdR/09i9u'), - (('0123456789abcde', 'adma'), 'KgKx1UQvdR/09i9u'), - (('0123456789abcde', 'user'), 'qLopkenJ4WBqxaZN'), - (('0123456789abcde', 'user1234'), 'qLopkenJ4WBqxaZN'), - - # 16 char password - (('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', '36'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', '365'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', '3333'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', '3636'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', '3653'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', 'user'), '.7nfVBEIEu4KbF/1'), - (('0123456789abcdef', 'user1234'), '.7nfVBEIEu4KbF/1'), - ] - - -#============================================================================= -# cisco asa -#============================================================================= -class cisco_asa_test(_PixAsaSharedTest): - handler = hash.cisco_asa - - known_correct_hashes = _PixAsaSharedTest.pix_asa_shared_hashes + [ - # - # passlib reference vectors (ASA-specific) - # - # NOTE: See 'pix_asa_shared_hashes' for general PIX+ASA vectors, - # and general notes about the 'passlib reference vectors' test set. - # - - # 13 char password - # NOTE: past this point, ASA pads to 32 bytes instead of 16 - # for all cases where user is set (secret + 4 bytes > 16), - # but still uses 16 bytes for enable pwds (secret <= 16). - # hashes w/ user WON'T match PIX, but "enable" passwords will. - (('0123456789abc', ''), 'eacOpB7vE7ZDukSF'), # confirmed ASA 9.6 - (('0123456789abc', '36'), 'FRV9JG18UBEgX0.O'), - (('0123456789abc', '365'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6 - (('0123456789abc', '3333'), 'NmrkP98nT7RAeKZz'), # confirmed ASA 9.6 - (('0123456789abc', '3636'), 'FRV9JG18UBEgX0.O'), # confirmed ASA 9.6 - (('0123456789abc', '3653'), 'NIwkusG9hmmMy6ZQ'), # confirmed ASA 9.6 - (('0123456789abc', 'user'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6 - (('0123456789abc', 'user1234'), '8Q/FZeam5ai1A47p'), # confirmed ASA 9.6 - - # 14 char password - (('0123456789abcd', ''), '6r8888iMxEoPdLp4'), # confirmed ASA 9.6 - (('0123456789abcd', '3'), 'yxGoujXKPduTVaYB'), - (('0123456789abcd', '36'), 'W0jckhnhjnr/DiT/'), - (('0123456789abcd', '365'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6 - (('0123456789abcd', '3333'), 'yxGoujXKPduTVaYB'), # confirmed ASA 9.6 - (('0123456789abcd', '3636'), 'W0jckhnhjnr/DiT/'), # confirmed ASA 9.6 - (('0123456789abcd', '3653'), 'HuVOxfMQNahaoF8u'), # confirmed ASA 9.6 - (('0123456789abcd', 'adm'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6 - (('0123456789abcd', 'adma'), 'RtOmSeoCs4AUdZqZ'), # confirmed ASA 9.6 - (('0123456789abcd', 'user'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6 - (('0123456789abcd', 'user1234'), 'rrucwrcM0h25pr.m'), # confirmed ASA 9.6 - - # 15 char password - (('0123456789abcde', ''), 'al1e0XFIugTYLai3'), # confirmed ASA 9.6 - (('0123456789abcde', '3'), 'nAZrQoHaL.fgrIqt'), - (('0123456789abcde', '36'), '2GxIQ6ICE795587X'), - (('0123456789abcde', '365'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6 - (('0123456789abcde', '3333'), 'nAZrQoHaL.fgrIqt'), # confirmed ASA 9.6 - (('0123456789abcde', '3636'), '2GxIQ6ICE795587X'), # confirmed ASA 9.6 - (('0123456789abcde', '3653'), 'QmDsGwCRBbtGEKqM'), # confirmed ASA 9.6 - (('0123456789abcde', 'adm'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6 - (('0123456789abcde', 'adma'), 'Aj2aP0d.nk62wl4m'), # confirmed ASA 9.6 - (('0123456789abcde', 'user'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6 - (('0123456789abcde', 'user1234'), 'etxiXfo.bINJcXI7'), # confirmed ASA 9.6 - - # 16 char password - (('0123456789abcdef', ''), '.7nfVBEIEu4KbF/1'), # confirmed ASA 9.6 - (('0123456789abcdef', '36'), 'GhI8.yFSC5lwoafg'), - (('0123456789abcdef', '365'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6 - (('0123456789abcdef', '3333'), 'Ghdi1IlsswgYzzMH'), # confirmed ASA 9.6 - (('0123456789abcdef', '3636'), 'GhI8.yFSC5lwoafg'), # confirmed ASA 9.6 - (('0123456789abcdef', '3653'), 'KFBI6cNQauyY6h/G'), # confirmed ASA 9.6 - (('0123456789abcdef', 'user'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6 - (('0123456789abcdef', 'user1234'), 'IneB.wc9sfRzLPoh'), # confirmed ASA 9.6 - - # 17 char password - # NOTE: past this point, ASA pads to 32 bytes instead of 16 - # for ALL cases, since secret > 16 bytes even for enable pwds; - # and so none of these rest here should match PIX. - (('0123456789abcdefq', ''), 'bKshl.EN.X3CVFRQ'), # confirmed ASA 9.6 - (('0123456789abcdefq', '36'), 'JAeTXHs0n30svlaG'), - (('0123456789abcdefq', '365'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6 - (('0123456789abcdefq', '3333'), 'USEJbxI6.VY4ecBP'), # confirmed ASA 9.6 - (('0123456789abcdefq', '3636'), 'JAeTXHs0n30svlaG'), # confirmed ASA 9.6 - (('0123456789abcdefq', '3653'), '4fKSSUBHT1ChGqHp'), # confirmed ASA 9.6 - (('0123456789abcdefq', 'user'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6 - (('0123456789abcdefq', 'user1234'), '/dwqyD7nGdwSrDwk'), # confirmed ASA 9.6 - - # 27 char password - (('0123456789abcdefqwertyuiopa', ''), '4wp19zS3OCe.2jt5'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', '36'), 'PjUoGqWBKPyV9qOe'), - (('0123456789abcdefqwertyuiopa', '365'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', '3333'), 'rd/ZMuGTJFIb2BNG'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', '3636'), 'PjUoGqWBKPyV9qOe'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', '3653'), 'bfCy6xFAe5O/gzvM'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', 'user'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopa', 'user1234'), 'zynfWw3UtszxLMgL'), # confirmed ASA 9.6 - - # 28 char password - # NOTE: past this point, ASA stops appending the username AT ALL, - # even though there's still room for the first few chars. - (('0123456789abcdefqwertyuiopas', ''), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopas', '36'), 'W6nbOddI0SutTK7m'), - (('0123456789abcdefqwertyuiopas', '365'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopas', 'user'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopas', 'user1234'), 'W6nbOddI0SutTK7m'), # confirmed ASA 9.6 - - # 32 char password - # NOTE: this is max size that ASA allows, and throws error for larger - (('0123456789abcdefqwertyuiopasdfgh', ''), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopasdfgh', '36'), '5hPT/iC6DnoBxo6a'), - (('0123456789abcdefqwertyuiopasdfgh', '365'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopasdfgh', 'user'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6 - (('0123456789abcdefqwertyuiopasdfgh', 'user1234'), '5hPT/iC6DnoBxo6a'), # confirmed ASA 9.6 - ] - - -#============================================================================= -# cisco type 7 -#============================================================================= -class cisco_type7_test(HandlerCase): - handler = hash.cisco_type7 - salt_bits = 4 - salt_type = int - - known_correct_hashes = [ - # - # http://mccltd.net/blog/?p=1034 - # - ("secure ", "04480E051A33490E"), - - # - # http://insecure.org/sploits/cisco.passwords.html - # - ("Its time to go to lunch!", - "153B1F1F443E22292D73212D5300194315591954465A0D0B59"), - - # - # http://blog.ioshints.info/2007/11/type-7-decryption-in-cisco-ios.html - # - ("t35t:pa55w0rd", "08351F1B1D431516475E1B54382F"), - - # - # http://www.m00nie.com/2011/09/cisco-type-7-password-decryption-and-encryption-with-perl/ - # - ("hiImTesting:)", "020E0D7206320A325847071E5F5E"), - - # - # http://packetlife.net/forums/thread/54/ - # - ("cisco123", "060506324F41584B56"), - ("cisco123", "1511021F07257A767B"), - - # - # source ? - # - ('Supe&8ZUbeRp4SS', "06351A3149085123301517391C501918"), - - # - # custom - # - - # ensures utf-8 used for unicode - (UPASS_TABLE, '0958EDC8A9F495F6F8A5FD'), - ] - - known_unidentified_hashes = [ - # salt with hex value - "0A480E051A33490E", - - # salt value > 52. this may in fact be valid, but we reject it for now - # (see docs for more). - '99400E4812', - ] - - def test_90_decode(self): - """test cisco_type7.decode()""" - from passlib.utils import to_unicode, to_bytes - - handler = self.handler - for secret, hash in self.known_correct_hashes: - usecret = to_unicode(secret) - bsecret = to_bytes(secret) - self.assertEqual(handler.decode(hash), usecret) - self.assertEqual(handler.decode(hash, None), bsecret) - - self.assertRaises(UnicodeDecodeError, handler.decode, - '0958EDC8A9F495F6F8A5FD', 'ascii') - - def test_91_salt(self): - """test salt value border cases""" - handler = self.handler - self.assertRaises(TypeError, handler, salt=None) - handler(salt=None, use_defaults=True) - self.assertRaises(TypeError, handler, salt='abc') - self.assertRaises(ValueError, handler, salt=-10) - self.assertRaises(ValueError, handler, salt=100) - - self.assertRaises(TypeError, handler.using, salt='abc') - self.assertRaises(ValueError, handler.using, salt=-10) - self.assertRaises(ValueError, handler.using, salt=100) - with self.assertWarningList("salt/offset must be.*"): - subcls = handler.using(salt=100, relaxed=True) - self.assertEqual(subcls(use_defaults=True).salt, 52) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_django.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_django.py deleted file mode 100644 index f7c9a0d..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_django.py +++ /dev/null @@ -1,413 +0,0 @@ -"""passlib.tests.test_handlers_django - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -import re -import warnings -# site -# pkg -from passlib import hash -from passlib.utils import repeat_string -from passlib.utils.compat import u -from passlib.tests.utils import TestCase, HandlerCase, skipUnless, SkipTest -from passlib.tests.test_handlers import UPASS_USD, UPASS_TABLE -from passlib.tests.test_ext_django import DJANGO_VERSION, MIN_DJANGO_VERSION, \ - check_django_hasher_has_backend -# module - -#============================================================================= -# django -#============================================================================= - -# standard string django uses -UPASS_LETMEIN = u('l\xe8tmein') - -def vstr(version): - return ".".join(str(e) for e in version) - -class _DjangoHelper(TestCase): - """ - mixin for HandlerCase subclasses that are testing a hasher - which is also present in django. - """ - __unittest_skip = True - - #: minimum django version where hash alg is present / that we support testing against - min_django_version = MIN_DJANGO_VERSION - - #: max django version where hash alg is present - #: TODO: for a bunch of the tests below, this is just max version where - #: settings.PASSWORD_HASHERS includes it by default -- could add helper to patch - #: desired django hasher back in for duration of test. - #: XXX: change this to "disabled_in_django_version" instead? - max_django_version = None - - def _require_django_support(self): - # make sure min django version - if DJANGO_VERSION < self.min_django_version: - raise self.skipTest("Django >= %s not installed" % vstr(self.min_django_version)) - if self.max_django_version and DJANGO_VERSION > self.max_django_version: - raise self.skipTest("Django <= %s not installed" % vstr(self.max_django_version)) - - # make sure django has a backend for specified hasher - name = self.handler.django_name - if not check_django_hasher_has_backend(name): - raise self.skipTest('django hasher %r not available' % name) - - return True - - extra_fuzz_verifiers = HandlerCase.fuzz_verifiers + ( - "fuzz_verifier_django", - ) - - def fuzz_verifier_django(self): - try: - self._require_django_support() - except SkipTest: - return None - from django.contrib.auth.hashers import check_password - - def verify_django(secret, hash): - """django/check_password""" - if self.handler.name == "django_bcrypt" and hash.startswith("bcrypt$$2y$"): - hash = hash.replace("$$2y$", "$$2a$") - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - return check_password(secret, hash) - return verify_django - - def test_90_django_reference(self): - """run known correct hashes through Django's check_password()""" - self._require_django_support() - # XXX: esp. when it's no longer supported by django, - # should verify it's *NOT* recognized - from django.contrib.auth.hashers import check_password - assert self.known_correct_hashes - for secret, hash in self.iter_known_hashes(): - self.assertTrue(check_password(secret, hash), - "secret=%r hash=%r failed to verify" % - (secret, hash)) - self.assertFalse(check_password('x' + secret, hash), - "mangled secret=%r hash=%r incorrect verified" % - (secret, hash)) - - def test_91_django_generation(self): - """test against output of Django's make_password()""" - self._require_django_support() - # XXX: esp. when it's no longer supported by django, - # should verify it's *NOT* recognized - from passlib.utils import tick - from django.contrib.auth.hashers import make_password - name = self.handler.django_name # set for all the django_* handlers - end = tick() + self.max_fuzz_time/2 - generator = self.FuzzHashGenerator(self, self.getRandom()) - while tick() < end: - secret, other = generator.random_password_pair() - if not secret: # django rejects empty passwords. - continue - if isinstance(secret, bytes): - secret = secret.decode("utf-8") - hash = make_password(secret, hasher=name) - self.assertTrue(self.do_identify(hash)) - self.assertTrue(self.do_verify(secret, hash)) - self.assertFalse(self.do_verify(other, hash)) - -class django_disabled_test(HandlerCase): - """test django_disabled""" - handler = hash.django_disabled - disabled_contains_salt = True - - known_correct_hashes = [ - # *everything* should hash to "!", and nothing should verify - ("password", "!"), - ("", "!"), - (UPASS_TABLE, "!"), - ] - - known_alternate_hashes = [ - # django 1.6 appends random alpnum string - ("!9wa845vn7098ythaehasldkfj", "password", "!"), - ] - -class django_des_crypt_test(HandlerCase, _DjangoHelper): - """test django_des_crypt""" - handler = hash.django_des_crypt - max_django_version = (1,9) - - known_correct_hashes = [ - # ensures only first two digits of salt count. - ("password", 'crypt$c2$c2M87q...WWcU'), - ("password", 'crypt$c2e86$c2M87q...WWcU'), - ("passwordignoreme", 'crypt$c2.AZ$c2M87q...WWcU'), - - # ensures utf-8 used for unicode - (UPASS_USD, 'crypt$c2e86$c2hN1Bxd6ZiWs'), - (UPASS_TABLE, 'crypt$0.aQs$0.wB.TT0Czvlo'), - (u("hell\u00D6"), "crypt$sa$saykDgk3BPZ9E"), - - # prevent regression of issue 22 - ("foo", 'crypt$MNVY.9ajgdvDQ$MNVY.9ajgdvDQ'), - ] - - known_alternate_hashes = [ - # ensure django 1.4 empty salt field is accepted; - # but that salt field is re-filled (for django 1.0 compatibility) - ('crypt$$c2M87q...WWcU', "password", 'crypt$c2$c2M87q...WWcU'), - ] - - known_unidentified_hashes = [ - 'sha1$aa$bb', - ] - - known_malformed_hashes = [ - # checksum too short - 'crypt$c2$c2M87q', - - # salt must be >2 - 'crypt$f$c2M87q...WWcU', - - # make sure first 2 chars of salt & chk field agree. - 'crypt$ffe86$c2M87q...WWcU', - ] - -class django_salted_md5_test(HandlerCase, _DjangoHelper): - """test django_salted_md5""" - handler = hash.django_salted_md5 - max_django_version = (1,9) - - known_correct_hashes = [ - # test extra large salt - ("password", 'md5$123abcdef$c8272612932975ee80e8a35995708e80'), - - # test django 1.4 alphanumeric salt - ("test", 'md5$3OpqnFAHW5CT$54b29300675271049a1ebae07b395e20'), - - # ensures utf-8 used for unicode - (UPASS_USD, 'md5$c2e86$92105508419a81a6babfaecf876a2fa0'), - (UPASS_TABLE, 'md5$d9eb8$01495b32852bffb27cf5d4394fe7a54c'), - ] - - known_unidentified_hashes = [ - 'sha1$aa$bb', - ] - - known_malformed_hashes = [ - # checksum too short - 'md5$aa$bb', - ] - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_salt_size(self): - # workaround for django14 regression -- - # 1.4 won't accept hashes with empty salt strings, unlike 1.3 and earlier. - # looks to be fixed in a future release -- https://code.djangoproject.com/ticket/18144 - # for now, we avoid salt_size==0 under 1.4 - handler = self.handler - default = handler.default_salt_size - assert handler.min_salt_size == 0 - lower = 1 - upper = handler.max_salt_size or default*4 - return self.randintgauss(lower, upper, default, default*.5) - -class django_salted_sha1_test(HandlerCase, _DjangoHelper): - """test django_salted_sha1""" - handler = hash.django_salted_sha1 - max_django_version = (1,9) - - known_correct_hashes = [ - # test extra large salt - ("password",'sha1$123abcdef$e4a1877b0e35c47329e7ed7e58014276168a37ba'), - - # test django 1.4 alphanumeric salt - ("test", 'sha1$bcwHF9Hy8lxS$6b4cfa0651b43161c6f1471ce9523acf1f751ba3'), - - # ensures utf-8 used for unicode - (UPASS_USD, 'sha1$c2e86$0f75c5d7fbd100d587c127ef0b693cde611b4ada'), - (UPASS_TABLE, 'sha1$6d853$ef13a4d8fb57aed0cb573fe9c82e28dc7fd372d4'), - - # generic password - ("MyPassword", 'sha1$54123$893cf12e134c3c215f3a76bd50d13f92404a54d3'), - ] - - known_unidentified_hashes = [ - 'md5$aa$bb', - ] - - known_malformed_hashes = [ - # checksum too short - 'sha1$c2e86$0f75', - ] - - # reuse custom random_salt_size() helper... - FuzzHashGenerator = django_salted_md5_test.FuzzHashGenerator - -class django_pbkdf2_sha256_test(HandlerCase, _DjangoHelper): - """test django_pbkdf2_sha256""" - handler = hash.django_pbkdf2_sha256 - - known_correct_hashes = [ - # - # custom - generated via django 1.4 hasher - # - ('not a password', - 'pbkdf2_sha256$10000$kjVJaVz6qsnJ$5yPHw3rwJGECpUf70daLGhOrQ5+AMxIJdz1c3bqK1Rs='), - (UPASS_TABLE, - 'pbkdf2_sha256$10000$bEwAfNrH1TlQ$OgYUblFNUX1B8GfMqaCYUK/iHyO0pa7STTDdaEJBuY0='), - ] - -class django_pbkdf2_sha1_test(HandlerCase, _DjangoHelper): - """test django_pbkdf2_sha1""" - handler = hash.django_pbkdf2_sha1 - - known_correct_hashes = [ - # - # custom - generated via django 1.4 hashers - # - ('not a password', - 'pbkdf2_sha1$10000$wz5B6WkasRoF$atJmJ1o+XfJxKq1+Nu1f1i57Z5I='), - (UPASS_TABLE, - 'pbkdf2_sha1$10000$KZKWwvqb8BfL$rw5pWsxJEU4JrZAQhHTCO+u0f5Y='), - ] - -@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available") -class django_bcrypt_test(HandlerCase, _DjangoHelper): - """test django_bcrypt""" - handler = hash.django_bcrypt - # XXX: not sure when this wasn't in default list anymore. somewhere in [2.0 - 2.2] - max_django_version = (2, 0) - fuzz_salts_need_bcrypt_repair = True - - known_correct_hashes = [ - # - # just copied and adapted a few test vectors from bcrypt (above), - # since django_bcrypt is just a wrapper for the real bcrypt class. - # - ('', 'bcrypt$$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'), - ('abcdefghijklmnopqrstuvwxyz', - 'bcrypt$$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'), - (UPASS_TABLE, - 'bcrypt$$2a$05$Z17AXnnlpzddNUvnC6cZNOSwMA/8oNiKnHTHTwLlBijfucQQlHjaG'), - ] - - # NOTE: the following have been cloned from _bcrypt_test() - - def populate_settings(self, kwds): - # speed up test w/ lower rounds - kwds.setdefault("rounds", 4) - super(django_bcrypt_test, self).populate_settings(kwds) - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(5, 8, 6, 1) - - def random_ident(self): - # omit multi-ident tests, only $2a$ counts for this class - # XXX: enable this to check 2a / 2b? - return None - -@skipUnless(hash.bcrypt.has_backend(), "no bcrypt backends available") -class django_bcrypt_sha256_test(HandlerCase, _DjangoHelper): - """test django_bcrypt_sha256""" - handler = hash.django_bcrypt_sha256 - forbidden_characters = None - fuzz_salts_need_bcrypt_repair = True - - known_correct_hashes = [ - # - # custom - generated via django 1.6 hasher - # - ('', - 'bcrypt_sha256$$2a$06$/3OeRpbOf8/l6nPPRdZPp.nRiyYqPobEZGdNRBWihQhiFDh1ws1tu'), - (UPASS_LETMEIN, - 'bcrypt_sha256$$2a$08$NDjSAIcas.EcoxCRiArvT.MkNiPYVhrsrnJsRkLueZOoV1bsQqlmC'), - (UPASS_TABLE, - 'bcrypt_sha256$$2a$06$kCXUnRFQptGg491siDKNTu8RxjBGSjALHRuvhPYNFsa4Ea5d9M48u'), - - # test >72 chars is hashed correctly -- under bcrypt these hash the same. - (repeat_string("abc123",72), - 'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61OySmyXA8FoY4PjGizjE1QSDfuL5MXNni'), - (repeat_string("abc123",72)+"qwr", - 'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61Ocy0BEz1RK6xslSNi8PlaLX2pe7x/KQG'), - (repeat_string("abc123",72)+"xyz", - 'bcrypt_sha256$$2a$06$Tg/oYyZTyAf.Nb3qSgN61OvY2zoRVUa2Pugv2ExVOUT2YmhvxUFUa'), - ] - - known_malformed_hashers = [ - # data in django salt field - 'bcrypt_sha256$xyz$2a$06$/3OeRpbOf8/l6nPPRdZPp.nRiyYqPobEZGdNRBWihQhiFDh1ws1tu', - ] - - # NOTE: the following have been cloned from _bcrypt_test() - - def populate_settings(self, kwds): - # speed up test w/ lower rounds - kwds.setdefault("rounds", 4) - super(django_bcrypt_sha256_test, self).populate_settings(kwds) - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(5, 8, 6, 1) - - def random_ident(self): - # omit multi-ident tests, only $2a$ counts for this class - # XXX: enable this to check 2a / 2b? - return None - -from passlib.tests.test_handlers_argon2 import _base_argon2_test - -@skipUnless(hash.argon2.has_backend(), "no argon2 backends available") -class django_argon2_test(HandlerCase, _DjangoHelper): - """test django_bcrypt""" - handler = hash.django_argon2 - - # NOTE: most of this adapted from _base_argon2_test & argon2pure test - - known_correct_hashes = [ - # sample test - ("password", 'argon2$argon2i$v=19$m=256,t=1,p=1$c29tZXNhbHQ$AJFIsNZTMKTAewB4+ETN1A'), - - # sample w/ all parameters different - ("password", 'argon2$argon2i$v=19$m=380,t=2,p=2$c29tZXNhbHQ$SrssP8n7m/12VWPM8dvNrw'), - - # generated from django 1.10.3 - (UPASS_LETMEIN, 'argon2$argon2i$v=19$m=512,t=2,p=2$V25jN1l4UUJZWkR1$MxpA1BD2Gh7+D79gaAw6sQ'), - ] - - def setUpWarnings(self): - super(django_argon2_test, self).setUpWarnings() - warnings.filterwarnings("ignore", ".*Using argon2pure backend.*") - - def do_stub_encrypt(self, handler=None, **settings): - # overriding default since no way to get stub config from argon2._calc_hash() - # (otherwise test_21b_max_rounds blocks trying to do max rounds) - handler = (handler or self.handler).using(**settings) - self = handler.wrapped(use_defaults=True) - self.checksum = self._stub_checksum - assert self.checksum - return handler._wrap_hash(self.to_string()) - - def test_03_legacy_hash_workflow(self): - # override base method - raise self.skipTest("legacy 1.6 workflow not supported") - - class FuzzHashGenerator(_base_argon2_test.FuzzHashGenerator): - - def random_type(self): - # override default since django only uses type I (see note in class) - return "I" - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(1, 3, 2, 1) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py deleted file mode 100644 index 4d2f048..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_pbkdf2.py +++ /dev/null @@ -1,480 +0,0 @@ -"""passlib.tests.test_handlers - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -# core -import logging -log = logging.getLogger(__name__) -import warnings -# site -# pkg -from passlib import hash -from passlib.utils.compat import u -from passlib.tests.utils import TestCase, HandlerCase -from passlib.tests.test_handlers import UPASS_WAV -# module - -#============================================================================= -# ldap_pbkdf2_{digest} -#============================================================================= -# NOTE: since these are all wrappers for the pbkdf2_{digest} hasehs, -# they don't extensive separate testing. - -class ldap_pbkdf2_test(TestCase): - - def test_wrappers(self): - """test ldap pbkdf2 wrappers""" - - self.assertTrue( - hash.ldap_pbkdf2_sha1.verify( - "password", - '{PBKDF2}1212$OB.dtnSEXZK8U5cgxU/GYQ$y5LKPOplRmok7CZp/aqVDVg8zGI', - ) - ) - - self.assertTrue( - hash.ldap_pbkdf2_sha256.verify( - "password", - '{PBKDF2-SHA256}1212$4vjV83LKPjQzk31VI4E0Vw$hsYF68OiOUPdDZ1Fg' - '.fJPeq1h/gXXY7acBp9/6c.tmQ' - ) - ) - - self.assertTrue( - hash.ldap_pbkdf2_sha512.verify( - "password", - '{PBKDF2-SHA512}1212$RHY0Fr3IDMSVO/RSZyb5ow$eNLfBK.eVozomMr.1gYa1' - '7k9B7KIK25NOEshvhrSX.esqY3s.FvWZViXz4KoLlQI.BzY/YTNJOiKc5gBYFYGww' - ) - ) - -#============================================================================= -# pbkdf2 hashes -#============================================================================= -class atlassian_pbkdf2_sha1_test(HandlerCase): - handler = hash.atlassian_pbkdf2_sha1 - - known_correct_hashes = [ - # - # generated using Jira - # - ("admin", '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/p'), - (UPASS_WAV, - "{PKCS5S2}cE9Yq6Am5tQGdHSHhky2XLeOnURwzaLBG2sur7FHKpvy2u0qDn6GcVGRjlmJoIUy"), - ] - - known_malformed_hashes = [ - # bad char ---\/ - '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy!0IPksHChwoTAVYFrhsgoq8/p' - - # bad size, missing padding - '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/' - - # bad size, with correct padding - '{PKCS5S2}c4xaeTQM0lUieMS3V5voiexyX9XhqC2dBd5ecVy60IPksHChwoTAVYFrhsgoq8/=' - ] - -class pbkdf2_sha1_test(HandlerCase): - handler = hash.pbkdf2_sha1 - known_correct_hashes = [ - ("password", '$pbkdf2$1212$OB.dtnSEXZK8U5cgxU/GYQ$y5LKPOplRmok7CZp/aqVDVg8zGI'), - (UPASS_WAV, - '$pbkdf2$1212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc'), - ] - - known_malformed_hashes = [ - # zero padded rounds field - '$pbkdf2$01212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc', - - # empty rounds field - '$pbkdf2$$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc', - - # too many field - '$pbkdf2$1212$THDqatpidANpadlLeTeOEg$HV3oi1k5C5LQCgG1BMOL.BX4YZc$', - ] - -class pbkdf2_sha256_test(HandlerCase): - handler = hash.pbkdf2_sha256 - known_correct_hashes = [ - ("password", - '$pbkdf2-sha256$1212$4vjV83LKPjQzk31VI4E0Vw$hsYF68OiOUPdDZ1Fg.fJPeq1h/gXXY7acBp9/6c.tmQ' - ), - (UPASS_WAV, - '$pbkdf2-sha256$1212$3SABFJGDtyhrQMVt1uABPw$WyaUoqCLgvz97s523nF4iuOqZNbp5Nt8do/cuaa7AiI' - ), - ] - -class pbkdf2_sha512_test(HandlerCase): - handler = hash.pbkdf2_sha512 - known_correct_hashes = [ - ("password", - '$pbkdf2-sha512$1212$RHY0Fr3IDMSVO/RSZyb5ow$eNLfBK.eVozomMr.1gYa1' - '7k9B7KIK25NOEshvhrSX.esqY3s.FvWZViXz4KoLlQI.BzY/YTNJOiKc5gBYFYGww' - ), - (UPASS_WAV, - '$pbkdf2-sha512$1212$KkbvoKGsAIcF8IslDR6skQ$8be/PRmd88Ps8fmPowCJt' - 'tH9G3vgxpG.Krjt3KT.NP6cKJ0V4Prarqf.HBwz0dCkJ6xgWnSj2ynXSV7MlvMa8Q' - ), - ] - -class cta_pbkdf2_sha1_test(HandlerCase): - handler = hash.cta_pbkdf2_sha1 - known_correct_hashes = [ - # - # test vectors from original implementation - # - (u("hashy the \N{SNOWMAN}"), '$p5k2$1000$ZxK4ZBJCfQg=$jJZVscWtO--p1-xIZl6jhO2LKR0='), - - # - # custom - # - ("password", "$p5k2$1$$h1TDLGSw9ST8UMAPeIE13i0t12c="), - (UPASS_WAV, - "$p5k2$4321$OTg3NjU0MzIx$jINJrSvZ3LXeIbUdrJkRpN62_WQ="), - ] - -class dlitz_pbkdf2_sha1_test(HandlerCase): - handler = hash.dlitz_pbkdf2_sha1 - known_correct_hashes = [ - # - # test vectors from original implementation - # - ('cloadm', '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'), - ('gnu', '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'), - ('dcl', '$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL'), - ('spam', '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'), - (UPASS_WAV, - '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'), - ] - -class grub_pbkdf2_sha512_test(HandlerCase): - handler = hash.grub_pbkdf2_sha512 - known_correct_hashes = [ - # - # test vectors generated from cmd line tool - # - - # salt=32 bytes - (UPASS_WAV, - 'grub.pbkdf2.sha512.10000.BCAC1CEC5E4341C8C511C529' - '7FA877BE91C2817B32A35A3ECF5CA6B8B257F751.6968526A' - '2A5B1AEEE0A29A9E057336B48D388FFB3F600233237223C21' - '04DE1752CEC35B0DD1ED49563398A282C0F471099C2803FBA' - '47C7919CABC43192C68F60'), - - # salt=64 bytes - ('toomanysecrets', - 'grub.pbkdf2.sha512.10000.9B436BB6978682363D5C449B' - 'BEAB322676946C632208BC1294D51F47174A9A3B04A7E4785' - '986CD4EA7470FAB8FE9F6BD522D1FC6C51109A8596FB7AD48' - '7C4493.0FE5EF169AFFCB67D86E2581B1E251D88C777B98BA' - '2D3256ECC9F765D84956FC5CA5C4B6FD711AA285F0A04DCF4' - '634083F9A20F4B6F339A52FBD6BED618E527B'), - - ] - -#============================================================================= -# scram hash -#============================================================================= -class scram_test(HandlerCase): - handler = hash.scram - - # TODO: need a bunch more reference vectors from some real - # SCRAM transactions. - known_correct_hashes = [ - # - # taken from example in SCRAM specification (rfc 5802) - # - ('pencil', '$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30'), - - # - # custom - # - - # same as 5802 example hash, but with sha-256 & sha-512 added. - ('pencil', '$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,' - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ'), - - # test unicode passwords & saslprep (all the passwords below - # should normalize to the same value: 'IX \xE0') - (u('IX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$' - 'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'), - (u('\u2168\u3000a\u0300'), '$scram$6400$0BojBCBE6P2/N4bQ$' - 'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'), - (u('\u00ADIX \xE0'), '$scram$6400$0BojBCBE6P2/N4bQ$' - 'sha-1=YniLes.b8WFMvBhtSACZyyvxeCc'), - ] - - known_malformed_hashes = [ - # zero-padding in rounds - '$scram$04096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', - - # non-digit in rounds - '$scram$409A$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', - - # bad char in salt ---\/ - '$scram$4096$QSXCR.Q6sek8bf9-$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', - - # bad char in digest ---\/ - '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX3-', - - # missing sections - '$scram$4096$QSXCR.Q6sek8bf92', - '$scram$4096$QSXCR.Q6sek8bf92$', - - # too many sections - '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30$', - - # missing separator - '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY', - - # too many chars in alg name - '$scram$4096$QSXCR.Q6sek8bf92$sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'shaxxx-190=HZbuOlKbWl.eR8AfIposuKbhX30', - - # missing sha-1 alg - '$scram$4096$QSXCR.Q6sek8bf92$sha-256=HZbuOlKbWl.eR8AfIposuKbhX30', - - # non-iana name - '$scram$4096$QSXCR.Q6sek8bf92$sha1=HZbuOlKbWl.eR8AfIposuKbhX30', - ] - - def setUp(self): - super(scram_test, self).setUp() - - # some platforms lack stringprep (e.g. Jython, IronPython) - self.require_stringprep() - - # silence norm_hash_name() warning - warnings.filterwarnings("ignore", r"norm_hash_name\(\): unknown hash") - - def test_90_algs(self): - """test parsing of 'algs' setting""" - defaults = dict(salt=b'A'*10, rounds=1000) - def parse(algs, **kwds): - for k in defaults: - kwds.setdefault(k, defaults[k]) - return self.handler(algs=algs, **kwds).algs - - # None -> default list - self.assertEqual(parse(None, use_defaults=True), hash.scram.default_algs) - self.assertRaises(TypeError, parse, None) - - # strings should be parsed - self.assertEqual(parse("sha1"), ["sha-1"]) - self.assertEqual(parse("sha1, sha256, md5"), ["md5","sha-1","sha-256"]) - - # lists should be normalized - self.assertEqual(parse(["sha-1","sha256"]), ["sha-1","sha-256"]) - - # sha-1 required - self.assertRaises(ValueError, parse, ["sha-256"]) - self.assertRaises(ValueError, parse, algs=[], use_defaults=True) - - # alg names must be < 10 chars - self.assertRaises(ValueError, parse, ["sha-1","shaxxx-190"]) - - # alg & checksum mutually exclusive. - self.assertRaises(RuntimeError, parse, ['sha-1'], - checksum={"sha-1": b"\x00"*20}) - - def test_90_checksums(self): - """test internal parsing of 'checksum' keyword""" - # check non-bytes checksum values are rejected - self.assertRaises(TypeError, self.handler, use_defaults=True, - checksum={'sha-1': u('X')*20}) - - # check sha-1 is required - self.assertRaises(ValueError, self.handler, use_defaults=True, - checksum={'sha-256': b'X'*32}) - - # XXX: anything else that's not tested by the other code already? - - def test_91_extract_digest_info(self): - """test scram.extract_digest_info()""" - edi = self.handler.extract_digest_info - - # return appropriate value or throw KeyError - h = "$scram$10$AAAAAA$sha-1=AQ,bbb=Ag,ccc=Aw" - s = b'\x00'*4 - self.assertEqual(edi(h,"SHA1"), (s,10, b'\x01')) - self.assertEqual(edi(h,"bbb"), (s,10, b'\x02')) - self.assertEqual(edi(h,"ccc"), (s,10, b'\x03')) - self.assertRaises(KeyError, edi, h, "ddd") - - # config strings should cause value error. - c = "$scram$10$....$sha-1,bbb,ccc" - self.assertRaises(ValueError, edi, c, "sha-1") - self.assertRaises(ValueError, edi, c, "bbb") - self.assertRaises(ValueError, edi, c, "ddd") - - def test_92_extract_digest_algs(self): - """test scram.extract_digest_algs()""" - eda = self.handler.extract_digest_algs - - self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30'), ["sha-1"]) - - self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30', format="hashlib"), - ["sha1"]) - - self.assertEqual(eda('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,' - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ'), - ["sha-1","sha-256","sha-512"]) - - def test_93_derive_digest(self): - """test scram.derive_digest()""" - # NOTE: this just does a light test, since derive_digest - # is used by hash / verify, and is tested pretty well via those. - hash = self.handler.derive_digest - - # check various encodings of password work. - s1 = b'\x01\x02\x03' - d1 = b'\xb2\xfb\xab\x82[tNuPnI\x8aZZ\x19\x87\xcen\xe9\xd3' - self.assertEqual(hash(u("\u2168"), s1, 1000, 'sha-1'), d1) - self.assertEqual(hash(b"\xe2\x85\xa8", s1, 1000, 'SHA-1'), d1) - self.assertEqual(hash(u("IX"), s1, 1000, 'sha1'), d1) - self.assertEqual(hash(b"IX", s1, 1000, 'SHA1'), d1) - - # check algs - self.assertEqual(hash("IX", s1, 1000, 'md5'), - b'3\x19\x18\xc0\x1c/\xa8\xbf\xe4\xa3\xc2\x8eM\xe8od') - self.assertRaises(ValueError, hash, "IX", s1, 1000, 'sha-666') - - # check rounds - self.assertRaises(ValueError, hash, "IX", s1, 0, 'sha-1') - - # unicode salts accepted as of passlib 1.7 (previous caused TypeError) - self.assertEqual(hash(u("IX"), s1.decode("latin-1"), 1000, 'sha1'), d1) - - def test_94_saslprep(self): - """test hash/verify use saslprep""" - # NOTE: this just does a light test that saslprep() is being - # called in various places, relying in saslpreps()'s tests - # to verify full normalization behavior. - - # hash unnormalized - h = self.do_encrypt(u("I\u00ADX")) - self.assertTrue(self.do_verify(u("IX"), h)) - self.assertTrue(self.do_verify(u("\u2168"), h)) - - # hash normalized - h = self.do_encrypt(u("\xF3")) - self.assertTrue(self.do_verify(u("o\u0301"), h)) - self.assertTrue(self.do_verify(u("\u200Do\u0301"), h)) - - # throws error if forbidden char provided - self.assertRaises(ValueError, self.do_encrypt, u("\uFDD0")) - self.assertRaises(ValueError, self.do_verify, u("\uFDD0"), h) - - def test_94_using_w_default_algs(self, param="default_algs"): - """using() -- 'default_algs' parameter""" - # create subclass - handler = self.handler - orig = list(handler.default_algs) # in case it's modified in place - subcls = handler.using(**{param: "sha1,md5"}) - - # shouldn't have changed handler - self.assertEqual(handler.default_algs, orig) - - # should have own set - self.assertEqual(subcls.default_algs, ["md5", "sha-1"]) - - # test hash output - h1 = subcls.hash("dummy") - self.assertEqual(handler.extract_digest_algs(h1), ["md5", "sha-1"]) - - def test_94_using_w_algs(self): - """using() -- 'algs' parameter""" - self.test_94_using_w_default_algs(param="algs") - - def test_94_needs_update_algs(self): - """needs_update() -- algs setting""" - handler1 = self.handler.using(algs="sha1,md5") - - # shouldn't need update, has same algs - h1 = handler1.hash("dummy") - self.assertFalse(handler1.needs_update(h1)) - - # *currently* shouldn't need update, has superset of algs required by handler2 - # (may change this policy) - handler2 = handler1.using(algs="sha1") - self.assertFalse(handler2.needs_update(h1)) - - # should need update, doesn't have all algs required by handler3 - handler3 = handler1.using(algs="sha1,sha256") - self.assertTrue(handler3.needs_update(h1)) - - def test_95_context_algs(self): - """test handling of 'algs' in context object""" - handler = self.handler - from passlib.context import CryptContext - c1 = CryptContext(["scram"], scram__algs="sha1,md5") - - h = c1.hash("dummy") - self.assertEqual(handler.extract_digest_algs(h), ["md5", "sha-1"]) - self.assertFalse(c1.needs_update(h)) - - c2 = c1.copy(scram__algs="sha1") - self.assertFalse(c2.needs_update(h)) - - c2 = c1.copy(scram__algs="sha1,sha256") - self.assertTrue(c2.needs_update(h)) - - def test_96_full_verify(self): - """test verify(full=True) flag""" - def vpart(s, h): - return self.handler.verify(s, h) - def vfull(s, h): - return self.handler.verify(s, h, full=True) - - # reference - h = ('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVY,' - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ') - self.assertTrue(vfull('pencil', h)) - self.assertFalse(vfull('tape', h)) - - # catch truncated digests. - h = ('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhV,' # -1 char - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ') - self.assertRaises(ValueError, vfull, 'pencil', h) - - # catch padded digests. - h = ('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' - 'sha-256=qXUXrlcvnaxxWG00DdRgVioR2gnUpuX5r.3EZ1rdhVYa,' # +1 char - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ') - self.assertRaises(ValueError, vfull, 'pencil', h) - - # catch hash containing digests belonging to diff passwords. - # proper behavior for quick-verify (the default) is undefined, - # but full-verify should throw error. - h = ('$scram$4096$QSXCR.Q6sek8bf92$' - 'sha-1=HZbuOlKbWl.eR8AfIposuKbhX30,' # 'pencil' - 'sha-256=R7RJDWIbeKRTFwhE9oxh04kab0CllrQ3kCcpZUcligc,' # 'tape' - 'sha-512=lzgniLFcvglRLS0gt.C4gy.NurS3OIOVRAU1zZOV4P.qFiVFO2/' # 'pencil' - 'edGQSu/kD1LwdX0SNV/KsPdHSwEl5qRTuZQ') - self.assertTrue(vpart('tape', h)) - self.assertFalse(vpart('pencil', h)) - self.assertRaises(ValueError, vfull, 'pencil', h) - self.assertRaises(ValueError, vfull, 'tape', h) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py b/.venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py deleted file mode 100644 index 5ab6d9f..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_handlers_scrypt.py +++ /dev/null @@ -1,111 +0,0 @@ -"""passlib.tests.test_handlers - tests for passlib hash algorithms""" -#============================================================================= -# imports -#============================================================================= -# core -import logging; log = logging.getLogger(__name__) -import warnings -warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*") -# site -# pkg -from passlib import hash -from passlib.tests.utils import HandlerCase, TEST_MODE -from passlib.tests.test_handlers import UPASS_TABLE, PASS_TABLE_UTF8 -# module - -#============================================================================= -# scrypt hash -#============================================================================= -class _scrypt_test(HandlerCase): - handler = hash.scrypt - - known_correct_hashes = [ - # - # excepted from test vectors from scrypt whitepaper - # (http://www.tarsnap.com/scrypt/scrypt.pdf, appendix b), - # and encoded using passlib's custom format - # - - # salt=b"" - ("", "$scrypt$ln=4,r=1,p=1$$d9ZXYjhleyA7GcpCwYoEl/FrSETjB0ro39/6P+3iFEI"), - - # salt=b"NaCl" - ("password", "$scrypt$ln=10,r=8,p=16$TmFDbA$/bq+HJ00cgB4VucZDQHp/nxq18vII3gw53N2Y0s3MWI"), - - # - # custom - # - - # simple test - ("test", '$scrypt$ln=8,r=8,p=1$wlhLyXmP8b53bm1NKYVQqg$mTpvG8lzuuDk+DWz8HZIB6Vum6erDuUm0As5yU+VxWA'), - - # different block value - ("password", '$scrypt$ln=8,r=2,p=1$dO6d0xoDoLT2PofQGoNQag$g/Wf2A0vhHhaJM+addK61QPBthSmYB6uVTtQzh8CM3o'), - - # different rounds - (UPASS_TABLE, '$scrypt$ln=7,r=8,p=1$jjGmtDamdA4BQAjBeA9BSA$OiWRHhQtpDx7M/793x6UXK14AD512jg/qNm/hkWZG4M'), - - # alt encoding - (PASS_TABLE_UTF8, '$scrypt$ln=7,r=8,p=1$jjGmtDamdA4BQAjBeA9BSA$OiWRHhQtpDx7M/793x6UXK14AD512jg/qNm/hkWZG4M'), - - # diff block & parallel counts as well - ("nacl", '$scrypt$ln=1,r=4,p=2$yhnD+J+Tci4lZCwFgHCuVQ$fAsEWmxSHuC0cHKMwKVFPzrQukgvK09Sj+NueTSxKds') - ] - - if TEST_MODE("full"): - # add some hashes with larger rounds value. - known_correct_hashes.extend([ - # - # from scrypt whitepaper - # - - # salt=b"SodiumChloride" - ("pleaseletmein", "$scrypt$ln=14,r=8,p=1$U29kaXVtQ2hsb3JpZGU" - "$cCO9yzr9c0hGHAbNgf046/2o+7qQT44+qbVD9lRdofI"), - - # - # openwall format (https://gitlab.com/jas/scrypt-unix-crypt/blob/master/unix-scrypt.txt) - # - ("pleaseletmein", - "$7$C6..../....SodiumChloride$kBGj9fHznVYFQMEn/qDCfrDevf9YDtcDdKvEqHJLV8D"), - - ]) - - known_malformed_hashes = [ - # missing 'p' value - '$scrypt$ln=10,r=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ', - - # rounds too low - '$scrypt$ln=0,r=1,p=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ', - - # invalid block size - '$scrypt$ln=10,r=A,p=1$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ', - - # r*p too large - '$scrypt$ln=10,r=134217728,p=8$wvif8/4fg1Cq9V7L2dv73w$bJcLia1lyfQ1X2x0xflehwVXPzWIUQWWdnlGwfVzBeQ', - ] - - def setUpWarnings(self): - super(_scrypt_test, self).setUpWarnings() - warnings.filterwarnings("ignore", ".*using builtin scrypt backend.*") - - def populate_settings(self, kwds): - # builtin is still just way too slow. - if self.backend == "builtin": - kwds.setdefault("rounds", 6) - super(_scrypt_test, self).populate_settings(kwds) - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - def random_rounds(self): - # decrease default rounds for fuzz testing to speed up volume. - return self.randintgauss(4, 10, 6, 1) - -# create test cases for specific backends -scrypt_stdlib_test = _scrypt_test.create_backend_case("stdlib") -scrypt_scrypt_test = _scrypt_test.create_backend_case("scrypt") -scrypt_builtin_test = _scrypt_test.create_backend_case("builtin") - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_hosts.py b/.venv/Lib/site-packages/passlib/tests/test_hosts.py deleted file mode 100644 index cbf93ab..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_hosts.py +++ /dev/null @@ -1,97 +0,0 @@ -"""test passlib.hosts""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib import hosts, hash as hashmod -from passlib.utils import unix_crypt_schemes -from passlib.tests.utils import TestCase -# module - -#============================================================================= -# test predefined app contexts -#============================================================================= -class HostsTest(TestCase): - """perform general tests to make sure contexts work""" - # NOTE: these tests are not really comprehensive, - # since they would do little but duplicate - # the presets in apps.py - # - # they mainly try to ensure no typos - # or dynamic behavior foul-ups. - - def check_unix_disabled(self, ctx): - for hash in [ - "", - "!", - "*", - "!$1$TXl/FX/U$BZge.lr.ux6ekjEjxmzwz0", - ]: - self.assertEqual(ctx.identify(hash), 'unix_disabled') - self.assertFalse(ctx.verify('test', hash)) - - def test_linux_context(self): - ctx = hosts.linux_context - for hash in [ - ('$6$rounds=41128$VoQLvDjkaZ6L6BIE$4pt.1Ll1XdDYduEwEYPCMOBiR6W6' - 'znsyUEoNlcVXpv2gKKIbQolgmTGe6uEEVJ7azUxuc8Tf7zV9SD2z7Ij751'), - ('$5$rounds=31817$iZGmlyBQ99JSB5n6$p4E.pdPBWx19OajgjLRiOW0itGny' - 'xDGgMlDcOsfaI17'), - '$1$TXl/FX/U$BZge.lr.ux6ekjEjxmzwz0', - 'kAJJz.Rwp0A/I', - ]: - self.assertTrue(ctx.verify("test", hash)) - self.check_unix_disabled(ctx) - - def test_bsd_contexts(self): - for ctx in [ - hosts.freebsd_context, - hosts.openbsd_context, - hosts.netbsd_context, - ]: - for hash in [ - '$1$TXl/FX/U$BZge.lr.ux6ekjEjxmzwz0', - 'kAJJz.Rwp0A/I', - ]: - self.assertTrue(ctx.verify("test", hash)) - h1 = "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS" - if hashmod.bcrypt.has_backend(): - self.assertTrue(ctx.verify("test", h1)) - else: - self.assertEqual(ctx.identify(h1), "bcrypt") - self.check_unix_disabled(ctx) - - def test_host_context(self): - ctx = getattr(hosts, "host_context", None) - if not ctx: - return self.skipTest("host_context not available on this platform") - - # validate schemes is non-empty, - # and contains unix_disabled + at least one real scheme - schemes = list(ctx.schemes()) - self.assertTrue(schemes, "appears to be unix system, but no known schemes supported by crypt") - self.assertTrue('unix_disabled' in schemes) - schemes.remove("unix_disabled") - self.assertTrue(schemes, "should have schemes beside fallback scheme") - self.assertTrue(set(unix_crypt_schemes).issuperset(schemes)) - - # check for hash support - self.check_unix_disabled(ctx) - for scheme, hash in [ - ("sha512_crypt", ('$6$rounds=41128$VoQLvDjkaZ6L6BIE$4pt.1Ll1XdDYduEwEYPCMOBiR6W6' - 'znsyUEoNlcVXpv2gKKIbQolgmTGe6uEEVJ7azUxuc8Tf7zV9SD2z7Ij751')), - ("sha256_crypt", ('$5$rounds=31817$iZGmlyBQ99JSB5n6$p4E.pdPBWx19OajgjLRiOW0itGny' - 'xDGgMlDcOsfaI17')), - ("md5_crypt", '$1$TXl/FX/U$BZge.lr.ux6ekjEjxmzwz0'), - ("des_crypt", 'kAJJz.Rwp0A/I'), - ]: - if scheme in schemes: - self.assertTrue(ctx.verify("test", hash)) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_pwd.py b/.venv/Lib/site-packages/passlib/tests/test_pwd.py deleted file mode 100644 index 2c983cd..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_pwd.py +++ /dev/null @@ -1,205 +0,0 @@ -"""passlib.tests -- tests for passlib.pwd""" -#============================================================================= -# imports -#============================================================================= -# core -import itertools -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.tests.utils import TestCase -# local -__all__ = [ - "UtilsTest", - "GenerateTest", - "StrengthTest", -] - -#============================================================================= -# -#============================================================================= -class UtilsTest(TestCase): - """test internal utilities""" - descriptionPrefix = "passlib.pwd" - - def test_self_info_rate(self): - """_self_info_rate()""" - from passlib.pwd import _self_info_rate - - self.assertEqual(_self_info_rate(""), 0) - - self.assertEqual(_self_info_rate("a" * 8), 0) - - self.assertEqual(_self_info_rate("ab"), 1) - self.assertEqual(_self_info_rate("ab" * 8), 1) - - self.assertEqual(_self_info_rate("abcd"), 2) - self.assertEqual(_self_info_rate("abcd" * 8), 2) - self.assertAlmostEqual(_self_info_rate("abcdaaaa"), 1.5488, places=4) - - # def test_total_self_info(self): - # """_total_self_info()""" - # from passlib.pwd import _total_self_info - # - # self.assertEqual(_total_self_info(""), 0) - # - # self.assertEqual(_total_self_info("a" * 8), 0) - # - # self.assertEqual(_total_self_info("ab"), 2) - # self.assertEqual(_total_self_info("ab" * 8), 16) - # - # self.assertEqual(_total_self_info("abcd"), 8) - # self.assertEqual(_total_self_info("abcd" * 8), 64) - # self.assertAlmostEqual(_total_self_info("abcdaaaa"), 12.3904, places=4) - -#============================================================================= -# word generation -#============================================================================= - -# import subject -from passlib.pwd import genword, default_charsets -ascii_62 = default_charsets['ascii_62'] -hex = default_charsets['hex'] - -class WordGeneratorTest(TestCase): - """test generation routines""" - descriptionPrefix = "passlib.pwd.genword()" - - def setUp(self): - super(WordGeneratorTest, self).setUp() - - # patch some RNG references so they're reproducible. - from passlib.pwd import SequenceGenerator - self.patchAttr(SequenceGenerator, "rng", - self.getRandom("pwd generator")) - - def assertResultContents(self, results, count, chars, unique=True): - """check result list matches expected count & charset""" - self.assertEqual(len(results), count) - if unique: - if unique is True: - unique = count - self.assertEqual(len(set(results)), unique) - self.assertEqual(set("".join(results)), set(chars)) - - def test_general(self): - """general behavior""" - - # basic usage - result = genword() - self.assertEqual(len(result), 9) - - # malformed keyword should have useful error. - self.assertRaisesRegex(TypeError, "(?i)unexpected keyword.*badkwd", genword, badkwd=True) - - def test_returns(self): - """'returns' keyword""" - # returns=int option - results = genword(returns=5000) - self.assertResultContents(results, 5000, ascii_62) - - # returns=iter option - gen = genword(returns=iter) - results = [next(gen) for _ in range(5000)] - self.assertResultContents(results, 5000, ascii_62) - - # invalid returns option - self.assertRaises(TypeError, genword, returns='invalid-type') - - def test_charset(self): - """'charset' & 'chars' options""" - # charset option - results = genword(charset="hex", returns=5000) - self.assertResultContents(results, 5000, hex) - - # chars option - # there are 3**3=27 possible combinations - results = genword(length=3, chars="abc", returns=5000) - self.assertResultContents(results, 5000, "abc", unique=27) - - # chars + charset - self.assertRaises(TypeError, genword, chars='abc', charset='hex') - - # TODO: test rng option - -#============================================================================= -# phrase generation -#============================================================================= - -# import subject -from passlib.pwd import genphrase -simple_words = ["alpha", "beta", "gamma"] - -class PhraseGeneratorTest(TestCase): - """test generation routines""" - descriptionPrefix = "passlib.pwd.genphrase()" - - def assertResultContents(self, results, count, words, unique=True, sep=" "): - """check result list matches expected count & charset""" - self.assertEqual(len(results), count) - if unique: - if unique is True: - unique = count - self.assertEqual(len(set(results)), unique) - out = set(itertools.chain.from_iterable(elem.split(sep) for elem in results)) - self.assertEqual(out, set(words)) - - def test_general(self): - """general behavior""" - - # basic usage - result = genphrase() - self.assertEqual(len(result.split(" ")), 4) # 48 / log(7776, 2) ~= 3.7 -> 4 - - # malformed keyword should have useful error. - self.assertRaisesRegex(TypeError, "(?i)unexpected keyword.*badkwd", genphrase, badkwd=True) - - def test_entropy(self): - """'length' & 'entropy' keywords""" - - # custom entropy - result = genphrase(entropy=70) - self.assertEqual(len(result.split(" ")), 6) # 70 / log(7776, 2) ~= 5.4 -> 6 - - # custom length - result = genphrase(length=3) - self.assertEqual(len(result.split(" ")), 3) - - # custom length < entropy - result = genphrase(length=3, entropy=48) - self.assertEqual(len(result.split(" ")), 4) - - # custom length > entropy - result = genphrase(length=4, entropy=12) - self.assertEqual(len(result.split(" ")), 4) - - def test_returns(self): - """'returns' keyword""" - # returns=int option - results = genphrase(returns=1000, words=simple_words) - self.assertResultContents(results, 1000, simple_words) - - # returns=iter option - gen = genphrase(returns=iter, words=simple_words) - results = [next(gen) for _ in range(1000)] - self.assertResultContents(results, 1000, simple_words) - - # invalid returns option - self.assertRaises(TypeError, genphrase, returns='invalid-type') - - def test_wordset(self): - """'wordset' & 'words' options""" - # wordset option - results = genphrase(words=simple_words, returns=5000) - self.assertResultContents(results, 5000, simple_words) - - # words option - results = genphrase(length=3, words=simple_words, returns=5000) - self.assertResultContents(results, 5000, simple_words, unique=3**3) - - # words + wordset - self.assertRaises(TypeError, genphrase, words=simple_words, wordset='bip39') - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_registry.py b/.venv/Lib/site-packages/passlib/tests/test_registry.py deleted file mode 100644 index 8cec48d..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_registry.py +++ /dev/null @@ -1,228 +0,0 @@ -"""tests for passlib.hash -- (c) Assurance Technologies 2003-2009""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -from logging import getLogger -import warnings -import sys -# site -# pkg -from passlib import hash, registry, exc -from passlib.registry import register_crypt_handler, register_crypt_handler_path, \ - get_crypt_handler, list_crypt_handlers, _unload_handler_name as unload_handler_name -import passlib.utils.handlers as uh -from passlib.tests.utils import TestCase -# module -log = getLogger(__name__) - -#============================================================================= -# dummy handlers -# -# NOTE: these are defined outside of test case -# since they're used by test_register_crypt_handler_path(), -# which needs them to be available as module globals. -#============================================================================= -class dummy_0(uh.StaticHandler): - name = "dummy_0" - -class alt_dummy_0(uh.StaticHandler): - name = "dummy_0" - -dummy_x = 1 - -#============================================================================= -# test registry -#============================================================================= -class RegistryTest(TestCase): - - descriptionPrefix = "passlib.registry" - - def setUp(self): - super(RegistryTest, self).setUp() - - # backup registry state & restore it after test. - locations = dict(registry._locations) - handlers = dict(registry._handlers) - def restore(): - registry._locations.clear() - registry._locations.update(locations) - registry._handlers.clear() - registry._handlers.update(handlers) - self.addCleanup(restore) - - def test_hash_proxy(self): - """test passlib.hash proxy object""" - # check dir works - dir(hash) - - # check repr works - repr(hash) - - # check non-existent attrs raise error - self.assertRaises(AttributeError, getattr, hash, 'fooey') - - # GAE tries to set __loader__, - # make sure that doesn't call register_crypt_handler. - old = getattr(hash, "__loader__", None) - test = object() - hash.__loader__ = test - self.assertIs(hash.__loader__, test) - if old is None: - del hash.__loader__ - self.assertFalse(hasattr(hash, "__loader__")) - else: - hash.__loader__ = old - self.assertIs(hash.__loader__, old) - - # check storing attr calls register_crypt_handler - class dummy_1(uh.StaticHandler): - name = "dummy_1" - hash.dummy_1 = dummy_1 - self.assertIs(get_crypt_handler("dummy_1"), dummy_1) - - # check storing under wrong name results in error - self.assertRaises(ValueError, setattr, hash, "dummy_1x", dummy_1) - - def test_register_crypt_handler_path(self): - """test register_crypt_handler_path()""" - # NOTE: this messes w/ internals of registry, shouldn't be used publically. - paths = registry._locations - - # check namespace is clear - self.assertTrue('dummy_0' not in paths) - self.assertFalse(hasattr(hash, 'dummy_0')) - - # check invalid names are rejected - self.assertRaises(ValueError, register_crypt_handler_path, - "dummy_0", ".test_registry") - self.assertRaises(ValueError, register_crypt_handler_path, - "dummy_0", __name__ + ":dummy_0:xxx") - self.assertRaises(ValueError, register_crypt_handler_path, - "dummy_0", __name__ + ":dummy_0.xxx") - - # try lazy load - register_crypt_handler_path('dummy_0', __name__) - self.assertTrue('dummy_0' in list_crypt_handlers()) - self.assertTrue('dummy_0' not in list_crypt_handlers(loaded_only=True)) - self.assertIs(hash.dummy_0, dummy_0) - self.assertTrue('dummy_0' in list_crypt_handlers(loaded_only=True)) - unload_handler_name('dummy_0') - - # try lazy load w/ alt - register_crypt_handler_path('dummy_0', __name__ + ':alt_dummy_0') - self.assertIs(hash.dummy_0, alt_dummy_0) - unload_handler_name('dummy_0') - - # check lazy load w/ wrong type fails - register_crypt_handler_path('dummy_x', __name__) - self.assertRaises(TypeError, get_crypt_handler, 'dummy_x') - - # check lazy load w/ wrong name fails - register_crypt_handler_path('alt_dummy_0', __name__) - self.assertRaises(ValueError, get_crypt_handler, "alt_dummy_0") - unload_handler_name("alt_dummy_0") - - # TODO: check lazy load which calls register_crypt_handler (warning should be issued) - sys.modules.pop("passlib.tests._test_bad_register", None) - register_crypt_handler_path("dummy_bad", "passlib.tests._test_bad_register") - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "xxxxxxxxxx", DeprecationWarning) - h = get_crypt_handler("dummy_bad") - from passlib.tests import _test_bad_register as tbr - self.assertIs(h, tbr.alt_dummy_bad) - - def test_register_crypt_handler(self): - """test register_crypt_handler()""" - - self.assertRaises(TypeError, register_crypt_handler, {}) - - self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name=None))) - self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="AB_CD"))) - self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="ab-cd"))) - self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="ab__cd"))) - self.assertRaises(ValueError, register_crypt_handler, type('x', (uh.StaticHandler,), dict(name="default"))) - - class dummy_1(uh.StaticHandler): - name = "dummy_1" - - class dummy_1b(uh.StaticHandler): - name = "dummy_1" - - self.assertTrue('dummy_1' not in list_crypt_handlers()) - - register_crypt_handler(dummy_1) - register_crypt_handler(dummy_1) - self.assertIs(get_crypt_handler("dummy_1"), dummy_1) - - self.assertRaises(KeyError, register_crypt_handler, dummy_1b) - self.assertIs(get_crypt_handler("dummy_1"), dummy_1) - - register_crypt_handler(dummy_1b, force=True) - self.assertIs(get_crypt_handler("dummy_1"), dummy_1b) - - self.assertTrue('dummy_1' in list_crypt_handlers()) - - def test_get_crypt_handler(self): - """test get_crypt_handler()""" - - class dummy_1(uh.StaticHandler): - name = "dummy_1" - - # without available handler - self.assertRaises(KeyError, get_crypt_handler, "dummy_1") - self.assertIs(get_crypt_handler("dummy_1", None), None) - - # already loaded handler - register_crypt_handler(dummy_1) - self.assertIs(get_crypt_handler("dummy_1"), dummy_1) - - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "handler names should be lower-case, and use underscores instead of hyphens:.*", UserWarning) - - # already loaded handler, using incorrect name - self.assertIs(get_crypt_handler("DUMMY-1"), dummy_1) - - # lazy load of unloaded handler, using incorrect name - register_crypt_handler_path('dummy_0', __name__) - self.assertIs(get_crypt_handler("DUMMY-0"), dummy_0) - - # check system & private names aren't returned - from passlib import hash - hash.__dict__["_fake"] = "dummy" - for name in ["_fake", "__package__"]: - self.assertRaises(KeyError, get_crypt_handler, name) - self.assertIs(get_crypt_handler(name, None), None) - - def test_list_crypt_handlers(self): - """test list_crypt_handlers()""" - from passlib.registry import list_crypt_handlers - - # check system & private names aren't returned - hash.__dict__["_fake"] = "dummy" - for name in list_crypt_handlers(): - self.assertFalse(name.startswith("_"), "%r: " % name) - unload_handler_name("_fake") - - def test_handlers(self): - """verify we have tests for all builtin handlers""" - from passlib.registry import list_crypt_handlers - from passlib.tests.test_handlers import get_handler_case, conditionally_available_hashes - for name in list_crypt_handlers(): - # skip some wrappers that don't need independant testing - if name.startswith("ldap_") and name[5:] in list_crypt_handlers(): - continue - if name in ["roundup_plaintext"]: - continue - # check the remaining ones all have a handler - try: - self.assertTrue(get_handler_case(name)) - except exc.MissingBackendError: - if name in conditionally_available_hashes: # expected to fail on some setups - continue - raise - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_totp.py b/.venv/Lib/site-packages/passlib/tests/test_totp.py deleted file mode 100644 index 604d2e9..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_totp.py +++ /dev/null @@ -1,1604 +0,0 @@ -"""passlib.tests -- test passlib.totp""" -#============================================================================= -# imports -#============================================================================= -# core -import datetime -from functools import partial -import logging; log = logging.getLogger(__name__) -import sys -import time as _time -# site -# pkg -from passlib import exc -from passlib.utils.compat import unicode, u -from passlib.tests.utils import TestCase, time_call -# subject -from passlib import totp as totp_module -from passlib.totp import TOTP, AppWallet, AES_SUPPORT -# local -__all__ = [ - "EngineTest", -] - -#============================================================================= -# helpers -#============================================================================= - -# XXX: python 3 changed what error base64.b16decode() throws, from TypeError to base64.Error(). -# it wasn't until 3.3 that base32decode() also got changed. -# really should normalize this in the code to a single BinaryDecodeError, -# predicting this cross-version is getting unmanagable. -Base32DecodeError = Base16DecodeError = TypeError -if sys.version_info >= (3,0): - from binascii import Error as Base16DecodeError -if sys.version_info >= (3,3): - from binascii import Error as Base32DecodeError - -PASS1 = "abcdef" -PASS2 = b"\x00\xFF" -KEY1 = '4AOGGDBBQSYHNTUZ' -KEY1_RAW = b'\xe0\x1cc\x0c!\x84\xb0v\xce\x99' -KEY2_RAW = b'\xee]\xcb9\x870\x06 D\xc8y/\xa54&\xe4\x9c\x13\xc2\x18' -KEY3 = 'S3JDVB7QD2R7JPXX' # used in docstrings -KEY4 = 'JBSWY3DPEHPK3PXP' # from google keyuri spec -KEY4_RAW = b'Hello!\xde\xad\xbe\xef' - -# NOTE: for randtime() below, -# * want at least 7 bits on fractional side, to test fractional times to at least 0.01s precision -# * want at least 32 bits on integer side, to test for 32-bit epoch issues. -# most systems *should* have 53 bit mantissa, leaving plenty of room on both ends, -# so using (1<<37) as scale, to allocate 16 bits on fractional side, but generate reasonable # of > 1<<32 times. -# sanity check that we're above 44 ensures minimum requirements (44 - 37 int = 7 frac) -assert sys.float_info.radix == 2, "unexpected float_info.radix" -assert sys.float_info.mant_dig >= 44, "double precision unexpectedly small" - -def _get_max_time_t(): - """ - helper to calc max_time_t constant (see below) - """ - value = 1 << 30 # even for 32 bit systems will handle this - year = 0 - while True: - next_value = value << 1 - try: - next_year = datetime.datetime.utcfromtimestamp(next_value-1).year - except (ValueError, OSError, OverflowError): - # utcfromtimestamp() may throw any of the following: - # - # * year out of range for datetime: - # py < 3.6 throws ValueError. - # (py 3.6.0 returns odd value instead, see workaround below) - # - # * int out of range for host's gmtime/localtime: - # py2 throws ValueError, py3 throws OSError. - # - # * int out of range for host's time_t: - # py2 throws ValueError, py3 throws OverflowError. - # - break - - # Workaround for python 3.6.0 issue -- - # Instead of throwing ValueError if year out of range for datetime, - # Python 3.6 will do some weird behavior that masks high bits - # e.g. (1<<40) -> year 36812, but (1<<41) -> year 6118. - # (Appears to be bug http://bugs.python.org/issue29100) - # This check stops at largest non-wrapping bit size. - if next_year < year: - break - - value = next_value - - # 'value-1' is maximum. - value -= 1 - - # check for crazy case where we're beyond what datetime supports - # (caused by bug 29100 again). compare to max value that datetime - # module supports -- datetime.datetime(9999, 12, 31, 23, 59, 59, 999999) - max_datetime_timestamp = 253402318800 - return min(value, max_datetime_timestamp) - -#: Rough approximation of max value acceptable by hosts's time_t. -#: This is frequently ~2**37 on 64 bit, and ~2**31 on 32 bit systems. -max_time_t = _get_max_time_t() - -def to_b32_size(raw_size): - return (raw_size * 8 + 4) // 5 - -#============================================================================= -# wallet -#============================================================================= -class AppWalletTest(TestCase): - descriptionPrefix = "passlib.totp.AppWallet" - - #============================================================================= - # constructor - #============================================================================= - - def test_secrets_types(self): - """constructor -- 'secrets' param -- input types""" - - # no secrets - wallet = AppWallet() - self.assertEqual(wallet._secrets, {}) - self.assertFalse(wallet.has_secrets) - - # dict - ref = {"1": b"aaa", "2": b"bbb"} - wallet = AppWallet(ref) - self.assertEqual(wallet._secrets, ref) - self.assertTrue(wallet.has_secrets) - - # # list - # wallet = AppWallet(list(ref.items())) - # self.assertEqual(wallet._secrets, ref) - - # # iter - # wallet = AppWallet(iter(ref.items())) - # self.assertEqual(wallet._secrets, ref) - - # "tag:value" string - wallet = AppWallet("\n 1: aaa\n# comment\n \n2: bbb ") - self.assertEqual(wallet._secrets, ref) - - # ensure ":" allowed in secret - wallet = AppWallet("1: aaa: bbb \n# comment\n \n2: bbb ") - self.assertEqual(wallet._secrets, {"1": b"aaa: bbb", "2": b"bbb"}) - - # json dict - wallet = AppWallet('{"1":"aaa","2":"bbb"}') - self.assertEqual(wallet._secrets, ref) - - # # json list - # wallet = AppWallet('[["1","aaa"],["2","bbb"]]') - # self.assertEqual(wallet._secrets, ref) - - # invalid type - self.assertRaises(TypeError, AppWallet, 123) - - # invalid json obj - self.assertRaises(TypeError, AppWallet, "[123]") - - # # invalid list items - # self.assertRaises(ValueError, AppWallet, ["1", b"aaa"]) - - # forbid empty secret - self.assertRaises(ValueError, AppWallet, {"1": "aaa", "2": ""}) - - def test_secrets_tags(self): - """constructor -- 'secrets' param -- tag/value normalization""" - - # test reference - ref = {"1": b"aaa", "02": b"bbb", "C": b"ccc"} - wallet = AppWallet(ref) - self.assertEqual(wallet._secrets, ref) - - # accept unicode - wallet = AppWallet({u("1"): b"aaa", u("02"): b"bbb", u("C"): b"ccc"}) - self.assertEqual(wallet._secrets, ref) - - # normalize int tags - wallet = AppWallet({1: b"aaa", "02": b"bbb", "C": b"ccc"}) - self.assertEqual(wallet._secrets, ref) - - # forbid non-str/int tags - self.assertRaises(TypeError, AppWallet, {(1,): "aaa"}) - - # accept valid tags - wallet = AppWallet({"1-2_3.4": b"aaa"}) - - # forbid invalid tags - self.assertRaises(ValueError, AppWallet, {"-abc": "aaa"}) - self.assertRaises(ValueError, AppWallet, {"ab*$": "aaa"}) - - # coerce value to bytes - wallet = AppWallet({"1": u("aaa"), "02": "bbb", "C": b"ccc"}) - self.assertEqual(wallet._secrets, ref) - - # forbid invalid value types - self.assertRaises(TypeError, AppWallet, {"1": 123}) - self.assertRaises(TypeError, AppWallet, {"1": None}) - self.assertRaises(TypeError, AppWallet, {"1": []}) - - # TODO: test secrets_path - - def test_default_tag(self): - """constructor -- 'default_tag' param""" - - # should sort numerically - wallet = AppWallet({"1": "one", "02": "two"}) - self.assertEqual(wallet.default_tag, "02") - self.assertEqual(wallet.get_secret(wallet.default_tag), b"two") - - # should sort alphabetically if non-digit present - wallet = AppWallet({"1": "one", "02": "two", "A": "aaa"}) - self.assertEqual(wallet.default_tag, "A") - self.assertEqual(wallet.get_secret(wallet.default_tag), b"aaa") - - # should use honor custom tag - wallet = AppWallet({"1": "one", "02": "two", "A": "aaa"}, default_tag="1") - self.assertEqual(wallet.default_tag, "1") - self.assertEqual(wallet.get_secret(wallet.default_tag), b"one") - - # throw error on unknown value - self.assertRaises(KeyError, AppWallet, {"1": "one", "02": "two", "A": "aaa"}, - default_tag="B") - - # should be empty - wallet = AppWallet() - self.assertEqual(wallet.default_tag, None) - self.assertRaises(KeyError, wallet.get_secret, None) - - # TODO: test 'cost' param - - #============================================================================= - # encrypt_key() & decrypt_key() helpers - #============================================================================= - def require_aes_support(self, canary=None): - if AES_SUPPORT: - canary and canary() - else: - canary and self.assertRaises(RuntimeError, canary) - raise self.skipTest("'cryptography' package not installed") - - def test_decrypt_key(self): - """.decrypt_key()""" - - wallet = AppWallet({"1": PASS1, "2": PASS2}) - - # check for support - CIPHER1 = dict(v=1, c=13, s='6D7N7W53O7HHS37NLUFQ', - k='MHCTEGSNPFN5CGBJ', t='1') - self.require_aes_support(canary=partial(wallet.decrypt_key, CIPHER1)) - - # reference key - self.assertEqual(wallet.decrypt_key(CIPHER1)[0], KEY1_RAW) - - # different salt used to encrypt same raw key - CIPHER2 = dict(v=1, c=13, s='SPZJ54Y6IPUD2BYA4C6A', - k='ZGDXXTVQOWYLC2AU', t='1') - self.assertEqual(wallet.decrypt_key(CIPHER2)[0], KEY1_RAW) - - # different sized key, password, and cost - CIPHER3 = dict(v=1, c=8, s='FCCTARTIJWE7CPQHUDKA', - k='D2DRS32YESGHHINWFFCELKN7Z6NAHM4M', t='2') - self.assertEqual(wallet.decrypt_key(CIPHER3)[0], KEY2_RAW) - - # wrong password should silently result in wrong key - temp = CIPHER1.copy() - temp.update(t='2') - self.assertEqual(wallet.decrypt_key(temp)[0], b'\xafD6.F7\xeb\x19\x05Q') - - # missing tag should throw error - temp = CIPHER1.copy() - temp.update(t='3') - self.assertRaises(KeyError, wallet.decrypt_key, temp) - - # unknown version should throw error - temp = CIPHER1.copy() - temp.update(v=999) - self.assertRaises(ValueError, wallet.decrypt_key, temp) - - def test_decrypt_key_needs_recrypt(self): - """.decrypt_key() -- needs_recrypt flag""" - self.require_aes_support() - - wallet = AppWallet({"1": PASS1, "2": PASS2}, encrypt_cost=13) - - # ref should be accepted - ref = dict(v=1, c=13, s='AAAA', k='AAAA', t='2') - self.assertFalse(wallet.decrypt_key(ref)[1]) - - # wrong cost - temp = ref.copy() - temp.update(c=8) - self.assertTrue(wallet.decrypt_key(temp)[1]) - - # wrong tag - temp = ref.copy() - temp.update(t="1") - self.assertTrue(wallet.decrypt_key(temp)[1]) - - # XXX: should this check salt_size? - - def assertSaneResult(self, result, wallet, key, tag="1", - needs_recrypt=False): - """check encrypt_key() result has expected format""" - - self.assertEqual(set(result), set(["v", "t", "c", "s", "k"])) - - self.assertEqual(result['v'], 1) - self.assertEqual(result['t'], tag) - self.assertEqual(result['c'], wallet.encrypt_cost) - - self.assertEqual(len(result['s']), to_b32_size(wallet.salt_size)) - self.assertEqual(len(result['k']), to_b32_size(len(key))) - - result_key, result_needs_recrypt = wallet.decrypt_key(result) - self.assertEqual(result_key, key) - self.assertEqual(result_needs_recrypt, needs_recrypt) - - def test_encrypt_key(self): - """.encrypt_key()""" - - # check for support - wallet = AppWallet({"1": PASS1}, encrypt_cost=5) - self.require_aes_support(canary=partial(wallet.encrypt_key, KEY1_RAW)) - - # basic behavior - result = wallet.encrypt_key(KEY1_RAW) - self.assertSaneResult(result, wallet, KEY1_RAW) - - # creates new salt each time - other = wallet.encrypt_key(KEY1_RAW) - self.assertSaneResult(result, wallet, KEY1_RAW) - self.assertNotEqual(other['s'], result['s']) - self.assertNotEqual(other['k'], result['k']) - - # honors custom cost - wallet2 = AppWallet({"1": PASS1}, encrypt_cost=6) - result = wallet2.encrypt_key(KEY1_RAW) - self.assertSaneResult(result, wallet2, KEY1_RAW) - - # honors default tag - wallet2 = AppWallet({"1": PASS1, "2": PASS2}) - result = wallet2.encrypt_key(KEY1_RAW) - self.assertSaneResult(result, wallet2, KEY1_RAW, tag="2") - - # honor salt size - wallet2 = AppWallet({"1": PASS1}) - wallet2.salt_size = 64 - result = wallet2.encrypt_key(KEY1_RAW) - self.assertSaneResult(result, wallet2, KEY1_RAW) - - # larger key - result = wallet.encrypt_key(KEY2_RAW) - self.assertSaneResult(result, wallet, KEY2_RAW) - - # border case: empty key - # XXX: might want to allow this, but documenting behavior for now - self.assertRaises(ValueError, wallet.encrypt_key, b"") - - def test_encrypt_cost_timing(self): - """verify cost parameter via timing""" - self.require_aes_support() - - # time default cost - wallet = AppWallet({"1": "aaa"}) - wallet.encrypt_cost -= 2 - delta, _ = time_call(partial(wallet.encrypt_key, KEY1_RAW), maxtime=0) - - # this should take (2**3=8) times as long - wallet.encrypt_cost += 3 - delta2, _ = time_call(partial(wallet.encrypt_key, KEY1_RAW), maxtime=0) - - # TODO: rework timing test here to inject mock pbkdf2_hmac() function instead; - # and test that it's being invoked w/ proper options. - self.assertAlmostEqual(delta2, delta*8, delta=(delta*8)*0.5) - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# common OTP code -#============================================================================= - -#: used as base value for RFC test vector keys -RFC_KEY_BYTES_20 = "12345678901234567890".encode("ascii") -RFC_KEY_BYTES_32 = (RFC_KEY_BYTES_20*2)[:32] -RFC_KEY_BYTES_64 = (RFC_KEY_BYTES_20*4)[:64] - -# TODO: this class is separate from TotpTest due to historical issue, -# when there was a base class, and a separate HOTP class. -# these test case classes should probably be combined. -class TotpTest(TestCase): - """ - common code shared by TotpTest & HotpTest - """ - #============================================================================= - # class attrs - #============================================================================= - - descriptionPrefix = "passlib.totp.TOTP" - - #============================================================================= - # setup - #============================================================================= - def setUp(self): - super(TotpTest, self).setUp() - - # clear norm_hash_name() cache so 'unknown hash' warnings get emitted each time - from passlib.crypto.digest import lookup_hash - lookup_hash.clear_cache() - - # monkeypatch module's rng to be deterministic - self.patchAttr(totp_module, "rng", self.getRandom()) - - #============================================================================= - # general helpers - #============================================================================= - def randtime(self): - """ - helper to generate random epoch time - :returns float: epoch time - """ - return self.getRandom().random() * max_time_t - - def randotp(self, cls=None, **kwds): - """ - helper which generates a random TOTP instance. - """ - rng = self.getRandom() - if "key" not in kwds: - kwds['new'] = True - kwds.setdefault("digits", rng.randint(6, 10)) - kwds.setdefault("alg", rng.choice(["sha1", "sha256", "sha512"])) - kwds.setdefault("period", rng.randint(10, 120)) - return (cls or TOTP)(**kwds) - - def test_randotp(self): - """ - internal test -- randotp() - """ - otp1 = self.randotp() - otp2 = self.randotp() - - self.assertNotEqual(otp1.key, otp2.key, "key not randomized:") - - # NOTE: has (1/5)**10 odds of failure - for _ in range(10): - if otp1.digits != otp2.digits: - break - otp2 = self.randotp() - else: - self.fail("digits not randomized") - - # NOTE: has (1/3)**10 odds of failure - for _ in range(10): - if otp1.alg != otp2.alg: - break - otp2 = self.randotp() - else: - self.fail("alg not randomized") - - #============================================================================= - # reference vector helpers - #============================================================================= - - #: default options used by test vectors (unless otherwise stated) - vector_defaults = dict(format="base32", alg="sha1", period=30, digits=8) - - #: various TOTP test vectors, - #: each element in list has format [options, (time, token <, int(expires)>), ...] - vectors = [ - - #------------------------------------------------------------------------- - # passlib test vectors - #------------------------------------------------------------------------- - - # 10 byte key, 6 digits - [dict(key="ACDEFGHJKL234567", digits=6), - # test fencepost to make sure we're rounding right - (1412873399, '221105'), # == 29 mod 30 - (1412873400, '178491'), # == 0 mod 30 - (1412873401, '178491'), # == 1 mod 30 - (1412873429, '178491'), # == 29 mod 30 - (1412873430, '915114'), # == 0 mod 30 - ], - - # 10 byte key, 8 digits - [dict(key="ACDEFGHJKL234567", digits=8), - # should be same as 6 digits (above), but w/ 2 more digits on left side of token. - (1412873399, '20221105'), # == 29 mod 30 - (1412873400, '86178491'), # == 0 mod 30 - (1412873401, '86178491'), # == 1 mod 30 - (1412873429, '86178491'), # == 29 mod 30 - (1412873430, '03915114'), # == 0 mod 30 - ], - - # sanity check on key used in docstrings - [dict(key="S3JD-VB7Q-D2R7-JPXX", digits=6), - (1419622709, '000492'), - (1419622739, '897212'), - ], - - #------------------------------------------------------------------------- - # reference vectors taken from http://tools.ietf.org/html/rfc6238, appendix B - # NOTE: while appendix B states same key used for all tests, the reference - # code in the appendix repeats the key up to the alg's block size, - # and uses *that* as the secret... so that's what we're doing here. - #------------------------------------------------------------------------- - - # sha1 test vectors - [dict(key=RFC_KEY_BYTES_20, format="raw", alg="sha1"), - (59, '94287082'), - (1111111109, '07081804'), - (1111111111, '14050471'), - (1234567890, '89005924'), - (2000000000, '69279037'), - (20000000000, '65353130'), - ], - - # sha256 test vectors - [dict(key=RFC_KEY_BYTES_32, format="raw", alg="sha256"), - (59, '46119246'), - (1111111109, '68084774'), - (1111111111, '67062674'), - (1234567890, '91819424'), - (2000000000, '90698825'), - (20000000000, '77737706'), - ], - - # sha512 test vectors - [dict(key=RFC_KEY_BYTES_64, format="raw", alg="sha512"), - (59, '90693936'), - (1111111109, '25091201'), - (1111111111, '99943326'), - (1234567890, '93441116'), - (2000000000, '38618901'), - (20000000000, '47863826'), - ], - - #------------------------------------------------------------------------- - # other test vectors - #------------------------------------------------------------------------- - - # generated at http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript - [dict(key="JBSWY3DPEHPK3PXP", digits=6), (1409192430, '727248'), (1419890990, '122419')], - [dict(key="JBSWY3DPEHPK3PXP", digits=9, period=41), (1419891152, '662331049')], - - # found in https://github.com/eloquent/otis/blob/develop/test/suite/Totp/Value/TotpValueGeneratorTest.php, line 45 - [dict(key=RFC_KEY_BYTES_20, format="raw", period=60), (1111111111, '19360094')], - [dict(key=RFC_KEY_BYTES_32, format="raw", alg="sha256", period=60), (1111111111, '40857319')], - [dict(key=RFC_KEY_BYTES_64, format="raw", alg="sha512", period=60), (1111111111, '37023009')], - - ] - - def iter_test_vectors(self): - """ - helper to iterate over test vectors. - yields ``(totp, time, token, expires, prefix)`` tuples. - """ - from passlib.totp import TOTP - for row in self.vectors: - kwds = self.vector_defaults.copy() - kwds.update(row[0]) - for entry in row[1:]: - if len(entry) == 3: - time, token, expires = entry - else: - time, token = entry - expires = None - # NOTE: not re-using otp between calls so that stateful methods - # (like .match) don't have problems. - log.debug("test vector: %r time=%r token=%r expires=%r", kwds, time, token, expires) - otp = TOTP(**kwds) - prefix = "alg=%r time=%r token=%r: " % (otp.alg, time, token) - yield otp, time, token, expires, prefix - - #============================================================================= - # constructor tests - #============================================================================= - def test_ctor_w_new(self): - """constructor -- 'new' parameter""" - - # exactly one of 'key' or 'new' is required - self.assertRaises(TypeError, TOTP) - self.assertRaises(TypeError, TOTP, key='4aoggdbbqsyhntuz', new=True) - - # generates new key - otp = TOTP(new=True) - otp2 = TOTP(new=True) - self.assertNotEqual(otp.key, otp2.key) - - def test_ctor_w_size(self): - """constructor -- 'size' parameter""" - - # should default to digest size, per RFC - self.assertEqual(len(TOTP(new=True, alg="sha1").key), 20) - self.assertEqual(len(TOTP(new=True, alg="sha256").key), 32) - self.assertEqual(len(TOTP(new=True, alg="sha512").key), 64) - - # explicit key size - self.assertEqual(len(TOTP(new=True, size=10).key), 10) - self.assertEqual(len(TOTP(new=True, size=16).key), 16) - - # for new=True, maximum size enforced (based on alg) - self.assertRaises(ValueError, TOTP, new=True, size=21, alg="sha1") - - # for new=True, minimum size enforced - self.assertRaises(ValueError, TOTP, new=True, size=9) - - # for existing key, minimum size is only warned about - with self.assertWarningList([ - dict(category=exc.PasslibSecurityWarning, message_re=".*for security purposes, secret key must be.*") - ]): - _ = TOTP('0A'*9, 'hex') - - def test_ctor_w_key_and_format(self): - """constructor -- 'key' and 'format' parameters""" - - # handle base32 encoding (the default) - self.assertEqual(TOTP(KEY1).key, KEY1_RAW) - - # .. w/ lower case - self.assertEqual(TOTP(KEY1.lower()).key, KEY1_RAW) - - # .. w/ spaces (e.g. user-entered data) - self.assertEqual(TOTP(' 4aog gdbb qsyh ntuz ').key, KEY1_RAW) - - # .. w/ invalid char - self.assertRaises(Base32DecodeError, TOTP, 'ao!ggdbbqsyhntuz') - - # handle hex encoding - self.assertEqual(TOTP('e01c630c2184b076ce99', 'hex').key, KEY1_RAW) - - # .. w/ invalid char - self.assertRaises(Base16DecodeError, TOTP, 'X01c630c2184b076ce99', 'hex') - - # handle raw bytes - self.assertEqual(TOTP(KEY1_RAW, "raw").key, KEY1_RAW) - - def test_ctor_w_alg(self): - """constructor -- 'alg' parameter""" - - # normalize hash names - self.assertEqual(TOTP(KEY1, alg="SHA-256").alg, "sha256") - self.assertEqual(TOTP(KEY1, alg="SHA256").alg, "sha256") - - # invalid alg - self.assertRaises(ValueError, TOTP, KEY1, alg="SHA-333") - - def test_ctor_w_digits(self): - """constructor -- 'digits' parameter""" - self.assertRaises(ValueError, TOTP, KEY1, digits=5) - self.assertEqual(TOTP(KEY1, digits=6).digits, 6) # min value - self.assertEqual(TOTP(KEY1, digits=10).digits, 10) # max value - self.assertRaises(ValueError, TOTP, KEY1, digits=11) - - def test_ctor_w_period(self): - """constructor -- 'period' parameter""" - - # default - self.assertEqual(TOTP(KEY1).period, 30) - - # explicit value - self.assertEqual(TOTP(KEY1, period=63).period, 63) - - # reject wrong type - self.assertRaises(TypeError, TOTP, KEY1, period=1.5) - self.assertRaises(TypeError, TOTP, KEY1, period='abc') - - # reject non-positive values - self.assertRaises(ValueError, TOTP, KEY1, period=0) - self.assertRaises(ValueError, TOTP, KEY1, period=-1) - - def test_ctor_w_label(self): - """constructor -- 'label' parameter""" - self.assertEqual(TOTP(KEY1).label, None) - self.assertEqual(TOTP(KEY1, label="foo@bar").label, "foo@bar") - self.assertRaises(ValueError, TOTP, KEY1, label="foo:bar") - - def test_ctor_w_issuer(self): - """constructor -- 'issuer' parameter""" - self.assertEqual(TOTP(KEY1).issuer, None) - self.assertEqual(TOTP(KEY1, issuer="foo.com").issuer, "foo.com") - self.assertRaises(ValueError, TOTP, KEY1, issuer="foo.com:bar") - - #============================================================================= - # using() tests - #============================================================================= - - # TODO: test using() w/ 'digits', 'alg', 'issue', 'wallet', **wallet_kwds - - def test_using_w_period(self): - """using() -- 'period' parameter""" - - # default - self.assertEqual(TOTP(KEY1).period, 30) - - # explicit value - self.assertEqual(TOTP.using(period=63)(KEY1).period, 63) - - # reject wrong type - self.assertRaises(TypeError, TOTP.using, period=1.5) - self.assertRaises(TypeError, TOTP.using, period='abc') - - # reject non-positive values - self.assertRaises(ValueError, TOTP.using, period=0) - self.assertRaises(ValueError, TOTP.using, period=-1) - - def test_using_w_now(self): - """using -- 'now' parameter""" - - # NOTE: reading time w/ normalize_time() to make sure custom .now actually has effect. - - # default -- time.time - otp = self.randotp() - self.assertIs(otp.now, _time.time) - self.assertAlmostEqual(otp.normalize_time(None), int(_time.time())) - - # custom function - counter = [123.12] - def now(): - counter[0] += 1 - return counter[0] - otp = self.randotp(cls=TOTP.using(now=now)) - # NOTE: TOTP() constructor invokes this as part of test, using up counter values 124 & 125 - self.assertEqual(otp.normalize_time(None), 126) - self.assertEqual(otp.normalize_time(None), 127) - - # require callable - self.assertRaises(TypeError, TOTP.using, now=123) - - # require returns int/float - msg_re = r"now\(\) function must return non-negative" - self.assertRaisesRegex(AssertionError, msg_re, TOTP.using, now=lambda: 'abc') - - # require returns non-negative value - self.assertRaisesRegex(AssertionError, msg_re, TOTP.using, now=lambda: -1) - - #============================================================================= - # internal method tests - #============================================================================= - - def test_normalize_token_instance(self, otp=None): - """normalize_token() -- instance method""" - if otp is None: - otp = self.randotp(digits=7) - - # unicode & bytes - self.assertEqual(otp.normalize_token(u('1234567')), '1234567') - self.assertEqual(otp.normalize_token(b'1234567'), '1234567') - - # int - self.assertEqual(otp.normalize_token(1234567), '1234567') - - # int which needs 0 padding - self.assertEqual(otp.normalize_token(234567), '0234567') - - # reject wrong types (float, None) - self.assertRaises(TypeError, otp.normalize_token, 1234567.0) - self.assertRaises(TypeError, otp.normalize_token, None) - - # too few digits - self.assertRaises(exc.MalformedTokenError, otp.normalize_token, '123456') - - # too many digits - self.assertRaises(exc.MalformedTokenError, otp.normalize_token, '01234567') - self.assertRaises(exc.MalformedTokenError, otp.normalize_token, 12345678) - - def test_normalize_token_class(self): - """normalize_token() -- class method""" - self.test_normalize_token_instance(otp=TOTP.using(digits=7)) - - def test_normalize_time(self): - """normalize_time()""" - TotpFactory = TOTP.using() - otp = self.randotp(TotpFactory) - - for _ in range(10): - time = self.randtime() - tint = int(time) - - self.assertEqual(otp.normalize_time(time), tint) - self.assertEqual(otp.normalize_time(tint + 0.5), tint) - - self.assertEqual(otp.normalize_time(tint), tint) - - dt = datetime.datetime.utcfromtimestamp(time) - self.assertEqual(otp.normalize_time(dt), tint) - - orig = TotpFactory.now - try: - TotpFactory.now = staticmethod(lambda: time) - self.assertEqual(otp.normalize_time(None), tint) - finally: - TotpFactory.now = orig - - self.assertRaises(TypeError, otp.normalize_time, '1234') - - #============================================================================= - # key attr tests - #============================================================================= - - def test_key_attrs(self): - """pretty_key() and .key attributes""" - rng = self.getRandom() - - # test key attrs - otp = TOTP(KEY1_RAW, "raw") - self.assertEqual(otp.key, KEY1_RAW) - self.assertEqual(otp.hex_key, 'e01c630c2184b076ce99') - self.assertEqual(otp.base32_key, KEY1) - - # test pretty_key() - self.assertEqual(otp.pretty_key(), '4AOG-GDBB-QSYH-NTUZ') - self.assertEqual(otp.pretty_key(sep=" "), '4AOG GDBB QSYH NTUZ') - self.assertEqual(otp.pretty_key(sep=False), KEY1) - self.assertEqual(otp.pretty_key(format="hex"), 'e01c-630c-2184-b076-ce99') - - # quick fuzz test: make attr access works for random key & random size - otp = TOTP(new=True, size=rng.randint(10, 20)) - _ = otp.hex_key - _ = otp.base32_key - _ = otp.pretty_key() - - #============================================================================= - # generate() tests - #============================================================================= - def test_totp_token(self): - """generate() -- TotpToken() class""" - from passlib.totp import TOTP, TotpToken - - # test known set of values - otp = TOTP('s3jdvb7qd2r7jpxx') - result = otp.generate(1419622739) - self.assertIsInstance(result, TotpToken) - self.assertEqual(result.token, '897212') - self.assertEqual(result.counter, 47320757) - ##self.assertEqual(result.start_time, 1419622710) - self.assertEqual(result.expire_time, 1419622740) - self.assertEqual(result, ('897212', 1419622740)) - self.assertEqual(len(result), 2) - self.assertEqual(result[0], '897212') - self.assertEqual(result[1], 1419622740) - self.assertRaises(IndexError, result.__getitem__, -3) - self.assertRaises(IndexError, result.__getitem__, 2) - self.assertTrue(result) - - # time dependant bits... - otp.now = lambda : 1419622739.5 - self.assertEqual(result.remaining, 0.5) - self.assertTrue(result.valid) - - otp.now = lambda : 1419622741 - self.assertEqual(result.remaining, 0) - self.assertFalse(result.valid) - - # same time -- shouldn't return same object, but should be equal - result2 = otp.generate(1419622739) - self.assertIsNot(result2, result) - self.assertEqual(result2, result) - - # diff time in period -- shouldn't return same object, but should be equal - result3 = otp.generate(1419622711) - self.assertIsNot(result3, result) - self.assertEqual(result3, result) - - # shouldn't be equal - result4 = otp.generate(1419622999) - self.assertNotEqual(result4, result) - - def test_generate(self): - """generate()""" - from passlib.totp import TOTP - - # generate token - otp = TOTP(new=True) - time = self.randtime() - result = otp.generate(time) - token = result.token - self.assertIsInstance(token, unicode) - start_time = result.counter * 30 - - # should generate same token for next 29s - self.assertEqual(otp.generate(start_time + 29).token, token) - - # and new one at 30s - self.assertNotEqual(otp.generate(start_time + 30).token, token) - - # verify round-trip conversion of datetime - dt = datetime.datetime.utcfromtimestamp(time) - self.assertEqual(int(otp.normalize_time(dt)), int(time)) - - # handle datetime object - self.assertEqual(otp.generate(dt).token, token) - - # omitting value should use current time - otp2 = TOTP.using(now=lambda: time)(key=otp.base32_key) - self.assertEqual(otp2.generate().token, token) - - # reject invalid time - self.assertRaises(ValueError, otp.generate, -1) - - def test_generate_w_reference_vectors(self): - """generate() -- reference vectors""" - for otp, time, token, expires, prefix in self.iter_test_vectors(): - # should output correct token for specified time - result = otp.generate(time) - self.assertEqual(result.token, token, msg=prefix) - self.assertEqual(result.counter, time // otp.period, msg=prefix) - if expires: - self.assertEqual(result.expire_time, expires) - - #============================================================================= - # TotpMatch() tests - #============================================================================= - - def assertTotpMatch(self, match, time, skipped=0, period=30, window=30, msg=''): - from passlib.totp import TotpMatch - - # test type - self.assertIsInstance(match, TotpMatch) - - # totp sanity check - self.assertIsInstance(match.totp, TOTP) - self.assertEqual(match.totp.period, period) - - # test attrs - self.assertEqual(match.time, time, msg=msg + " matched time:") - expected = time // period - counter = expected + skipped - self.assertEqual(match.counter, counter, msg=msg + " matched counter:") - self.assertEqual(match.expected_counter, expected, msg=msg + " expected counter:") - self.assertEqual(match.skipped, skipped, msg=msg + " skipped:") - self.assertEqual(match.cache_seconds, period + window) - expire_time = (counter + 1) * period - self.assertEqual(match.expire_time, expire_time) - self.assertEqual(match.cache_time, expire_time + window) - - # test tuple - self.assertEqual(len(match), 2) - self.assertEqual(match, (counter, time)) - self.assertRaises(IndexError, match.__getitem__, -3) - self.assertEqual(match[0], counter) - self.assertEqual(match[1], time) - self.assertRaises(IndexError, match.__getitem__, 2) - - # test bool - self.assertTrue(match) - - def test_totp_match_w_valid_token(self): - """match() -- valid TotpMatch object""" - time = 141230981 - token = '781501' - otp = TOTP.using(now=lambda: time + 24 * 3600)(KEY3) - result = otp.match(token, time) - self.assertTotpMatch(result, time=time, skipped=0) - - def test_totp_match_w_older_token(self): - """match() -- valid TotpMatch object with future token""" - from passlib.totp import TotpMatch - - time = 141230981 - token = '781501' - otp = TOTP.using(now=lambda: time + 24 * 3600)(KEY3) - result = otp.match(token, time - 30) - self.assertTotpMatch(result, time=time - 30, skipped=1) - - def test_totp_match_w_new_token(self): - """match() -- valid TotpMatch object with past token""" - time = 141230981 - token = '781501' - otp = TOTP.using(now=lambda: time + 24 * 3600)(KEY3) - result = otp.match(token, time + 30) - self.assertTotpMatch(result, time=time + 30, skipped=-1) - - def test_totp_match_w_invalid_token(self): - """match() -- invalid TotpMatch object""" - time = 141230981 - token = '781501' - otp = TOTP.using(now=lambda: time + 24 * 3600)(KEY3) - self.assertRaises(exc.InvalidTokenError, otp.match, token, time + 60) - - #============================================================================= - # match() tests - #============================================================================= - - def assertVerifyMatches(self, expect_skipped, token, time, # * - otp, gen_time=None, **kwds): - """helper to test otp.match() output is correct""" - # NOTE: TotpMatch return type tested more throughly above ^^^ - msg = "key=%r alg=%r period=%r token=%r gen_time=%r time=%r:" % \ - (otp.base32_key, otp.alg, otp.period, token, gen_time, time) - result = otp.match(token, time, **kwds) - self.assertTotpMatch(result, - time=otp.normalize_time(time), - period=otp.period, - window=kwds.get("window", 30), - skipped=expect_skipped, - msg=msg) - - def assertVerifyRaises(self, exc_class, token, time, # * - otp, gen_time=None, - **kwds): - """helper to test otp.match() throws correct error""" - # NOTE: TotpMatch return type tested more throughly above ^^^ - msg = "key=%r alg=%r period=%r token=%r gen_time=%r time=%r:" % \ - (otp.base32_key, otp.alg, otp.period, token, gen_time, time) - return self.assertRaises(exc_class, otp.match, token, time, - __msg__=msg, **kwds) - - def test_match_w_window(self): - """match() -- 'time' and 'window' parameters""" - - # init generator & helper - otp = self.randotp() - period = otp.period - time = self.randtime() - token = otp.generate(time).token - common = dict(otp=otp, gen_time=time) - assertMatches = partial(self.assertVerifyMatches, **common) - assertRaises = partial(self.assertVerifyRaises, **common) - - #------------------------------- - # basic validation, and 'window' parameter - #------------------------------- - - # validate against previous counter (passes if window >= period) - assertRaises(exc.InvalidTokenError, token, time - period, window=0) - assertMatches(+1, token, time - period, window=period) - assertMatches(+1, token, time - period, window=2 * period) - - # validate against current counter - assertMatches(0, token, time, window=0) - - # validate against next counter (passes if window >= period) - assertRaises(exc.InvalidTokenError, token, time + period, window=0) - assertMatches(-1, token, time + period, window=period) - assertMatches(-1, token, time + period, window=2 * period) - - # validate against two time steps later (should never pass) - assertRaises(exc.InvalidTokenError, token, time + 2 * period, window=0) - assertRaises(exc.InvalidTokenError, token, time + 2 * period, window=period) - assertMatches(-2, token, time + 2 * period, window=2 * period) - - # TODO: test window values that aren't multiples of period - # (esp ensure counter rounding works correctly) - - #------------------------------- - # time normalization - #------------------------------- - - # handle datetimes - dt = datetime.datetime.utcfromtimestamp(time) - assertMatches(0, token, dt, window=0) - - # reject invalid time - assertRaises(ValueError, token, -1) - - def test_match_w_skew(self): - """match() -- 'skew' parameters""" - # init generator & helper - otp = self.randotp() - period = otp.period - time = self.randtime() - common = dict(otp=otp, gen_time=time) - assertMatches = partial(self.assertVerifyMatches, **common) - assertRaises = partial(self.assertVerifyRaises, **common) - - # assume client is running far behind server / has excessive transmission delay - skew = 3 * period - behind_token = otp.generate(time - skew).token - assertRaises(exc.InvalidTokenError, behind_token, time, window=0) - assertMatches(-3, behind_token, time, window=0, skew=-skew) - - # assume client is running far ahead of server - ahead_token = otp.generate(time + skew).token - assertRaises(exc.InvalidTokenError, ahead_token, time, window=0) - assertMatches(+3, ahead_token, time, window=0, skew=skew) - - # TODO: test skew + larger window - - def test_match_w_reuse(self): - """match() -- 'reuse' and 'last_counter' parameters""" - - # init generator & helper - otp = self.randotp() - period = otp.period - time = self.randtime() - tdata = otp.generate(time) - token = tdata.token - counter = tdata.counter - expire_time = tdata.expire_time - common = dict(otp=otp, gen_time=time) - assertMatches = partial(self.assertVerifyMatches, **common) - assertRaises = partial(self.assertVerifyRaises, **common) - - # last counter unset -- - # previous period's token should count as valid - assertMatches(-1, token, time + period, window=period) - - # last counter set 2 periods ago -- - # previous period's token should count as valid - assertMatches(-1, token, time + period, last_counter=counter-1, - window=period) - - # last counter set 2 periods ago -- - # 2 periods ago's token should NOT count as valid - assertRaises(exc.InvalidTokenError, token, time + 2 * period, - last_counter=counter, window=period) - - # last counter set 1 period ago -- - # previous period's token should now be rejected as 'used' - err = assertRaises(exc.UsedTokenError, token, time + period, - last_counter=counter, window=period) - self.assertEqual(err.expire_time, expire_time) - - # last counter set to current period -- - # current period's token should be rejected - err = assertRaises(exc.UsedTokenError, token, time, - last_counter=counter, window=0) - self.assertEqual(err.expire_time, expire_time) - - def test_match_w_token_normalization(self): - """match() -- token normalization""" - # setup test helper - otp = TOTP('otxl2f5cctbprpzx') - match = otp.match - time = 1412889861 - - # separators / spaces should be stripped (orig token '332136') - self.assertTrue(match(' 3 32-136 ', time)) - - # ascii bytes - self.assertTrue(match(b'332136', time)) - - # too few digits - self.assertRaises(exc.MalformedTokenError, match, '12345', time) - - # invalid char - self.assertRaises(exc.MalformedTokenError, match, '12345X', time) - - # leading zeros count towards size - self.assertRaises(exc.MalformedTokenError, match, '0123456', time) - - def test_match_w_reference_vectors(self): - """match() -- reference vectors""" - for otp, time, token, expires, msg in self.iter_test_vectors(): - # create wrapper - match = otp.match - - # token should match against time - result = match(token, time) - self.assertTrue(result) - self.assertEqual(result.counter, time // otp.period, msg=msg) - - # should NOT match against another time - self.assertRaises(exc.InvalidTokenError, match, token, time + 100, window=0) - - #============================================================================= - # verify() tests - #============================================================================= - def test_verify(self): - """verify()""" - # NOTE: since this is thin wrapper around .from_source() and .match(), - # just testing basic behavior here. - - from passlib.totp import TOTP - - time = 1412889861 - TotpFactory = TOTP.using(now=lambda: time) - - # successful match - source1 = dict(v=1, type="totp", key='otxl2f5cctbprpzx') - match = TotpFactory.verify('332136', source1) - self.assertTotpMatch(match, time=time) - - # failed match - source1 = dict(v=1, type="totp", key='otxl2f5cctbprpzx') - self.assertRaises(exc.InvalidTokenError, TotpFactory.verify, '332155', source1) - - # bad source - source1 = dict(v=1, type="totp") - self.assertRaises(ValueError, TotpFactory.verify, '332155', source1) - - # successful match -- json source - source1json = '{"v": 1, "type": "totp", "key": "otxl2f5cctbprpzx"}' - match = TotpFactory.verify('332136', source1json) - self.assertTotpMatch(match, time=time) - - # successful match -- URI - source1uri = 'otpauth://totp/Label?secret=otxl2f5cctbprpzx' - match = TotpFactory.verify('332136', source1uri) - self.assertTotpMatch(match, time=time) - - #============================================================================= - # serialization frontend tests - #============================================================================= - def test_from_source(self): - """from_source()""" - from passlib.totp import TOTP - from_source = TOTP.from_source - - # uri (unicode) - otp = from_source(u("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "issuer=Example")) - self.assertEqual(otp.key, KEY4_RAW) - - # uri (bytes) - otp = from_source(b"otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&" - b"issuer=Example") - self.assertEqual(otp.key, KEY4_RAW) - - # dict - otp = from_source(dict(v=1, type="totp", key=KEY4)) - self.assertEqual(otp.key, KEY4_RAW) - - # json (unicode) - otp = from_source(u('{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}')) - self.assertEqual(otp.key, KEY4_RAW) - - # json (bytes) - otp = from_source(b'{"v": 1, "type": "totp", "key": "JBSWY3DPEHPK3PXP"}') - self.assertEqual(otp.key, KEY4_RAW) - - # TOTP object -- return unchanged - self.assertIs(from_source(otp), otp) - - # TOTP object w/ different wallet -- return new one. - wallet1 = AppWallet() - otp1 = TOTP.using(wallet=wallet1).from_source(otp) - self.assertIsNot(otp1, otp) - self.assertEqual(otp1.to_dict(), otp.to_dict()) - - # TOTP object w/ same wallet -- return original - otp2 = TOTP.using(wallet=wallet1).from_source(otp1) - self.assertIs(otp2, otp1) - - # random string - self.assertRaises(ValueError, from_source, u("foo")) - self.assertRaises(ValueError, from_source, b"foo") - - #============================================================================= - # uri serialization tests - #============================================================================= - def test_from_uri(self): - """from_uri()""" - from passlib.totp import TOTP - from_uri = TOTP.from_uri - - # URIs from https://code.google.com/p/google-authenticator/wiki/KeyUriFormat - - #-------------------------------------------------------------------------------- - # canonical uri - #-------------------------------------------------------------------------------- - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "issuer=Example") - self.assertIsInstance(otp, TOTP) - self.assertEqual(otp.key, KEY4_RAW) - self.assertEqual(otp.label, "alice@google.com") - self.assertEqual(otp.issuer, "Example") - self.assertEqual(otp.alg, "sha1") # default - self.assertEqual(otp.period, 30) # default - self.assertEqual(otp.digits, 6) # default - - #-------------------------------------------------------------------------------- - # secret param - #-------------------------------------------------------------------------------- - - # secret case insensitive - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=jbswy3dpehpk3pxp&" - "issuer=Example") - self.assertEqual(otp.key, KEY4_RAW) - - # missing secret - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?digits=6") - - # undecodable secret - self.assertRaises(Base32DecodeError, from_uri, "otpauth://totp/Example:alice@google.com?" - "secret=JBSWY3DPEHP@3PXP") - - #-------------------------------------------------------------------------------- - # label param - #-------------------------------------------------------------------------------- - - # w/ encoded space - otp = from_uri("otpauth://totp/Provider1:Alice%20Smith?secret=JBSWY3DPEHPK3PXP&" - "issuer=Provider1") - self.assertEqual(otp.label, "Alice Smith") - self.assertEqual(otp.issuer, "Provider1") - - # w/ encoded space and colon - # (note url has leading space before 'alice') -- taken from KeyURI spec - otp = from_uri("otpauth://totp/Big%20Corporation%3A%20alice@bigco.com?" - "secret=JBSWY3DPEHPK3PXP") - self.assertEqual(otp.label, "alice@bigco.com") - self.assertEqual(otp.issuer, "Big Corporation") - - #-------------------------------------------------------------------------------- - # issuer param / prefix - #-------------------------------------------------------------------------------- - - # 'new style' issuer only - otp = from_uri("otpauth://totp/alice@bigco.com?secret=JBSWY3DPEHPK3PXP&issuer=Big%20Corporation") - self.assertEqual(otp.label, "alice@bigco.com") - self.assertEqual(otp.issuer, "Big Corporation") - - # new-vs-old issuer mismatch - self.assertRaises(ValueError, TOTP.from_uri, - "otpauth://totp/Provider1:alice?secret=JBSWY3DPEHPK3PXP&issuer=Provider2") - - #-------------------------------------------------------------------------------- - # algorithm param - #-------------------------------------------------------------------------------- - - # custom alg - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&algorithm=SHA256") - self.assertEqual(otp.alg, "sha256") - - # unknown alg - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?" - "secret=JBSWY3DPEHPK3PXP&algorithm=SHA333") - - #-------------------------------------------------------------------------------- - # digit param - #-------------------------------------------------------------------------------- - - # custom digits - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&digits=8") - self.assertEqual(otp.digits, 8) - - # digits out of range / invalid - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&digits=A") - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&digits=%20") - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&digits=15") - - #-------------------------------------------------------------------------------- - # period param - #-------------------------------------------------------------------------------- - - # custom period - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&period=63") - self.assertEqual(otp.period, 63) - - # reject period < 1 - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?" - "secret=JBSWY3DPEHPK3PXP&period=0") - - self.assertRaises(ValueError, from_uri, "otpauth://totp/Example:alice@google.com?" - "secret=JBSWY3DPEHPK3PXP&period=-1") - - #-------------------------------------------------------------------------------- - # unrecognized param - #-------------------------------------------------------------------------------- - - # should issue warning, but otherwise ignore extra param - with self.assertWarningList([ - dict(category=exc.PasslibRuntimeWarning, message_re="unexpected parameters encountered") - ]): - otp = from_uri("otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "foo=bar&period=63") - self.assertEqual(otp.base32_key, KEY4) - self.assertEqual(otp.period, 63) - - def test_to_uri(self): - """to_uri()""" - - #------------------------------------------------------------------------- - # label & issuer parameters - #------------------------------------------------------------------------- - - # with label & issuer - otp = TOTP(KEY4, alg="sha1", digits=6, period=30) - self.assertEqual(otp.to_uri("alice@google.com", "Example Org"), - "otpauth://totp/Example%20Org:alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "issuer=Example%20Org") - - # label is required - self.assertRaises(ValueError, otp.to_uri, None, "Example Org") - - # with label only - self.assertEqual(otp.to_uri("alice@google.com"), - "otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP") - - # with default label from constructor - otp.label = "alice@google.com" - self.assertEqual(otp.to_uri(), - "otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP") - - # with default label & default issuer from constructor - otp.issuer = "Example Org" - self.assertEqual(otp.to_uri(), - "otpauth://totp/Example%20Org:alice@google.com?secret=JBSWY3DPEHPK3PXP" - "&issuer=Example%20Org") - - # reject invalid label - self.assertRaises(ValueError, otp.to_uri, "label:with:semicolons") - - # reject invalid issuer - self.assertRaises(ValueError, otp.to_uri, "alice@google.com", "issuer:with:semicolons") - - #------------------------------------------------------------------------- - # algorithm parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, alg="sha256").to_uri("alice@google.com"), - "otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "algorithm=SHA256") - - #------------------------------------------------------------------------- - # digits parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, digits=8).to_uri("alice@google.com"), - "otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "digits=8") - - #------------------------------------------------------------------------- - # period parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, period=63).to_uri("alice@google.com"), - "otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP&" - "period=63") - - #============================================================================= - # dict serialization tests - #============================================================================= - def test_from_dict(self): - """from_dict()""" - from passlib.totp import TOTP - from_dict = TOTP.from_dict - - #-------------------------------------------------------------------------------- - # canonical simple example - #-------------------------------------------------------------------------------- - otp = from_dict(dict(v=1, type="totp", key=KEY4, label="alice@google.com", issuer="Example")) - self.assertIsInstance(otp, TOTP) - self.assertEqual(otp.key, KEY4_RAW) - self.assertEqual(otp.label, "alice@google.com") - self.assertEqual(otp.issuer, "Example") - self.assertEqual(otp.alg, "sha1") # default - self.assertEqual(otp.period, 30) # default - self.assertEqual(otp.digits, 6) # default - - #-------------------------------------------------------------------------------- - # metadata - #-------------------------------------------------------------------------------- - - # missing version - self.assertRaises(ValueError, from_dict, dict(type="totp", key=KEY4)) - - # invalid version - self.assertRaises(ValueError, from_dict, dict(v=0, type="totp", key=KEY4)) - self.assertRaises(ValueError, from_dict, dict(v=999, type="totp", key=KEY4)) - - # missing type - self.assertRaises(ValueError, from_dict, dict(v=1, key=KEY4)) - - #-------------------------------------------------------------------------------- - # secret param - #-------------------------------------------------------------------------------- - - # secret case insensitive - otp = from_dict(dict(v=1, type="totp", key=KEY4.lower(), label="alice@google.com", issuer="Example")) - self.assertEqual(otp.key, KEY4_RAW) - - # missing secret - self.assertRaises(ValueError, from_dict, dict(v=1, type="totp")) - - # undecodable secret - self.assertRaises(Base32DecodeError, from_dict, - dict(v=1, type="totp", key="JBSWY3DPEHP@3PXP")) - - #-------------------------------------------------------------------------------- - # label & issuer params - #-------------------------------------------------------------------------------- - - otp = from_dict(dict(v=1, type="totp", key=KEY4, label="Alice Smith", issuer="Provider1")) - self.assertEqual(otp.label, "Alice Smith") - self.assertEqual(otp.issuer, "Provider1") - - #-------------------------------------------------------------------------------- - # algorithm param - #-------------------------------------------------------------------------------- - - # custom alg - otp = from_dict(dict(v=1, type="totp", key=KEY4, alg="sha256")) - self.assertEqual(otp.alg, "sha256") - - # unknown alg - self.assertRaises(ValueError, from_dict, dict(v=1, type="totp", key=KEY4, alg="sha333")) - - #-------------------------------------------------------------------------------- - # digit param - #-------------------------------------------------------------------------------- - - # custom digits - otp = from_dict(dict(v=1, type="totp", key=KEY4, digits=8)) - self.assertEqual(otp.digits, 8) - - # digits out of range / invalid - self.assertRaises(TypeError, from_dict, dict(v=1, type="totp", key=KEY4, digits="A")) - self.assertRaises(ValueError, from_dict, dict(v=1, type="totp", key=KEY4, digits=15)) - - #-------------------------------------------------------------------------------- - # period param - #-------------------------------------------------------------------------------- - - # custom period - otp = from_dict(dict(v=1, type="totp", key=KEY4, period=63)) - self.assertEqual(otp.period, 63) - - # reject period < 1 - self.assertRaises(ValueError, from_dict, dict(v=1, type="totp", key=KEY4, period=0)) - self.assertRaises(ValueError, from_dict, dict(v=1, type="totp", key=KEY4, period=-1)) - - #-------------------------------------------------------------------------------- - # unrecognized param - #-------------------------------------------------------------------------------- - self.assertRaises(TypeError, from_dict, dict(v=1, type="totp", key=KEY4, INVALID=123)) - - def test_to_dict(self): - """to_dict()""" - - #------------------------------------------------------------------------- - # label & issuer parameters - #------------------------------------------------------------------------- - - # without label or issuer - otp = TOTP(KEY4, alg="sha1", digits=6, period=30) - self.assertEqual(otp.to_dict(), dict(v=1, type="totp", key=KEY4)) - - # with label & issuer from constructor - otp = TOTP(KEY4, alg="sha1", digits=6, period=30, - label="alice@google.com", issuer="Example Org") - self.assertEqual(otp.to_dict(), - dict(v=1, type="totp", key=KEY4, - label="alice@google.com", issuer="Example Org")) - - # with label only - otp = TOTP(KEY4, alg="sha1", digits=6, period=30, - label="alice@google.com") - self.assertEqual(otp.to_dict(), - dict(v=1, type="totp", key=KEY4, - label="alice@google.com")) - - # with issuer only - otp = TOTP(KEY4, alg="sha1", digits=6, period=30, - issuer="Example Org") - self.assertEqual(otp.to_dict(), - dict(v=1, type="totp", key=KEY4, - issuer="Example Org")) - - # don't serialize default issuer - TotpFactory = TOTP.using(issuer="Example Org") - otp = TotpFactory(KEY4) - self.assertEqual(otp.to_dict(), dict(v=1, type="totp", key=KEY4)) - - # don't serialize default issuer *even if explicitly set* - otp = TotpFactory(KEY4, issuer="Example Org") - self.assertEqual(otp.to_dict(), dict(v=1, type="totp", key=KEY4)) - - #------------------------------------------------------------------------- - # algorithm parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, alg="sha256").to_dict(), - dict(v=1, type="totp", key=KEY4, alg="sha256")) - - #------------------------------------------------------------------------- - # digits parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, digits=8).to_dict(), - dict(v=1, type="totp", key=KEY4, digits=8)) - - #------------------------------------------------------------------------- - # period parameter - #------------------------------------------------------------------------- - self.assertEqual(TOTP(KEY4, period=63).to_dict(), - dict(v=1, type="totp", key=KEY4, period=63)) - - # TODO: to_dict() - # with encrypt=False - # with encrypt="auto" + wallet + secrets - # with encrypt="auto" + wallet + no secrets - # with encrypt="auto" + no wallet - # with encrypt=True + wallet + secrets - # with encrypt=True + wallet + no secrets - # with encrypt=True + no wallet - # that 'changed' is set for old versions, and old encryption tags. - - #============================================================================= - # json serialization tests - #============================================================================= - - # TODO: from_json() / to_json(). - # (skipped for right now cause just wrapper for from_dict/to_dict) - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_utils.py b/.venv/Lib/site-packages/passlib/tests/test_utils.py deleted file mode 100644 index 59ba160..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_utils.py +++ /dev/null @@ -1,1171 +0,0 @@ -"""tests for passlib.util""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -from functools import partial -import warnings -# site -# pkg -# module -from passlib.utils import is_ascii_safe, to_bytes -from passlib.utils.compat import irange, PY2, PY3, u, unicode, join_bytes, PYPY -from passlib.tests.utils import TestCase, hb, run_with_fixed_seeds - -#============================================================================= -# byte funcs -#============================================================================= -class MiscTest(TestCase): - """tests various parts of utils module""" - - # NOTE: could test xor_bytes(), but it's exercised well enough by pbkdf2 test - - def test_compat(self): - """test compat's lazymodule""" - from passlib.utils import compat - # "" - self.assertRegex(repr(compat), - r"^$") - - # test synthentic dir() - dir(compat) - self.assertTrue('UnicodeIO' in dir(compat)) - self.assertTrue('irange' in dir(compat)) - - def test_classproperty(self): - from passlib.utils.decor import classproperty - - class test(object): - xvar = 1 - @classproperty - def xprop(cls): - return cls.xvar - - self.assertEqual(test.xprop, 1) - prop = test.__dict__['xprop'] - self.assertIs(prop.im_func, prop.__func__) - - def test_deprecated_function(self): - from passlib.utils.decor import deprecated_function - # NOTE: not comprehensive, just tests the basic behavior - - @deprecated_function(deprecated="1.6", removed="1.8") - def test_func(*args): - """test docstring""" - return args - - self.assertTrue(".. deprecated::" in test_func.__doc__) - - with self.assertWarningList(dict(category=DeprecationWarning, - message="the function passlib.tests.test_utils.test_func() " - "is deprecated as of Passlib 1.6, and will be " - "removed in Passlib 1.8." - )): - self.assertEqual(test_func(1,2), (1,2)) - - def test_memoized_property(self): - from passlib.utils.decor import memoized_property - - class dummy(object): - counter = 0 - - @memoized_property - def value(self): - value = self.counter - self.counter = value+1 - return value - - d = dummy() - self.assertEqual(d.value, 0) - self.assertEqual(d.value, 0) - self.assertEqual(d.counter, 1) - - prop = dummy.value - if not PY3: - self.assertIs(prop.im_func, prop.__func__) - - def test_getrandbytes(self): - """getrandbytes()""" - from passlib.utils import getrandbytes - wrapper = partial(getrandbytes, self.getRandom()) - self.assertEqual(len(wrapper(0)), 0) - a = wrapper(10) - b = wrapper(10) - self.assertIsInstance(a, bytes) - self.assertEqual(len(a), 10) - self.assertEqual(len(b), 10) - self.assertNotEqual(a, b) - - @run_with_fixed_seeds(count=1024) - def test_getrandstr(self, seed): - """getrandstr()""" - from passlib.utils import getrandstr - - wrapper = partial(getrandstr, self.getRandom(seed=seed)) - - # count 0 - self.assertEqual(wrapper('abc',0), '') - - # count <0 - self.assertRaises(ValueError, wrapper, 'abc', -1) - - # letters 0 - self.assertRaises(ValueError, wrapper, '', 0) - - # letters 1 - self.assertEqual(wrapper('a', 5), 'aaaaa') - - # NOTE: the following parts are non-deterministic, - # with a small chance of failure (outside chance it may pick - # a string w/o one char, even more remote chance of picking - # same string). to combat this, we run it against multiple - # fixed seeds (using run_with_fixed_seeds decorator), - # and hope that they're sufficient to test the range of behavior. - - # letters - x = wrapper(u('abc'), 32) - y = wrapper(u('abc'), 32) - self.assertIsInstance(x, unicode) - self.assertNotEqual(x,y) - self.assertEqual(sorted(set(x)), [u('a'),u('b'),u('c')]) - - # bytes - x = wrapper(b'abc', 32) - y = wrapper(b'abc', 32) - self.assertIsInstance(x, bytes) - self.assertNotEqual(x,y) - # NOTE: decoding this due to py3 bytes - self.assertEqual(sorted(set(x.decode("ascii"))), [u('a'),u('b'),u('c')]) - - def test_generate_password(self): - """generate_password()""" - from passlib.utils import generate_password - warnings.filterwarnings("ignore", "The function.*generate_password\(\) is deprecated") - self.assertEqual(len(generate_password(15)), 15) - - def test_is_crypt_context(self): - """test is_crypt_context()""" - from passlib.utils import is_crypt_context - from passlib.context import CryptContext - cc = CryptContext(["des_crypt"]) - self.assertTrue(is_crypt_context(cc)) - self.assertFalse(not is_crypt_context(cc)) - - def test_genseed(self): - """test genseed()""" - import random - from passlib.utils import genseed - rng = random.Random(genseed()) - a = rng.randint(0, 10**10) - - rng = random.Random(genseed()) - b = rng.randint(0, 10**10) - - self.assertNotEqual(a,b) - - rng.seed(genseed(rng)) - - def test_crypt(self): - """test crypt.crypt() wrappers""" - from passlib.utils import has_crypt, safe_crypt, test_crypt - from passlib.registry import get_supported_os_crypt_schemes, get_crypt_handler - - # test everything is disabled - supported = get_supported_os_crypt_schemes() - if not has_crypt: - self.assertEqual(supported, ()) - self.assertEqual(safe_crypt("test", "aa"), None) - self.assertFalse(test_crypt("test", "aaqPiZY5xR5l.")) # des_crypt() hash of "test" - raise self.skipTest("crypt.crypt() not available") - - # expect there to be something supported, if crypt() is present - if not supported: - # NOTE: failures here should be investigated. usually means one of: - # 1) at least one of passlib's os_crypt detection routines is giving false negative - # 2) crypt() ONLY supports some hash alg which passlib doesn't know about - # 3) crypt() is present but completely disabled (never encountered this yet) - raise self.fail("crypt() present, but no supported schemes found!") - - # pick cheap alg if possible, with minimum rounds, to speed up this test. - # NOTE: trusting hasher class works properly (should have been verified using it's own UTs) - for scheme in ("md5_crypt", "sha256_crypt"): - if scheme in supported: - break - else: - scheme = supported[-1] - hasher = get_crypt_handler(scheme) - if getattr(hasher, "min_rounds", None): - hasher = hasher.using(rounds=hasher.min_rounds) - - # helpers to generate hashes & config strings to work with - def get_hash(secret): - assert isinstance(secret, unicode) - hash = hasher.hash(secret) - if isinstance(hash, bytes): # py2 - hash = hash.decode("utf-8") - assert isinstance(hash, unicode) - return hash - - # test ascii password & return type - s1 = u("test") - h1 = get_hash(s1) - result = safe_crypt(s1, h1) - self.assertIsInstance(result, unicode) - self.assertEqual(result, h1) - self.assertEqual(safe_crypt(to_bytes(s1), to_bytes(h1)), h1) - - # make sure crypt doesn't just blindly return h1 for whatever we pass in - h1x = h1[:-2] + 'xx' - self.assertEqual(safe_crypt(s1, h1x), h1) - - # test utf-8 / unicode password - s2 = u('test\u1234') - h2 = get_hash(s2) - self.assertEqual(safe_crypt(s2, h2), h2) - self.assertEqual(safe_crypt(to_bytes(s2), to_bytes(h2)), h2) - - # test rejects null chars in password - self.assertRaises(ValueError, safe_crypt, '\x00', h1) - - # check test_crypt() - self.assertTrue(test_crypt("test", h1)) - self.assertFalse(test_crypt("test", h1x)) - - # check crypt returning variant error indicators - # some platforms return None on errors, others empty string, - # The BSDs in some cases return ":" - import passlib.utils as mod - orig = mod._crypt - try: - retval = None - mod._crypt = lambda secret, hash: retval - - for retval in [None, "", ":", ":0", "*0"]: - self.assertEqual(safe_crypt("test", h1), None) - self.assertFalse(test_crypt("test", h1)) - - retval = 'xxx' - self.assertEqual(safe_crypt("test", h1), "xxx") - self.assertFalse(test_crypt("test", h1)) - - finally: - mod._crypt = orig - - def test_consteq(self): - """test consteq()""" - # NOTE: this test is kind of over the top, but that's only because - # this is used for the critical task of comparing hashes for equality. - from passlib.utils import consteq, str_consteq - - # ensure error raises for wrong types - self.assertRaises(TypeError, consteq, u(''), b'') - self.assertRaises(TypeError, consteq, u(''), 1) - self.assertRaises(TypeError, consteq, u(''), None) - - self.assertRaises(TypeError, consteq, b'', u('')) - self.assertRaises(TypeError, consteq, b'', 1) - self.assertRaises(TypeError, consteq, b'', None) - - self.assertRaises(TypeError, consteq, None, u('')) - self.assertRaises(TypeError, consteq, None, b'') - self.assertRaises(TypeError, consteq, 1, u('')) - self.assertRaises(TypeError, consteq, 1, b'') - - def consteq_supports_string(value): - # under PY2, it supports all unicode strings (when present at all), - # under PY3, compare_digest() only supports ascii unicode strings. - # confirmed for: cpython 2.7.9, cpython 3.4, pypy, pypy3, pyston - return (consteq is str_consteq or PY2 or is_ascii_safe(value)) - - # check equal inputs compare correctly - for value in [ - u("a"), - u("abc"), - u("\xff\xa2\x12\x00")*10, - ]: - if consteq_supports_string(value): - self.assertTrue(consteq(value, value), "value %r:" % (value,)) - else: - self.assertRaises(TypeError, consteq, value, value) - self.assertTrue(str_consteq(value, value), "value %r:" % (value,)) - - value = value.encode("latin-1") - self.assertTrue(consteq(value, value), "value %r:" % (value,)) - - # check non-equal inputs compare correctly - for l,r in [ - # check same-size comparisons with differing contents fail. - (u("a"), u("c")), - (u("abcabc"), u("zbaabc")), - (u("abcabc"), u("abzabc")), - (u("abcabc"), u("abcabz")), - ((u("\xff\xa2\x12\x00")*10)[:-1] + u("\x01"), - u("\xff\xa2\x12\x00")*10), - - # check different-size comparisons fail. - (u(""), u("a")), - (u("abc"), u("abcdef")), - (u("abc"), u("defabc")), - (u("qwertyuiopasdfghjklzxcvbnm"), u("abc")), - ]: - if consteq_supports_string(l) and consteq_supports_string(r): - self.assertFalse(consteq(l, r), "values %r %r:" % (l,r)) - self.assertFalse(consteq(r, l), "values %r %r:" % (r,l)) - else: - self.assertRaises(TypeError, consteq, l, r) - self.assertRaises(TypeError, consteq, r, l) - self.assertFalse(str_consteq(l, r), "values %r %r:" % (l,r)) - self.assertFalse(str_consteq(r, l), "values %r %r:" % (r,l)) - - l = l.encode("latin-1") - r = r.encode("latin-1") - self.assertFalse(consteq(l, r), "values %r %r:" % (l,r)) - self.assertFalse(consteq(r, l), "values %r %r:" % (r,l)) - - # TODO: add some tests to ensure we take THETA(strlen) time. - # this might be hard to do reproducably. - # NOTE: below code was used to generate stats for analysis - ##from math import log as logb - ##import timeit - ##multipliers = [ 1< encode() -> decode() -> raw - # - - # generate some random bytes - size = rng.randint(1 if saw_zero else 0, 12) - if not size: - saw_zero = True - enc_size = (4*size+2)//3 - raw = getrandbytes(rng, size) - - # encode them, check invariants - encoded = engine.encode_bytes(raw) - self.assertEqual(len(encoded), enc_size) - - # make sure decode returns original - result = engine.decode_bytes(encoded) - self.assertEqual(result, raw) - - # - # test encoded -> decode() -> encode() -> encoded - # - - # generate some random encoded data - if size % 4 == 1: - size += rng.choice([-1,1,2]) - raw_size = 3*size//4 - encoded = getrandstr(rng, engine.bytemap, size) - - # decode them, check invariants - raw = engine.decode_bytes(encoded) - self.assertEqual(len(raw), raw_size, "encoded %d:" % size) - - # make sure encode returns original (barring padding bits) - result = engine.encode_bytes(raw) - if size % 4: - self.assertEqual(result[:-1], encoded[:-1]) - else: - self.assertEqual(result, encoded) - - def test_repair_unused(self): - """test repair_unused()""" - # NOTE: this test relies on encode_bytes() always returning clear - # padding bits - which should be ensured by test vectors. - from passlib.utils import getrandstr - rng = self.getRandom() - engine = self.engine - check_repair_unused = self.engine.check_repair_unused - i = 0 - while i < 300: - size = rng.randint(0,23) - cdata = getrandstr(rng, engine.charmap, size).encode("ascii") - if size & 3 == 1: - # should throw error - self.assertRaises(ValueError, check_repair_unused, cdata) - continue - rdata = engine.encode_bytes(engine.decode_bytes(cdata)) - if rng.random() < .5: - cdata = cdata.decode("ascii") - rdata = rdata.decode("ascii") - if cdata == rdata: - # should leave unchanged - ok, result = check_repair_unused(cdata) - self.assertFalse(ok) - self.assertEqual(result, rdata) - else: - # should repair bits - self.assertNotEqual(size % 4, 0) - ok, result = check_repair_unused(cdata) - self.assertTrue(ok) - self.assertEqual(result, rdata) - i += 1 - - #=================================================================== - # test transposed encode/decode - encoding independant - #=================================================================== - # NOTE: these tests assume normal encode/decode has been tested elsewhere. - - transposed = [ - # orig, result, transpose map - (b"\x33\x22\x11", b"\x11\x22\x33",[2,1,0]), - (b"\x22\x33\x11", b"\x11\x22\x33",[1,2,0]), - ] - - transposed_dups = [ - # orig, result, transpose projection - (b"\x11\x11\x22", b"\x11\x22\x33",[0,0,1]), - ] - - def test_encode_transposed_bytes(self): - """test encode_transposed_bytes()""" - engine = self.engine - for result, input, offsets in self.transposed + self.transposed_dups: - tmp = engine.encode_transposed_bytes(input, offsets) - out = engine.decode_bytes(tmp) - self.assertEqual(out, result) - - self.assertRaises(TypeError, engine.encode_transposed_bytes, u("a"), []) - - def test_decode_transposed_bytes(self): - """test decode_transposed_bytes()""" - engine = self.engine - for input, result, offsets in self.transposed: - tmp = engine.encode_bytes(input) - out = engine.decode_transposed_bytes(tmp, offsets) - self.assertEqual(out, result) - - def test_decode_transposed_bytes_bad(self): - """test decode_transposed_bytes() fails if map is a one-way""" - engine = self.engine - for input, _, offsets in self.transposed_dups: - tmp = engine.encode_bytes(input) - self.assertRaises(TypeError, engine.decode_transposed_bytes, tmp, - offsets) - - #=================================================================== - # test 6bit handling - #=================================================================== - def check_int_pair(self, bits, encoded_pairs): - """helper to check encode_intXX & decode_intXX functions""" - rng = self.getRandom() - engine = self.engine - encode = getattr(engine, "encode_int%s" % bits) - decode = getattr(engine, "decode_int%s" % bits) - pad = -bits % 6 - chars = (bits+pad)//6 - upper = 1< block_size, and wrong type - self.assertRaises(ValueError, helper, keylen=-1) - self.assertRaises(ValueError, helper, keylen=17, hash='md5') - self.assertRaises(TypeError, helper, keylen='1') - -#============================================================================= -# test PBKDF2 support -#============================================================================= -class Pbkdf2_Test(TestCase): - """test pbkdf2() support""" - descriptionPrefix = "passlib.utils.pbkdf2.pbkdf2()" - - pbkdf2_test_vectors = [ - # (result, secret, salt, rounds, keylen, prf="sha1") - - # - # from rfc 3962 - # - - # test case 1 / 128 bit - ( - hb("cdedb5281bb2f801565a1122b2563515"), - b"password", b"ATHENA.MIT.EDUraeburn", 1, 16 - ), - - # test case 2 / 128 bit - ( - hb("01dbee7f4a9e243e988b62c73cda935d"), - b"password", b"ATHENA.MIT.EDUraeburn", 2, 16 - ), - - # test case 2 / 256 bit - ( - hb("01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86"), - b"password", b"ATHENA.MIT.EDUraeburn", 2, 32 - ), - - # test case 3 / 256 bit - ( - hb("5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13"), - b"password", b"ATHENA.MIT.EDUraeburn", 1200, 32 - ), - - # test case 4 / 256 bit - ( - hb("d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee"), - b"password", b'\x12\x34\x56\x78\x78\x56\x34\x12', 5, 32 - ), - - # test case 5 / 256 bit - ( - hb("139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1"), - b"X"*64, b"pass phrase equals block size", 1200, 32 - ), - - # test case 6 / 256 bit - ( - hb("9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a"), - b"X"*65, b"pass phrase exceeds block size", 1200, 32 - ), - - # - # from rfc 6070 - # - ( - hb("0c60c80f961f0e71f3a9b524af6012062fe037a6"), - b"password", b"salt", 1, 20, - ), - - ( - hb("ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"), - b"password", b"salt", 2, 20, - ), - - ( - hb("4b007901b765489abead49d926f721d065a429c1"), - b"password", b"salt", 4096, 20, - ), - - # just runs too long - could enable if ALL option is set - ##( - ## - ## unhexlify("eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"), - ## "password", "salt", 16777216, 20, - ##), - - ( - hb("3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"), - b"passwordPASSWORDpassword", - b"saltSALTsaltSALTsaltSALTsaltSALTsalt", - 4096, 25, - ), - - ( - hb("56fa6aa75548099dcc37d7f03425e0c3"), - b"pass\00word", b"sa\00lt", 4096, 16, - ), - - # - # from example in http://grub.enbug.org/Authentication - # - ( - hb("887CFF169EA8335235D8004242AA7D6187A41E3187DF0CE14E256D85ED" - "97A97357AAA8FF0A3871AB9EEFF458392F462F495487387F685B7472FC" - "6C29E293F0A0"), - b"hello", - hb("9290F727ED06C38BA4549EF7DE25CF5642659211B7FC076F2D28FEFD71" - "784BB8D8F6FB244A8CC5C06240631B97008565A120764C0EE9C2CB0073" - "994D79080136"), - 10000, 64, "hmac-sha512" - ), - - # - # custom - # - ( - hb('e248fb6b13365146f8ac6307cc222812'), - b"secret", b"salt", 10, 16, "hmac-sha1", - ), - ( - hb('e248fb6b13365146f8ac6307cc2228127872da6d'), - b"secret", b"salt", 10, None, "hmac-sha1", - ), - - ] - - def setUp(self): - super(Pbkdf2_Test, self).setUp() - warnings.filterwarnings("ignore", ".*passlib.utils.pbkdf2.*deprecated", DeprecationWarning) - - def test_known(self): - """test reference vectors""" - from passlib.utils.pbkdf2 import pbkdf2 - for row in self.pbkdf2_test_vectors: - correct, secret, salt, rounds, keylen = row[:5] - prf = row[5] if len(row) == 6 else "hmac-sha1" - result = pbkdf2(secret, salt, rounds, keylen, prf) - self.assertEqual(result, correct) - - def test_border(self): - """test border cases""" - from passlib.utils.pbkdf2 import pbkdf2 - def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"): - return pbkdf2(secret, salt, rounds, keylen, prf) - helper() - - # invalid rounds - self.assertRaises(ValueError, helper, rounds=-1) - self.assertRaises(ValueError, helper, rounds=0) - self.assertRaises(TypeError, helper, rounds='x') - - # invalid keylen - self.assertRaises(ValueError, helper, keylen=-1) - self.assertRaises(ValueError, helper, keylen=0) - helper(keylen=1) - self.assertRaises(OverflowError, helper, keylen=20*(2**32-1)+1) - self.assertRaises(TypeError, helper, keylen='x') - - # invalid secret/salt type - self.assertRaises(TypeError, helper, salt=5) - self.assertRaises(TypeError, helper, secret=5) - - # invalid hash - self.assertRaises(ValueError, helper, prf='hmac-foo') - self.assertRaises(NotImplementedError, helper, prf='foo') - self.assertRaises(TypeError, helper, prf=5) - - def test_default_keylen(self): - """test keylen==None""" - from passlib.utils.pbkdf2 import pbkdf2 - def helper(secret=b'password', salt=b'salt', rounds=1, keylen=None, prf="hmac-sha1"): - return pbkdf2(secret, salt, rounds, keylen, prf) - self.assertEqual(len(helper(prf='hmac-sha1')), 20) - self.assertEqual(len(helper(prf='hmac-sha256')), 32) - - def test_custom_prf(self): - """test custom prf function""" - from passlib.utils.pbkdf2 import pbkdf2 - def prf(key, msg): - return hashlib.md5(key+msg+b'fooey').digest() - self.assertRaises(NotImplementedError, pbkdf2, b'secret', b'salt', 1000, 20, prf) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/test_win32.py b/.venv/Lib/site-packages/passlib/tests/test_win32.py deleted file mode 100644 index e818b62..0000000 --- a/.venv/Lib/site-packages/passlib/tests/test_win32.py +++ /dev/null @@ -1,50 +0,0 @@ -"""tests for passlib.win32 -- (c) Assurance Technologies 2003-2009""" -#============================================================================= -# imports -#============================================================================= -# core -import warnings -# site -# pkg -from passlib.tests.utils import TestCase -# module -from passlib.utils.compat import u - -#============================================================================= -# -#============================================================================= -class UtilTest(TestCase): - """test util funcs in passlib.win32""" - - ##test hashes from http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx - ## among other places - - def setUp(self): - super(UtilTest, self).setUp() - warnings.filterwarnings("ignore", - "the 'passlib.win32' module is deprecated") - - def test_lmhash(self): - from passlib.win32 import raw_lmhash - for secret, hash in [ - ("OLDPASSWORD", u("c9b81d939d6fd80cd408e6b105741864")), - ("NEWPASSWORD", u('09eeab5aa415d6e4d408e6b105741864')), - ("welcome", u("c23413a8a1e7665faad3b435b51404ee")), - ]: - result = raw_lmhash(secret, hex=True) - self.assertEqual(result, hash) - - def test_nthash(self): - warnings.filterwarnings("ignore", - r"nthash\.raw_nthash\(\) is deprecated") - from passlib.win32 import raw_nthash - for secret, hash in [ - ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")), - ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")), - ]: - result = raw_nthash(secret, hex=True) - self.assertEqual(result, hash) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/tox_support.py b/.venv/Lib/site-packages/passlib/tests/tox_support.py deleted file mode 100644 index 43170bc..0000000 --- a/.venv/Lib/site-packages/passlib/tests/tox_support.py +++ /dev/null @@ -1,83 +0,0 @@ -"""passlib.tests.tox_support - helper script for tox tests""" -#============================================================================= -# init script env -#============================================================================= -import os, sys -root_dir = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) -sys.path.insert(0, root_dir) - -#============================================================================= -# imports -#============================================================================= -# core -import re -import logging; log = logging.getLogger(__name__) -# site -# pkg -from passlib.utils.compat import print_ -# local -__all__ = [ -] - -#============================================================================= -# main -#============================================================================= -TH_PATH = "passlib.tests.test_handlers" - -def do_hash_tests(*args): - """return list of hash algorithm tests that match regexes""" - if not args: - print(TH_PATH) - return - suffix = '' - args = list(args) - while True: - if args[0] == "--method": - suffix = '.' + args[1] - del args[:2] - else: - break - from passlib.tests import test_handlers - names = [TH_PATH + ":" + name + suffix for name in dir(test_handlers) - if not name.startswith("_") and any(re.match(arg,name) for arg in args)] - print_("\n".join(names)) - return not names - -def do_preset_tests(name): - """return list of preset test names""" - if name == "django" or name == "django-hashes": - do_hash_tests("django_.*_test", "hex_md5_test") - if name == "django": - print_("passlib.tests.test_ext_django") - else: - raise ValueError("unknown name: %r" % name) - -def do_setup_gae(path, runtime): - """write fake GAE ``app.yaml`` to current directory so nosegae will work""" - from passlib.tests.utils import set_file - set_file(os.path.join(path, "app.yaml"), """\ -application: fake-app -version: 2 -runtime: %s -api_version: 1 -threadsafe: no - -handlers: -- url: /.* - script: dummy.py - -libraries: -- name: django - version: "latest" -""" % runtime) - -def main(cmd, *args): - return globals()["do_" + cmd](*args) - -if __name__ == "__main__": - import sys - sys.exit(main(*sys.argv[1:]) or 0) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/tests/utils.py b/.venv/Lib/site-packages/passlib/tests/utils.py deleted file mode 100644 index 79a9f9f..0000000 --- a/.venv/Lib/site-packages/passlib/tests/utils.py +++ /dev/null @@ -1,3621 +0,0 @@ -"""helpers for passlib unittests""" -#============================================================================= -# imports -#============================================================================= -from __future__ import with_statement -# core -from binascii import unhexlify -import contextlib -from functools import wraps, partial -import hashlib -import logging; log = logging.getLogger(__name__) -import random -import re -import os -import sys -import tempfile -import threading -import time -from passlib.exc import PasslibHashWarning, PasslibConfigWarning -from passlib.utils.compat import PY3, JYTHON -import warnings -from warnings import warn -# site -# pkg -from passlib import exc -from passlib.exc import MissingBackendError -import passlib.registry as registry -from passlib.tests.backports import TestCase as _TestCase, skip, skipIf, skipUnless, SkipTest -from passlib.utils import has_rounds_info, has_salt_info, rounds_cost_values, \ - rng as sys_rng, getrandstr, is_ascii_safe, to_native_str, \ - repeat_string, tick, batch -from passlib.utils.compat import iteritems, irange, u, unicode, PY2, nullcontext -from passlib.utils.decor import classproperty -import passlib.utils.handlers as uh -# local -__all__ = [ - # util funcs - 'TEST_MODE', - 'set_file', 'get_file', - - # unit testing - 'TestCase', - 'HandlerCase', -] - -#============================================================================= -# environment detection -#============================================================================= -# figure out if we're running under GAE; -# some tests (e.g. FS writing) should be skipped. -# XXX: is there better way to do this? -try: - import google.appengine -except ImportError: - GAE = False -else: - GAE = True - -def ensure_mtime_changed(path): - """ensure file's mtime has changed""" - # NOTE: this is hack to deal w/ filesystems whose mtime resolution is >= 1s, - # when a test needs to be sure the mtime changed after writing to the file. - last = os.path.getmtime(path) - while os.path.getmtime(path) == last: - time.sleep(0.1) - os.utime(path, None) - -def _get_timer_resolution(timer): - def sample(): - start = cur = timer() - while start == cur: - cur = timer() - return cur-start - return min(sample() for _ in range(3)) -TICK_RESOLUTION = _get_timer_resolution(tick) - -#============================================================================= -# test mode -#============================================================================= -_TEST_MODES = ["quick", "default", "full"] -_test_mode = _TEST_MODES.index(os.environ.get("PASSLIB_TEST_MODE", - "default").strip().lower()) - -def TEST_MODE(min=None, max=None): - """check if test for specified mode should be enabled. - - ``"quick"`` - run the bare minimum tests to ensure functionality. - variable-cost hashes are tested at their lowest setting. - hash algorithms are only tested against the backend that will - be used on the current host. no fuzz testing is done. - - ``"default"`` - same as ``"quick"``, except: hash algorithms are tested - at default levels, and a brief round of fuzz testing is done - for each hash. - - ``"full"`` - extra regression and internal tests are enabled, hash algorithms are tested - against all available backends, unavailable ones are mocked whre possible, - additional time is devoted to fuzz testing. - """ - if min and _test_mode < _TEST_MODES.index(min): - return False - if max and _test_mode > _TEST_MODES.index(max): - return False - return True - -#============================================================================= -# hash object inspection -#============================================================================= -def has_relaxed_setting(handler): - """check if handler supports 'relaxed' kwd""" - # FIXME: I've been lazy, should probably just add 'relaxed' kwd - # to all handlers that derive from GenericHandler - - # ignore wrapper classes for now.. though could introspec. - if hasattr(handler, "orig_prefix"): - return False - - return 'relaxed' in handler.setting_kwds or issubclass(handler, - uh.GenericHandler) - -def get_effective_rounds(handler, rounds=None): - """get effective rounds value from handler""" - handler = unwrap_handler(handler) - return handler(rounds=rounds, use_defaults=True).rounds - -def is_default_backend(handler, backend): - """check if backend is the default for source""" - try: - orig = handler.get_backend() - except MissingBackendError: - return False - try: - handler.set_backend("default") - return handler.get_backend() == backend - finally: - handler.set_backend(orig) - -def iter_alt_backends(handler, current=None, fallback=False): - """ - iterate over alternate backends available to handler. - - .. warning:: - not thread-safe due to has_backend() call - """ - if current is None: - current = handler.get_backend() - backends = handler.backends - idx = backends.index(current)+1 if fallback else 0 - for backend in backends[idx:]: - if backend != current and handler.has_backend(backend): - yield backend - -def get_alt_backend(*args, **kwds): - for backend in iter_alt_backends(*args, **kwds): - return backend - return None - -def unwrap_handler(handler): - """return original handler, removing any wrapper objects""" - while hasattr(handler, "wrapped"): - handler = handler.wrapped - return handler - -def handler_derived_from(handler, base): - """ - test if was derived from via . - """ - # XXX: need way to do this more formally via ifc, - # for now just hacking in the cases we encounter in testing. - if handler == base: - return True - elif isinstance(handler, uh.PrefixWrapper): - while handler: - if handler == base: - return True - # helper set by PrefixWrapper().using() just for this case... - handler = handler._derived_from - return False - elif isinstance(handler, type) and issubclass(handler, uh.MinimalHandler): - return issubclass(handler, base) - else: - raise NotImplementedError("don't know how to inspect handler: %r" % (handler,)) - -@contextlib.contextmanager -def patch_calc_min_rounds(handler): - """ - internal helper for do_config_encrypt() -- - context manager which temporarily replaces handler's _calc_checksum() - with one that uses min_rounds; useful when trying to generate config - with high rounds value, but don't care if output is correct. - """ - if isinstance(handler, type) and issubclass(handler, uh.HasRounds): - # XXX: also require GenericHandler for this branch? - wrapped = handler._calc_checksum - def wrapper(self, *args, **kwds): - rounds = self.rounds - try: - self.rounds = self.min_rounds - return wrapped(self, *args, **kwds) - finally: - self.rounds = rounds - handler._calc_checksum = wrapper - try: - yield - finally: - handler._calc_checksum = wrapped - elif isinstance(handler, uh.PrefixWrapper): - with patch_calc_min_rounds(handler.wrapped): - yield - else: - yield - return - -#============================================================================= -# misc helpers -#============================================================================= -def set_file(path, content): - """set file to specified bytes""" - if isinstance(content, unicode): - content = content.encode("utf-8") - with open(path, "wb") as fh: - fh.write(content) - -def get_file(path): - """read file as bytes""" - with open(path, "rb") as fh: - return fh.read() - -def tonn(source): - """convert native string to non-native string""" - if not isinstance(source, str): - return source - elif PY3: - return source.encode("utf-8") - else: - try: - return source.decode("utf-8") - except UnicodeDecodeError: - return source.decode("latin-1") - -def hb(source): - """ - helper for represent byte strings in hex. - - usage: ``hb("deadbeef23")`` - """ - return unhexlify(re.sub(r"\s", "", source)) - -def limit(value, lower, upper): - if value < lower: - return lower - elif value > upper: - return upper - return value - -def quicksleep(delay): - """because time.sleep() doesn't even have 10ms accuracy on some OSes""" - start = tick() - while tick()-start < delay: - pass - -def time_call(func, setup=None, maxtime=1, bestof=10): - """ - timeit() wrapper which tries to get as accurate a measurement as possible w/in maxtime seconds. - - :returns: - ``(avg_seconds_per_call, log10_number_of_repetitions)`` - """ - from timeit import Timer - from math import log - timer = Timer(func, setup=setup or '') - number = 1 - end = tick() + maxtime - while True: - delta = min(timer.repeat(bestof, number)) - if tick() >= end: - return delta/number, int(log(number, 10)) - number *= 10 - -def run_with_fixed_seeds(count=128, master_seed=0x243F6A8885A308D3): - """ - decorator run test method w/ multiple fixed seeds. - """ - def builder(func): - @wraps(func) - def wrapper(*args, **kwds): - rng = random.Random(master_seed) - for _ in irange(count): - kwds['seed'] = rng.getrandbits(32) - func(*args, **kwds) - return wrapper - return builder - -#============================================================================= -# custom test harness -#============================================================================= - -class TestCase(_TestCase): - """passlib-specific test case class - - this class adds a number of features to the standard TestCase... - * common prefix for all test descriptions - * resets warnings filter & registry for every test - * tweaks to message formatting - * __msg__ kwd added to assertRaises() - * suite of methods for matching against warnings - """ - #=================================================================== - # add various custom features - #=================================================================== - - #--------------------------------------------------------------- - # make it easy for test cases to add common prefix to shortDescription - #--------------------------------------------------------------- - - # string prepended to all tests in TestCase - descriptionPrefix = None - - def shortDescription(self): - """wrap shortDescription() method to prepend descriptionPrefix""" - desc = super(TestCase, self).shortDescription() - prefix = self.descriptionPrefix - if prefix: - desc = "%s: %s" % (prefix, desc or str(self)) - return desc - - #--------------------------------------------------------------- - # hack things so nose and ut2 both skip subclasses who have - # "__unittest_skip=True" set, or whose names start with "_" - #--------------------------------------------------------------- - @classproperty - def __unittest_skip__(cls): - # NOTE: this attr is technically a unittest2 internal detail. - name = cls.__name__ - return name.startswith("_") or \ - getattr(cls, "_%s__unittest_skip" % name, False) - - @classproperty - def __test__(cls): - # make nose just proxy __unittest_skip__ - return not cls.__unittest_skip__ - - # flag to skip *this* class - __unittest_skip = True - - #--------------------------------------------------------------- - # reset warning filters & registry before each test - #--------------------------------------------------------------- - - # flag to reset all warning filters & ignore state - resetWarningState = True - - def setUp(self): - super(TestCase, self).setUp() - self.setUpWarnings() - # have uh.debug_only_repr() return real values for duration of test - self.patchAttr(exc, "ENABLE_DEBUG_ONLY_REPR", True) - - def setUpWarnings(self): - """helper to init warning filters before subclass setUp()""" - if self.resetWarningState: - ctx = reset_warnings() - ctx.__enter__() - self.addCleanup(ctx.__exit__) - - # ignore security warnings, tests may deliberately cause these - # TODO: may want to filter out a few of this, but not blanket filter... - # warnings.filterwarnings("ignore", category=exc.PasslibSecurityWarning) - - # ignore warnings about PasswordHash features deprecated in 1.7 - # TODO: should be cleaned in 2.0, when support will be dropped. - # should be kept until then, so we test the legacy paths. - warnings.filterwarnings("ignore", r"the method .*\.(encrypt|genconfig|genhash)\(\) is deprecated") - warnings.filterwarnings("ignore", r"the 'vary_rounds' option is deprecated") - warnings.filterwarnings("ignore", r"Support for `(py-bcrypt|bcryptor)` is deprecated") - - #--------------------------------------------------------------- - # tweak message formatting so longMessage mode is only enabled - # if msg ends with ":", and turn on longMessage by default. - #--------------------------------------------------------------- - longMessage = True - - def _formatMessage(self, msg, std): - if self.longMessage and msg and msg.rstrip().endswith(":"): - return '%s %s' % (msg.rstrip(), std) - else: - return msg or std - - #--------------------------------------------------------------- - # override assertRaises() to support '__msg__' keyword, - # and to return the caught exception for further examination - #--------------------------------------------------------------- - def assertRaises(self, _exc_type, _callable=None, *args, **kwds): - msg = kwds.pop("__msg__", None) - if _callable is None: - # FIXME: this ignores 'msg' - return super(TestCase, self).assertRaises(_exc_type, None, - *args, **kwds) - try: - result = _callable(*args, **kwds) - except _exc_type as err: - return err - std = "function returned %r, expected it to raise %r" % (result, - _exc_type) - raise self.failureException(self._formatMessage(msg, std)) - - #--------------------------------------------------------------- - # forbid a bunch of deprecated aliases so I stop using them - #--------------------------------------------------------------- - def assertEquals(self, *a, **k): - raise AssertionError("this alias is deprecated by unittest2") - assertNotEquals = assertRegexMatches = assertEquals - - #=================================================================== - # custom methods for matching warnings - #=================================================================== - def assertWarning(self, warning, - message_re=None, message=None, - category=None, - filename_re=None, filename=None, - lineno=None, - msg=None, - ): - """check if warning matches specified parameters. - 'warning' is the instance of Warning to match against; - can also be instance of WarningMessage (as returned by catch_warnings). - """ - # check input type - if hasattr(warning, "category"): - # resolve WarningMessage -> Warning, but preserve original - wmsg = warning - warning = warning.message - else: - # no original WarningMessage, passed raw Warning - wmsg = None - - # tests that can use a warning instance or WarningMessage object - if message: - self.assertEqual(str(warning), message, msg) - if message_re: - self.assertRegex(str(warning), message_re, msg) - if category: - self.assertIsInstance(warning, category, msg) - - # tests that require a WarningMessage object - if filename or filename_re: - if not wmsg: - raise TypeError("matching on filename requires a " - "WarningMessage instance") - real = wmsg.filename - if real.endswith(".pyc") or real.endswith(".pyo"): - # FIXME: should use a stdlib call to resolve this back - # to module's original filename. - real = real[:-1] - if filename: - self.assertEqual(real, filename, msg) - if filename_re: - self.assertRegex(real, filename_re, msg) - if lineno: - if not wmsg: - raise TypeError("matching on lineno requires a " - "WarningMessage instance") - self.assertEqual(wmsg.lineno, lineno, msg) - - class _AssertWarningList(warnings.catch_warnings): - """context manager for assertWarningList()""" - def __init__(self, case, **kwds): - self.case = case - self.kwds = kwds - self.__super = super(TestCase._AssertWarningList, self) - self.__super.__init__(record=True) - - def __enter__(self): - self.log = self.__super.__enter__() - - def __exit__(self, *exc_info): - self.__super.__exit__(*exc_info) - if exc_info[0] is None: - self.case.assertWarningList(self.log, **self.kwds) - - def assertWarningList(self, wlist=None, desc=None, msg=None): - """check that warning list (e.g. from catch_warnings) matches pattern""" - if desc is None: - assert wlist is not None - return self._AssertWarningList(self, desc=wlist, msg=msg) - # TODO: make this display better diff of *which* warnings did not match - assert desc is not None - if not isinstance(desc, (list,tuple)): - desc = [desc] - for idx, entry in enumerate(desc): - if isinstance(entry, str): - entry = dict(message_re=entry) - elif isinstance(entry, type) and issubclass(entry, Warning): - entry = dict(category=entry) - elif not isinstance(entry, dict): - raise TypeError("entry must be str, warning, or dict") - try: - data = wlist[idx] - except IndexError: - break - self.assertWarning(data, msg=msg, **entry) - else: - if len(wlist) == len(desc): - return - std = "expected %d warnings, found %d: wlist=%s desc=%r" % \ - (len(desc), len(wlist), self._formatWarningList(wlist), desc) - raise self.failureException(self._formatMessage(msg, std)) - - def consumeWarningList(self, wlist, desc=None, *args, **kwds): - """[deprecated] assertWarningList() variant that clears list afterwards""" - if desc is None: - desc = [] - self.assertWarningList(wlist, desc, *args, **kwds) - del wlist[:] - - def _formatWarning(self, entry): - tail = "" - if hasattr(entry, "message"): - # WarningMessage instance. - tail = " filename=%r lineno=%r" % (entry.filename, entry.lineno) - if entry.line: - tail += " line=%r" % (entry.line,) - entry = entry.message - cls = type(entry) - return "<%s.%s message=%r%s>" % (cls.__module__, cls.__name__, - str(entry), tail) - - def _formatWarningList(self, wlist): - return "[%s]" % ", ".join(self._formatWarning(entry) for entry in wlist) - - #=================================================================== - # capability tests - #=================================================================== - def require_stringprep(self): - """helper to skip test if stringprep is missing""" - from passlib.utils import stringprep - if not stringprep: - from passlib.utils import _stringprep_missing_reason - raise self.skipTest("not available - stringprep module is " + - _stringprep_missing_reason) - - def require_TEST_MODE(self, level): - """skip test for all PASSLIB_TEST_MODE values below """ - if not TEST_MODE(level): - raise self.skipTest("requires >= %r test mode" % level) - - def require_writeable_filesystem(self): - """skip test if writeable FS not available""" - if GAE: - return self.skipTest("GAE doesn't offer read/write filesystem access") - - #=================================================================== - # reproducible random helpers - #=================================================================== - - #: global thread lock for random state - #: XXX: could split into global & per-instance locks if need be - _random_global_lock = threading.Lock() - - #: cache of global seed value, initialized on first call to getRandom() - _random_global_seed = None - - #: per-instance cache of name -> RNG - _random_cache = None - - def getRandom(self, name="default", seed=None): - """ - Return a :class:`random.Random` object for current test method to use. - Within an instance, multiple calls with the same name will return - the same object. - - When first created, each RNG will be seeded with value derived from - a global seed, the test class module & name, the current test method name, - and the **name** parameter. - - The global seed taken from the $RANDOM_TEST_SEED env var, - the $PYTHONHASHSEED env var, or a randomly generated the - first time this method is called. In all cases, the value - is logged for reproducibility. - - :param name: - name to uniquely identify separate RNGs w/in a test - (e.g. for threaded tests). - - :param seed: - override global seed when initialzing rng. - - :rtype: random.Random - """ - # check cache - cache = self._random_cache - if cache and name in cache: - return cache[name] - - with self._random_global_lock: - - # check cache again, and initialize it - cache = self._random_cache - if cache and name in cache: - return cache[name] - elif not cache: - cache = self._random_cache = {} - - # init global seed - global_seed = seed or TestCase._random_global_seed - if global_seed is None: - # NOTE: checking PYTHONHASHSEED, because if that's set, - # the test runner wants something reproducible. - global_seed = TestCase._random_global_seed = \ - int(os.environ.get("RANDOM_TEST_SEED") or - os.environ.get("PYTHONHASHSEED") or - sys_rng.getrandbits(32)) - # XXX: would it be better to print() this? - log.info("using RANDOM_TEST_SEED=%d", global_seed) - - # create seed - cls = type(self) - source = "\n".join([str(global_seed), cls.__module__, cls.__name__, - self._testMethodName, name]) - digest = hashlib.sha256(source.encode("utf-8")).hexdigest() - seed = int(digest[:16], 16) - - # create rng - value = cache[name] = random.Random(seed) - return value - - #=================================================================== - # subtests - #=================================================================== - - has_real_subtest = hasattr(_TestCase, "subTest") - - @contextlib.contextmanager - def subTest(self, *args, **kwds): - """ - wrapper/backport for .subTest() which also traps SkipTest errors. - (see source for details) - """ - # this function works around two things: - # * TestCase.subTest() wasn't added until Py34; so for older python versions, - # we either need unittest2 installed, or provide stub of our own. - # this method provides a stub if needed (based on .has_real_subtest check) - # - # * as 2020-10-08, .subTest() doesn't play nicely w/ .skipTest(); - # and also makes it hard to debug which subtest had a failure. - # (see https://bugs.python.org/issue25894 and https://bugs.python.org/issue35327) - # this method traps skipTest exceptions, and adds some logging to help debug - # which subtest caused the issue. - - # setup way to log subtest info - # XXX: would like better way to inject messages into test output; - # but this at least gets us something for debugging... - # NOTE: this hack will miss parent params if called from nested .subTest() - def _render_title(_msg=None, **params): - out = ("[%s] " % _msg if _msg else "") - if params: - out += "(%s)" % " ".join("%s=%r" % tuple(item) for item in params.items()) - return out.strip() or "" - - test_log = self.getLogger() - title = _render_title(*args, **kwds) - - # use real subtest manager if available - if self.has_real_subtest: - ctx = super(TestCase, self).subTest(*args, **kwds) - else: - ctx = nullcontext() - - # run the subtest - with ctx: - test_log.info("running subtest: %s", title) - try: - yield - except SkipTest: - # silence "SkipTest" exceptions, want to keep running next subtest. - test_log.info("subtest skipped: %s", title) - pass - except Exception as err: - # log unhandled exception occurred - # (assuming traceback will be reported up higher, so not bothering here) - test_log.warning("subtest failed: %s: %s: %r", title, type(err).__name__, str(err)) - raise - - # XXX: check for "failed" state in ``self._outcome`` before writing this? - test_log.info("subtest passed: %s", title) - - #=================================================================== - # other - #=================================================================== - _mktemp_queue = None - - def mktemp(self, *args, **kwds): - """create temp file that's cleaned up at end of test""" - self.require_writeable_filesystem() - fd, path = tempfile.mkstemp(*args, **kwds) - os.close(fd) - queue = self._mktemp_queue - if queue is None: - queue = self._mktemp_queue = [] - def cleaner(): - for path in queue: - if os.path.exists(path): - os.remove(path) - del queue[:] - self.addCleanup(cleaner) - queue.append(path) - return path - - def patchAttr(self, obj, attr, value, require_existing=True, wrap=False): - """monkeypatch object value, restoring original value on cleanup""" - try: - orig = getattr(obj, attr) - except AttributeError: - if require_existing: - raise - def cleanup(): - try: - delattr(obj, attr) - except AttributeError: - pass - self.addCleanup(cleanup) - else: - self.addCleanup(setattr, obj, attr, orig) - if wrap: - value = partial(value, orig) - wraps(orig)(value) - setattr(obj, attr, value) - - def getLogger(self): - """ - return logger named after current test. - """ - cls = type(self) - # NOTE: conditional on qualname for PY2 compat - path = cls.__module__ + "." + getattr(cls, "__qualname__", cls.__name__) - name = self._testMethodName - if name: - path = path + "." + name - return logging.getLogger(path) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# other unittest helpers -#============================================================================= - -RESERVED_BACKEND_NAMES = ["any", "default"] - - -def doesnt_require_backend(func): - """ - decorator for HandlerCase.create_backend_case() -- - used to decorate methods that should be run even if backend isn't present - (by default, full test suite is skipped when backend is missing) - - NOTE: tests decorated with this should not rely on handler have expected (or any!) backend. - """ - func._doesnt_require_backend = True - return func - - -class HandlerCase(TestCase): - """base class for testing password hash handlers (esp passlib.utils.handlers subclasses) - - In order to use this to test a handler, - create a subclass will all the appropriate attributes - filled as listed in the example below, - and run the subclass via unittest. - - .. todo:: - - Document all of the options HandlerCase offers. - - .. note:: - - This is subclass of :class:`unittest.TestCase` - (or :class:`unittest2.TestCase` if available). - """ - #=================================================================== - # class attrs - should be filled in by subclass - #=================================================================== - - #--------------------------------------------------------------- - # handler setup - #--------------------------------------------------------------- - - # handler class to test [required] - handler = None - - # if set, run tests against specified backend - backend = None - - #--------------------------------------------------------------- - # test vectors - #--------------------------------------------------------------- - - # list of (secret, hash) tuples which are known to be correct - known_correct_hashes = [] - - # list of (config, secret, hash) tuples are known to be correct - known_correct_configs = [] - - # list of (alt_hash, secret, hash) tuples, where alt_hash is a hash - # using an alternate representation that should be recognized and verify - # correctly, but should be corrected to match hash when passed through - # genhash() - known_alternate_hashes = [] - - # hashes so malformed they aren't even identified properly - known_unidentified_hashes = [] - - # hashes which are identifiabled but malformed - they should identify() - # as True, but cause an error when passed to genhash/verify. - known_malformed_hashes = [] - - # list of (handler name, hash) pairs for other algorithm's hashes that - # handler shouldn't identify as belonging to it this list should generally - # be sufficient (if handler name in list, that entry will be skipped) - known_other_hashes = [ - ('des_crypt', '6f8c114b58f2c'), - ('md5_crypt', '$1$dOHYPKoP$tnxS1T8Q6VVn3kpV8cN6o.'), - ('sha512_crypt', "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywW" - "vt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1"), - ] - - # passwords used to test basic hash behavior - generally - # don't need to be overidden. - stock_passwords = [ - u("test"), - u("\u20AC\u00A5$"), - b'\xe2\x82\xac\xc2\xa5$' - ] - - #--------------------------------------------------------------- - # option flags - #--------------------------------------------------------------- - - # whether hash is case insensitive - # True, False, or special value "verify-only" (which indicates - # hash contains case-sensitive portion, but verifies is case-insensitive) - secret_case_insensitive = False - - # flag if scheme accepts ALL hash strings (e.g. plaintext) - accepts_all_hashes = False - - # flag if scheme has "is_disabled" set, and contains 'salted' data - disabled_contains_salt = False - - # flag/hack to filter PasslibHashWarning issued by test_72_configs() - filter_config_warnings = False - - # forbid certain characters in passwords - @classproperty - def forbidden_characters(cls): - # anything that supports crypt() interface should forbid null chars, - # since crypt() uses null-terminated strings. - if 'os_crypt' in getattr(cls.handler, "backends", ()): - return b"\x00" - return None - - #=================================================================== - # internal class attrs - #=================================================================== - __unittest_skip = True - - @property - def descriptionPrefix(self): - handler = self.handler - name = handler.name - if hasattr(handler, "get_backend"): - name += " (%s backend)" % (handler.get_backend(),) - return name - - #=================================================================== - # support methods - #=================================================================== - - #--------------------------------------------------------------- - # configuration helpers - #--------------------------------------------------------------- - @classmethod - def iter_known_hashes(cls): - """iterate through known (secret, hash) pairs""" - for secret, hash in cls.known_correct_hashes: - yield secret, hash - for config, secret, hash in cls.known_correct_configs: - yield secret, hash - for alt, secret, hash in cls.known_alternate_hashes: - yield secret, hash - - def get_sample_hash(self): - """test random sample secret/hash pair""" - known = list(self.iter_known_hashes()) - return self.getRandom().choice(known) - - #--------------------------------------------------------------- - # test helpers - #--------------------------------------------------------------- - def check_verify(self, secret, hash, msg=None, negate=False): - """helper to check verify() outcome, honoring is_disabled_handler""" - result = self.do_verify(secret, hash) - self.assertTrue(result is True or result is False, - "verify() returned non-boolean value: %r" % (result,)) - if self.handler.is_disabled or negate: - if not result: - return - if not msg: - msg = ("verify incorrectly returned True: secret=%r, hash=%r" % - (secret, hash)) - raise self.failureException(msg) - else: - if result: - return - if not msg: - msg = "verify failed: secret=%r, hash=%r" % (secret, hash) - raise self.failureException(msg) - - def check_returned_native_str(self, result, func_name): - self.assertIsInstance(result, str, - "%s() failed to return native string: %r" % (func_name, result,)) - - #--------------------------------------------------------------- - # PasswordHash helpers - wraps all calls to PasswordHash api, - # so that subclasses can fill in defaults and account for other specialized behavior - #--------------------------------------------------------------- - def populate_settings(self, kwds): - """subclassable method to populate default settings""" - # use lower rounds settings for certain test modes - handler = self.handler - if 'rounds' in handler.setting_kwds and 'rounds' not in kwds: - mn = handler.min_rounds - df = handler.default_rounds - if TEST_MODE(max="quick"): - # use minimum rounds for quick mode - kwds['rounds'] = max(3, mn) - else: - # use default/16 otherwise - factor = 3 - if getattr(handler, "rounds_cost", None) == "log2": - df -= factor - else: - df //= (1<= 1") - - # check min_salt_size - if cls.min_salt_size < 0: - raise AssertionError("min_salt_chars must be >= 0") - if mx_set and cls.min_salt_size > cls.max_salt_size: - raise AssertionError("min_salt_chars must be <= max_salt_chars") - - # check default_salt_size - if cls.default_salt_size < cls.min_salt_size: - raise AssertionError("default_salt_size must be >= min_salt_size") - if mx_set and cls.default_salt_size > cls.max_salt_size: - raise AssertionError("default_salt_size must be <= max_salt_size") - - # check for 'salt_size' keyword - # NOTE: skipping warning if default salt size is already maxed out - # (might change that in future) - if 'salt_size' not in cls.setting_kwds and (not mx_set or cls.default_salt_size < cls.max_salt_size): - warn('%s: hash handler supports range of salt sizes, ' - 'but doesn\'t offer \'salt_size\' setting' % (cls.name,)) - - # check salt_chars & default_salt_chars - if cls.salt_chars: - if not cls.default_salt_chars: - raise AssertionError("default_salt_chars must not be empty") - for c in cls.default_salt_chars: - if c not in cls.salt_chars: - raise AssertionError("default_salt_chars must be subset of salt_chars: %r not in salt_chars" % (c,)) - else: - if not cls.default_salt_chars: - raise AssertionError("default_salt_chars MUST be specified if salt_chars is empty") - - @property - def salt_bits(self): - """calculate number of salt bits in hash""" - # XXX: replace this with bitsize() method? - handler = self.handler - assert has_salt_info(handler), "need explicit bit-size for " + handler.name - from math import log - # FIXME: this may be off for case-insensitive hashes, but that accounts - # for ~1 bit difference, which is good enough for test_11() - return int(handler.default_salt_size * - log(len(handler.default_salt_chars), 2)) - - def test_11_unique_salt(self): - """test hash() / genconfig() creates new salt each time""" - self.require_salt() - # odds of picking 'n' identical salts at random is '(.5**salt_bits)**n'. - # we want to pick the smallest N needed s.t. odds are <1/10**d, just - # to eliminate false-positives. which works out to n>3.33+d-salt_bits. - # for 1/1e12 odds, n=1 is sufficient for most hashes, but a few border cases (e.g. - # cisco_type7) have < 16 bits of salt, requiring more. - samples = max(1, 4 + 12 - self.salt_bits) - - def sampler(func): - value1 = func() - for _ in irange(samples): - value2 = func() - if value1 != value2: - return - raise self.failureException("failed to find different salt after " - "%d samples" % (samples,)) - sampler(self.do_genconfig) - sampler(lambda: self.do_encrypt("stub")) - - def test_12_min_salt_size(self): - """test hash() / genconfig() honors min_salt_size""" - self.require_salt_info() - - handler = self.handler - salt_char = handler.salt_chars[0:1] - min_size = handler.min_salt_size - - # - # check min is accepted - # - s1 = salt_char * min_size - self.do_genconfig(salt=s1) - - self.do_encrypt('stub', salt_size=min_size) - - # - # check min-1 is rejected - # - if min_size > 0: - self.assertRaises(ValueError, self.do_genconfig, - salt=s1[:-1]) - - self.assertRaises(ValueError, self.do_encrypt, 'stub', - salt_size=min_size-1) - - def test_13_max_salt_size(self): - """test hash() / genconfig() honors max_salt_size""" - self.require_salt_info() - - handler = self.handler - max_size = handler.max_salt_size - salt_char = handler.salt_chars[0:1] - - # NOTE: skipping this for hashes like argon2 since max_salt_size takes WAY too much memory - if max_size is None or max_size > (1 << 20): - # - # if it's not set, salt should never be truncated; so test it - # with an unreasonably large salt. - # - s1 = salt_char * 1024 - c1 = self.do_stub_encrypt(salt=s1) - c2 = self.do_stub_encrypt(salt=s1 + salt_char) - self.assertNotEqual(c1, c2) - - self.do_stub_encrypt(salt_size=1024) - - else: - # - # check max size is accepted - # - s1 = salt_char * max_size - c1 = self.do_stub_encrypt(salt=s1) - - self.do_stub_encrypt(salt_size=max_size) - - # - # check max size + 1 is rejected - # - s2 = s1 + salt_char - self.assertRaises(ValueError, self.do_stub_encrypt, salt=s2) - - self.assertRaises(ValueError, self.do_stub_encrypt, salt_size=max_size + 1) - - # - # should accept too-large salt in relaxed mode - # - if has_relaxed_setting(handler): - with warnings.catch_warnings(record=True): # issues passlibhandlerwarning - c2 = self.do_stub_encrypt(salt=s2, relaxed=True) - self.assertEqual(c2, c1) - - # - # if min_salt supports it, check smaller than mx is NOT truncated - # - if handler.min_salt_size < max_size: - c3 = self.do_stub_encrypt(salt=s1[:-1]) - self.assertNotEqual(c3, c1) - - # whether salt should be passed through bcrypt repair function - fuzz_salts_need_bcrypt_repair = False - - def prepare_salt(self, salt): - """prepare generated salt""" - if self.fuzz_salts_need_bcrypt_repair: - from passlib.utils.binary import bcrypt64 - salt = bcrypt64.repair_unused(salt) - return salt - - def test_14_salt_chars(self): - """test hash() honors salt_chars""" - self.require_salt_info() - - handler = self.handler - mx = handler.max_salt_size - mn = handler.min_salt_size - cs = handler.salt_chars - raw = isinstance(cs, bytes) - - # make sure all listed chars are accepted - for salt in batch(cs, mx or 32): - if len(salt) < mn: - salt = repeat_string(salt, mn) - salt = self.prepare_salt(salt) - self.do_stub_encrypt(salt=salt) - - # check some invalid salt chars, make sure they're rejected - source = u('\x00\xff') - if raw: - source = source.encode("latin-1") - chunk = max(mn, 1) - for c in source: - if c not in cs: - self.assertRaises(ValueError, self.do_stub_encrypt, salt=c*chunk, - __msg__="invalid salt char %r:" % (c,)) - - @property - def salt_type(self): - """hack to determine salt keyword's datatype""" - # NOTE: cisco_type7 uses 'int' - if getattr(self.handler, "_salt_is_bytes", False): - return bytes - else: - return unicode - - def test_15_salt_type(self): - """test non-string salt values""" - self.require_salt() - salt_type = self.salt_type - salt_size = getattr(self.handler, "min_salt_size", 0) or 8 - - # should always throw error for random class. - class fake(object): - pass - self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=fake()) - - # unicode should be accepted only if salt_type is unicode. - if salt_type is not unicode: - self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=u('x') * salt_size) - - # bytes should be accepted only if salt_type is bytes, - # OR if salt type is unicode and running PY2 - to allow native strings. - if not (salt_type is bytes or (PY2 and salt_type is unicode)): - self.assertRaises(TypeError, self.do_encrypt, 'stub', salt=b'x' * salt_size) - - def test_using_salt_size(self): - """Handler.using() -- default_salt_size""" - self.require_salt_info() - - handler = self.handler - mn = handler.min_salt_size - mx = handler.max_salt_size - df = handler.default_salt_size - - # should prevent setting below handler limit - self.assertRaises(ValueError, handler.using, default_salt_size=-1) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(default_salt_size=-1, relaxed=True) - self.assertEqual(temp.default_salt_size, mn) - - # should prevent setting above handler limit - if mx: - self.assertRaises(ValueError, handler.using, default_salt_size=mx+1) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(default_salt_size=mx+1, relaxed=True) - self.assertEqual(temp.default_salt_size, mx) - - # try setting to explicit value - if mn != mx: - temp = handler.using(default_salt_size=mn+1) - self.assertEqual(temp.default_salt_size, mn+1) - self.assertEqual(handler.default_salt_size, df) - - temp = handler.using(default_salt_size=mn+2) - self.assertEqual(temp.default_salt_size, mn+2) - self.assertEqual(handler.default_salt_size, df) - - # accept strings - if mn == mx: - ref = mn - else: - ref = mn + 1 - temp = handler.using(default_salt_size=str(ref)) - self.assertEqual(temp.default_salt_size, ref) - - # reject invalid strings - self.assertRaises(ValueError, handler.using, default_salt_size=str(ref) + "xxx") - - # honor 'salt_size' alias - temp = handler.using(salt_size=ref) - self.assertEqual(temp.default_salt_size, ref) - - #=================================================================== - # rounds - #=================================================================== - def require_rounds_info(self): - if not has_rounds_info(self.handler): - raise self.skipTest("handler lacks rounds attributes") - - def test_20_optional_rounds_attributes(self): - """validate optional rounds attributes""" - self.require_rounds_info() - - cls = self.handler - AssertionError = self.failureException - - # check max_rounds - if cls.max_rounds is None: - raise AssertionError("max_rounds not specified") - if cls.max_rounds < 1: - raise AssertionError("max_rounds must be >= 1") - - # check min_rounds - if cls.min_rounds < 0: - raise AssertionError("min_rounds must be >= 0") - if cls.min_rounds > cls.max_rounds: - raise AssertionError("min_rounds must be <= max_rounds") - - # check default_rounds - if cls.default_rounds is not None: - if cls.default_rounds < cls.min_rounds: - raise AssertionError("default_rounds must be >= min_rounds") - if cls.default_rounds > cls.max_rounds: - raise AssertionError("default_rounds must be <= max_rounds") - - # check rounds_cost - if cls.rounds_cost not in rounds_cost_values: - raise AssertionError("unknown rounds cost constant: %r" % (cls.rounds_cost,)) - - def test_21_min_rounds(self): - """test hash() / genconfig() honors min_rounds""" - self.require_rounds_info() - handler = self.handler - min_rounds = handler.min_rounds - - # check min is accepted - self.do_genconfig(rounds=min_rounds) - self.do_encrypt('stub', rounds=min_rounds) - - # check min-1 is rejected - self.assertRaises(ValueError, self.do_genconfig, rounds=min_rounds-1) - self.assertRaises(ValueError, self.do_encrypt, 'stub', rounds=min_rounds-1) - - # TODO: check relaxed mode clips min-1 - - def test_21b_max_rounds(self): - """test hash() / genconfig() honors max_rounds""" - self.require_rounds_info() - handler = self.handler - max_rounds = handler.max_rounds - - if max_rounds is not None: - # check max+1 is rejected - self.assertRaises(ValueError, self.do_genconfig, rounds=max_rounds+1) - self.assertRaises(ValueError, self.do_encrypt, 'stub', rounds=max_rounds+1) - - # handle max rounds - if max_rounds is None: - self.do_stub_encrypt(rounds=(1 << 31) - 1) - else: - self.do_stub_encrypt(rounds=max_rounds) - - # TODO: check relaxed mode clips max+1 - - #-------------------------------------------------------------------------------------- - # HasRounds.using() / .needs_update() -- desired rounds limits - #-------------------------------------------------------------------------------------- - def _create_using_rounds_helper(self): - """ - setup test helpers for testing handler.using()'s rounds parameters. - """ - self.require_rounds_info() - handler = self.handler - - if handler.name == "bsdi_crypt": - # hack to bypass bsdi-crypt's "odd rounds only" behavior, messes up this test - orig_handler = handler - handler = handler.using() - handler._generate_rounds = classmethod(lambda cls: super(orig_handler, cls)._generate_rounds()) - - # create some fake values to test with - orig_min_rounds = handler.min_rounds - orig_max_rounds = handler.max_rounds - orig_default_rounds = handler.default_rounds - medium = ((orig_max_rounds or 9999) + orig_min_rounds) // 2 - if medium == orig_default_rounds: - medium += 1 - small = (orig_min_rounds + medium) // 2 - large = ((orig_max_rounds or 9999) + medium) // 2 - - if handler.name == "bsdi_crypt": - # hack to avoid even numbered rounds - small |= 1 - medium |= 1 - large |= 1 - adj = 2 - else: - adj = 1 - - # create a subclass with small/medium/large as new default desired values - with self.assertWarningList([]): - subcls = handler.using( - min_desired_rounds=small, - max_desired_rounds=large, - default_rounds=medium, - ) - - # return helpers - return handler, subcls, small, medium, large, adj - - def test_has_rounds_using_harness(self): - """ - HasRounds.using() -- sanity check test harness - """ - # setup helpers - self.require_rounds_info() - handler = self.handler - orig_min_rounds = handler.min_rounds - orig_max_rounds = handler.max_rounds - orig_default_rounds = handler.default_rounds - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - - # shouldn't affect original handler at all - self.assertEqual(handler.min_rounds, orig_min_rounds) - self.assertEqual(handler.max_rounds, orig_max_rounds) - self.assertEqual(handler.min_desired_rounds, None) - self.assertEqual(handler.max_desired_rounds, None) - self.assertEqual(handler.default_rounds, orig_default_rounds) - - # should affect subcls' desired value, but not hard min/max - self.assertEqual(subcls.min_rounds, orig_min_rounds) - self.assertEqual(subcls.max_rounds, orig_max_rounds) - self.assertEqual(subcls.default_rounds, medium) - self.assertEqual(subcls.min_desired_rounds, small) - self.assertEqual(subcls.max_desired_rounds, large) - - def test_has_rounds_using_w_min_rounds(self): - """ - HasRounds.using() -- min_rounds / min_desired_rounds - """ - # setup helpers - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - orig_min_rounds = handler.min_rounds - orig_max_rounds = handler.max_rounds - orig_default_rounds = handler.default_rounds - - # .using() should clip values below valid minimum, w/ warning - if orig_min_rounds > 0: - self.assertRaises(ValueError, handler.using, min_desired_rounds=orig_min_rounds - adj) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(min_desired_rounds=orig_min_rounds - adj, relaxed=True) - self.assertEqual(temp.min_desired_rounds, orig_min_rounds) - - # .using() should clip values above valid maximum, w/ warning - if orig_max_rounds: - self.assertRaises(ValueError, handler.using, min_desired_rounds=orig_max_rounds + adj) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(min_desired_rounds=orig_max_rounds + adj, relaxed=True) - self.assertEqual(temp.min_desired_rounds, orig_max_rounds) - - # .using() should allow values below previous desired minimum, w/o warning - with self.assertWarningList([]): - temp = subcls.using(min_desired_rounds=small - adj) - self.assertEqual(temp.min_desired_rounds, small - adj) - - # .using() should allow values w/in previous range - temp = subcls.using(min_desired_rounds=small + 2 * adj) - self.assertEqual(temp.min_desired_rounds, small + 2 * adj) - - # .using() should allow values above previous desired maximum, w/o warning - with self.assertWarningList([]): - temp = subcls.using(min_desired_rounds=large + adj) - self.assertEqual(temp.min_desired_rounds, large + adj) - - # hash() etc should allow explicit values below desired minimum - # NOTE: formerly issued a warning in passlib 1.6, now just a wrapper for .using() - self.assertEqual(get_effective_rounds(subcls, small + adj), small + adj) - self.assertEqual(get_effective_rounds(subcls, small), small) - with self.assertWarningList([]): - self.assertEqual(get_effective_rounds(subcls, small - adj), small - adj) - - # 'min_rounds' should be treated as alias for 'min_desired_rounds' - temp = handler.using(min_rounds=small) - self.assertEqual(temp.min_desired_rounds, small) - - # should be able to specify strings - temp = handler.using(min_rounds=str(small)) - self.assertEqual(temp.min_desired_rounds, small) - - # invalid strings should cause error - self.assertRaises(ValueError, handler.using, min_rounds=str(small) + "xxx") - - def test_has_rounds_replace_w_max_rounds(self): - """ - HasRounds.using() -- max_rounds / max_desired_rounds - """ - # setup helpers - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - orig_min_rounds = handler.min_rounds - orig_max_rounds = handler.max_rounds - - # .using() should clip values below valid minimum w/ warning - if orig_min_rounds > 0: - self.assertRaises(ValueError, handler.using, max_desired_rounds=orig_min_rounds - adj) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(max_desired_rounds=orig_min_rounds - adj, relaxed=True) - self.assertEqual(temp.max_desired_rounds, orig_min_rounds) - - # .using() should clip values above valid maximum, w/ warning - if orig_max_rounds: - self.assertRaises(ValueError, handler.using, max_desired_rounds=orig_max_rounds + adj) - with self.assertWarningList([PasslibHashWarning]): - temp = handler.using(max_desired_rounds=orig_max_rounds + adj, relaxed=True) - self.assertEqual(temp.max_desired_rounds, orig_max_rounds) - - # .using() should clip values below previous minimum, w/ warning - with self.assertWarningList([PasslibConfigWarning]): - temp = subcls.using(max_desired_rounds=small - adj) - self.assertEqual(temp.max_desired_rounds, small) - - # .using() should reject explicit min > max - self.assertRaises(ValueError, subcls.using, - min_desired_rounds=medium+adj, - max_desired_rounds=medium-adj) - - # .using() should allow values w/in previous range - temp = subcls.using(min_desired_rounds=large - 2 * adj) - self.assertEqual(temp.min_desired_rounds, large - 2 * adj) - - # .using() should allow values above previous desired maximum, w/o warning - with self.assertWarningList([]): - temp = subcls.using(max_desired_rounds=large + adj) - self.assertEqual(temp.max_desired_rounds, large + adj) - - # hash() etc should allow explicit values above desired minimum, w/o warning - # NOTE: formerly issued a warning in passlib 1.6, now just a wrapper for .using() - self.assertEqual(get_effective_rounds(subcls, large - adj), large - adj) - self.assertEqual(get_effective_rounds(subcls, large), large) - with self.assertWarningList([]): - self.assertEqual(get_effective_rounds(subcls, large + adj), large + adj) - - # 'max_rounds' should be treated as alias for 'max_desired_rounds' - temp = handler.using(max_rounds=large) - self.assertEqual(temp.max_desired_rounds, large) - - # should be able to specify strings - temp = handler.using(max_desired_rounds=str(large)) - self.assertEqual(temp.max_desired_rounds, large) - - # invalid strings should cause error - self.assertRaises(ValueError, handler.using, max_desired_rounds=str(large) + "xxx") - - def test_has_rounds_using_w_default_rounds(self): - """ - HasRounds.using() -- default_rounds - """ - # setup helpers - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - orig_max_rounds = handler.max_rounds - - # XXX: are there any other cases that need testing? - - # implicit default rounds -- increase to min_rounds - temp = subcls.using(min_rounds=medium+adj) - self.assertEqual(temp.default_rounds, medium+adj) - - # implicit default rounds -- decrease to max_rounds - temp = subcls.using(max_rounds=medium-adj) - self.assertEqual(temp.default_rounds, medium-adj) - - # explicit default rounds below desired minimum - # XXX: make this a warning if min is implicit? - self.assertRaises(ValueError, subcls.using, default_rounds=small-adj) - - # explicit default rounds above desired maximum - # XXX: make this a warning if max is implicit? - if orig_max_rounds: - self.assertRaises(ValueError, subcls.using, default_rounds=large+adj) - - # hash() etc should implicit default rounds, but get overridden - self.assertEqual(get_effective_rounds(subcls), medium) - self.assertEqual(get_effective_rounds(subcls, medium+adj), medium+adj) - - # should be able to specify strings - temp = handler.using(default_rounds=str(medium)) - self.assertEqual(temp.default_rounds, medium) - - # invalid strings should cause error - self.assertRaises(ValueError, handler.using, default_rounds=str(medium) + "xxx") - - def test_has_rounds_using_w_rounds(self): - """ - HasRounds.using() -- rounds - """ - # setup helpers - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - orig_max_rounds = handler.max_rounds - - # 'rounds' should be treated as fallback for min, max, and default - temp = subcls.using(rounds=medium+adj) - self.assertEqual(temp.min_desired_rounds, medium+adj) - self.assertEqual(temp.default_rounds, medium+adj) - self.assertEqual(temp.max_desired_rounds, medium+adj) - - # 'rounds' should be treated as fallback for min, max, and default - temp = subcls.using(rounds=medium+1, min_rounds=small+adj, - default_rounds=medium, max_rounds=large-adj) - self.assertEqual(temp.min_desired_rounds, small+adj) - self.assertEqual(temp.default_rounds, medium) - self.assertEqual(temp.max_desired_rounds, large-adj) - - def test_has_rounds_using_w_vary_rounds_parsing(self): - """ - HasRounds.using() -- vary_rounds parsing - """ - # setup helpers - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - - def parse(value): - return subcls.using(vary_rounds=value).vary_rounds - - # floats should be preserved - self.assertEqual(parse(0.1), 0.1) - self.assertEqual(parse('0.1'), 0.1) - - # 'xx%' should be converted to float - self.assertEqual(parse('10%'), 0.1) - - # ints should be preserved - self.assertEqual(parse(1000), 1000) - self.assertEqual(parse('1000'), 1000) - - # float bounds should be enforced - self.assertRaises(ValueError, parse, -0.1) - self.assertRaises(ValueError, parse, 1.1) - - def test_has_rounds_using_w_vary_rounds_generation(self): - """ - HasRounds.using() -- vary_rounds generation - """ - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - - def get_effective_range(cls): - seen = set(get_effective_rounds(cls) for _ in irange(1000)) - return min(seen), max(seen) - - def assert_rounds_range(vary_rounds, lower, upper): - temp = subcls.using(vary_rounds=vary_rounds) - seen_lower, seen_upper = get_effective_range(temp) - self.assertEqual(seen_lower, lower, "vary_rounds had wrong lower limit:") - self.assertEqual(seen_upper, upper, "vary_rounds had wrong upper limit:") - - # test static - assert_rounds_range(0, medium, medium) - assert_rounds_range("0%", medium, medium) - - # test absolute - assert_rounds_range(adj, medium - adj, medium + adj) - assert_rounds_range(50, max(small, medium - 50), min(large, medium + 50)) - - # test relative - should shift over at 50% mark - if handler.rounds_cost == "log2": - # log rounds "50%" variance should only increase/decrease by 1 cost value - assert_rounds_range("1%", medium, medium) - assert_rounds_range("49%", medium, medium) - assert_rounds_range("50%", medium - adj, medium) - else: - # for linear rounds, range is frequently so huge, won't ever see ends. - # so we just check it's within an expected range. - lower, upper = get_effective_range(subcls.using(vary_rounds="50%")) - - self.assertGreaterEqual(lower, max(small, medium * 0.5)) - self.assertLessEqual(lower, max(small, medium * 0.8)) - - self.assertGreaterEqual(upper, min(large, medium * 1.2)) - self.assertLessEqual(upper, min(large, medium * 1.5)) - - def test_has_rounds_using_and_needs_update(self): - """ - HasRounds.using() -- desired_rounds + needs_update() - """ - handler, subcls, small, medium, large, adj = self._create_using_rounds_helper() - - temp = subcls.using(min_desired_rounds=small+2, max_desired_rounds=large-2) - - # generate some sample hashes - small_hash = self.do_stub_encrypt(subcls, rounds=small) - medium_hash = self.do_stub_encrypt(subcls, rounds=medium) - large_hash = self.do_stub_encrypt(subcls, rounds=large) - - # everything should be w/in bounds for original handler - self.assertFalse(subcls.needs_update(small_hash)) - self.assertFalse(subcls.needs_update(medium_hash)) - self.assertFalse(subcls.needs_update(large_hash)) - - # small & large should require update for temp handler - self.assertTrue(temp.needs_update(small_hash)) - self.assertFalse(temp.needs_update(medium_hash)) - self.assertTrue(temp.needs_update(large_hash)) - - #=================================================================== - # idents - #=================================================================== - def require_many_idents(self): - handler = self.handler - if not isinstance(handler, type) or not issubclass(handler, uh.HasManyIdents): - raise self.skipTest("handler doesn't derive from HasManyIdents") - - def test_30_HasManyIdents(self): - """validate HasManyIdents configuration""" - cls = self.handler - self.require_many_idents() - - # check settings - self.assertTrue('ident' in cls.setting_kwds) - - # check ident_values list - for value in cls.ident_values: - self.assertIsInstance(value, unicode, - "cls.ident_values must be unicode:") - self.assertTrue(len(cls.ident_values)>1, - "cls.ident_values must have 2+ elements:") - - # check default_ident value - self.assertIsInstance(cls.default_ident, unicode, - "cls.default_ident must be unicode:") - self.assertTrue(cls.default_ident in cls.ident_values, - "cls.default_ident must specify member of cls.ident_values") - - # check optional aliases list - if cls.ident_aliases: - for alias, ident in iteritems(cls.ident_aliases): - self.assertIsInstance(alias, unicode, - "cls.ident_aliases keys must be unicode:") # XXX: allow ints? - self.assertIsInstance(ident, unicode, - "cls.ident_aliases values must be unicode:") - self.assertTrue(ident in cls.ident_values, - "cls.ident_aliases must map to cls.ident_values members: %r" % (ident,)) - - # check constructor validates ident correctly. - handler = cls - hash = self.get_sample_hash()[1] - kwds = handler.parsehash(hash) - del kwds['ident'] - - # ... accepts good ident - handler(ident=cls.default_ident, **kwds) - - # ... requires ident w/o defaults - self.assertRaises(TypeError, handler, **kwds) - - # ... supplies default ident - handler(use_defaults=True, **kwds) - - # ... rejects bad ident - self.assertRaises(ValueError, handler, ident='xXx', **kwds) - - # TODO: check various supported idents - - def test_has_many_idents_using(self): - """HasManyIdents.using() -- 'default_ident' and 'ident' keywords""" - self.require_many_idents() - - # pick alt ident to test with - handler = self.handler - orig_ident = handler.default_ident - for alt_ident in handler.ident_values: - if alt_ident != orig_ident: - break - else: - raise AssertionError("expected to find alternate ident: default=%r values=%r" % - (orig_ident, handler.ident_values)) - - def effective_ident(cls): - cls = unwrap_handler(cls) - return cls(use_defaults=True).ident - - # keep default if nothing else specified - subcls = handler.using() - self.assertEqual(subcls.default_ident, orig_ident) - - # accepts alt ident - subcls = handler.using(default_ident=alt_ident) - self.assertEqual(subcls.default_ident, alt_ident) - self.assertEqual(handler.default_ident, orig_ident) - - # check subcls actually *generates* default ident, - # and that we didn't affect orig handler - self.assertEqual(effective_ident(subcls), alt_ident) - self.assertEqual(effective_ident(handler), orig_ident) - - # rejects bad ident - self.assertRaises(ValueError, handler.using, default_ident='xXx') - - # honor 'ident' alias - subcls = handler.using(ident=alt_ident) - self.assertEqual(subcls.default_ident, alt_ident) - self.assertEqual(handler.default_ident, orig_ident) - - # forbid both at same time - self.assertRaises(TypeError, handler.using, default_ident=alt_ident, ident=alt_ident) - - # check ident aliases are being honored - if handler.ident_aliases: - for alias, ident in handler.ident_aliases.items(): - subcls = handler.using(ident=alias) - self.assertEqual(subcls.default_ident, ident, msg="alias %r:" % alias) - - #=================================================================== - # password size limits - #=================================================================== - def test_truncate_error_setting(self): - """ - validate 'truncate_error' setting & related attributes - """ - # If it doesn't have truncate_size set, - # it shouldn't support truncate_error - hasher = self.handler - if hasher.truncate_size is None: - self.assertNotIn("truncate_error", hasher.setting_kwds) - return - - # if hasher defaults to silently truncating, - # it MUST NOT use .truncate_verify_reject, - # because resulting hashes wouldn't verify! - if not hasher.truncate_error: - self.assertFalse(hasher.truncate_verify_reject) - - # if hasher doesn't have configurable policy, - # it must throw error by default - if "truncate_error" not in hasher.setting_kwds: - self.assertTrue(hasher.truncate_error) - return - - # test value parsing - def parse_value(value): - return hasher.using(truncate_error=value).truncate_error - self.assertEqual(parse_value(None), hasher.truncate_error) - self.assertEqual(parse_value(True), True) - self.assertEqual(parse_value("true"), True) - self.assertEqual(parse_value(False), False) - self.assertEqual(parse_value("false"), False) - self.assertRaises(ValueError, parse_value, "xxx") - - def test_secret_wo_truncate_size(self): - """ - test no password size limits enforced (if truncate_size=None) - """ - # skip if hasher has a maximum password size - hasher = self.handler - if hasher.truncate_size is not None: - self.assertGreaterEqual(hasher.truncate_size, 1) - raise self.skipTest("truncate_size is set") - - # NOTE: this doesn't do an exhaustive search to verify algorithm - # doesn't have some cutoff point, it just tries - # 1024-character string, and alters the last char. - # as long as algorithm doesn't clip secret at point <1024, - # the new secret shouldn't verify. - - # hash a 1024-byte secret - secret = "too many secrets" * 16 - alt = "x" - hash = self.do_encrypt(secret) - - # check that verify doesn't silently reject secret - # (i.e. hasher mistakenly honors .truncate_verify_reject) - verify_success = not hasher.is_disabled - self.assertEqual(self.do_verify(secret, hash), verify_success, - msg="verify rejected correct secret") - - # alter last byte, should get different hash, which won't verify - alt_secret = secret[:-1] + alt - self.assertFalse(self.do_verify(alt_secret, hash), - "full password not used in digest") - - def test_secret_w_truncate_size(self): - """ - test password size limits raise truncate_error (if appropriate) - """ - #-------------------------------------------------- - # check if test is applicable - #-------------------------------------------------- - handler = self.handler - truncate_size = handler.truncate_size - if not truncate_size: - raise self.skipTest("truncate_size not set") - - #-------------------------------------------------- - # setup vars - #-------------------------------------------------- - # try to get versions w/ and w/o truncate_error set. - # set to None if policy isn't configruable - size_error_type = exc.PasswordSizeError - if "truncate_error" in handler.setting_kwds: - without_error = handler.using(truncate_error=False) - with_error = handler.using(truncate_error=True) - size_error_type = exc.PasswordTruncateError - elif handler.truncate_error: - without_error = None - with_error = handler - else: - # NOTE: this mode is currently an error in test_truncate_error_setting() - without_error = handler - with_error = None - - # create some test secrets - base = "too many secrets" - alt = "x" # char that's not in base, used to mutate test secrets - long_secret = repeat_string(base, truncate_size+1) - short_secret = long_secret[:-1] - alt_long_secret = long_secret[:-1] + alt - alt_short_secret = short_secret[:-1] + alt - - # init flags - short_verify_success = not handler.is_disabled - long_verify_success = short_verify_success and \ - not handler.truncate_verify_reject - - #-------------------------------------------------- - # do tests on length secret, and resulting hash. - # should pass regardless of truncate_error policy. - #-------------------------------------------------- - assert without_error or with_error - for cand_hasher in [without_error, with_error]: - - # create & hash string that's exactly chars. - short_hash = self.do_encrypt(short_secret, handler=cand_hasher) - - # check hash verifies, regardless of .truncate_verify_reject - self.assertEqual(self.do_verify(short_secret, short_hash, - handler=cand_hasher), - short_verify_success) - - # changing 'th char should invalidate hash - # if this fails, means (reported) truncate_size is too large. - self.assertFalse(self.do_verify(alt_short_secret, short_hash, - handler=with_error), - "truncate_size value is too large") - - # verify should truncate long secret before comparing - # (unless truncate_verify_reject is set) - self.assertEqual(self.do_verify(long_secret, short_hash, - handler=cand_hasher), - long_verify_success) - - #-------------------------------------------------- - # do tests on length secret, - # w/ truncate error disabled (should silently truncate) - #-------------------------------------------------- - if without_error: - - # create & hash string that's exactly truncate_size+1 chars - long_hash = self.do_encrypt(long_secret, handler=without_error) - - # check verifies against secret (unless truncate_verify_reject=True) - self.assertEqual(self.do_verify(long_secret, long_hash, - handler=without_error), - short_verify_success) - - # check mutating last char doesn't change outcome. - # if this fails, means (reported) truncate_size is too small. - self.assertEqual(self.do_verify(alt_long_secret, long_hash, - handler=without_error), - short_verify_success) - - # check short_secret verifies against this hash - # if this fails, means (reported) truncate_size is too large. - self.assertTrue(self.do_verify(short_secret, long_hash, - handler=without_error)) - - #-------------------------------------------------- - # do tests on length secret, - # w/ truncate error - #-------------------------------------------------- - if with_error: - - # with errors enabled, should forbid truncation. - err = self.assertRaises(size_error_type, self.do_encrypt, - long_secret, handler=with_error) - self.assertEqual(err.max_size, truncate_size) - - #=================================================================== - # password contents - #=================================================================== - def test_61_secret_case_sensitive(self): - """test password case sensitivity""" - hash_insensitive = self.secret_case_insensitive is True - verify_insensitive = self.secret_case_insensitive in [True, - "verify-only"] - - # test hashing lower-case verifies against lower & upper - lower = 'test' - upper = 'TEST' - h1 = self.do_encrypt(lower) - if verify_insensitive and not self.handler.is_disabled: - self.assertTrue(self.do_verify(upper, h1), - "verify() should not be case sensitive") - else: - self.assertFalse(self.do_verify(upper, h1), - "verify() should be case sensitive") - - # test hashing upper-case verifies against lower & upper - h2 = self.do_encrypt(upper) - if verify_insensitive and not self.handler.is_disabled: - self.assertTrue(self.do_verify(lower, h2), - "verify() should not be case sensitive") - else: - self.assertFalse(self.do_verify(lower, h2), - "verify() should be case sensitive") - - # test genhash - # XXX: 2.0: what about 'verify-only' hashes once genhash() is removed? - # won't have easy way to recreate w/ same config to see if hash differs. - # (though only hash this applies to is mssql2000) - h2 = self.do_genhash(upper, h1) - if hash_insensitive or (self.handler.is_disabled and not self.disabled_contains_salt): - self.assertEqual(h2, h1, - "genhash() should not be case sensitive") - else: - self.assertNotEqual(h2, h1, - "genhash() should be case sensitive") - - def test_62_secret_border(self): - """test non-string passwords are rejected""" - hash = self.get_sample_hash()[1] - - # secret=None - self.assertRaises(TypeError, self.do_encrypt, None) - self.assertRaises(TypeError, self.do_genhash, None, hash) - self.assertRaises(TypeError, self.do_verify, None, hash) - - # secret=int (picked as example of entirely wrong class) - self.assertRaises(TypeError, self.do_encrypt, 1) - self.assertRaises(TypeError, self.do_genhash, 1, hash) - self.assertRaises(TypeError, self.do_verify, 1, hash) - - # xxx: move to password size limits section, above? - def test_63_large_secret(self): - """test MAX_PASSWORD_SIZE is enforced""" - from passlib.exc import PasswordSizeError - from passlib.utils import MAX_PASSWORD_SIZE - secret = '.' * (1+MAX_PASSWORD_SIZE) - hash = self.get_sample_hash()[1] - err = self.assertRaises(PasswordSizeError, self.do_genhash, secret, hash) - self.assertEqual(err.max_size, MAX_PASSWORD_SIZE) - self.assertRaises(PasswordSizeError, self.do_encrypt, secret) - self.assertRaises(PasswordSizeError, self.do_verify, secret, hash) - - def test_64_forbidden_chars(self): - """test forbidden characters not allowed in password""" - chars = self.forbidden_characters - if not chars: - raise self.skipTest("none listed") - base = u('stub') - if isinstance(chars, bytes): - from passlib.utils.compat import iter_byte_chars - chars = iter_byte_chars(chars) - base = base.encode("ascii") - for c in chars: - self.assertRaises(ValueError, self.do_encrypt, base + c + base) - - #=================================================================== - # check identify(), verify(), genhash() against test vectors - #=================================================================== - def is_secret_8bit(self, secret): - secret = self.populate_context(secret, {}) - return not is_ascii_safe(secret) - - def expect_os_crypt_failure(self, secret): - """ - check if we're expecting potential verify failure due to crypt.crypt() encoding limitation - """ - if PY3 and self.backend == "os_crypt" and isinstance(secret, bytes): - try: - secret.decode("utf-8") - except UnicodeDecodeError: - return True - return False - - def test_70_hashes(self): - """test known hashes""" - - # sanity check - self.assertTrue(self.known_correct_hashes or self.known_correct_configs, - "test must set at least one of 'known_correct_hashes' " - "or 'known_correct_configs'") - - # run through known secret/hash pairs - saw8bit = False - for secret, hash in self.iter_known_hashes(): - if self.is_secret_8bit(secret): - saw8bit = True - - # hash should be positively identified by handler - self.assertTrue(self.do_identify(hash), - "identify() failed to identify hash: %r" % (hash,)) - - # check if what we're about to do is expected to fail due to crypt.crypt() limitation. - expect_os_crypt_failure = self.expect_os_crypt_failure(secret) - try: - - # secret should verify successfully against hash - self.check_verify(secret, hash, "verify() of known hash failed: " - "secret=%r, hash=%r" % (secret, hash)) - - # genhash() should reproduce same hash - result = self.do_genhash(secret, hash) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) - if self.handler.is_disabled and self.disabled_contains_salt: - continue - self.assertEqual(result, hash, "genhash() failed to reproduce " - "known hash: secret=%r, hash=%r: result=%r" % - (secret, hash, result)) - - except MissingBackendError: - if not expect_os_crypt_failure: - raise - - # would really like all handlers to have at least one 8-bit test vector - if not saw8bit: - warn("%s: no 8-bit secrets tested" % self.__class__) - - def test_71_alternates(self): - """test known alternate hashes""" - if not self.known_alternate_hashes: - raise self.skipTest("no alternate hashes provided") - for alt, secret, hash in self.known_alternate_hashes: - - # hash should be positively identified by handler - self.assertTrue(self.do_identify(hash), - "identify() failed to identify alternate hash: %r" % - (hash,)) - - # secret should verify successfully against hash - self.check_verify(secret, alt, "verify() of known alternate hash " - "failed: secret=%r, hash=%r" % (secret, alt)) - - # genhash() should reproduce canonical hash - result = self.do_genhash(secret, alt) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) - if self.handler.is_disabled and self.disabled_contains_salt: - continue - self.assertEqual(result, hash, "genhash() failed to normalize " - "known alternate hash: secret=%r, alt=%r, hash=%r: " - "result=%r" % (secret, alt, hash, result)) - - def test_72_configs(self): - """test known config strings""" - # special-case handlers without settings - if not self.handler.setting_kwds: - self.assertFalse(self.known_correct_configs, - "handler should not have config strings") - raise self.skipTest("hash has no settings") - - if not self.known_correct_configs: - # XXX: make this a requirement? - raise self.skipTest("no config strings provided") - - # make sure config strings work (hashes in list tested in test_70) - if self.filter_config_warnings: - warnings.filterwarnings("ignore", category=PasslibHashWarning) - for config, secret, hash in self.known_correct_configs: - - # config should be positively identified by handler - self.assertTrue(self.do_identify(config), - "identify() failed to identify known config string: %r" % - (config,)) - - # verify() should throw error for config strings. - self.assertRaises(ValueError, self.do_verify, secret, config, - __msg__="verify() failed to reject config string: %r" % - (config,)) - - # genhash() should reproduce hash from config. - result = self.do_genhash(secret, config) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) - self.assertEqual(result, hash, "genhash() failed to reproduce " - "known hash from config: secret=%r, config=%r, hash=%r: " - "result=%r" % (secret, config, hash, result)) - - def test_73_unidentified(self): - """test known unidentifiably-mangled strings""" - if not self.known_unidentified_hashes: - raise self.skipTest("no unidentified hashes provided") - for hash in self.known_unidentified_hashes: - - # identify() should reject these - self.assertFalse(self.do_identify(hash), - "identify() incorrectly identified known unidentifiable " - "hash: %r" % (hash,)) - - # verify() should throw error - self.assertRaises(ValueError, self.do_verify, 'stub', hash, - __msg__= "verify() failed to throw error for unidentifiable " - "hash: %r" % (hash,)) - - # genhash() should throw error - self.assertRaises(ValueError, self.do_genhash, 'stub', hash, - __msg__= "genhash() failed to throw error for unidentifiable " - "hash: %r" % (hash,)) - - def test_74_malformed(self): - """test known identifiable-but-malformed strings""" - if not self.known_malformed_hashes: - raise self.skipTest("no malformed hashes provided") - for hash in self.known_malformed_hashes: - - # identify() should accept these - self.assertTrue(self.do_identify(hash), - "identify() failed to identify known malformed " - "hash: %r" % (hash,)) - - # verify() should throw error - self.assertRaises(ValueError, self.do_verify, 'stub', hash, - __msg__= "verify() failed to throw error for malformed " - "hash: %r" % (hash,)) - - # genhash() should throw error - self.assertRaises(ValueError, self.do_genhash, 'stub', hash, - __msg__= "genhash() failed to throw error for malformed " - "hash: %r" % (hash,)) - - def test_75_foreign(self): - """test known foreign hashes""" - if self.accepts_all_hashes: - raise self.skipTest("not applicable") - if not self.known_other_hashes: - raise self.skipTest("no foreign hashes provided") - for name, hash in self.known_other_hashes: - # NOTE: most tests use default list of foreign hashes, - # so they may include ones belonging to that hash... - # hence the 'own' logic. - - if name == self.handler.name: - # identify should accept these - self.assertTrue(self.do_identify(hash), - "identify() failed to identify known hash: %r" % (hash,)) - - # verify & genhash should NOT throw error - self.do_verify('stub', hash) - result = self.do_genhash('stub', hash) - self.assertIsInstance(result, str, - "genhash() failed to return native string: %r" % (result,)) - - else: - # identify should reject these - self.assertFalse(self.do_identify(hash), - "identify() incorrectly identified hash belonging to " - "%s: %r" % (name, hash)) - - # verify should throw error - self.assertRaises(ValueError, self.do_verify, 'stub', hash, - __msg__= "verify() failed to throw error for hash " - "belonging to %s: %r" % (name, hash,)) - - # genhash() should throw error - self.assertRaises(ValueError, self.do_genhash, 'stub', hash, - __msg__= "genhash() failed to throw error for hash " - "belonging to %s: %r" % (name, hash)) - - def test_76_hash_border(self): - """test non-string hashes are rejected""" - # - # test hash=None is handled correctly - # - self.assertRaises(TypeError, self.do_identify, None) - self.assertRaises(TypeError, self.do_verify, 'stub', None) - - # NOTE: changed in 1.7 -- previously 'None' would be accepted when config strings not supported. - self.assertRaises(TypeError, self.do_genhash, 'stub', None) - - # - # test hash=int is rejected (picked as example of entirely wrong type) - # - self.assertRaises(TypeError, self.do_identify, 1) - self.assertRaises(TypeError, self.do_verify, 'stub', 1) - self.assertRaises(TypeError, self.do_genhash, 'stub', 1) - - # - # test hash='' is rejected for all but the plaintext hashes - # - for hash in [u(''), b'']: - if self.accepts_all_hashes: - # then it accepts empty string as well. - self.assertTrue(self.do_identify(hash)) - self.do_verify('stub', hash) - result = self.do_genhash('stub', hash) - self.check_returned_native_str(result, "genhash") - else: - # otherwise it should reject them - self.assertFalse(self.do_identify(hash), - "identify() incorrectly identified empty hash") - self.assertRaises(ValueError, self.do_verify, 'stub', hash, - __msg__="verify() failed to reject empty hash") - self.assertRaises(ValueError, self.do_genhash, 'stub', hash, - __msg__="genhash() failed to reject empty hash") - - # - # test identify doesn't throw decoding errors on 8-bit input - # - self.do_identify('\xe2\x82\xac\xc2\xa5$') # utf-8 - self.do_identify('abc\x91\x00') # non-utf8 - - #=================================================================== - # test parsehash() - #=================================================================== - - #: optional list of known parse hash results for hasher - known_parsehash_results = [] - - def require_parsehash(self): - if not hasattr(self.handler, "parsehash"): - raise SkipTest("parsehash() not implemented") - - def test_70_parsehash(self): - """ - parsehash() - """ - # TODO: would like to enhance what this test covers - - self.require_parsehash() - handler = self.handler - - # calls should succeed, and return dict - hash = self.do_encrypt("stub") - result = handler.parsehash(hash) - self.assertIsInstance(result, dict) - # TODO: figure out what invariants we can reliably parse, - # or maybe make subclasses specify that? - - # w/ checksum=False, should omit that key - result2 = handler.parsehash(hash, checksum=False) - correct2 = result.copy() - correct2.pop("checksum", None) - self.assertEqual(result2, correct2) - - # w/ sanitize=True - # correct output should mask salt / checksum; - # but all else should be the same - result3 = handler.parsehash(hash, sanitize=True) - correct3 = result.copy() - if PY2: - # silence warning about bytes & unicode not comparing - # (sanitize may convert bytes into base64 text) - warnings.filterwarnings("ignore", ".*unequal comparison failed to convert.*", - category=UnicodeWarning) - for key in ("salt", "checksum"): - if key in result3: - self.assertNotEqual(result3[key], correct3[key]) - self.assert_is_masked(result3[key]) - correct3[key] = result3[key] - self.assertEqual(result3, correct3) - - def assert_is_masked(self, value): - """ - check value properly masked by :func:`passlib.utils.mask_value` - """ - if value is None: - return - self.assertIsInstance(value, unicode) - # assumes mask_value() defaults will never show more than chars (4); - # and show nothing if size less than 1/ (8). - ref = value if len(value) < 8 else value[4:] - if set(ref) == set(["*"]): - return True - raise self.fail("value not masked: %r" % value) - - def test_71_parsehash_results(self): - """ - parsehash() -- known outputs - """ - self.require_parsehash() - samples = self.known_parsehash_results - if not samples: - raise self.skipTest("no samples present") - # XXX: expand to test w/ checksum=False and/or sanitize=True? - # or read "_unsafe_settings"? - for hash, correct in self.known_parsehash_results: - result = self.handler.parsehash(hash) - self.assertEqual(result, correct, "hash=%r:" % hash) - - #=================================================================== - # fuzz testing - #=================================================================== - def test_77_fuzz_input(self, threaded=False): - """fuzz testing -- random passwords and options - - This test attempts to perform some basic fuzz testing of the hash, - based on whatever information can be found about it. - It does as much as it can within a fixed amount of time - (defaults to 1 second, but can be overridden via $PASSLIB_TEST_FUZZ_TIME). - It tests the following: - - * randomly generated passwords including extended unicode chars - * randomly selected rounds values (if rounds supported) - * randomly selected salt sizes (if salts supported) - * randomly selected identifiers (if multiple found) - * runs output of selected backend against other available backends - (if any) to detect errors occurring between different backends. - * runs output against other "external" verifiers such as OS crypt() - - :param report_thread_state: - if true, writes state of loop to current_thread().passlib_fuzz_state. - used to help debug multi-threaded fuzz test issues (below) - """ - if self.handler.is_disabled: - raise self.skipTest("not applicable") - - # gather info - from passlib.utils import tick - max_time = self.max_fuzz_time - if max_time <= 0: - raise self.skipTest("disabled by test mode") - verifiers = self.get_fuzz_verifiers(threaded=threaded) - def vname(v): - return (v.__doc__ or v.__name__).splitlines()[0] - - # init rng -- using separate one for each thread - # so things are predictable for given RANDOM_TEST_SEED - # (relies on test_78_fuzz_threading() to give threads unique names) - if threaded: - thread_name = threading.current_thread().name - else: - thread_name = "fuzz test" - rng = self.getRandom(name=thread_name) - generator = self.FuzzHashGenerator(self, rng) - - # do as many tests as possible for max_time seconds - log.debug("%s: %s: started; max_time=%r verifiers=%d (%s)", - self.descriptionPrefix, thread_name, max_time, len(verifiers), - ", ".join(vname(v) for v in verifiers)) - start = tick() - stop = start + max_time - count = 0 - while tick() <= stop: - # generate random password & options - opts = generator.generate() - secret = opts['secret'] - other = opts['other'] - settings = opts['settings'] - ctx = opts['context'] - if ctx: - settings['context'] = ctx - - # create new hash - hash = self.do_encrypt(secret, **settings) - ##log.debug("fuzz test: hash=%r secret=%r other=%r", - ## hash, secret, other) - - # run through all verifiers we found. - for verify in verifiers: - name = vname(verify) - result = verify(secret, hash, **ctx) - if result == "skip": # let verifiers signal lack of support - continue - assert result is True or result is False - if not result: - raise self.failureException("failed to verify against %r verifier: " - "secret=%r config=%r hash=%r" % - (name, secret, settings, hash)) - # occasionally check that some other secrets WON'T verify - # against this hash. - if rng.random() < .1: - result = verify(other, hash, **ctx) - if result and result != "skip": - raise self.failureException("was able to verify wrong " - "password using %s: wrong_secret=%r real_secret=%r " - "config=%r hash=%r" % (name, other, secret, settings, hash)) - count += 1 - - log.debug("%s: %s: done; elapsed=%r count=%r", - self.descriptionPrefix, thread_name, tick() - start, count) - - def test_78_fuzz_threading(self): - """multithreaded fuzz testing -- random password & options using multiple threads - - run test_77 simultaneously in multiple threads - in an attempt to detect any concurrency issues - (e.g. the bug fixed by pybcrypt 0.3) - """ - self.require_TEST_MODE("full") - import threading - - # check if this test should run - if self.handler.is_disabled: - raise self.skipTest("not applicable") - thread_count = self.fuzz_thread_count - if thread_count < 1 or self.max_fuzz_time <= 0: - raise self.skipTest("disabled by test mode") - - # buffer to hold errors thrown by threads - failed_lock = threading.Lock() - failed = [0] - - # launch threads, all of which run - # test_77_fuzz_input(), and see if any errors get thrown. - # if hash has concurrency issues, this should reveal it. - def wrapper(): - try: - self.test_77_fuzz_input(threaded=True) - except SkipTest: - pass - except: - with failed_lock: - failed[0] += 1 - raise - def launch(n): - cls = type(self) - name = "Fuzz-Thread-%d ('%s:%s.%s')" % (n, cls.__module__, cls.__name__, - self._testMethodName) - thread = threading.Thread(target=wrapper, name=name) - thread.setDaemon(True) - thread.start() - return thread - threads = [launch(n) for n in irange(thread_count)] - - # wait until all threads exit - timeout = self.max_fuzz_time * thread_count * 4 - stalled = 0 - for thread in threads: - thread.join(timeout) - if not thread.is_alive(): - continue - # XXX: not sure why this is happening, main one seems 1/4 times for sun_md5_crypt - log.error("%s timed out after %f seconds", thread.name, timeout) - stalled += 1 - - # if any thread threw an error, raise one ourselves. - if failed[0]: - raise self.fail("%d/%d threads failed concurrent fuzz testing " - "(see error log for details)" % (failed[0], thread_count)) - if stalled: - raise self.fail("%d/%d threads stalled during concurrent fuzz testing " - "(see error log for details)" % (stalled, thread_count)) - - #--------------------------------------------------------------- - # fuzz constants & helpers - #--------------------------------------------------------------- - - @property - def max_fuzz_time(self): - """amount of time to spend on fuzz testing""" - value = float(os.environ.get("PASSLIB_TEST_FUZZ_TIME") or 0) - if value: - return value - elif TEST_MODE(max="quick"): - return 0 - elif TEST_MODE(max="default"): - return 1 - else: - return 5 - - @property - def fuzz_thread_count(self): - """number of threads for threaded fuzz testing""" - value = int(os.environ.get("PASSLIB_TEST_FUZZ_THREADS") or 0) - if value: - return value - elif TEST_MODE(max="quick"): - return 0 - else: - return 10 - - #--------------------------------------------------------------- - # fuzz verifiers - #--------------------------------------------------------------- - - #: list of custom fuzz-test verifiers (in addition to hasher itself, - #: and backend-specific wrappers of hasher). each element is - #: name of method that will return None / a verifier callable. - fuzz_verifiers = ("fuzz_verifier_default",) - - def get_fuzz_verifiers(self, threaded=False): - """return list of password verifiers (including external libs) - - used by fuzz testing. - verifiers should be callable with signature - ``func(password: unicode, hash: ascii str) -> ok: bool``. - """ - handler = self.handler - verifiers = [] - - # call all methods starting with prefix in order to create - for method_name in self.fuzz_verifiers: - func = getattr(self, method_name)() - if func is not None: - verifiers.append(func) - - # create verifiers for any other available backends - # NOTE: skipping this under threading test, - # since backend switching isn't threadsafe (yet) - if hasattr(handler, "backends") and TEST_MODE("full") and not threaded: - def maker(backend): - def func(secret, hash): - orig_backend = handler.get_backend() - try: - handler.set_backend(backend) - return handler.verify(secret, hash) - finally: - handler.set_backend(orig_backend) - func.__name__ = "check_" + backend + "_backend" - func.__doc__ = backend + "-backend" - return func - for backend in iter_alt_backends(handler): - verifiers.append(maker(backend)) - - return verifiers - - def fuzz_verifier_default(self): - # test against self - def check_default(secret, hash, **ctx): - return self.do_verify(secret, hash, **ctx) - if self.backend: - check_default.__doc__ = self.backend + "-backend" - else: - check_default.__doc__ = "self" - return check_default - - #--------------------------------------------------------------- - # fuzz settings generation - #--------------------------------------------------------------- - class FuzzHashGenerator(object): - """ - helper which takes care of generating random - passwords & configuration options to test hash with. - separate from test class so we can create one per thread. - """ - #========================================================== - # class attrs - #========================================================== - - # alphabet for randomly generated passwords - password_alphabet = u('qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113') - - # encoding when testing bytes - password_encoding = "utf-8" - - # map of setting kwd -> method name. - # will ignore setting if method returns None. - # subclasses should make copy of dict. - settings_map = dict(rounds="random_rounds", - salt_size="random_salt_size", - ident="random_ident") - - # map of context kwd -> method name. - context_map = {} - - #========================================================== - # init / generation - #========================================================== - - def __init__(self, test, rng): - self.test = test - self.handler = test.handler - self.rng = rng - - def generate(self): - """ - generate random password and options for fuzz testing. - :returns: - `(secret, other_secret, settings_kwds, context_kwds)` - """ - def gendict(map): - out = {} - for key, meth in map.items(): - value = getattr(self, meth)() - if value is not None: - out[key] = value - return out - secret, other = self.random_password_pair() - return dict(secret=secret, - other=other, - settings=gendict(self.settings_map), - context=gendict(self.context_map), - ) - - #========================================================== - # helpers - #========================================================== - def randintgauss(self, lower, upper, mu, sigma): - """generate random int w/ gauss distirbution""" - value = self.rng.normalvariate(mu, sigma) - return int(limit(value, lower, upper)) - - #========================================================== - # settings generation - #========================================================== - - def random_rounds(self): - handler = self.handler - if not has_rounds_info(handler): - return None - default = handler.default_rounds or handler.min_rounds - lower = handler.min_rounds - if handler.rounds_cost == "log2": - upper = default - else: - upper = min(default*2, handler.max_rounds) - return self.randintgauss(lower, upper, default, default*.5) - - def random_salt_size(self): - handler = self.handler - if not (has_salt_info(handler) and 'salt_size' in handler.setting_kwds): - return None - default = handler.default_salt_size - lower = handler.min_salt_size - upper = handler.max_salt_size or default*4 - return self.randintgauss(lower, upper, default, default*.5) - - def random_ident(self): - rng = self.rng - handler = self.handler - if 'ident' not in handler.setting_kwds or not hasattr(handler, "ident_values"): - return None - if rng.random() < .5: - return None - # resolve wrappers before reading values - handler = getattr(handler, "wrapped", handler) - return rng.choice(handler.ident_values) - - #========================================================== - # fuzz password generation - #========================================================== - def random_password_pair(self): - """generate random password, and non-matching alternate password""" - secret = self.random_password() - while True: - other = self.random_password() - if self.accept_password_pair(secret, other): - break - rng = self.rng - if rng.randint(0,1): - secret = secret.encode(self.password_encoding) - if rng.randint(0,1): - other = other.encode(self.password_encoding) - return secret, other - - def random_password(self): - """generate random passwords for fuzz testing""" - # occasionally try an empty password - rng = self.rng - if rng.random() < .0001: - return u('') - - # check if truncate size needs to be considered - handler = self.handler - truncate_size = handler.truncate_error and handler.truncate_size - max_size = truncate_size or 999999 - - # pick endpoint - if max_size < 50 or rng.random() < .5: - # chance of small password (~15 chars) - size = self.randintgauss(1, min(max_size, 50), 15, 15) - else: - # otherwise large password (~70 chars) - size = self.randintgauss(50, min(max_size, 99), 70, 20) - - # generate random password - result = getrandstr(rng, self.password_alphabet, size) - - # trim ones that encode past truncate point. - if truncate_size and isinstance(result, unicode): - while len(result.encode("utf-8")) > truncate_size: - result = result[:-1] - - return result - - def accept_password_pair(self, secret, other): - """verify fuzz pair contains different passwords""" - return secret != other - - #========================================================== - # eoc FuzzGenerator - #========================================================== - - #=================================================================== - # "disabled hasher" api - #=================================================================== - - def test_disable_and_enable(self): - """.disable() / .enable() methods""" - # - # setup - # - handler = self.handler - if not handler.is_disabled: - self.assertFalse(hasattr(handler, "disable")) - self.assertFalse(hasattr(handler, "enable")) - self.assertFalse(self.disabled_contains_salt) - raise self.skipTest("not applicable") - - # - # disable() - # - - # w/o existing hash - disabled_default = handler.disable() - self.assertIsInstance(disabled_default, str, - msg="disable() must return native string") - self.assertTrue(handler.identify(disabled_default), - msg="identify() didn't recognize disable() result: %r" % (disabled_default)) - - # w/ existing hash - stub = self.getRandom().choice(self.known_other_hashes)[1] - disabled_stub = handler.disable(stub) - self.assertIsInstance(disabled_stub, str, - msg="disable() must return native string") - self.assertTrue(handler.identify(disabled_stub), - msg="identify() didn't recognize disable() result: %r" % (disabled_stub)) - - # - # enable() - # - - # w/o original hash - self.assertRaisesRegex(ValueError, "cannot restore original hash", - handler.enable, disabled_default) - - # w/ original hash - try: - result = handler.enable(disabled_stub) - error = None - except ValueError as e: - result = None - error = e - - if error is None: - # if supports recovery, should have returned stub (e.g. unix_disabled); - self.assertIsInstance(result, str, - msg="enable() must return native string") - self.assertEqual(result, stub) - else: - # if doesn't, should have thrown appropriate error - self.assertIsInstance(error, ValueError) - self.assertRegex("cannot restore original hash", str(error)) - - # - # test repeating disable() & salting state - # - - # repeating disabled - disabled_default2 = handler.disable() - if self.disabled_contains_salt: - # should return new salt for each call (e.g. django_disabled) - self.assertNotEqual(disabled_default2, disabled_default) - elif error is None: - # should return same result for each hash, but unique across hashes - self.assertEqual(disabled_default2, disabled_default) - - # repeating same hash ... - disabled_stub2 = handler.disable(stub) - if self.disabled_contains_salt: - # ... should return different string (if salted) - self.assertNotEqual(disabled_stub2, disabled_stub) - else: - # ... should return same string - self.assertEqual(disabled_stub2, disabled_stub) - - # using different hash ... - disabled_other = handler.disable(stub + 'xxx') - if self.disabled_contains_salt or error is None: - # ... should return different string (if salted or hash encoded) - self.assertNotEqual(disabled_other, disabled_stub) - else: - # ... should return same string - self.assertEqual(disabled_other, disabled_stub) - - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# HandlerCase mixins providing additional tests for certain hashes -#============================================================================= -class OsCryptMixin(HandlerCase): - """helper used by create_backend_case() which adds additional features - to test the os_crypt backend. - - * if crypt support is missing, inserts fake crypt support to simulate - a working safe_crypt, to test passlib's codepath as fully as possible. - - * extra tests to verify non-conformant crypt implementations are handled - correctly. - - * check that native crypt support is detected correctly for known platforms. - """ - #=================================================================== - # class attrs - #=================================================================== - - # platforms that are known to support / not support this hash natively. - # list of (platform_regex, True|False|None) entries. - platform_crypt_support = [] - - #=================================================================== - # instance attrs - #=================================================================== - __unittest_skip = True - - # force this backend - backend = "os_crypt" - - # flag read by HandlerCase to detect if fake os crypt is enabled. - using_patched_crypt = False - - #=================================================================== - # setup - #=================================================================== - def setUp(self): - assert self.backend == "os_crypt" - if not self.handler.has_backend("os_crypt"): - # XXX: currently, any tests that use this are skipped entirely! (see issue 120) - self._patch_safe_crypt() - super(OsCryptMixin, self).setUp() - - @classmethod - def _get_safe_crypt_handler_backend(cls): - """ - return (handler, backend) pair to use for faking crypt.crypt() support for hash. - backend will be None if none availabe. - """ - # find handler that generates safe_crypt() compatible hash - handler = unwrap_handler(cls.handler) - - # hack to prevent recursion issue when .has_backend() is called - handler.get_backend() - - # find backend which isn't os_crypt - alt_backend = get_alt_backend(handler, "os_crypt") - return handler, alt_backend - - @property - def has_os_crypt_fallback(self): - """ - test if there's a fallback handler to test against if os_crypt can't support - a specified secret (may be explicitly set to False for some subclasses) - """ - return self._get_safe_crypt_handler_backend()[0] is not None - - def _patch_safe_crypt(self): - """if crypt() doesn't support current hash alg, this patches - safe_crypt() so that it transparently uses another one of the handler's - backends, so that we can go ahead and test as much of code path - as possible. - """ - # find handler & backend - handler, alt_backend = self._get_safe_crypt_handler_backend() - if not alt_backend: - raise AssertionError("handler has no available alternate backends!") - - # create subclass of handler, which we swap to an alternate backend - alt_handler = handler.using() - alt_handler.set_backend(alt_backend) - - def crypt_stub(secret, hash): - hash = alt_handler.genhash(secret, hash) - assert isinstance(hash, str) - return hash - - import passlib.utils as mod - self.patchAttr(mod, "_crypt", crypt_stub) - self.using_patched_crypt = True - - @classmethod - def _get_skip_backend_reason(cls, backend): - """ - make sure os_crypt backend is tested - when it's known os_crypt will be faked by _patch_safe_crypt() - """ - assert backend == "os_crypt" - reason = super(OsCryptMixin, cls)._get_skip_backend_reason(backend) - - from passlib.utils import has_crypt - if reason == cls._BACKEND_NOT_AVAILABLE and has_crypt: - if TEST_MODE("full") and cls._get_safe_crypt_handler_backend()[1]: - # in this case, _patch_safe_crypt() will monkeypatch os_crypt - # to use another backend, just so we can test os_crypt fully. - return None - else: - return "hash not supported by os crypt()" - - return reason - - #=================================================================== - # custom tests - #=================================================================== - - # TODO: turn into decorator, and use mock library. - def _use_mock_crypt(self): - """ - patch passlib.utils.safe_crypt() so it returns mock value for duration of test. - returns function whose .return_value controls what's returned. - this defaults to None. - """ - import passlib.utils as mod - - def mock_crypt(secret, config): - # let 'test' string through so _load_os_crypt_backend() will still work - if secret == "test": - return mock_crypt.__wrapped__(secret, config) - else: - return mock_crypt.return_value - - mock_crypt.__wrapped__ = mod._crypt - mock_crypt.return_value = None - - self.patchAttr(mod, "_crypt", mock_crypt) - - return mock_crypt - - def test_80_faulty_crypt(self): - """test with faulty crypt()""" - hash = self.get_sample_hash()[1] - exc_types = (exc.InternalBackendError,) - mock_crypt = self._use_mock_crypt() - - def test(value): - # set safe_crypt() to return specified value, and - # make sure assertion error is raised by handler. - mock_crypt.return_value = value - self.assertRaises(exc_types, self.do_genhash, "stub", hash) - self.assertRaises(exc_types, self.do_encrypt, "stub") - self.assertRaises(exc_types, self.do_verify, "stub", hash) - - test('$x' + hash[2:]) # detect wrong prefix - test(hash[:-1]) # detect too short - test(hash + 'x') # detect too long - - def test_81_crypt_fallback(self): - """test per-call crypt() fallback""" - - # mock up safe_crypt to return None - mock_crypt = self._use_mock_crypt() - mock_crypt.return_value = None - - if self.has_os_crypt_fallback: - # handler should have a fallback to use when os_crypt backend refuses to handle secret. - h1 = self.do_encrypt("stub") - h2 = self.do_genhash("stub", h1) - self.assertEqual(h2, h1) - self.assertTrue(self.do_verify("stub", h1)) - else: - # handler should give up - from passlib.exc import InternalBackendError as err_type - hash = self.get_sample_hash()[1] - self.assertRaises(err_type, self.do_encrypt, 'stub') - self.assertRaises(err_type, self.do_genhash, 'stub', hash) - self.assertRaises(err_type, self.do_verify, 'stub', hash) - - @doesnt_require_backend - def test_82_crypt_support(self): - """ - test platform-specific crypt() support detection - - NOTE: this is mainly just a sanity check to ensure the runtime - detection is functioning correctly on some known platforms, - so that we can feel more confident it'll work right on unknown ones. - """ - - # skip wrapper handlers, won't ever have crypt support - if hasattr(self.handler, "orig_prefix"): - raise self.skipTest("not applicable to wrappers") - - # look for first entry that matches current system - # XXX: append "/" + platform.release() to string? - # XXX: probably should rework to support rows being dicts w/ "minver" / "maxver" keys, - # instead of hack where we add major # as part of platform regex. - using_backend = not self.using_patched_crypt - name = self.handler.name - platform = sys.platform - for pattern, expected in self.platform_crypt_support: - if re.match(pattern, platform): - break - else: - raise self.skipTest("no data for %r platform (current host support = %r)" % - (platform, using_backend)) - - # rules can use "state=None" to signal varied support; - # e.g. platform='freebsd8' ... sha256_crypt not added until 8.3 - if expected is None: - raise self.skipTest("varied support on %r platform (current host support = %r)" % - (platform, using_backend)) - - # compare expectation vs reality - if expected == using_backend: - pass - elif expected: - self.fail("expected %r platform would have native support for %r" % - (platform, name)) - else: - self.fail("did not expect %r platform would have native support for %r" % - (platform, name)) - - #=================================================================== - # fuzzy verified support -- add additional verifier that uses os crypt() - #=================================================================== - - def fuzz_verifier_crypt(self): - """test results against OS crypt()""" - - # don't use this if we're faking safe_crypt (pointless test), - # or if handler is a wrapper (only original handler will be supported by os) - handler = self.handler - if self.using_patched_crypt or hasattr(handler, "wrapped"): - return None - - # create a wrapper for fuzzy verified to use - from crypt import crypt - from passlib.utils import _safe_crypt_lock - encoding = self.FuzzHashGenerator.password_encoding - - def check_crypt(secret, hash): - """stdlib-crypt""" - if not self.crypt_supports_variant(hash): - return "skip" - # XXX: any reason not to use safe_crypt() here? or just want to test against bare metal? - secret = to_native_str(secret, encoding) - with _safe_crypt_lock: - return crypt(secret, hash) == hash - - return check_crypt - - def crypt_supports_variant(self, hash): - """ - fuzzy_verified_crypt() helper -- - used to determine if os crypt() supports a particular hash variant. - """ - return True - - #=================================================================== - # eoc - #=================================================================== - -class UserHandlerMixin(HandlerCase): - """helper for handlers w/ 'user' context kwd; mixin for HandlerCase - - this overrides the HandlerCase test harness methods - so that a username is automatically inserted to hash/verify - calls. as well, passing in a pair of strings as the password - will be interpreted as (secret,user) - """ - #=================================================================== - # option flags - #=================================================================== - default_user = "user" - requires_user = True - user_case_insensitive = False - - #=================================================================== - # instance attrs - #=================================================================== - __unittest_skip = True - - #=================================================================== - # custom tests - #=================================================================== - def test_80_user(self): - """test user context keyword""" - handler = self.handler - password = 'stub' - hash = handler.hash(password, user=self.default_user) - - if self.requires_user: - self.assertRaises(TypeError, handler.hash, password) - self.assertRaises(TypeError, handler.genhash, password, hash) - self.assertRaises(TypeError, handler.verify, password, hash) - else: - # e.g. cisco_pix works with or without one. - handler.hash(password) - handler.genhash(password, hash) - handler.verify(password, hash) - - def test_81_user_case(self): - """test user case sensitivity""" - lower = self.default_user.lower() - upper = lower.upper() - hash = self.do_encrypt('stub', context=dict(user=lower)) - if self.user_case_insensitive: - self.assertTrue(self.do_verify('stub', hash, user=upper), - "user should not be case sensitive") - else: - self.assertFalse(self.do_verify('stub', hash, user=upper), - "user should be case sensitive") - - def test_82_user_salt(self): - """test user used as salt""" - config = self.do_stub_encrypt() - h1 = self.do_genhash('stub', config, user='admin') - h2 = self.do_genhash('stub', config, user='admin') - self.assertEqual(h2, h1) - h3 = self.do_genhash('stub', config, user='root') - self.assertNotEqual(h3, h1) - - # TODO: user size? kinda dicey, depends on algorithm. - - #=================================================================== - # override test helpers - #=================================================================== - def populate_context(self, secret, kwds): - """insert username into kwds""" - if isinstance(secret, tuple): - secret, user = secret - elif not self.requires_user: - return secret - else: - user = self.default_user - if 'user' not in kwds: - kwds['user'] = user - return secret - - #=================================================================== - # modify fuzz testing - #=================================================================== - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - context_map = HandlerCase.FuzzHashGenerator.context_map.copy() - context_map.update(user="random_user") - - user_alphabet = u("asdQWE123") - - def random_user(self): - rng = self.rng - if not self.test.requires_user and rng.random() < .1: - return None - return getrandstr(rng, self.user_alphabet, rng.randint(2,10)) - - #=================================================================== - # eoc - #=================================================================== - -class EncodingHandlerMixin(HandlerCase): - """helper for handlers w/ 'encoding' context kwd; mixin for HandlerCase - - this overrides the HandlerCase test harness methods - so that an encoding can be inserted to hash/verify - calls by passing in a pair of strings as the password - will be interpreted as (secret,encoding) - """ - #=================================================================== - # instance attrs - #=================================================================== - __unittest_skip = True - - # restrict stock passwords & fuzz alphabet to latin-1, - # so different encodings can be tested safely. - stock_passwords = [ - u("test"), - b"test", - u("\u00AC\u00BA"), - ] - - class FuzzHashGenerator(HandlerCase.FuzzHashGenerator): - - password_alphabet = u('qwerty1234<>.@*#! \u00AC') - - def populate_context(self, secret, kwds): - """insert encoding into kwds""" - if isinstance(secret, tuple): - secret, encoding = secret - kwds.setdefault('encoding', encoding) - return secret - #=================================================================== - # eoc - #=================================================================== - -#============================================================================= -# warnings helpers -#============================================================================= -class reset_warnings(warnings.catch_warnings): - """catch_warnings() wrapper which clears warning registry & filters""" - - def __init__(self, reset_filter="always", reset_registry=".*", **kwds): - super(reset_warnings, self).__init__(**kwds) - self._reset_filter = reset_filter - self._reset_registry = re.compile(reset_registry) if reset_registry else None - - def __enter__(self): - # let parent class archive filter state - ret = super(reset_warnings, self).__enter__() - - # reset the filter to list everything - if self._reset_filter: - warnings.resetwarnings() - warnings.simplefilter(self._reset_filter) - - # archive and clear the __warningregistry__ key for all modules - # that match the 'reset' pattern. - pattern = self._reset_registry - if pattern: - backup = self._orig_registry = {} - for name, mod in list(sys.modules.items()): - if mod is None or not pattern.match(name): - continue - reg = getattr(mod, "__warningregistry__", None) - if reg: - backup[name] = reg.copy() - reg.clear() - return ret - - def __exit__(self, *exc_info): - # restore warning registry for all modules - pattern = self._reset_registry - if pattern: - # restore registry backup, clearing all registry entries that we didn't archive - backup = self._orig_registry - for name, mod in list(sys.modules.items()): - if mod is None or not pattern.match(name): - continue - reg = getattr(mod, "__warningregistry__", None) - if reg: - reg.clear() - orig = backup.get(name) - if orig: - if reg is None: - setattr(mod, "__warningregistry__", orig) - else: - reg.update(orig) - super(reset_warnings, self).__exit__(*exc_info) - -#============================================================================= -# eof -#============================================================================= diff --git a/.venv/Lib/site-packages/passlib/totp.py b/.venv/Lib/site-packages/passlib/totp.py deleted file mode 100644 index 9ad5000..0000000 --- a/.venv/Lib/site-packages/passlib/totp.py +++ /dev/null @@ -1,1908 +0,0 @@ -"""passlib.totp -- TOTP / RFC6238 / Google Authenticator utilities.""" -#============================================================================= -# imports -#============================================================================= -from __future__ import absolute_import, division, print_function -from passlib.utils.compat import PY3 -# core -import base64 -import calendar -import json -import logging; log = logging.getLogger(__name__) -import math -import struct -import sys -import time as _time -import re -if PY3: - from urllib.parse import urlparse, parse_qsl, quote, unquote -else: - from urllib import quote, unquote - from urlparse import urlparse, parse_qsl -from warnings import warn -# site -try: - # TOTP encrypted keys only supported if cryptography (https://cryptography.io) is installed - from cryptography.hazmat.backends import default_backend as _cg_default_backend - import cryptography.hazmat.primitives.ciphers.algorithms - import cryptography.hazmat.primitives.ciphers.modes - from cryptography.hazmat.primitives import ciphers as _cg_ciphers - del cryptography -except ImportError: - log.debug("can't import 'cryptography' package, totp encryption disabled") - _cg_ciphers = _cg_default_backend = None -# pkg -from passlib import exc -from passlib.exc import TokenError, MalformedTokenError, InvalidTokenError, UsedTokenError -from passlib.utils import (to_unicode, to_bytes, consteq, - getrandbytes, rng, SequenceMixin, xor_bytes, getrandstr) -from passlib.utils.binary import BASE64_CHARS, b32encode, b32decode -from passlib.utils.compat import (u, unicode, native_string_types, bascii_to_str, int_types, num_types, - irange, byte_elem_value, UnicodeIO, suppress_cause) -from passlib.utils.decor import hybrid_method, memoized_property -from passlib.crypto.digest import lookup_hash, compile_hmac, pbkdf2_hmac -from passlib.hash import pbkdf2_sha256 -# local -__all__ = [ - # frontend classes - "AppWallet", - "TOTP", - - # errors (defined in passlib.exc, but exposed here for convenience) - "TokenError", - "MalformedTokenError", - "InvalidTokenError", - "UsedTokenError", - - # internal helper classes - "TotpToken", - "TotpMatch", -] - -#============================================================================= -# HACK: python < 2.7.4's urlparse() won't parse query strings unless the url scheme -# is one of the schemes in the urlparse.uses_query list. 2.7 abandoned -# this, and parses query if present, regardless of the scheme. -# as a workaround for older versions, we add "otpauth" to the known list. -# this was fixed by https://bugs.python.org/issue9374, in 2.7.4 release. -#============================================================================= -if sys.version_info < (2,7,4): - from urlparse import uses_query - if "otpauth" not in uses_query: - uses_query.append("otpauth") - log.debug("registered 'otpauth' scheme with urlparse.uses_query") - del uses_query - -#============================================================================= -# internal helpers -#============================================================================= - -#----------------------------------------------------------------------------- -# token parsing / rendering helpers -#----------------------------------------------------------------------------- - -#: regex used to clean whitespace from tokens & keys -_clean_re = re.compile(u(r"\s|[-=]"), re.U) - -_chunk_sizes = [4,6,5] - -def _get_group_size(klen): - """ - helper for group_string() -- - calculates optimal size of group for given string size. - """ - # look for exact divisor - for size in _chunk_sizes: - if not klen % size: - return size - # fallback to divisor with largest remainder - # (so chunks are as close to even as possible) - best = _chunk_sizes[0] - rem = 0 - for size in _chunk_sizes: - if klen % size > rem: - best = size - rem = klen % size - return best - -def group_string(value, sep="-"): - """ - reformat string into (roughly) evenly-sized groups, separated by **sep**. - useful for making tokens & keys easier to read by humans. - """ - klen = len(value) - size = _get_group_size(klen) - return sep.join(value[o:o+size] for o in irange(0, klen, size)) - -#----------------------------------------------------------------------------- -# encoding helpers -#----------------------------------------------------------------------------- - -def _decode_bytes(key, format): - """ - internal TOTP() helper -- - decodes key according to specified format. - """ - if format == "raw": - if not isinstance(key, bytes): - raise exc.ExpectedTypeError(key, "bytes", "key") - return key - # for encoded data, key must be either unicode or ascii-encoded bytes, - # and must contain a hex or base32 string. - key = to_unicode(key, param="key") - key = _clean_re.sub("", key).encode("utf-8") # strip whitespace & hypens - if format == "hex" or format == "base16": - return base64.b16decode(key.upper()) - elif format == "base32": - return b32decode(key) - # XXX: add base64 support? - else: - raise ValueError("unknown byte-encoding format: %r" % (format,)) - -#============================================================================= -# OTP management -#============================================================================= - -#: flag for detecting if encrypted totp support is present -AES_SUPPORT = bool(_cg_ciphers) - -#: regex for validating secret tags -_tag_re = re.compile("(?i)^[a-z0-9][a-z0-9_.-]*$") - -class AppWallet(object): - """ - This class stores application-wide secrets that can be used - to encrypt & decrypt TOTP keys for storage. - It's mostly an internal detail, applications usually just need - to pass ``secrets`` or ``secrets_path`` to :meth:`TOTP.using`. - - .. seealso:: - - :ref:`totp-storing-instances` for more details on this workflow. - - Arguments - ========= - :param secrets: - Dict of application secrets to use when encrypting/decrypting - stored TOTP keys. This should include a secret to use when encrypting - new keys, but may contain additional older secrets to decrypt - existing stored keys. - - The dict should map tags -> secrets, so that each secret is identified - by a unique tag. This tag will be stored along with the encrypted - key in order to determine which secret should be used for decryption. - Tag should be string that starts with regex range ``[a-z0-9]``, - and the remaining characters must be in ``[a-z0-9_.-]``. - - It is recommended to use something like a incremental counter - ("1", "2", ...), an ISO date ("2016-01-01", "2016-05-16", ...), - or a timestamp ("19803495", "19813495", ...) when assigning tags. - - This mapping be provided in three formats: - - * A python dict mapping tag -> secret - * A JSON-formatted string containing the dict - * A multiline string with the format ``"tag: value\\ntag: value\\n..."`` - - (This last format is mainly useful when loading from a text file via **secrets_path**) - - .. seealso:: :func:`generate_secret` to create a secret with sufficient entropy - - :param secrets_path: - Alternately, callers can specify a separate file where the - application-wide secrets are stored, using either of the string - formats described in **secrets**. - - :param default_tag: - Specifies which tag in **secrets** should be used as the default - for encrypting new keys. If omitted, the tags will be sorted, - and the largest tag used as the default. - - if all tags are numeric, they will be sorted numerically; - otherwise they will be sorted alphabetically. - this permits tags to be assigned numerically, - or e.g. using ``YYYY-MM-DD`` dates. - - :param encrypt_cost: - Optional time-cost factor for key encryption. - This value corresponds to log2() of the number of PBKDF2 - rounds used. - - .. warning:: - - The application secret(s) should be stored in a secure location by - your application, and each secret should contain a large amount - of entropy (to prevent brute-force attacks if the encrypted keys - are leaked). - - :func:`generate_secret` is provided as a convenience helper - to generate a new application secret of suitable size. - - Best practice is to load these values from a file via **secrets_path**, - and then have your application give up permission to read this file - once it's running. - - Public Methods - ============== - .. autoattribute:: has_secrets - .. autoattribute:: default_tag - - Semi-Private Methods - ==================== - The following methods are used internally by the :class:`TOTP` - class in order to encrypt & decrypt keys using the provided application - secrets. They will generally not be publically useful, and may have their - API changed periodically. - - .. automethod:: get_secret - .. automethod:: encrypt_key - .. automethod:: decrypt_key - """ - #======================================================================== - # instance attrs - #======================================================================== - - #: default salt size for encrypt_key() output - salt_size = 12 - - #: default cost (log2 of pbkdf2 rounds) for encrypt_key() output - #: NOTE: this is relatively low, since the majority of the security - #: relies on a high entropy secret to pass to AES. - encrypt_cost = 14 - - #: map of secret tag -> secret bytes - _secrets = None - - #: tag for default secret - default_tag = None - - #======================================================================== - # init - #======================================================================== - def __init__(self, secrets=None, default_tag=None, encrypt_cost=None, - secrets_path=None): - - # TODO: allow a lot more things to be customized from here, - # e.g. setting default TOTP constructor options. - - # - # init cost - # - if encrypt_cost is not None: - if isinstance(encrypt_cost, native_string_types): - encrypt_cost = int(encrypt_cost) - assert encrypt_cost >= 0 - self.encrypt_cost = encrypt_cost - - # - # init secrets map - # - - # load secrets from file (if needed) - if secrets_path is not None: - if secrets is not None: - raise TypeError("'secrets' and 'secrets_path' are mutually exclusive") - secrets = open(secrets_path, "rt").read() - - # parse & store secrets - secrets = self._secrets = self._parse_secrets(secrets) - - # - # init default tag/secret - # - if secrets: - if default_tag is not None: - # verify that tag is present in map - self.get_secret(default_tag) - elif all(tag.isdigit() for tag in secrets): - default_tag = max(secrets, key=int) - else: - default_tag = max(secrets) - self.default_tag = default_tag - - def _parse_secrets(self, source): - """ - parse 'secrets' parameter - - :returns: - Dict[tag:str, secret:bytes] - """ - # parse string formats - # to make this easy to pass in configuration from a separate file, - # 'secrets' can be string using two formats -- json & "tag:value\n" - check_type = True - if isinstance(source, native_string_types): - if source.lstrip().startswith(("[", "{")): - # json list / dict - source = json.loads(source) - elif "\n" in source and ":" in source: - # multiline string containing series of "tag: value\n" rows; - # empty and "#\n" rows are ignored - def iter_pairs(source): - for line in source.splitlines(): - line = line.strip() - if line and not line.startswith("#"): - tag, secret = line.split(":", 1) - yield tag.strip(), secret.strip() - source = iter_pairs(source) - check_type = False - else: - raise ValueError("unrecognized secrets string format") - - # ensure we have iterable of (tag, value) pairs - if source is None: - return {} - elif isinstance(source, dict): - source = source.items() - # XXX: could support iterable of (tag,value) pairs, but not yet needed... - # elif check_type and (isinstance(source, str) or not isinstance(source, Iterable)): - elif check_type: - raise TypeError("'secrets' must be mapping, or list of items") - - # parse into final dict, normalizing contents - return dict(self._parse_secret_pair(tag, value) - for tag, value in source) - - def _parse_secret_pair(self, tag, value): - if isinstance(tag, native_string_types): - pass - elif isinstance(tag, int): - tag = str(tag) - else: - raise TypeError("tag must be unicode/string: %r" % (tag,)) - if not _tag_re.match(tag): - raise ValueError("tag contains invalid characters: %r" % (tag,)) - if not isinstance(value, bytes): - value = to_bytes(value, param="secret %r" % (tag,)) - if not value: - raise ValueError("tag contains empty secret: %r" % (tag,)) - return tag, value - - #======================================================================== - # accessing secrets - #======================================================================== - - @property - def has_secrets(self): - """whether at least one application secret is present""" - return self.default_tag is not None - - def get_secret(self, tag): - """ - resolve a secret tag to the secret (as bytes). - throws a KeyError if not found. - """ - secrets = self._secrets - if not secrets: - raise KeyError("no application secrets configured") - try: - return secrets[tag] - except KeyError: - raise suppress_cause(KeyError("unknown secret tag: %r" % (tag,))) - - #======================================================================== - # encrypted key helpers -- used internally by TOTP - #======================================================================== - - @staticmethod - def _cipher_aes_key(value, secret, salt, cost, decrypt=False): - """ - Internal helper for :meth:`encrypt_key` -- - handles lowlevel encryption/decryption. - - Algorithm details: - - This function uses PBKDF2-HMAC-SHA256 to generate a 32-byte AES key - and a 16-byte IV from the application secret & random salt. - It then uses AES-256-CTR to encrypt/decrypt the TOTP key. - - CTR mode was chosen over CBC because the main attack scenario here - is that the attacker has stolen the database, and is trying to decrypt a TOTP key - (the plaintext value here). To make it hard for them, we want every password - to decrypt to a potentially valid key -- thus need to avoid any authentication - or padding oracle attacks. While some random padding construction could be devised - to make this work for CBC mode, a stream cipher mode is just plain simpler. - OFB/CFB modes would also work here, but seeing as they have malleability - and cyclic issues (though remote and barely relevant here), - CTR was picked as the best overall choice. - """ - # make sure backend AES support is available - if _cg_ciphers is None: - raise RuntimeError("TOTP encryption requires 'cryptography' package " - "(https://cryptography.io)") - - # use pbkdf2 to derive both key (32 bytes) & iv (16 bytes) - # NOTE: this requires 2 sha256 blocks to be calculated. - keyiv = pbkdf2_hmac("sha256", secret, salt=salt, rounds=(1 << cost), keylen=48) - - # use AES-256-CTR to encrypt/decrypt input value - cipher = _cg_ciphers.Cipher(_cg_ciphers.algorithms.AES(keyiv[:32]), - _cg_ciphers.modes.CTR(keyiv[32:]), - _cg_default_backend()) - ctx = cipher.decryptor() if decrypt else cipher.encryptor() - return ctx.update(value) + ctx.finalize() - - def encrypt_key(self, key): - """ - Helper used to encrypt TOTP keys for storage. - - :param key: - TOTP key to encrypt, as raw bytes. - - :returns: - dict containing encrypted TOTP key & configuration parameters. - this format should be treated as opaque, and potentially subject - to change, though it is designed to be easily serialized/deserialized - (e.g. via JSON). - - .. note:: - - This function requires installation of the external - `cryptography `_ package. - - To give some algorithm details: This function uses AES-256-CTR to encrypt - the provided data. It takes the application secret and randomly generated salt, - and uses PBKDF2-HMAC-SHA256 to combine them and generate the AES key & IV. - """ - if not key: - raise ValueError("no key provided") - salt = getrandbytes(rng, self.salt_size) - cost = self.encrypt_cost - tag = self.default_tag - if not tag: - raise TypeError("no application secrets configured, can't encrypt OTP key") - ckey = self._cipher_aes_key(key, self.get_secret(tag), salt, cost) - # XXX: switch to base64? - return dict(v=1, c=cost, t=tag, s=b32encode(salt), k=b32encode(ckey)) - - def decrypt_key(self, enckey): - """ - Helper used to decrypt TOTP keys from storage format. - Consults configured secrets to decrypt key. - - :param source: - source object, as returned by :meth:`encrypt_key`. - - :returns: - ``(key, needs_recrypt)`` -- - - **key** will be the decrypted key, as bytes. - - **needs_recrypt** will be a boolean flag indicating - whether encryption cost or default tag is too old, - and henace that key needs re-encrypting before storing. - - .. note:: - - This function requires installation of the external - `cryptography `_ package. - """ - if not isinstance(enckey, dict): - raise TypeError("'enckey' must be dictionary") - version = enckey.get("v", None) - needs_recrypt = False - if version == 1: - _cipher_key = self._cipher_aes_key - else: - raise ValueError("missing / unrecognized 'enckey' version: %r" % (version,)) - tag = enckey['t'] - cost = enckey['c'] - key = _cipher_key( - value=b32decode(enckey['k']), - secret=self.get_secret(tag), - salt=b32decode(enckey['s']), - cost=cost, - ) - if cost != self.encrypt_cost or tag != self.default_tag: - needs_recrypt = True - return key, needs_recrypt - - #============================================================================= - # eoc - #============================================================================= - -#============================================================================= -# TOTP class -#============================================================================= - -#: helper to convert HOTP counter to bytes -_pack_uint64 = struct.Struct(">Q").pack - -#: helper to extract value from HOTP digest -_unpack_uint32 = struct.Struct(">I").unpack - -#: dummy bytes used as temp key for .using() method -_DUMMY_KEY = b"\x00" * 16 - -class TOTP(object): - """ - Helper for generating and verifying TOTP codes. - - Given a secret key and set of configuration options, this object - offers methods for token generation, token validation, and serialization. - It can also be used to track important persistent TOTP state, - such as the last counter used. - - This class accepts the following options - (only **key** and **format** may be specified as positional arguments). - - :arg str key: - The secret key to use. By default, should be encoded as - a base32 string (see **format** for other encodings). - - Exactly one of **key** or ``new=True`` must be specified. - - :arg str format: - The encoding used by the **key** parameter. May be one of: - ``"base32"`` (base32-encoded string), - ``"hex"`` (hexadecimal string), or ``"raw"`` (raw bytes). - Defaults to ``"base32"``. - - :param bool new: - If ``True``, a new key will be generated using :class:`random.SystemRandom`. - - Exactly one ``new=True`` or **key** must be specified. - - :param str label: - Label to associate with this token when generating a URI. - Displayed to user by most OTP client applications (e.g. Google Authenticator), - and typically has format such as ``"John Smith"`` or ``"jsmith@webservice.example.org"``. - Defaults to ``None``. - See :meth:`to_uri` for details. - - :param str issuer: - String identifying the token issuer (e.g. the domain name of your service). - Used internally by some OTP client applications (e.g. Google Authenticator) to distinguish entries - which otherwise have the same label. - Optional but strongly recommended if you're rendering to a URI. - Defaults to ``None``. - See :meth:`to_uri` for details. - - :param int size: - Number of bytes when generating new keys. Defaults to size of hash algorithm (e.g. 20 for SHA1). - - .. warning:: - - Overriding the default values for ``digits``, ``period``, or ``alg`` may - cause problems with some OTP client programs (such as Google Authenticator), - which may have these defaults hardcoded. - - :param int digits: - The number of digits in the generated / accepted tokens. Defaults to ``6``. - Must be in range [6 .. 10]. - - .. rst-class:: inline-title - .. caution:: - Due to a limitation of the HOTP algorithm, the 10th digit can only take on values 0 .. 2, - and thus offers very little extra security. - - :param str alg: - Name of hash algorithm to use. Defaults to ``"sha1"``. - ``"sha256"`` and ``"sha512"`` are also accepted, per :rfc:`6238`. - - :param int period: - The time-step period to use, in integer seconds. Defaults to ``30``. - - .. - See the passlib documentation for a full list of attributes & methods. - """ - #============================================================================= - # class attrs - #============================================================================= - - #: minimum number of bytes to allow in key, enforced by passlib. - # XXX: see if spec says anything relevant to this. - _min_key_size = 10 - - #: minimum & current serialization version (may be set independently by subclasses) - min_json_version = json_version = 1 - - #: AppWallet that this class will use for encrypting/decrypting keys. - #: (can be overwritten via the :meth:`TOTP.using()` constructor) - wallet = None - - #: function to get system time in seconds, as needed by :meth:`generate` and :meth:`verify`. - #: defaults to :func:`time.time`, but can be overridden on a per-instance basis. - now = _time.time - - #============================================================================= - # instance attrs - #============================================================================= - - #--------------------------------------------------------------------------- - # configuration attrs - #--------------------------------------------------------------------------- - - #: [private] secret key as raw :class:`!bytes` - #: see .key property for public access. - _key = None - - #: [private] cached copy of encrypted secret, - #: so .to_json() doesn't have to re-encrypt on each call. - _encrypted_key = None - - #: [private] cached copy of keyed HMAC function, - #: so ._generate() doesn't have to rebuild this each time - #: ._find_match() invokes it. - _keyed_hmac = None - - #: number of digits in the generated tokens. - digits = 6 - - #: name of hash algorithm in use (e.g. ``"sha1"``) - alg = "sha1" - - #: default label for :meth:`to_uri` - label = None - - #: default issuer for :meth:`to_uri` - issuer = None - - #: number of seconds per counter step. - #: *(TOTP uses an internal time-derived counter which - #: increments by 1 every* :attr:`!period` *seconds)*. - period = 30 - - #--------------------------------------------------------------------------- - # state attrs - #--------------------------------------------------------------------------- - - #: Flag set by deserialization methods to indicate the object needs to be re-serialized. - #: This can be for a number of reasons -- encoded using deprecated format, - #: or encrypted using a deprecated key or too few rounds. - changed = False - - #============================================================================= - # prototype construction - #============================================================================= - @classmethod - def using(cls, digits=None, alg=None, period=None, - issuer=None, wallet=None, now=None, **kwds): - """ - Dynamically create subtype of :class:`!TOTP` class - which has the specified defaults set. - - :parameters: **digits, alg, period, issuer**: - - All these options are the same as in the :class:`TOTP` constructor, - and the resulting class will use any values you specify here - as the default for all TOTP instances it creates. - - :param wallet: - Optional :class:`AppWallet` that will be used for encrypting/decrypting keys. - - :param secrets, secrets_path, encrypt_cost: - - If specified, these options will be passed to the :class:`AppWallet` constructor, - allowing you to directly specify the secret keys that should be used - to encrypt & decrypt stored keys. - - :returns: - subclass of :class:`!TOTP`. - - This method is useful for creating a TOTP class configured - to use your application's secrets for encrypting & decrypting - keys, as well as create new keys using it's desired configuration defaults. - - As an example:: - - >>> # your application can create a custom class when it initializes - >>> from passlib.totp import TOTP, generate_secret - >>> TotpFactory = TOTP.using(secrets={"1": generate_secret()}) - - >>> # subsequent TOTP objects created from this factory - >>> # will use the specified secrets to encrypt their keys... - >>> totp = TotpFactory.new() - >>> totp.to_dict() - {'enckey': {'c': 14, - 'k': 'H77SYXWORDPGVOQTFRR2HFUB3C45XXI7', - 's': 'G5DOQPIHIBUM2OOHHADQ', - 't': '1', - 'v': 1}, - 'type': 'totp', - 'v': 1} - - .. seealso:: :ref:`totp-creation` and :ref:`totp-storing-instances` tutorials for a usage example - """ - # XXX: could add support for setting default match 'window' and 'reuse' policy - - # :param now: - # Optional callable that should return current time for generator to use. - # Default to :func:`time.time`. This optional is generally not needed, - # and is mainly present for examples & unit-testing. - - subcls = type("TOTP", (cls,), {}) - - def norm_param(attr, value): - """ - helper which uses constructor to validate parameter value. - it returns corresponding attribute, so we use normalized value. - """ - # NOTE: this creates *subclass* instance, - # so normalization takes into account any custom params - # already stored. - kwds = dict(key=_DUMMY_KEY, format="raw") - kwds[attr] = value - obj = subcls(**kwds) - return getattr(obj, attr) - - if digits is not None: - subcls.digits = norm_param("digits", digits) - - if alg is not None: - subcls.alg = norm_param("alg", alg) - - if period is not None: - subcls.period = norm_param("period", period) - - # XXX: add default size as configurable parameter? - - if issuer is not None: - subcls.issuer = norm_param("issuer", issuer) - - if kwds: - subcls.wallet = AppWallet(**kwds) - if wallet: - raise TypeError("'wallet' and 'secrets' keywords are mutually exclusive") - elif wallet is not None: - if not isinstance(wallet, AppWallet): - raise exc.ExpectedTypeError(wallet, AppWallet, "wallet") - subcls.wallet = wallet - - if now is not None: - assert isinstance(now(), num_types) and now() >= 0, \ - "now() function must return non-negative int/float" - subcls.now = staticmethod(now) - - return subcls - - #============================================================================= - # init - #============================================================================= - - @classmethod - def new(cls, **kwds): - """ - convenience alias for creating new TOTP key, same as ``TOTP(new=True)`` - """ - return cls(new=True, **kwds) - - def __init__(self, key=None, format="base32", - # keyword only... - new=False, digits=None, alg=None, size=None, period=None, - label=None, issuer=None, changed=False, - **kwds): - super(TOTP, self).__init__(**kwds) - if changed: - self.changed = changed - - # validate & normalize alg - info = lookup_hash(alg or self.alg) - self.alg = info.name - digest_size = info.digest_size - if digest_size < 4: - raise RuntimeError("%r hash digest too small" % alg) - - # parse or generate new key - if new: - # generate new key - if key: - raise TypeError("'key' and 'new=True' are mutually exclusive") - if size is None: - # default to digest size, per RFC 6238 Section 5.1 - size = digest_size - elif size > digest_size: - # not forbidden by spec, but would just be wasted bytes. - # maybe just warn about this? - raise ValueError("'size' should be less than digest size " - "(%d)" % digest_size) - self.key = getrandbytes(rng, size) - elif not key: - raise TypeError("must specify either an existing 'key', or 'new=True'") - elif format == "encrypted": - # NOTE: this handles decrypting & setting '.key' - self.encrypted_key = key - elif key: - # use existing key, encoded using specified - self.key = _decode_bytes(key, format) - - # enforce min key size - if len(self.key) < self._min_key_size: - # only making this fatal for new=True, - # so that existing (but ridiculously small) keys can still be used. - msg = "for security purposes, secret key must be >= %d bytes" % self._min_key_size - if new: - raise ValueError(msg) - else: - warn(msg, exc.PasslibSecurityWarning, stacklevel=1) - - # validate digits - if digits is None: - digits = self.digits - if not isinstance(digits, int_types): - raise TypeError("digits must be an integer, not a %r" % type(digits)) - if digits < 6 or digits > 10: - raise ValueError("digits must in range(6,11)") - self.digits = digits - - # validate label - if label: - self._check_label(label) - self.label = label - - # validate issuer - if issuer: - self._check_issuer(issuer) - self.issuer = issuer - - # init period - if period is not None: - self._check_serial(period, "period", minval=1) - self.period = period - - #============================================================================= - # helpers to verify value types & ranges - #============================================================================= - - @staticmethod - def _check_serial(value, param, minval=0): - """ - check that serial value (e.g. 'counter') is non-negative integer - """ - if not isinstance(value, int_types): - raise exc.ExpectedTypeError(value, "int", param) - if value < minval: - raise ValueError("%s must be >= %d" % (param, minval)) - - @staticmethod - def _check_label(label): - """ - check that label doesn't contain chars forbidden by KeyURI spec - """ - if label and ":" in label: - raise ValueError("label may not contain ':'") - - @staticmethod - def _check_issuer(issuer): - """ - check that issuer doesn't contain chars forbidden by KeyURI spec - """ - if issuer and ":" in issuer: - raise ValueError("issuer may not contain ':'") - - #============================================================================= - # key attributes - #============================================================================= - - #------------------------------------------------------------------ - # raw key - #------------------------------------------------------------------ - @property - def key(self): - """ - secret key as raw bytes - """ - return self._key - - @key.setter - def key(self, value): - # set key - if not isinstance(value, bytes): - raise exc.ExpectedTypeError(value, bytes, "key") - self._key = value - - # clear cached properties derived from key - self._encrypted_key = self._keyed_hmac = None - - #------------------------------------------------------------------ - # encrypted key - #------------------------------------------------------------------ - @property - def encrypted_key(self): - """ - secret key, encrypted using application secret. - this match the output of :meth:`AppWallet.encrypt_key`, - and should be treated as an opaque json serializable object. - """ - enckey = self._encrypted_key - if enckey is None: - wallet = self.wallet - if not wallet: - raise TypeError("no application secrets present, can't encrypt TOTP key") - enckey = self._encrypted_key = wallet.encrypt_key(self.key) - return enckey - - @encrypted_key.setter - def encrypted_key(self, value): - wallet = self.wallet - if not wallet: - raise TypeError("no application secrets present, can't decrypt TOTP key") - self.key, needs_recrypt = wallet.decrypt_key(value) - if needs_recrypt: - # mark as changed so it gets re-encrypted & written to db - self.changed = True - else: - # cache encrypted key for re-use - self._encrypted_key = value - - #------------------------------------------------------------------ - # pretty-printed / encoded key helpers - #------------------------------------------------------------------ - - @property - def hex_key(self): - """ - secret key encoded as hexadecimal string - """ - return bascii_to_str(base64.b16encode(self.key)).lower() - - @property - def base32_key(self): - """ - secret key encoded as base32 string - """ - return b32encode(self.key) - - def pretty_key(self, format="base32", sep="-"): - """ - pretty-print the secret key. - - This is mainly useful for situations where the user cannot get the qrcode to work, - and must enter the key manually into their TOTP client. It tries to format - the key in a manner that is easier for humans to read. - - :param format: - format to output secret key. ``"hex"`` and ``"base32"`` are both accepted. - - :param sep: - separator to insert to break up key visually. - can be any of ``"-"`` (the default), ``" "``, or ``False`` (no separator). - - :return: - key as native string. - - Usage example:: - - >>> t = TOTP('s3jdvb7qd2r7jpxx') - >>> t.pretty_key() - 'S3JD-VB7Q-D2R7-JPXX' - """ - if format == "hex" or format == "base16": - key = self.hex_key - elif format == "base32": - key = self.base32_key - else: - raise ValueError("unknown byte-encoding format: %r" % (format,)) - if sep: - key = group_string(key, sep) - return key - - #============================================================================= - # time & token parsing - #============================================================================= - - @classmethod - def normalize_time(cls, time): - """ - Normalize time value to unix epoch seconds. - - :arg time: - Can be ``None``, :class:`!datetime`, - or unix epoch timestamp as :class:`!float` or :class:`!int`. - If ``None``, uses current system time. - Naive datetimes are treated as UTC. - - :returns: - unix epoch timestamp as :class:`int`. - """ - if isinstance(time, int_types): - return time - elif isinstance(time, float): - return int(time) - elif time is None: - return int(cls.now()) - elif hasattr(time, "utctimetuple"): - # coerce datetime to UTC timestamp - # NOTE: utctimetuple() assumes naive datetimes are in UTC - # NOTE: we explicitly *don't* want microseconds. - return calendar.timegm(time.utctimetuple()) - else: - raise exc.ExpectedTypeError(time, "int, float, or datetime", "time") - - def _time_to_counter(self, time): - """ - convert timestamp to HOTP counter using :attr:`period`. - """ - return time // self.period - - def _counter_to_time(self, counter): - """ - convert HOTP counter to timestamp using :attr:`period`. - """ - return counter * self.period - - @hybrid_method - def normalize_token(self_or_cls, token): - """ - Normalize OTP token representation: - strips whitespace, converts integers to a zero-padded string, - validates token content & number of digits. - - This is a hybrid method -- it can be called at the class level, - as ``TOTP.normalize_token()``, or the instance level as ``TOTP().normalize_token()``. - It will normalize to the instance-specific number of :attr:`~TOTP.digits`, - or use the class default. - - :arg token: - token as ascii bytes, unicode, or an integer. - - :raises ValueError: - if token has wrong number of digits, or contains non-numeric characters. - - :returns: - token as :class:`!unicode` string, containing only digits 0-9. - """ - digits = self_or_cls.digits - if isinstance(token, int_types): - token = u("%0*d") % (digits, token) - else: - token = to_unicode(token, param="token") - token = _clean_re.sub(u(""), token) - if not token.isdigit(): - raise MalformedTokenError("Token must contain only the digits 0-9") - if len(token) != digits: - raise MalformedTokenError("Token must have exactly %d digits" % digits) - return token - - #============================================================================= - # token generation - #============================================================================= - -# # debug helper -# def generate_range(self, size, time=None): -# counter = self._time_to_counter(time) - (size + 1) // 2 -# end = counter + size -# while counter <= end: -# token = self._generate(counter) -# yield TotpToken(self, token, counter) -# counter += 1 - - def generate(self, time=None): - """ - Generate token for specified time - (uses current time if none specified). - - :arg time: - Can be ``None``, a :class:`!datetime`, - or class:`!float` / :class:`!int` unix epoch timestamp. - If ``None`` (the default), uses current system time. - Naive datetimes are treated as UTC. - - :returns: - - A :class:`TotpToken` instance, which can be treated - as a sequence of ``(token, expire_time)`` -- see that class - for more details. - - Usage example:: - - >>> # generate a new token, wrapped in a TotpToken instance... - >>> otp = TOTP('s3jdvb7qd2r7jpxx') - >>> otp.generate(1419622739) - - - >>> # when you just need the token... - >>> otp.generate(1419622739).token - '897212' - """ - time = self.normalize_time(time) - counter = self._time_to_counter(time) - if counter < 0: - raise ValueError("timestamp must be >= 0") - token = self._generate(counter) - return TotpToken(self, token, counter) - - def _generate(self, counter): - """ - base implementation of HOTP token generation algorithm. - - :arg counter: HOTP counter, as non-negative integer - :returns: token as unicode string - """ - # generate digest - assert isinstance(counter, int_types), "counter must be integer" - assert counter >= 0, "counter must be non-negative" - keyed_hmac = self._keyed_hmac - if keyed_hmac is None: - keyed_hmac = self._keyed_hmac = compile_hmac(self.alg, self.key) - digest = keyed_hmac(_pack_uint64(counter)) - digest_size = keyed_hmac.digest_info.digest_size - assert len(digest) == digest_size, "digest_size: sanity check failed" - - # derive 31-bit token value - assert digest_size >= 20, "digest_size: sanity check 2 failed" # otherwise 0xF+4 will run off end of hash. - offset = byte_elem_value(digest[-1]) & 0xF - value = _unpack_uint32(digest[offset:offset+4])[0] & 0x7fffffff - - # render to decimal string, return last chars - # NOTE: the 10'th digit is not as secure, as it can only take on values 0-2, not 0-9, - # due to 31-bit mask on int ">I". But some servers / clients use it :| - # if 31-bit mask removed (which breaks spec), would only get values 0-4. - digits = self.digits - assert 0 < digits < 11, "digits: sanity check failed" - return (u("%0*d") % (digits, value))[-digits:] - - #============================================================================= - # token verification - #============================================================================= - - @classmethod - def verify(cls, token, source, **kwds): - r""" - Convenience wrapper around :meth:`TOTP.from_source` and :meth:`TOTP.match`. - - This parses a TOTP key & configuration from the specified source, - and tries and match the token. - It's designed to parallel the :meth:`passlib.ifc.PasswordHash.verify` method. - - :param token: - Token string to match. - - :param source: - Serialized TOTP key. - Can be anything accepted by :meth:`TOTP.from_source`. - - :param \\*\\*kwds: - All additional keywords passed to :meth:`TOTP.match`. - - :return: - A :class:`TotpMatch` instance, or raises a :exc:`TokenError`. - """ - return cls.from_source(source).match(token, **kwds) - - def match(self, token, time=None, window=30, skew=0, last_counter=None): - """ - Match TOTP token against specified timestamp. - Searches within a window before & after the provided time, - in order to account for transmission delay and small amounts of skew in the client's clock. - - :arg token: - Token to validate. - may be integer or string (whitespace and hyphens are ignored). - - :param time: - Unix epoch timestamp, can be any of :class:`!float`, :class:`!int`, or :class:`!datetime`. - if ``None`` (the default), uses current system time. - *this should correspond to the time the token was received from the client*. - - :param int window: - How far backward and forward in time to search for a match. - Measured in seconds. Defaults to ``30``. Typically only useful if set - to multiples of :attr:`period`. - - :param int skew: - Adjust timestamp by specified value, to account for excessive - client clock skew. Measured in seconds. Defaults to ``0``. - - Negative skew (the common case) indicates transmission delay, - and/or that the client clock is running behind the server. - - Positive skew indicates the client clock is running ahead of the server - (and by enough that it cancels out any negative skew added by - the transmission delay). - - You should ensure the server clock uses a reliable time source such as NTP, - so that only the client clock's inaccuracy needs to be accounted for. - - This is an advanced parameter that should usually be left at ``0``; - The **window** parameter is usually enough to account - for any observed transmission delay. - - :param last_counter: - Optional value of last counter value that was successfully used. - If specified, verify will never search earlier counters, - no matter how large the window is. - - Useful when client has previously authenticated, - and thus should never provide a token older than previously - verified value. - - :raises ~passlib.exc.TokenError: - - If the token is malformed, fails to match, or has already been used. - - :returns TotpMatch: - - Returns a :class:`TotpMatch` instance on successful match. - Can be treated as tuple of ``(counter, time)``. - Raises error if token is malformed / can't be verified. - - Usage example:: - - >>> totp = TOTP('s3jdvb7qd2r7jpxx') - - >>> # valid token for this time period - >>> totp.match('897212', 1419622729) - - - >>> # token from counter step 30 sec ago (within allowed window) - >>> totp.match('000492', 1419622729) - - - >>> # invalid token -- token from 60 sec ago (outside of window) - >>> totp.match('760389', 1419622729) - Traceback: - ... - InvalidTokenError: Token did not match - """ - time = self.normalize_time(time) - self._check_serial(window, "window") - - client_time = time + skew - if last_counter is None: - last_counter = -1 - start = max(last_counter, self._time_to_counter(client_time - window)) - end = self._time_to_counter(client_time + window) + 1 - # XXX: could pass 'expected = _time_to_counter(client_time + TRANSMISSION_DELAY)' - # to the _find_match() method, would help if window set to very large value. - - counter = self._find_match(token, start, end) - assert counter >= last_counter, "sanity check failed: counter went backward" - - if counter == last_counter: - raise UsedTokenError(expire_time=(last_counter + 1) * self.period) - - # NOTE: By returning match tied to

`` 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/site-packages/pip/_internal/cli/base_command.py b/.venv/Lib/site-packages/pip/_internal/cli/base_command.py deleted file mode 100644 index 5bd7e67..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/base_command.py +++ /dev/null @@ -1,216 +0,0 @@ -"""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, - ) - - # 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("[present-rich] %s", exc) - 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/site-packages/pip/_internal/cli/cmdoptions.py b/.venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py deleted file mode 100644 index b4e2560..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/cmdoptions.py +++ /dev/null @@ -1,1049 +0,0 @@ -""" -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 dist_restriction_set and not options.target_dir: - raise CommandError( - "Can not use any platform or abi specific options unless " - "installing via '--target'" - ) - - -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." - ), -) - -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.", -) - -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 = "invalid --python-version value: {!r}: {}".format( - value, - 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 older binary packages over newer source packages.", - ) - - -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 is installed - # and raise error if it is not. - if not importlib.util.find_spec("setuptools"): - msg = "It is not possible to use --no-use-pep517 without setuptools 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") # noqa - dest = getattr(parser.values, option.dest) - if dest is None: - dest = {} - setattr(parser.values, option.dest, dest) - dest[key] = val - - -config_settings: Callable[..., Option] = partial( - Option, - "--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.", -) - -install_options: Callable[..., Option] = partial( - Option, - "--install-option", - dest="install_options", - action="append", - metavar="options", - help="Extra arguments to be supplied to the setup.py install " - 'command (use like --install-option="--install-scripts=/usr/local/' - 'bin"). Use multiple --install-option options to pass multiple ' - "options to setup.py install. If you are using an option with a " - "directory path, be sure to use absolute path.", -) - -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=False, - 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( - "Arguments to {} must be a hash name " # noqa - "followed by a value, like --hash=sha256:" - "abcde...".format(opt_str) - ) - if algo not in STRONG_HASHES: - parser.error( - "Allowed hash algorithms for {} are {}.".format( # noqa - 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.", -) - - -use_new_feature: Callable[..., Option] = partial( - Option, - "--use-feature", - dest="features_enabled", - metavar="feature", - action="append", - default=[], - choices=[ - "fast-deps", - "truststore", - "no-binary-enable-wheel-cache", - ], - 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, - 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/site-packages/pip/_internal/cli/command_context.py b/.venv/Lib/site-packages/pip/_internal/cli/command_context.py deleted file mode 100644 index 139995a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/command_context.py +++ /dev/null @@ -1,27 +0,0 @@ -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/site-packages/pip/_internal/cli/main.py b/.venv/Lib/site-packages/pip/_internal/cli/main.py deleted file mode 100644 index 0e31221..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/main.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Primary application entrypoint. -""" -import locale -import logging -import os -import sys -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:] - - # 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/site-packages/pip/_internal/cli/main_parser.py b/.venv/Lib/site-packages/pip/_internal/cli/main_parser.py deleted file mode 100644 index 5ade356..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/main_parser.py +++ /dev/null @@ -1,134 +0,0 @@ -"""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/site-packages/pip/_internal/cli/parser.py b/.venv/Lib/site-packages/pip/_internal/cli/parser.py deleted file mode 100644 index c762cf2..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/parser.py +++ /dev/null @@ -1,294 +0,0 @@ -"""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( - "{} is not a valid value for {} option, " # noqa - "please specify a boolean value like yes/no, " - "true/false or 1/0 instead.".format(val, key) - ) - 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( - "{} is not a valid value for {} option, " # noqa - "please instead specify either a non-negative integer " - "or a boolean value like yes/no or false/true " - "which is equivalent to 1/0.".format(val, key) - ) - 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/site-packages/pip/_internal/cli/progress_bars.py b/.venv/Lib/site-packages/pip/_internal/cli/progress_bars.py deleted file mode 100644 index 0ad1403..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/progress_bars.py +++ /dev/null @@ -1,68 +0,0 @@ -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/site-packages/pip/_internal/cli/req_command.py b/.venv/Lib/site-packages/pip/_internal/cli/req_command.py deleted file mode 100644 index 1044809..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/req_command.py +++ /dev/null @@ -1,502 +0,0 @@ -"""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: - import truststore - except ImportError: - raise CommandError( - "To use the truststore feature, 'truststore' must be installed into " - "pip's current environment." - ) - - 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") 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 - - 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 "2020-resolver" - - @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 - - resolver_variant = cls.determine_resolver_variant(options) - if resolver_variant == "2020-resolver": - 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: - 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, - ) - - @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, - config_settings=getattr(options, "config_settings", None), - ) - 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 == "2020-resolver": - 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, - 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, - ) - 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/site-packages/pip/_internal/cli/spinners.py b/.venv/Lib/site-packages/pip/_internal/cli/spinners.py deleted file mode 100644 index cf2b976..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/spinners.py +++ /dev/null @@ -1,159 +0,0 @@ -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/site-packages/pip/_internal/cli/status_codes.py b/.venv/Lib/site-packages/pip/_internal/cli/status_codes.py deleted file mode 100644 index 5e29502..0000000 --- a/.venv/Lib/site-packages/pip/_internal/cli/status_codes.py +++ /dev/null @@ -1,6 +0,0 @@ -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/site-packages/pip/_internal/commands/__init__.py b/.venv/Lib/site-packages/pip/_internal/commands/__init__.py deleted file mode 100644 index 858a410..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/__init__.py +++ /dev/null @@ -1,132 +0,0 @@ -""" -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/site-packages/pip/_internal/commands/cache.py b/.venv/Lib/site-packages/pip/_internal/commands/cache.py deleted file mode 100644 index c5f0330..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/cache.py +++ /dev/null @@ -1,223 +0,0 @@ -import os -import textwrap -from optparse import Values -from typing import Any, List - -import pip._internal.utils.filesystem as filesystem -from pip._internal.cli.base_command import Command -from pip._internal.cli.status_codes import ERROR, SUCCESS -from pip._internal.exceptions import CommandError, PipError -from pip._internal.utils.logging import getLogger - -logger = getLogger(__name__) - - -class CacheCommand(Command): - """ - Inspect and manage pip's wheel cache. - - Subcommands: - - - dir: Show the cache directory. - - info: Show information about the cache. - - list: List filenames of packages stored in the cache. - - remove: Remove one or more package from the cache. - - purge: Remove all items from the cache. - - ```` 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") - wheels_cache_location = self._cache_dir(options, "wheels") - http_cache_size = filesystem.format_directory_size(http_cache_location) - wheels_cache_size = filesystem.format_directory_size(wheels_cache_location) - - message = ( - textwrap.dedent( - """ - Package index page cache location: {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} - """ - ) - .format( - http_cache_location=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 not files: - return - - results = [] - for filename in files: - results.append(filename) - - logger.info("\n".join(sorted(results))) - - 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 += ' for pattern "{}"'.format(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]: - http_dir = self._cache_dir(options, "http") - return filesystem.find_files(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/site-packages/pip/_internal/commands/check.py b/.venv/Lib/site-packages/pip/_internal/commands/check.py deleted file mode 100644 index 3864220..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/check.py +++ /dev/null @@ -1,53 +0,0 @@ -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, -) -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() - 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/site-packages/pip/_internal/commands/completion.py b/.venv/Lib/site-packages/pip/_internal/commands/completion.py deleted file mode 100644 index deaa308..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/completion.py +++ /dev/null @@ -1,126 +0,0 @@ -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": """ - function _pip_completion {{ - local words cword - read -Ac words - read -cn cword - reply=( $( COMP_WORDS="$words[*]" \\ - COMP_CWORD=$(( cword-1 )) \\ - PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )) - }} - compctl -K _pip_completion {prog} - """, - "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/site-packages/pip/_internal/commands/configuration.py b/.venv/Lib/site-packages/pip/_internal/commands/configuration.py deleted file mode 100644 index 84b134e..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/configuration.py +++ /dev/null @@ -1,282 +0,0 @@ -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( - "Editor Subprocess exited with exit code {}".format(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 = ( - "Got unexpected number of arguments, expected {}. " - '(example: "{} config {}")' - ).format(n, get_prog(), 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/site-packages/pip/_internal/commands/debug.py b/.venv/Lib/site-packages/pip/_internal/commands/debug.py deleted file mode 100644 index 6fad1fe..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/debug.py +++ /dev/null @@ -1,199 +0,0 @@ -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) -> ModuleType: - # Module name can be uppercase in vendor.txt for some reason... - module_name = module_name.lower() - # PATCH: setuptools is actually only pkg_resources. - if module_name == "setuptools": - module_name = "pkg_resources" - - __import__(f"pip._vendor.{module_name}", globals(), locals(), level=0) - return getattr(pip._vendor, module_name) - - -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 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" - " be {})".format(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_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 = "Compatible tags: {}{}".format(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 = ( - "...\n[First {tag_limit} tags shown. Pass --verbose to show all.]" - ).format(tag_limit=tag_limit) - logger.info(msg) - - -def ca_bundle_info(config: Configuration) -> str: - levels = set() - for key, _ in config.items(): - levels.add(key.split(".")[0]) - - 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/site-packages/pip/_internal/commands/download.py b/.venv/Lib/site-packages/pip/_internal/commands/download.py deleted file mode 100644 index 4132e08..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/download.py +++ /dev/null @@ -1,149 +0,0 @@ -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 ( - LegacySetupPyOptionsCheckMode, - 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, LegacySetupPyOptionsCheckMode.DOWNLOAD - ) - - 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) - if downloaded: - write_output("Successfully downloaded %s", " ".join(downloaded)) - - return SUCCESS diff --git a/.venv/Lib/site-packages/pip/_internal/commands/freeze.py b/.venv/Lib/site-packages/pip/_internal/commands/freeze.py deleted file mode 100644 index 5fa6d39..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/freeze.py +++ /dev/null @@ -1,97 +0,0 @@ -import sys -from optparse import Values -from typing import 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 - -DEV_PKGS = {"pip", "setuptools", "distribute", "wheel"} - - -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/site-packages/pip/_internal/commands/hash.py b/.venv/Lib/site-packages/pip/_internal/commands/hash.py deleted file mode 100644 index 042dac8..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/hash.py +++ /dev/null @@ -1,59 +0,0 @@ -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/site-packages/pip/_internal/commands/help.py b/.venv/Lib/site-packages/pip/_internal/commands/help.py deleted file mode 100644 index 6206631..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/help.py +++ /dev/null @@ -1,41 +0,0 @@ -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/site-packages/pip/_internal/commands/index.py b/.venv/Lib/site-packages/pip/_internal/commands/index.py deleted file mode 100644 index b4bf0ac..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/index.py +++ /dev/null @@ -1,138 +0,0 @@ -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. - """ - - 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( - "No matching distribution found for {}".format(query) - ) - - formatted_versions = [str(ver) for ver in sorted(versions, reverse=True)] - latest = formatted_versions[0] - - write_output("{} ({})".format(query, latest)) - write_output("Available versions: {}".format(", ".join(formatted_versions))) - print_dist_installation_info(query, latest) diff --git a/.venv/Lib/site-packages/pip/_internal/commands/inspect.py b/.venv/Lib/site-packages/pip/_internal/commands/inspect.py deleted file mode 100644 index a4e3599..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/inspect.py +++ /dev/null @@ -1,97 +0,0 @@ -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: - logger.warning( - "pip inspect is currently an experimental command. " - "The output format may change in a future release without prior warning." - ) - - 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": "0", - "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/site-packages/pip/_internal/commands/install.py b/.venv/Lib/site-packages/pip/_internal/commands/install.py deleted file mode 100644 index e081c27..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/install.py +++ /dev/null @@ -1,860 +0,0 @@ -import errno -import json -import operator -import os -import shutil -import site -from optparse import SUPPRESS_HELP, Values -from typing import Iterable, List, Optional - -from pip._vendor.packaging.utils import canonicalize_name -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.format_control import FormatControl -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, - LegacySetupPyOptionsCheckMode, - check_legacy_setup_py_options, -) -from pip._internal.utils.compat import WINDOWS -from pip._internal.utils.deprecation import ( - LegacyInstallReasonFailedBdistWheel, - deprecated, -) -from pip._internal.utils.distutils_args import parse_distutils_args -from pip._internal.utils.filesystem import test_writable_dir -from pip._internal.utils.logging import getLogger -from pip._internal.utils.misc import ( - 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 ( - BdistWheelAllowedPredicate, - build, - should_build_for_install_command, -) - -logger = getLogger(__name__) - - -def get_check_bdist_wheel_allowed( - format_control: FormatControl, -) -> BdistWheelAllowedPredicate: - def check_binary_allowed(req: InstallRequirement) -> bool: - canonical_name = canonicalize_name(req.name or "") - allowed_formats = format_control.get_allowed_formats(canonical_name) - return "binary" in allowed_formats - - return check_binary_allowed - - -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" - ), - ) - - 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.config_settings()) - self.cmd_opts.add_option(cmdoptions.install_options()) - 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'") - - upgrade_strategy = "to-satisfy-only" - if options.upgrade: - upgrade_strategy = options.upgrade_strategy - - cmdoptions.check_dist_restriction(options, check_target=True) - - install_options = options.install_options or [] - - 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, LegacySetupPyOptionsCheckMode.INSTALL - ) - - if "no-binary-enable-wheel-cache" in options.features_enabled: - # TODO: remove format_control from WheelCache when the deprecation cycle - # is over - wheel_cache = WheelCache(options.cache_dir) - else: - if options.format_control.no_binary: - deprecated( - reason=( - "--no-binary currently disables reading from " - "the cache of locally built wheels. In the future " - "--no-binary will not influence the wheel cache." - ), - replacement="to use the --no-cache-dir option", - feature_flag="no-binary-enable-wheel-cache", - issue=11453, - gone_in="23.1", - ) - wheel_cache = WheelCache(options.cache_dir, options.format_control) - - # 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 - - reject_location_related_install_options(reqs, options.install_options) - - 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: - logger.warning( - "--report is currently an experimental option. " - "The output format may change in a future release " - "without prior warning." - ) - - 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: - 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) - - check_bdist_wheel_allowed = get_check_bdist_wheel_allowed( - finder.format_control - ) - - reqs_to_build = [ - r - for r in requirement_set.requirements.values() - if should_build_for_install_command(r, check_bdist_wheel_allowed) - ] - - _, build_failures = build( - reqs_to_build, - wheel_cache=wheel_cache, - verify=True, - build_options=[], - global_options=global_options, - ) - - # If we're using PEP 517, we cannot do a legacy setup.py install - # so we fail here. - pep517_build_failure_names: List[str] = [ - r.name for r in build_failures if r.use_pep517 # type: ignore - ] - if pep517_build_failure_names: - raise InstallationError( - "Could not build wheels for {}, which is required to " - "install pyproject.toml-based projects".format( - ", ".join(pep517_build_failure_names) - ) - ) - - # For now, we just warn about failures building legacy - # requirements, as we'll fall through to a setup.py install for - # those. - for r in build_failures: - if not r.use_pep517: - r.legacy_install_reason = LegacyInstallReasonFailedBdistWheel - - 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, - install_options, - 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) # noqa - - 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 == "2020-resolver" - 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 = ( - "{name} {version} requires {requirement}, " - "which is not installed." - ).format( - name=project_name, - version=version, - requirement=dependency[1], - ) - 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 == "2020-resolver" 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 reject_location_related_install_options( - requirements: List[InstallRequirement], options: Optional[List[str]] -) -> None: - """If any location-changing --install-option arguments were passed for - requirements or on the command-line, then show a deprecation warning. - """ - - def format_options(option_names: Iterable[str]) -> List[str]: - return ["--{}".format(name.replace("_", "-")) for name in option_names] - - offenders = [] - - for requirement in requirements: - install_options = requirement.install_options - location_options = parse_distutils_args(install_options) - if location_options: - offenders.append( - "{!r} from {}".format( - format_options(location_options.keys()), requirement - ) - ) - - if options: - location_options = parse_distutils_args(options) - if location_options: - offenders.append( - "{!r} from command line".format(format_options(location_options.keys())) - ) - - if not offenders: - return - - raise CommandError( - "Location-changing options found in --install-option: {}." - " This is unsupported, use pip-level options like --user," - " --prefix, --root, and --target instead.".format("; ".join(offenders)) - ) - - -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/site-packages/pip/_internal/commands/list.py b/.venv/Lib/site-packages/pip/_internal/commands/list.py deleted file mode 100644 index 8e1426d..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/list.py +++ /dev/null @@ -1,365 +0,0 @@ -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] - - -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", - ) - - 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' can not 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 dist.latest_version > dist.version - ] - - def get_uptodate( - self, packages: "_ProcessedDists", options: Values - ) -> "_ProcessedDists": - return [ - dist - for dist in self.iter_packages_latest_infos(packages, options) - if dist.latest_version == 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(map(lambda x: "-" * x, 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/site-packages/pip/_internal/commands/search.py b/.venv/Lib/site-packages/pip/_internal/commands/search.py deleted file mode 100644 index 03ed925..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/search.py +++ /dev/null @@ -1,174 +0,0 @@ -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/site-packages/pip/_internal/commands/show.py b/.venv/Lib/site-packages/pip/_internal/commands/show.py deleted file mode 100644 index 212167c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/show.py +++ /dev/null @@ -1,183 +0,0 @@ -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 - 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 "", - 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) - 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/site-packages/pip/_internal/commands/uninstall.py b/.venv/Lib/site-packages/pip/_internal/commands/uninstall.py deleted file mode 100644 index dea8077..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/uninstall.py +++ /dev/null @@ -1,106 +0,0 @@ -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 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.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}")' - ) - - 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/site-packages/pip/_internal/commands/wheel.py b/.venv/Lib/site-packages/pip/_internal/commands/wheel.py deleted file mode 100644 index 1afbd56..0000000 --- a/.venv/Lib/site-packages/pip/_internal/commands/wheel.py +++ /dev/null @@ -1,203 +0,0 @@ -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, - LegacySetupPyOptionsCheckMode, - check_legacy_setup_py_options, -) -from pip._internal.utils.deprecation import deprecated -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) - wheel_cache = WheelCache(options.cache_dir, options.format_control) - - 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, LegacySetupPyOptionsCheckMode.WHEEL - ) - - if "no-binary-enable-wheel-cache" in options.features_enabled: - # TODO: remove format_control from WheelCache when the deprecation cycle - # is over - wheel_cache = WheelCache(options.cache_dir) - else: - if options.format_control.no_binary: - deprecated( - reason=( - "--no-binary currently disables reading from " - "the cache of locally built wheels. In the future " - "--no-binary will not influence the wheel cache." - ), - replacement="to use the --no-cache-dir option", - feature_flag="no-binary-enable-wheel-cache", - issue=11453, - gone_in="23.1", - ) - wheel_cache = WheelCache(options.cache_dir, options.format_control) - - 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) - - # 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/site-packages/pip/_internal/configuration.py b/.venv/Lib/site-packages/pip/_internal/configuration.py deleted file mode 100644 index 8fd46c9..0000000 --- a/.venv/Lib/site-packages/pip/_internal/configuration.py +++ /dev/null @@ -1,374 +0,0 @@ -"""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. " - "Perhaps you wanted to use 'global.{}' instead?" - ).format(name) - 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)) - - with open(fname, "w") as f: - parser.write(f) - - # - # 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. - """ - # SMELL: Move the conditions out of this function - - # environment variables have the lowest priority - config_file = os.environ.get("PIP_CONFIG_FILE", None) - if config_file is not None: - yield kinds.ENV, [config_file] - else: - yield kinds.ENV, [] - - config_files = get_configuration_files() - - # at the base we have any global configuration - yield kinds.GLOBAL, config_files[kinds.GLOBAL] - - # per-user configuration next - should_load_user_config = not self.isolated and not ( - config_file and os.path.exists(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] - - # finally virtualenv configuration first trumping others - yield kinds.SITE, config_files[kinds.SITE] - - 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/site-packages/pip/_internal/distributions/__init__.py b/.venv/Lib/site-packages/pip/_internal/distributions/__init__.py deleted file mode 100644 index 9a89a83..0000000 --- a/.venv/Lib/site-packages/pip/_internal/distributions/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -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/site-packages/pip/_internal/distributions/base.py b/.venv/Lib/site-packages/pip/_internal/distributions/base.py deleted file mode 100644 index 75ce2dc..0000000 --- a/.venv/Lib/site-packages/pip/_internal/distributions/base.py +++ /dev/null @@ -1,39 +0,0 @@ -import abc - -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. - """ - - def __init__(self, req: InstallRequirement) -> None: - super().__init__() - self.req = req - - @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/site-packages/pip/_internal/distributions/installed.py b/.venv/Lib/site-packages/pip/_internal/distributions/installed.py deleted file mode 100644 index edb38aa..0000000 --- a/.venv/Lib/site-packages/pip/_internal/distributions/installed.py +++ /dev/null @@ -1,23 +0,0 @@ -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. - """ - - 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/site-packages/pip/_internal/distributions/sdist.py b/.venv/Lib/site-packages/pip/_internal/distributions/sdist.py deleted file mode 100644 index 4c25647..0000000 --- a/.venv/Lib/site-packages/pip/_internal/distributions/sdist.py +++ /dev/null @@ -1,150 +0,0 @@ -import logging -from typing import Iterable, 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`. - """ - - 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/site-packages/pip/_internal/distributions/wheel.py b/.venv/Lib/site-packages/pip/_internal/distributions/wheel.py deleted file mode 100644 index 03aac77..0000000 --- a/.venv/Lib/site-packages/pip/_internal/distributions/wheel.py +++ /dev/null @@ -1,34 +0,0 @@ -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. - """ - - 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/site-packages/pip/_internal/exceptions.py b/.venv/Lib/site-packages/pip/_internal/exceptions.py deleted file mode 100644 index 2ab1f59..0000000 --- a/.venv/Lib/site-packages/pip/_internal/exceptions.py +++ /dev/null @@ -1,660 +0,0 @@ -"""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 re -from itertools import chain, groupby, repeat -from typing import TYPE_CHECKING, Dict, 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 - - -# -# 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 "None {} metadata found for distribution: {}".format( - self.metadata_name, - 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 LegacyInstallFailure(DiagnosticPipError): - """Error occurred while executing `setup.py install`""" - - reference = "legacy-install-failure" - - def __init__(self, package_details: str) -> None: - super().__init__( - message="Encountered error while trying to install package.", - context=package_details, - hint_stmt="See above for output from the failure.", - note_stmt="This is an issue with the package mentioned above, not pip.", - ) - - -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.original_link - # 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 " {}:\n{}".format(self._requirement_name(), 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( - (" Expected {} {}".format(next(prefix), e)) for e in expecteds - ) - lines.append( - " Got {}\n".format(self.gots[hash_name].hexdigest()) - ) - 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}" diff --git a/.venv/Lib/site-packages/pip/_internal/index/__init__.py b/.venv/Lib/site-packages/pip/_internal/index/__init__.py deleted file mode 100644 index 7a17b7b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/index/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Index interaction code -""" diff --git a/.venv/Lib/site-packages/pip/_internal/index/collector.py b/.venv/Lib/site-packages/pip/_internal/index/collector.py deleted file mode 100644 index 0120610..0000000 --- a/.venv/Lib/site-packages/pip/_internal/index/collector.py +++ /dev/null @@ -1,505 +0,0 @@ -""" -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 respones 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, - ) - 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, - ) - 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/site-packages/pip/_internal/index/package_finder.py b/.venv/Lib/site-packages/pip/_internal/index/package_finder.py deleted file mode 100644 index 9bf247f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/index/package_finder.py +++ /dev/null @@ -1,1025 +0,0 @@ -"""Routines related to PyPI, indexes""" - -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -import enum -import functools -import itertools -import logging -import re -from typing import 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 - -__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_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: 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_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( - "{} is not a supported wheel for this platform. It " - "can't be sorted.".format(wheel.filename) - ) - if self._prefer_binary: - binary_preference = 1 - if wheel.build_tag is not None: - match = re.match(r"^(\d+)(.*)$", wheel.build_tag) - 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( - "No matching distribution found for {}".format(req) - ) - - best_installed = False - if installed_version and ( - best_candidate is None or best_candidate.version <= installed_version - ): - best_installed = True - - if not upgrade and installed_version is not None: - if best_installed: - logger.debug( - "Existing installed version (%s) is most up-to-date and " - "satisfies requirement", - installed_version, - ) - else: - logger.debug( - "Existing installed version (%s) satisfies requirement " - "(most up-to-date version is %s)", - installed_version, - best_candidate.version, - ) - return None - - if best_installed: - # 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 - - logger.debug( - "Using version %s (newest of versions: %s)", - best_candidate.version, - _format_versions(best_candidate_result.iter_applicable()), - ) - return best_candidate - - -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/site-packages/pip/_internal/index/sources.py b/.venv/Lib/site-packages/pip/_internal/index/sources.py deleted file mode 100644 index eec3f12..0000000 --- a/.venv/Lib/site-packages/pip/_internal/index/sources.py +++ /dev/null @@ -1,224 +0,0 @@ -import logging -import mimetypes -import os -import pathlib -from typing import Callable, Iterable, Optional, Tuple - -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 _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. - """ - - def __init__( - self, - candidates_from_page: CandidatesFromPage, - path: str, - ) -> None: - self._candidates_from_page = candidates_from_page - self._path = pathlib.Path(os.path.realpath(path)) - - @property - def link(self) -> Optional[Link]: - return None - - def page_candidates(self) -> FoundCandidates: - for path in self._path.iterdir(): - url = path_to_url(str(path)) - if not _is_html_file(url): - continue - yield from self._candidates_from_page(Link(url)) - - def file_links(self) -> FoundLinks: - for path in self._path.iterdir(): - url = path_to_url(str(path)) - if _is_html_file(url): - continue - 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, -) -> 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, - ) - 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/site-packages/pip/_internal/locations/__init__.py b/.venv/Lib/site-packages/pip/_internal/locations/__init__.py deleted file mode 100644 index 60afe0a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/locations/__init__.py +++ /dev/null @@ -1,528 +0,0 @@ -import functools -import logging -import os -import pathlib -import sys -import sysconfig -from typing import Any, Dict, Generator, List, 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_prefixed_libs", - "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 - - -def _deduplicated(v1: str, v2: str) -> List[str]: - """Deduplicate values from a list.""" - if v1 == v2: - return [v1] - return [v1, v2] - - -def _looks_like_apple_library(path: str) -> bool: - """Apple patches sysconfig to *always* look under */Library/Python*.""" - if sys.platform[:6] != "darwin": - return False - return path == f"/Library/Python/{get_major_minor_version()}/site-packages" - - -def get_prefixed_libs(prefix: str) -> List[str]: - """Return the lib locations under ``prefix``.""" - new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix) - if _USE_SYSCONFIG: - return _deduplicated(new_pure, new_plat) - - old_pure, old_plat = _distutils.get_prefixed_libs(prefix) - old_lib_paths = _deduplicated(old_pure, old_plat) - - # Apple's Python (shipped with Xcode and Command Line Tools) hard-code - # platlib and purelib to '/Library/Python/X.Y/site-packages'. This will - # cause serious build isolation bugs when Apple starts shipping 3.10 because - # pip will install build backends to the wrong location. This tells users - # who is at fault so Apple may notice it and fix the issue in time. - if all(_looks_like_apple_library(p) for p in old_lib_paths): - deprecated( - reason=( - "Python distributed by Apple's Command Line Tools incorrectly " - "patches sysconfig to always point to '/Library/Python'. This " - "will cause build isolation to operate incorrectly on Python " - "3.10 or later. Please help report this to Apple so they can " - "fix this. https://developer.apple.com/bug-reporting/" - ), - replacement=None, - gone_in=None, - ) - return old_lib_paths - - warned = [ - _warn_if_mismatch( - pathlib.Path(old_pure), - pathlib.Path(new_pure), - key="prefixed-purelib", - ), - _warn_if_mismatch( - pathlib.Path(old_plat), - pathlib.Path(new_plat), - key="prefixed-platlib", - ), - ] - if any(warned): - _log_context(prefix=prefix) - - return old_lib_paths diff --git a/.venv/Lib/site-packages/pip/_internal/locations/_distutils.py b/.venv/Lib/site-packages/pip/_internal/locations/_distutils.py deleted file mode 100644 index c7712f0..0000000 --- a/.venv/Lib/site-packages/pip/_internal/locations/_distutils.py +++ /dev/null @@ -1,180 +0,0 @@ -"""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, Tuple, 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: - # Typeshed does not include find_config_files() for some reason. - paths = d.find_config_files() # type: ignore - 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(dict(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) - - -def get_prefixed_libs(prefix: str) -> Tuple[str, str]: - return ( - get_python_lib(plat_specific=False, prefix=prefix), - get_python_lib(plat_specific=True, prefix=prefix), - ) diff --git a/.venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py b/.venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py deleted file mode 100644 index 0bbc928..0000000 --- a/.venv/Lib/site-packages/pip/_internal/locations/_sysconfig.py +++ /dev/null @@ -1,218 +0,0 @@ -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"] - - -def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]: - paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix}) - return (paths["purelib"], paths["platlib"]) diff --git a/.venv/Lib/site-packages/pip/_internal/locations/base.py b/.venv/Lib/site-packages/pip/_internal/locations/base.py deleted file mode 100644 index 3f7de00..0000000 --- a/.venv/Lib/site-packages/pip/_internal/locations/base.py +++ /dev/null @@ -1,81 +0,0 @@ -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: typing.Optional[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/site-packages/pip/_internal/main.py b/.venv/Lib/site-packages/pip/_internal/main.py deleted file mode 100644 index 33c6d24..0000000 --- a/.venv/Lib/site-packages/pip/_internal/main.py +++ /dev/null @@ -1,12 +0,0 @@ -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/site-packages/pip/_internal/metadata/__init__.py b/.venv/Lib/site-packages/pip/_internal/metadata/__init__.py deleted file mode 100644 index 9f73ca7..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/__init__.py +++ /dev/null @@ -1,127 +0,0 @@ -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 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): - 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/site-packages/pip/_internal/metadata/_json.py b/.venv/Lib/site-packages/pip/_internal/metadata/_json.py deleted file mode 100644 index 336b52f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/_json.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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) - ] - else: - value = sanitise_header(msg.get(field)) - 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/site-packages/pip/_internal/metadata/base.py b/.venv/Lib/site-packages/pip/_internal/metadata/base.py deleted file mode 100644 index cafb79f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/base.py +++ /dev/null @@ -1,688 +0,0 @@ -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 -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.packaging import safe_extra -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. - """ - 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(): - if entry.extra in known_extras: - continue - known_extras.add(entry.extra) - yield entry.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(): - if entry.extra and entry.marker: - marker = f'({entry.marker}) and extra == "{safe_extra(entry.extra)}"' - elif entry.extra: - marker = f'extra == "{safe_extra(entry.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/site-packages/pip/_internal/metadata/importlib/__init__.py b/.venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py deleted file mode 100644 index 5e7af9f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/importlib/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from ._dists import Distribution -from ._envs import Environment - -__all__ = ["Distribution", "Environment"] diff --git a/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py b/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py deleted file mode 100644 index 593bff2..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_compat.py +++ /dev/null @@ -1,55 +0,0 @@ -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/site-packages/pip/_internal/metadata/importlib/_dists.py b/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py deleted file mode 100644 index 65c043c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_dists.py +++ /dev/null @@ -1,224 +0,0 @@ -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.packaging import safe_extra -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 ( - safe_extra(extra) for extra in self.metadata.get_all("Provides-Extra", []) - ) - - def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]: - contexts: Sequence[Dict[str, str]] = [{"extra": safe_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/site-packages/pip/_internal/metadata/importlib/_envs.py b/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py deleted file mode 100644 index cbec59e..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/importlib/_envs.py +++ /dev/null @@ -1,188 +0,0 @@ -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=None, - ) - - -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) # TODO: Enable this. - 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/site-packages/pip/_internal/metadata/pkg_resources.py b/.venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py deleted file mode 100644 index f330ef1..0000000 --- a/.venv/Lib/site-packages/pip/_internal/metadata/pkg_resources.py +++ /dev/null @@ -1,270 +0,0 @@ -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, -) - -logger = logging.getLogger(__name__) - - -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(extras).intersection(self._dist.extras) - return self._dist.requires(extras) - - def iter_provided_extras(self) -> Iterable[str]: - return 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/site-packages/pip/_internal/models/__init__.py b/.venv/Lib/site-packages/pip/_internal/models/__init__.py deleted file mode 100644 index 7855226..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""A package that contains models that represent entities. -""" diff --git a/.venv/Lib/site-packages/pip/_internal/models/candidate.py b/.venv/Lib/site-packages/pip/_internal/models/candidate.py deleted file mode 100644 index a4963ae..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/candidate.py +++ /dev/null @@ -1,34 +0,0 @@ -from pip._vendor.packaging.version import parse as parse_version - -from pip._internal.models.link import Link -from pip._internal.utils.models import KeyBasedCompareMixin - - -class InstallationCandidate(KeyBasedCompareMixin): - """Represents a potential "candidate" for installation.""" - - __slots__ = ["name", "version", "link"] - - def __init__(self, name: str, version: str, link: Link) -> 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 "{!r} candidate (version {} at {})".format( - self.name, - self.version, - self.link, - ) diff --git a/.venv/Lib/site-packages/pip/_internal/models/direct_url.py b/.venv/Lib/site-packages/pip/_internal/models/direct_url.py deleted file mode 100644 index e75feda..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/direct_url.py +++ /dev/null @@ -1,212 +0,0 @@ -""" 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( - "{!r} has unexpected type for {} (expected {})".format( - value, key, 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, - ) -> None: - self.hash = hash - - @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")) - - def _to_dict(self) -> Dict[str, Any]: - return _filter_none(hash=self.hash) - - -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/site-packages/pip/_internal/models/format_control.py b/.venv/Lib/site-packages/pip/_internal/models/format_control.py deleted file mode 100644 index db3995e..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/format_control.py +++ /dev/null @@ -1,80 +0,0 @@ -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 "{}({}, {})".format( - 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/site-packages/pip/_internal/models/index.py b/.venv/Lib/site-packages/pip/_internal/models/index.py deleted file mode 100644 index b94c325..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/index.py +++ /dev/null @@ -1,28 +0,0 @@ -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/site-packages/pip/_internal/models/installation_report.py b/.venv/Lib/site-packages/pip/_internal/models/installation_report.py deleted file mode 100644 index 965f095..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/installation_report.py +++ /dev/null @@ -1,53 +0,0 @@ -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.hash 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": bool(ireq.original_link), - # 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"] = list(sorted(ireq.extras)) - return res - - def to_dict(self) -> Dict[str, Any]: - return { - "version": "0", - "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/site-packages/pip/_internal/models/link.py b/.venv/Lib/site-packages/pip/_internal/models/link.py deleted file mode 100644 index c792d12..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/link.py +++ /dev/null @@ -1,507 +0,0 @@ -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.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_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._hash_re.match(f"{self.name}={self.value}") - - @classmethod - @functools.lru_cache(maxsize=None) - def split_hash_name_and_value(cls, url: str) -> Optional["LinkHash"]: - """Search a string for a checksum algorithm name and encoded output value.""" - match = cls._hash_re.search(url) - if match is None: - return None - name, value = match.groups() - return cls(name=name, value=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) - - -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", - "dist_info_metadata", - "link_hash", - "cache_link_parsing", - ] - - def __init__( - self, - url: str, - comes_from: Optional[Union[str, "IndexContent"]] = None, - requires_python: Optional[str] = None, - yanked_reason: Optional[str] = None, - dist_info_metadata: Optional[str] = None, - link_hash: Optional[LinkHash] = 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 dist_info_metadata: the metadata attached to the file, or None if no such - metadata is provided. This is the value of the "data-dist-info-metadata" - attribute, if present, in a simple repository HTML link. This may be parsed - into its own `Link` by `self.metadata_link()`. See PEP 658 for more - information and the specification. - :param link_hash: a checksum for the content the link points to. If not - provided, this will be extracted from the link URL, if the URL has - any checksum. - :param cache_link_parsing: A flag that is used elsewhere to determine - whether resources retrieved from this link - should be cached. PyPI index 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. - """ - - # 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 - self._hashes = hashes if hashes is not None else {} - - self.comes_from = comes_from - self.requires_python = requires_python if requires_python else None - self.yanked_reason = yanked_reason - self.dist_info_metadata = dist_info_metadata - self.link_hash = link_hash or LinkHash.split_hash_name_and_value(self._url) - - super().__init__(key=url, defining_class=Link) - - self.cache_link_parsing = cache_link_parsing - - @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") - dist_info_metadata = file_data.get("dist-info-metadata") - hashes = file_data.get("hashes", {}) - - # 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, - dist_info_metadata=dist_info_metadata, - ) - - @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") - dist_info_metadata = anchor_attribs.get("data-dist-info-metadata") - - return cls( - url, - comes_from=page_url, - requires_python=pyrequire, - yanked_reason=yanked_reason, - dist_info_metadata=dist_info_metadata, - ) - - def __str__(self) -> str: - if self.requires_python: - rp = f" (requires-python:{self.requires_python})" - else: - rp = "" - if self.comes_from: - return "{} (from {}){}".format( - redact_auth_from_url(self._url), 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=([^&]*)") - - @property - def egg_fragment(self) -> Optional[str]: - match = self._egg_fragment_re.search(self._url) - if not match: - return None - return match.group(1) - - _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"]: - """Implementation of PEP 658 parsing.""" - # Note that Link.from_element() parsing the "data-dist-info-metadata" attribute - # from an HTML anchor tag is typically how the Link.dist_info_metadata attribute - # gets set. - if self.dist_info_metadata is None: - return None - metadata_url = f"{self.url_without_fragment}.metadata" - link_hash: Optional[LinkHash] = None - # If data-dist-info-metadata="true" is set, then the metadata file exists, - # but there is no information about its checksum or anything else. - if self.dist_info_metadata != "true": - link_hash = LinkHash.split_hash_name_and_value(self.dist_info_metadata) - return Link(metadata_url, link_hash=link_hash) - - def as_hashes(self) -> Optional[Hashes]: - if self.link_hash is not None: - return self.link_hash.as_hashes() - return None - - @property - def hash(self) -> Optional[str]: - if self.link_hash is not None: - return self.link_hash.value - return None - - @property - def hash_name(self) -> Optional[str]: - if self.link_hash is not None: - return self.link_hash.name - return 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 self.link_hash is not None - - def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: - """ - Return True if the link has a hash and it is allowed by `hashes`. - """ - if self.link_hash is None: - return False - return self.link_hash.is_hash_allowed(hashes) - - -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/site-packages/pip/_internal/models/scheme.py b/.venv/Lib/site-packages/pip/_internal/models/scheme.py deleted file mode 100644 index f51190a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/scheme.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -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/site-packages/pip/_internal/models/search_scope.py b/.venv/Lib/site-packages/pip/_internal/models/search_scope.py deleted file mode 100644 index a64af73..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/search_scope.py +++ /dev/null @@ -1,133 +0,0 @@ -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/site-packages/pip/_internal/models/selection_prefs.py b/.venv/Lib/site-packages/pip/_internal/models/selection_prefs.py deleted file mode 100644 index 977bc4c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/selection_prefs.py +++ /dev/null @@ -1,51 +0,0 @@ -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/site-packages/pip/_internal/models/target_python.py b/.venv/Lib/site-packages/pip/_internal/models/target_python.py deleted file mode 100644 index 744bd7e..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/target_python.py +++ /dev/null @@ -1,110 +0,0 @@ -import sys -from typing import List, Optional, 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", - ] - - 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_tags(). - self._valid_tags: Optional[List[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_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 diff --git a/.venv/Lib/site-packages/pip/_internal/models/wheel.py b/.venv/Lib/site-packages/pip/_internal/models/wheel.py deleted file mode 100644 index a5dc12b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/models/wheel.py +++ /dev/null @@ -1,92 +0,0 @@ -"""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/site-packages/pip/_internal/network/__init__.py b/.venv/Lib/site-packages/pip/_internal/network/__init__.py deleted file mode 100644 index b51bde9..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Contains purely network-related utilities. -""" diff --git a/.venv/Lib/site-packages/pip/_internal/network/auth.py b/.venv/Lib/site-packages/pip/_internal/network/auth.py deleted file mode 100644 index ca42798..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/auth.py +++ /dev/null @@ -1,323 +0,0 @@ -"""Network Authentication Helpers - -Contains interface (MultiDomainBasicAuth) and associated glue code for -providing credentials in the context of network requests. -""" - -import urllib.parse -from typing import Any, Dict, List, Optional, Tuple - -from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth -from pip._vendor.requests.models import Request, Response -from pip._vendor.requests.utils import get_netrc_auth - -from pip._internal.utils.logging import getLogger -from pip._internal.utils.misc import ( - ask, - ask_input, - ask_password, - remove_auth_from_url, - split_auth_netloc_from_url, -) -from pip._internal.vcs.versioncontrol import AuthInfo - -logger = getLogger(__name__) - -Credentials = Tuple[str, str, str] - -try: - import keyring -except ImportError: - keyring = None # type: ignore[assignment] -except Exception as exc: - logger.warning( - "Keyring is skipped due to an exception: %s", - str(exc), - ) - keyring = None # type: ignore[assignment] - - -def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]: - """Return the tuple auth for a given url from keyring.""" - global keyring - if not url or not keyring: - return None - - try: - try: - get_credential = keyring.get_credential - except AttributeError: - pass - else: - logger.debug("Getting credentials from keyring for %s", url) - cred = get_credential(url, username) - if cred is not None: - return cred.username, cred.password - return None - - if username: - logger.debug("Getting password from keyring for %s", url) - password = keyring.get_password(url, username) - if password: - return username, password - - except Exception as exc: - logger.warning( - "Keyring is skipped due to an exception: %s", - str(exc), - ) - keyring = None # type: ignore[assignment] - return None - - -class MultiDomainBasicAuth(AuthBase): - def __init__( - self, prompting: bool = True, index_urls: Optional[List[str]] = None - ) -> None: - self.prompting = prompting - self.index_urls = index_urls - 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 - - 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 - - for u in self.index_urls: - prefix = remove_auth_from_url(u).rstrip("/") + "/" - if url.startswith(prefix): - return u - return None - - 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 = ( - get_keyring_auth(index_url, username) or - 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 not username: - return None, None, False - auth = 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 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 - - # We are not able to prompt the user so simply return the response - if not self.prompting: - return resp - - parsed = urllib.parse.urlparse(resp.url) - - # Query the keyring for credentials: - username, password = self._get_new_credentials( - resp.url, - allow_netrc=False, - allow_keyring=True, - ) - - # 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 = (parsed.netloc, username, password) - - # Consume content and release the original connection to allow our new - # request to reuse the same one. - 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 keyring is not None, "should never reach here without keyring" - if not keyring: - return - - creds = self._credentials_to_save - self._credentials_to_save = None - if creds and resp.status_code < 400: - try: - logger.info("Saving credentials to keyring") - keyring.set_password(*creds) - except Exception: - logger.exception("Failed to save credentials") diff --git a/.venv/Lib/site-packages/pip/_internal/network/cache.py b/.venv/Lib/site-packages/pip/_internal/network/cache.py deleted file mode 100644 index a81a239..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/cache.py +++ /dev/null @@ -1,69 +0,0 @@ -"""HTTP cache implementation. -""" - -import os -from contextlib import contextmanager -from typing import Generator, Optional - -from pip._vendor.cachecontrol.cache import BaseCache -from pip._vendor.cachecontrol.caches import FileCache -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(BaseCache): - """ - A file based cache which is safe to use even when the target directory may - not be accessible or writable. - """ - - 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 = FileCache.encode(name) - parts = list(hashed[:5]) + [hashed] - return os.path.join(self.directory, *parts) - - def get(self, key: str) -> Optional[bytes]: - path = self._get_cache_path(key) - with suppressed_cache_errors(): - with open(path, "rb") as f: - return f.read() - - def set(self, key: str, value: bytes, expires: Optional[int] = None) -> None: - path = self._get_cache_path(key) - with suppressed_cache_errors(): - ensure_dir(os.path.dirname(path)) - - with adjacent_tmp_file(path) as f: - f.write(value) - - replace(f.name, path) - - def delete(self, key: str) -> None: - path = self._get_cache_path(key) - with suppressed_cache_errors(): - os.remove(path) diff --git a/.venv/Lib/site-packages/pip/_internal/network/download.py b/.venv/Lib/site-packages/pip/_internal/network/download.py deleted file mode 100644 index 79b82a5..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/download.py +++ /dev/null @@ -1,186 +0,0 @@ -"""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 = "{} ({})".format(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/site-packages/pip/_internal/network/lazy_wheel.py b/.venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py deleted file mode 100644 index 854a6fa..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/lazy_wheel.py +++ /dev/null @@ -1,210 +0,0 @@ -"""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/site-packages/pip/_internal/network/session.py b/.venv/Lib/site-packages/pip/_internal/network/session.py deleted file mode 100644 index e512ac7..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/session.py +++ /dev/null @@ -1,518 +0,0 @@ -"""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 520 or 527 - may indicate transient error in CloudFlare - status_forcelist=[500, 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) - - host_port = parse_netloc(host) - if host_port not in self.pip_trusted_origins: - self.pip_trusted_origins.append(host_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 host_port[1]: - 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/site-packages/pip/_internal/network/utils.py b/.venv/Lib/site-packages/pip/_internal/network/utils.py deleted file mode 100644 index 134848a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/utils.py +++ /dev/null @@ -1,96 +0,0 @@ -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/site-packages/pip/_internal/network/xmlrpc.py b/.venv/Lib/site-packages/pip/_internal/network/xmlrpc.py deleted file mode 100644 index 4a7d55d..0000000 --- a/.venv/Lib/site-packages/pip/_internal/network/xmlrpc.py +++ /dev/null @@ -1,60 +0,0 @@ -"""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 - -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: bytes, - 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/site-packages/pip/_internal/operations/__init__.py b/.venv/Lib/site-packages/pip/_internal/operations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_internal/operations/check.py b/.venv/Lib/site-packages/pip/_internal/operations/check.py deleted file mode 100644 index fb3ac8b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/check.py +++ /dev/null @@ -1,149 +0,0 @@ -"""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.utils import NormalizedName, canonicalize_name - -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 - -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. - """ - - 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() - 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 diff --git a/.venv/Lib/site-packages/pip/_internal/operations/freeze.py b/.venv/Lib/site-packages/pip/_internal/operations/freeze.py deleted file mode 100644 index 930d4c6..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/freeze.py +++ /dev/null @@ -1,254 +0,0 @@ -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: - 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/site-packages/pip/_internal/operations/install/__init__.py b/.venv/Lib/site-packages/pip/_internal/operations/install/__init__.py deleted file mode 100644 index 24d6a5d..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/install/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""For modules related to installing packages. -""" diff --git a/.venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py b/.venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py deleted file mode 100644 index bb548cd..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/install/editable_legacy.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Legacy editable installation process, i.e. `setup.py develop`. -""" -import logging -from typing import List, 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( - install_options: List[str], - 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, - install_options=install_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/site-packages/pip/_internal/operations/install/legacy.py b/.venv/Lib/site-packages/pip/_internal/operations/install/legacy.py deleted file mode 100644 index 290967d..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/install/legacy.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Legacy installation process, i.e. `setup.py install`. -""" - -import logging -import os -from typing import List, Optional, Sequence - -from pip._internal.build_env import BuildEnvironment -from pip._internal.exceptions import InstallationError, LegacyInstallFailure -from pip._internal.locations.base import change_root -from pip._internal.models.scheme import Scheme -from pip._internal.utils.misc import ensure_dir -from pip._internal.utils.setuptools_build import make_setuptools_install_args -from pip._internal.utils.subprocess import runner_with_spinner_message -from pip._internal.utils.temp_dir import TempDirectory - -logger = logging.getLogger(__name__) - - -def write_installed_files_from_setuptools_record( - record_lines: List[str], - root: Optional[str], - req_description: str, -) -> None: - def prepend_root(path: str) -> str: - if root is None or not os.path.isabs(path): - return path - else: - return change_root(root, path) - - for line in record_lines: - directory = os.path.dirname(line) - if directory.endswith(".egg-info"): - egg_info_dir = prepend_root(directory) - break - else: - message = ( - "{} did not indicate that it installed an " - ".egg-info directory. Only setup.py projects " - "generating .egg-info directories are supported." - ).format(req_description) - raise InstallationError(message) - - new_lines = [] - for line in record_lines: - filename = line.strip() - if os.path.isdir(filename): - filename += os.path.sep - new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir)) - new_lines.sort() - ensure_dir(egg_info_dir) - inst_files_path = os.path.join(egg_info_dir, "installed-files.txt") - with open(inst_files_path, "w") as f: - f.write("\n".join(new_lines) + "\n") - - -def install( - install_options: List[str], - global_options: Sequence[str], - root: Optional[str], - home: Optional[str], - prefix: Optional[str], - use_user_site: bool, - pycompile: bool, - scheme: Scheme, - setup_py_path: str, - isolated: bool, - req_name: str, - build_env: BuildEnvironment, - unpacked_source_directory: str, - req_description: str, -) -> bool: - - header_dir = scheme.headers - - with TempDirectory(kind="record") as temp_dir: - try: - record_filename = os.path.join(temp_dir.path, "install-record.txt") - install_args = make_setuptools_install_args( - setup_py_path, - global_options=global_options, - install_options=install_options, - record_filename=record_filename, - root=root, - prefix=prefix, - header_dir=header_dir, - home=home, - use_user_site=use_user_site, - no_user_config=isolated, - pycompile=pycompile, - ) - - runner = runner_with_spinner_message( - f"Running setup.py install for {req_name}" - ) - with build_env: - runner( - cmd=install_args, - cwd=unpacked_source_directory, - ) - - if not os.path.exists(record_filename): - logger.debug("Record file %s not found", record_filename) - # Signal to the caller that we didn't install the new package - return False - - except Exception as e: - # Signal to the caller that we didn't install the new package - raise LegacyInstallFailure(package_details=req_name) from e - - # At this point, we have successfully installed the requirement. - - # We intentionally do not use any encoding to read the file because - # setuptools writes the file using distutils.file_util.write_file, - # which does not specify an encoding. - with open(record_filename) as f: - record_lines = f.read().splitlines() - - write_installed_files_from_setuptools_record(record_lines, root, req_description) - return True diff --git a/.venv/Lib/site-packages/pip/_internal/operations/install/wheel.py b/.venv/Lib/site-packages/pip/_internal/operations/install/wheel.py deleted file mode 100644 index c799413..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/install/wheel.py +++ /dev/null @@ -1,738 +0,0 @@ -"""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(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.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(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 = "script {} is".format(sorted_scripts[0]) - else: - start_text = "scripts {} are".format( - ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] - ) - - msg_lines.append( - "The {} installed in '{}' which is not on PATH.".format( - start_text, parent_dir - ) - ) - - 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)) - for installed_record_path in installed.values(): - installed_rows.append((installed_record_path, "", "")) - return installed_rows - - -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( - "pip{} = {}".format(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( - "easy_install-{} = {}".format( - 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__( - "Invalid script entry point: {} - A callable " - "suffix is required. Cf https://packaging.python.org/" - "specifications/entry-points/#use-for-scripts for more " - "information.".format(entry_point) - ) - - -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 = "For req: {}. {}".format(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/site-packages/pip/_internal/operations/prepare.py b/.venv/Lib/site-packages/pip/_internal/operations/prepare.py deleted file mode 100644 index 4bf414c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/operations/prepare.py +++ /dev/null @@ -1,667 +0,0 @@ -"""Prepares a distribution for installation -""" - -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -import logging -import mimetypes -import os -import shutil -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, - PreviousBuildDirError, - 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.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, - is_installable_dir, -) -from pip._internal.utils.temp_dir import TempDirectory -from pip._internal.utils.unpacking import unpack_file -from pip._internal.vcs import vcs - -logger = logging.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) - with build_tracker.track(req): - 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] -) -> 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: - 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, - ) -> 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 - - # 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.original_link_is_in_wheel_cache: - message = "Processing %s" - information = str(display_path(req.link.file_path)) - else: - message = "Collecting %s" - information = str(req.req or req) - - if (message, information) != self._previous_requirement_header: - self._previous_requirement_header = (message, information) - logger.info(message, information) - - if req.original_link_is_in_wheel_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, - ) - - # If a checkout exists, it's unwise to keep going. version - # inconsistencies are logged later, but do not fail the - # installation. - # FIXME: this won't upgrade when there's an existing - # package unpacked in `req.source_dir` - # TODO: this check is now probably dead code - if is_installable_dir(req.source_dir): - raise PreviousBuildDirError( - "pip can't proceed with requirements '{}' due to a" - "pre-existing build directory ({}). 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.".format(req, req.source_dir) - ) - - 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 req.original_link is None 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.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.info( - "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 metadata_dist.raw_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] - req.local_file_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) - - 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 - - self._ensure_link_req_src_dir(req, parallel_builds) - hashes = self._get_linked_req_hashes(req) - - 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( - "Could not install requirement {} because of HTTP " - "error {} for URL {}".format(req, exc, 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. - if ( - isinstance(req.download_info.info, ArchiveInfo) - and not req.download_info.info.hash - and local_file - ): - hash = hash_file(local_file.path)[0].hexdigest() - 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( - "The editable requirement {} cannot be installed when " - "requiring hashes, because there is no single file to " - "hash.".format(req) - ) - 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 " - "is set to {}".format(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/site-packages/pip/_internal/pyproject.py b/.venv/Lib/site-packages/pip/_internal/pyproject.py deleted file mode 100644 index 1e9119f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/pyproject.py +++ /dev/null @@ -1,175 +0,0 @@ -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. - - # We fallback to PEP 517 when without setuptools, - # 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 - elif use_pep517 is None: - use_pep517 = has_pyproject or not importlib.util.find_spec("setuptools") - - # 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", "wheel"], - "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, or wheel - # (which is needed by the backend) in their requirements. So we - # make a note to check that those requirements are 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", "wheel"] - - return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/.venv/Lib/site-packages/pip/_internal/req/__init__.py b/.venv/Lib/site-packages/pip/_internal/req/__init__.py deleted file mode 100644 index 8d56359..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -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], - install_options: List[str], - 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( - install_options, - 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/site-packages/pip/_internal/req/constructors.py b/.venv/Lib/site-packages/pip/_internal/req/constructors.py deleted file mode 100644 index dea7c3b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/constructors.py +++ /dev/null @@ -1,501 +0,0 @@ -"""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 logging -import os -import re -from typing import Any, Dict, 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 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, - options: Optional[Dict[str, Any]] = None, - constraint: bool = False, - user_supplied: bool = False, - permit_editable_wheels: bool = False, - config_settings: Optional[Dict[str, 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, - install_options=options.get("install_options", []) if options else [], - global_options=options.get("global_options", []) if options else [], - hash_options=options.get("hashes", {}) if options else {}, - 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, - options: Optional[Dict[str, Any]] = None, - constraint: bool = False, - line_source: Optional[str] = None, - user_supplied: bool = False, - config_settings: Optional[Dict[str, 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, - install_options=options.get("install_options", []) if options else [], - global_options=options.get("global_options", []) if options else [], - hash_options=options.get("hashes", {}) if options else {}, - 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, - config_settings: Optional[Dict[str, str]] = None, -) -> 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" - "{} depends on {} ".format(comes_from.name, req) - ) - - return InstallRequirement( - req, - comes_from, - isolated=isolated, - use_pep517=use_pep517, - user_supplied=user_supplied, - config_settings=config_settings, - ) - - -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, 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, - options=parsed_req.options, - 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, - install_options=ireq.install_options, - global_options=ireq.global_options, - hash_options=ireq.hash_options, - config_settings=ireq.config_settings, - user_supplied=ireq.user_supplied, - ) diff --git a/.venv/Lib/site-packages/pip/_internal/req/req_file.py b/.venv/Lib/site-packages/pip/_internal/req/req_file.py deleted file mode 100644 index 11ec699..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/req_file.py +++ /dev/null @@ -1,544 +0,0 @@ -""" -Requirements file parsing -""" - -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.install_options, - cmdoptions.global_options, - cmdoptions.hash, -] - -# the 'dest' string values -SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] - - -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 - - if line.is_editable: - # For editable requirements, we don't support per-requirement - # options, so just return the parsed requirement. - return ParsedRequirement( - requirement=line.requirement, - is_editable=line.is_editable, - comes_from=line_comes_from, - constraint=line.constraint, - ) - else: - # get the options that apply to requirements - req_options = {} - for dest in SUPPORTED_OPTIONS_REQ_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 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/site-packages/pip/_internal/req/req_install.py b/.venv/Lib/site-packages/pip/_internal/req/req_install.py deleted file mode 100644 index 5f29261..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/req_install.py +++ /dev/null @@ -1,942 +0,0 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -import functools -import logging -import os -import shutil -import sys -import uuid -import zipfile -from enum import Enum -from optparse import Values -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.pep517.wrappers import Pep517HookCaller - -from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment -from pip._internal.exceptions import InstallationError, LegacyInstallFailure -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.legacy import install as install_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 LegacyInstallReason, deprecated -from pip._internal.utils.direct_url_helpers import ( - direct_url_for_editable, - direct_url_from_link, -) -from pip._internal.utils.hashes import Hashes -from pip._internal.utils.misc import ( - ConfiguredPep517HookCaller, - ask_path_exists, - backup_dir, - display_path, - hide_url, - 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.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, - install_options: Optional[List[str]] = None, - global_options: Optional[List[str]] = None, - hash_options: Optional[Dict[str, List[str]]] = None, - config_settings: Optional[Dict[str, 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 - self.legacy_install_reason: Optional[LegacyInstallReason] = None - - # 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)) - - if link is None and req and req.url: - # PEP 508 URL requirement - link = Link(req.url) - self.link = self.original_link = link - self.original_link_is_in_wheel_cache = False - - # 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 = {safe_extra(extra) for extra in 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.install_options = install_options if install_options else [] - 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[Pep517HookCaller] = 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 - - # This requirement needs more preparation before it can be built - self.needs_more_preparation = False - - def __str__(self) -> str: - if self.req: - s = str(self.req) - if self.link: - s += " from {}".format(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: - s += " in {}".format(display_path(self.satisfied_by.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 = ("{}={!r}".format(attr, attributes[attr]) 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: - return self.req.specifier - - @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. - """ - specifiers = self.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}) 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() - link = self.link if trust_internet else self.original_link - if link and link.hash: - 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: - 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.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: - 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 - - # Things valid for sdists - @property - def unpacked_source_directory(self) -> str: - 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: - 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 = ConfiguredPep517HookCaller( - 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 - 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: - return get_wheel_distribution( - FilesystemWheel(self.local_file_path), canonicalize_name(self.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 - version = self.metadata["version"] - if 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, - ) - - # 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 - - path = os.path.join(parentdir, path) - name = _clean_zip_name(path, rootdir) - return self.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( - "The file {} exists. (i)gnore, (w)ipe, " - "(b)ackup, (a)bort ".format(display_path(archive_path)), - ("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, - install_options: List[str], - 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: - scheme = get_scheme( - self.name, - user=use_user_site, - home=home, - root=root, - isolated=self.isolated, - prefix=prefix, - ) - - global_options = global_options if global_options is not None else [] - if self.editable and not self.is_wheel: - install_editable_legacy( - install_options, - global_options, - prefix=prefix, - home=home, - use_user_site=use_user_site, - name=self.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 - - if self.is_wheel: - assert self.local_file_path - direct_url = None - # TODO this can be refactored to direct_url = self.download_info - if self.editable: - direct_url = direct_url_for_editable(self.unpacked_source_directory) - elif self.original_link: - direct_url = direct_url_from_link( - self.original_link, - self.source_dir, - self.original_link_is_in_wheel_cache, - ) - install_wheel( - self.name, - self.local_file_path, - scheme=scheme, - req_description=str(self.req), - pycompile=pycompile, - warn_script_location=warn_script_location, - direct_url=direct_url, - requested=self.user_supplied, - ) - self.install_succeeded = True - return - - # TODO: Why don't we do this for editable installs? - - # Extend the list of global and install options passed on to - # the setup.py call with the ones from the requirements file. - # Options specified in requirements file override those - # specified on the command line, since the last option given - # to setup.py is the one that is used. - global_options = list(global_options) + self.global_options - install_options = list(install_options) + self.install_options - - try: - if ( - self.legacy_install_reason is not None - and self.legacy_install_reason.emit_before_install - ): - self.legacy_install_reason.emit_deprecation(self.name) - success = install_legacy( - install_options=install_options, - global_options=global_options, - root=root, - home=home, - prefix=prefix, - use_user_site=use_user_site, - pycompile=pycompile, - scheme=scheme, - setup_py_path=self.setup_py_path, - isolated=self.isolated, - req_name=self.name, - build_env=self.build_env, - unpacked_source_directory=self.unpacked_source_directory, - req_description=str(self.req), - ) - except LegacyInstallFailure as exc: - self.install_succeeded = False - raise exc - except Exception: - self.install_succeeded = True - raise - - self.install_succeeded = success - - if ( - success - and self.legacy_install_reason is not None - and self.legacy_install_reason.emit_after_success - ): - self.legacy_install_reason.emit_deprecation(self.name) - - -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 _install_option_ignored( - install_options: List[str], reqs: List[InstallRequirement] -) -> bool: - for req in reqs: - if (install_options or req.install_options) and not req.use_pep517: - return False - return True - - -class LegacySetupPyOptionsCheckMode(Enum): - INSTALL = 1 - WHEEL = 2 - DOWNLOAD = 3 - - -def check_legacy_setup_py_options( - options: Values, - reqs: List[InstallRequirement], - mode: LegacySetupPyOptionsCheckMode, -) -> None: - has_install_options = _has_option(options, reqs, "install_options") - has_build_options = _has_option(options, reqs, "build_options") - has_global_options = _has_option(options, reqs, "global_options") - legacy_setup_py_options_present = ( - has_install_options or has_build_options or has_global_options - ) - if not legacy_setup_py_options_present: - return - - options.format_control.disallow_binaries() - logger.warning( - "Implying --no-binary=:all: due to the presence of " - "--build-option / --global-option / --install-option. " - "Consider using --config-settings for more flexibility.", - ) - if mode == LegacySetupPyOptionsCheckMode.INSTALL and has_install_options: - if _install_option_ignored(options.install_options, reqs): - logger.warning( - "Ignoring --install-option when building using PEP 517", - ) - else: - deprecated( - reason=( - "--install-option is deprecated because " - "it forces pip to use the 'setup.py install' " - "command which is itself deprecated." - ), - issue=11358, - replacement="to use --config-settings", - gone_in="23.1", - ) diff --git a/.venv/Lib/site-packages/pip/_internal/req/req_set.py b/.venv/Lib/site-packages/pip/_internal/req/req_set.py deleted file mode 100644 index ec7a6e0..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/req_set.py +++ /dev/null @@ -1,82 +0,0 @@ -import logging -from collections import OrderedDict -from typing import Dict, List - -from pip._vendor.packaging.utils import canonicalize_name - -from pip._internal.req.req_install import InstallRequirement - -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 - ] diff --git a/.venv/Lib/site-packages/pip/_internal/req/req_uninstall.py b/.venv/Lib/site-packages/pip/_internal/req/req_uninstall.py deleted file mode 100644 index 15b6738..0000000 --- a/.venv/Lib/site-packages/pip/_internal/req/req_uninstall.py +++ /dev/null @@ -1,640 +0,0 @@ -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, is_local, normalize_path, renames, rmtree -from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory - -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 = "Cannot uninstall {dist}, RECORD file not found.".format(dist=dist) - installer = dist.installer - if not installer or installer == "pip": - dep = "{}=={}".format(dist.raw_name, dist.version) - msg += ( - " You might be able to recover from this via: " - "'pip install --force-reinstall --no-deps {}'.".format(dep) - ) - else: - msg += " Hint: The package was installed by {}.".format(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) - - # probably this one https://github.com/python/mypy/issues/390 - _normcased_files = set(map(os.path.normcase, files)) # type: ignore - - 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.items(): - 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() - - def _permitted(self, path: str) -> bool: - """ - Return True if the given path is one we are permitted to - remove/modify, False otherwise. - - """ - return is_local(path) - - 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(normalize_path(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 = normalize_path(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 = normalize_path(link_pointer) - assert os.path.samefile( - normalized_link_pointer, normalized_dist_location - ), ( - f"Egg-link {link_pointer} does not match installed location of " - f"{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/site-packages/pip/_internal/resolution/__init__.py b/.venv/Lib/site-packages/pip/_internal/resolution/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_internal/resolution/base.py b/.venv/Lib/site-packages/pip/_internal/resolution/base.py deleted file mode 100644 index 42dade1..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/base.py +++ /dev/null @@ -1,20 +0,0 @@ -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/site-packages/pip/_internal/resolution/legacy/__init__.py b/.venv/Lib/site-packages/pip/_internal/resolution/legacy/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py b/.venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py deleted file mode 100644 index fb49d41..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/legacy/resolver.py +++ /dev/null @@ -1,600 +0,0 @@ -"""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( - "{} is not a supported wheel on this platform.".format( - wheel.filename - ) - ) - - # 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( - "Could not satisfy constraints for '{}': " - "installation from path or url cannot be " - "constrained to a version".format(install_req.name) - ) - # 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 " - "yanked version: {candidate}\n" - "Reason for being yanked: {reason}" - ).format(candidate=best_candidate, reason=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.original_link_is_in_wheel_cache = True - 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.hash 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/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py deleted file mode 100644 index b206692..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/base.py +++ /dev/null @@ -1,141 +0,0 @@ -from typing import FrozenSet, Iterable, Optional, Tuple, Union - -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name -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: str, extras: FrozenSet[str]) -> str: - if not extras: - return project - canonical_extras = sorted(canonicalize_name(e) for e in extras) - return "{}[{}]".format(project, ",".join(canonical_extras)) - - -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/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py deleted file mode 100644 index f5bc343..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/candidates.py +++ /dev/null @@ -1,556 +0,0 @@ -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, - options=dict( - install_options=template.install_options, - global_options=template.global_options, - hashes=template.hash_options, - ), - config_settings=template.config_settings, - ) - ireq.original_link = template.original_link - ireq.link = link - return ireq - - -def make_install_req_from_editable( - link: Link, template: InstallRequirement -) -> InstallRequirement: - assert template.editable, "template not editable" - return 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, - options=dict( - install_options=template.install_options, - global_options=template.global_options, - hashes=template.hash_options, - ), - config_settings=template.config_settings, - ) - - -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, - options=dict( - install_options=template.install_options, - global_options=template.global_options, - hashes=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 "{class_name}({link!r})".format( - class_name=self.__class__.__name__, - link=str(self._link), - ) - - 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 self._factory.make_requirement_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(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: - if cache_entry.persistent and template.link is template.original_link: - ireq.original_link_is_in_wheel_cache = True - 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.hash 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 - - # 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 "{class_name}({distribution!r})".format( - class_name=self.__class__.__name__, - distribution=self.dist, - ) - - 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: - return self.dist.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 self._factory.make_requirement_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], - ) -> None: - self.base = base - self.extras = extras - - def __str__(self) -> str: - name, rest = str(self.base).split(" ", 1) - return "{}[{}] {}".format(name, ",".join(self.extras), rest) - - def __repr__(self) -> str: - return "{class_name}(base={base!r}, extras={extras!r})".format( - class_name=self.__class__.__name__, - base=self.base, - extras=self.extras, - ) - - 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 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 - - # The user may have specified extras that the candidate doesn't - # support. We ignore any unsupported extras here. - valid_extras = self.extras.intersection(self.base.dist.iter_provided_extras()) - invalid_extras = self.extras.difference(self.base.dist.iter_provided_extras()) - for extra in sorted(invalid_extras): - logger.warning( - "%s %s does not provide the extra '%s'", - self.base.name, - self.version, - extra, - ) - - for r in self.base.dist.iter_dependencies(valid_extras): - requirement = factory.make_requirement_from_spec( - str(r), self.base._ireq, valid_extras - ) - if requirement: - yield requirement - - 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/site-packages/pip/_internal/resolution/resolvelib/factory.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py deleted file mode 100644 index a4c24b5..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/factory.py +++ /dev/null @@ -1,731 +0,0 @@ -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_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, - 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[str]], 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_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] - ) -> ExtrasCandidate: - cache_key = (id(base), extras) - try: - candidate = self._extras_candidate_cache[cache_key] - except KeyError: - candidate = ExtrasCandidate(base, extras) - 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) - - def _make_candidate_from_link( - self, - link: Link, - extras: FrozenSet[str], - template: InstallRequirement, - name: Optional[NormalizedName], - version: Optional[CandidateVersion], - ) -> Optional[Candidate]: - # 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 - - base: BaseCandidate = 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 - base = self._link_candidate_cache[link] - - if not extras: - return base - return self._make_extras_candidate(base, extras) - - 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_candidate_from_link( - link, - extras=frozenset(), - 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 explicit candidates - # from entries from extra-less identifier. - with contextlib.suppress(InvalidRequirement): - parsed_requirement = get_requirement(identifier) - explicit_candidates.update( - self._iter_explicit_candidates_from_base( - requirements.get(parsed_requirement.name, ()), - frozenset(parsed_requirement.extras), - ), - ) - - # 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_requirement_from_install_req( - self, ireq: InstallRequirement, requested_extras: Iterable[str] - ) -> Optional[Requirement]: - if not ireq.match_markers(requested_extras): - logger.info( - "Ignoring %s: markers '%s' don't match your environment", - ireq.name, - ireq.markers, - ) - return None - if not ireq.link: - return SpecifierRequirement(ireq) - self._fail_if_link_is_unsupported_wheel(ireq.link) - cand = self._make_candidate_from_link( - ireq.link, - extras=frozenset(ireq.extras), - template=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] - return UnsatisfiableRequirement(canonicalize_name(ireq.name)) - return self.make_requirement_from_candidate(cand) - - 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: - req = self._make_requirement_from_install_req( - ireq, - requested_extras=(), - ) - if req is None: - continue - if ireq.user_supplied and req.name not in collected.user_requested: - collected.user_requested[req.name] = i - collected.requirements.append(req) - return collected - - def make_requirement_from_candidate( - self, candidate: Candidate - ) -> ExplicitRequirement: - return ExplicitRequirement(candidate) - - def make_requirement_from_spec( - self, - specifier: str, - comes_from: Optional[InstallRequirement], - requested_extras: Iterable[str] = (), - ) -> Optional[Requirement]: - ireq = self._make_install_req_from_spec(specifier, comes_from) - return self._make_requirement_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 or self.preparer.require_hashes: - 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 = [str(v) for v in sorted({c.version for c in cands})] - - 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 = ( - "Cannot install {} because these package versions " - "have conflicting dependencies.".format(info) - ) - 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/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py deleted file mode 100644 index 8663097..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py +++ /dev/null @@ -1,155 +0,0 @@ -"""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/site-packages/pip/_internal/resolution/resolvelib/provider.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py deleted file mode 100644 index 6300dfc..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/provider.py +++ /dev/null @@ -1,248 +0,0 @@ -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( # type: ignore - 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. - * 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). - """ - lookups = (r.get_candidate_lookup() for r, _ in information[identifier]) - candidate, ireqs = zip(*lookups) - 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 - 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 = 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 - - # HACK: Setuptools have a very long and solid backward compatibility - # track record, and extremely few projects would request a narrow, - # non-recent version range of it since that would break a lot things. - # (Most projects specify it only to request for an installer feature, - # which does not work, but that's another topic.) Intentionally - # delaying Setuptools helps reduce branches the resolver has to check. - # This serves as a temporary fix for issues like "apache-airflow[all]" - # while we work on "proper" branch pruning techniques. - delay_this = identifier == "setuptools" - - # 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, - delay_this, - 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/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py deleted file mode 100644 index 6ced532..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/reporter.py +++ /dev/null @@ -1,68 +0,0 @@ -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.backtracks_by_package: DefaultDict[str, int] = defaultdict(int) - - self._messages_at_backtrack = { - 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 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 backtracking(self, candidate: Candidate) -> None: - self.backtracks_by_package[candidate.name] += 1 - - count = self.backtracks_by_package[candidate.name] - if count not in self._messages_at_backtrack: - return - - message = self._messages_at_backtrack[count] - logger.info("INFO: %s", message.format(package_name=candidate.name)) - - -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 backtracking(self, candidate: Candidate) -> None: - logger.info("Reporter.backtracking(%r)", candidate) - - def pinning(self, candidate: Candidate) -> None: - logger.info("Reporter.pinning(%r)", candidate) diff --git a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py deleted file mode 100644 index f561f1f..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/requirements.py +++ /dev/null @@ -1,166 +0,0 @@ -from pip._vendor.packaging.specifiers import SpecifierSet -from pip._vendor.packaging.utils import NormalizedName, canonicalize_name - -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 "{class_name}({candidate!r})".format( - class_name=self.__class__.__name__, - candidate=self.candidate, - ) - - @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(ireq.extras) - - def __str__(self) -> str: - return str(self._ireq.req) - - def __repr__(self) -> str: - return "{class_name}({requirement!r})".format( - class_name=self.__class__.__name__, - requirement=str(self._ireq.req), - ) - - @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 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 "{class_name}({specifier!r})".format( - class_name=self.__class__.__name__, - specifier=str(self.specifier), - ) - - @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 "{class_name}({name!r})".format( - class_name=self.__class__.__name__, - name=str(self._name), - ) - - @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/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py deleted file mode 100644 index a605d6c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/resolution/resolvelib/resolver.py +++ /dev/null @@ -1,296 +0,0 @@ -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.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 .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: - try_to_avoid_resolution_too_deep = 2000000 - result = self._result = resolver.resolve( - collected.requirements, max_rounds=try_to_avoid_resolution_too_deep - ) - - 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) - for candidate in result.mapping.values(): - ireq = candidate.get_install_requirement() - if ireq is None: - 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) - 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/site-packages/pip/_internal/self_outdated_check.py b/.venv/Lib/site-packages/pip/_internal/self_outdated_check.py deleted file mode 100644 index 9e2149c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/self_outdated_check.py +++ /dev/null @@ -1,239 +0,0 @@ -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 - -_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" - - -logger = logging.getLogger(__name__) - - -def _get_statefile_name(key: str) -> str: - key_bytes = key.encode() - name = hashlib.sha224(key_bytes).hexdigest() - return name - - -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 - - seven_days_in_seconds = 7 * 24 * 60 * 60 - - # Determine if we need to refresh the state - last_check = datetime.datetime.strptime(self._state["last_check"], _DATE_FMT) - seconds_since_last_check = (current_time - last_check).total_seconds() - if seconds_since_last_check > seven_days_in_seconds: - 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.strftime(_DATE_FMT), - "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 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 -) -> 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 - - return str(best_candidate.version) - - -def _self_version_check_logic( - *, - state: SelfCheckState, - current_time: datetime.datetime, - local_version: DistributionVersion, - get_remote_version: Callable[[], str], -) -> Optional[UpgradePrompt]: - remote_version_str = state.get(current_time) - if remote_version_str is None: - remote_version_str = get_remote_version() - 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.utcnow(), - 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("[present-rich] %s", upgrade_prompt) - 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/site-packages/pip/_internal/utils/__init__.py b/.venv/Lib/site-packages/pip/_internal/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_internal/utils/_log.py b/.venv/Lib/site-packages/pip/_internal/utils/_log.py deleted file mode 100644 index 92c4c6a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/_log.py +++ /dev/null @@ -1,38 +0,0 @@ -"""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/site-packages/pip/_internal/utils/appdirs.py b/.venv/Lib/site-packages/pip/_internal/utils/appdirs.py deleted file mode 100644 index 16933bf..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/appdirs.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -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/site-packages/pip/_internal/utils/compat.py b/.venv/Lib/site-packages/pip/_internal/utils/compat.py deleted file mode 100644 index 3f4d300..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/compat.py +++ /dev/null @@ -1,63 +0,0 @@ -"""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/site-packages/pip/_internal/utils/compatibility_tags.py b/.venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py deleted file mode 100644 index b6ed9a7..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/compatibility_tags.py +++ /dev/null @@ -1,165 +0,0 @@ -"""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/site-packages/pip/_internal/utils/datetime.py b/.venv/Lib/site-packages/pip/_internal/utils/datetime.py deleted file mode 100644 index 8668b3b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/datetime.py +++ /dev/null @@ -1,11 +0,0 @@ -"""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/site-packages/pip/_internal/utils/deprecation.py b/.venv/Lib/site-packages/pip/_internal/utils/deprecation.py deleted file mode 100644 index 18e9be9..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/deprecation.py +++ /dev/null @@ -1,188 +0,0 @@ -""" -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) - - -class LegacyInstallReason: - def __init__( - self, - reason: str, - replacement: Optional[str] = None, - gone_in: Optional[str] = None, - feature_flag: Optional[str] = None, - issue: Optional[int] = None, - emit_after_success: bool = False, - emit_before_install: bool = False, - ): - self._reason = reason - self._replacement = replacement - self._gone_in = gone_in - self._feature_flag = feature_flag - self._issue = issue - self.emit_after_success = emit_after_success - self.emit_before_install = emit_before_install - - def emit_deprecation(self, name: str) -> None: - deprecated( - reason=self._reason.format(name=name), - replacement=self._replacement, - gone_in=self._gone_in, - feature_flag=self._feature_flag, - issue=self._issue, - ) - - -LegacyInstallReasonFailedBdistWheel = LegacyInstallReason( - reason=( - "{name} was installed using the legacy 'setup.py install' " - "method, because a wheel could not be built for it." - ), - replacement="to fix the wheel build issue reported above", - gone_in="23.1", - issue=8368, - emit_after_success=True, -) - - -LegacyInstallReasonMissingWheelPackage = LegacyInstallReason( - reason=( - "{name} is being installed using the legacy " - "'setup.py install' method, because it does not have a " - "'pyproject.toml' and the 'wheel' package " - "is not installed." - ), - replacement="to enable the '--use-pep517' option", - gone_in="23.1", - issue=8559, - emit_before_install=True, -) - -LegacyInstallReasonNoBinaryForcesSetuptoolsInstall = LegacyInstallReason( - reason=( - "{name} is being installed using the legacy " - "'setup.py install' method, because the '--no-binary' option was enabled " - "for it and this currently disables local wheel building for projects that " - "don't have a 'pyproject.toml' file." - ), - replacement="to enable the '--use-pep517' option", - gone_in="23.1", - issue=11451, - emit_before_install=True, -) diff --git a/.venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py b/.venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py deleted file mode 100644 index 0e8e5e1..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/direct_url_helpers.py +++ /dev/null @@ -1,87 +0,0 @@ -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/site-packages/pip/_internal/utils/distutils_args.py b/.venv/Lib/site-packages/pip/_internal/utils/distutils_args.py deleted file mode 100644 index 2fd1862..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/distutils_args.py +++ /dev/null @@ -1,43 +0,0 @@ -from getopt import GetoptError, getopt -from typing import Dict, List - -_options = [ - "exec-prefix=", - "home=", - "install-base=", - "install-data=", - "install-headers=", - "install-lib=", - "install-platlib=", - "install-purelib=", - "install-scripts=", - "prefix=", - "root=", - "user", -] - - -def parse_distutils_args(args: List[str]) -> Dict[str, str]: - """Parse provided arguments, returning an object that has the matched arguments. - - Any unknown arguments are ignored. - """ - result = {} - for arg in args: - try: - parsed_opt, _ = getopt(args=[arg], shortopts="", longopts=_options) - except GetoptError: - # We don't care about any other options, which here may be - # considered unrecognized since our option list is not - # exhaustive. - continue - - if not parsed_opt: - continue - - option = parsed_opt[0] - name_from_parsed = option[0][2:].replace("-", "_") - value_from_parsed = option[1] or "true" - result[name_from_parsed] = value_from_parsed - - return result diff --git a/.venv/Lib/site-packages/pip/_internal/utils/egg_link.py b/.venv/Lib/site-packages/pip/_internal/utils/egg_link.py deleted file mode 100644 index 9e0da8d..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/egg_link.py +++ /dev/null @@ -1,75 +0,0 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -import os -import re -import sys -from typing import 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_name(raw_name: str) -> 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. - """ - return re.sub("[^A-Za-z0-9.]+", "-", 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_name = _egg_link_name(raw_name) - for path_item in sys.path: - 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 = [] - 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_name = _egg_link_name(raw_name) - for site in sites: - egglink = os.path.join(site, egg_link_name) - if os.path.isfile(egglink): - return egglink - return None diff --git a/.venv/Lib/site-packages/pip/_internal/utils/encoding.py b/.venv/Lib/site-packages/pip/_internal/utils/encoding.py deleted file mode 100644 index 008f06a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/encoding.py +++ /dev/null @@ -1,36 +0,0 @@ -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/site-packages/pip/_internal/utils/entrypoints.py b/.venv/Lib/site-packages/pip/_internal/utils/entrypoints.py deleted file mode 100644 index 1501369..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/entrypoints.py +++ /dev/null @@ -1,84 +0,0 @@ -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/site-packages/pip/_internal/utils/filesystem.py b/.venv/Lib/site-packages/pip/_internal/utils/filesystem.py deleted file mode 100644 index 83c2df7..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/filesystem.py +++ /dev/null @@ -1,153 +0,0 @@ -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/site-packages/pip/_internal/utils/filetypes.py b/.venv/Lib/site-packages/pip/_internal/utils/filetypes.py deleted file mode 100644 index 5948570..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/filetypes.py +++ /dev/null @@ -1,27 +0,0 @@ -"""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/site-packages/pip/_internal/utils/glibc.py b/.venv/Lib/site-packages/pip/_internal/utils/glibc.py deleted file mode 100644 index 7bd3c20..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/glibc.py +++ /dev/null @@ -1,88 +0,0 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -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: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": - _, version = os.confstr("CS_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/site-packages/pip/_internal/utils/hashes.py b/.venv/Lib/site-packages/pip/_internal/utils/hashes.py deleted file mode 100644 index 7672730..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/hashes.py +++ /dev/null @@ -1,144 +0,0 @@ -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 __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/site-packages/pip/_internal/utils/inject_securetransport.py b/.venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py deleted file mode 100644 index 276aa79..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/inject_securetransport.py +++ /dev/null @@ -1,35 +0,0 @@ -"""A helper module that injects SecureTransport, on import. - -The import should be done as early as possible, to ensure all requests and -sessions (or whatever) are created after injecting SecureTransport. - -Note that we only do the injection on macOS, when the linked OpenSSL is too -old to handle TLSv1.2. -""" - -import sys - - -def inject_securetransport() -> None: - # Only relevant on macOS - if sys.platform != "darwin": - return - - try: - import ssl - except ImportError: - return - - # Checks for OpenSSL 1.0.1 - if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100F: - return - - try: - from pip._vendor.urllib3.contrib import securetransport - except (ImportError, OSError): - return - - securetransport.inject_into_urllib3() - - -inject_securetransport() diff --git a/.venv/Lib/site-packages/pip/_internal/utils/logging.py b/.venv/Lib/site-packages/pip/_internal/utils/logging.py deleted file mode 100644 index c10e1f4..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/logging.py +++ /dev/null @@ -1,348 +0,0 @@ -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 record.msg == "[present-rich] %s" and len(record.args) == 1: - rich_renderable = record.args[0] - 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/site-packages/pip/_internal/utils/misc.py b/.venv/Lib/site-packages/pip/_internal/utils/misc.py deleted file mode 100644 index a8f4cb5..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/misc.py +++ /dev/null @@ -1,723 +0,0 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - -import contextlib -import errno -import getpass -import hashlib -import io -import logging -import os -import posixpath -import shutil -import stat -import sys -import urllib.parse -from io import StringIO -from itertools import filterfalse, tee, zip_longest -from types import TracebackType -from typing import ( - Any, - BinaryIO, - Callable, - ContextManager, - Dict, - Generator, - Iterable, - Iterator, - List, - Optional, - TextIO, - Tuple, - Type, - TypeVar, - cast, -) - -from pip._vendor.pep517 import Pep517HookCaller -from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed - -from pip import __version__ -from pip._internal.exceptions import CommandError -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", - "ConfiguredPep517HookCaller", -] - - -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]]] - - -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 "pip {} from {} (python {})".format( - __version__, - pip_pkg_dir, - 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) -> None: - shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler) - - -def rmtree_errorhandler(func: Callable[..., Any], path: str, exc_info: ExcInfo) -> None: - """On Windows, the files in .svn are read-only, so when rmtree() tries to - remove them, an exception is thrown. We catch that here, remove the - read-only attribute, and hopefully continue without problems.""" - try: - has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE) - except OSError: - # it's equivalent to os.path.exists - return - - if has_attr_readonly: - # convert to read/write - os.chmod(path, stat.S_IWRITE) - # use the original function to repeat the operation - func(path) - return - else: - raise - - -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 "{:.1f} MB".format(bytes / 1000.0 / 1000) - elif bytes > 10 * 1000: - return "{} kB".format(int(bytes / 1000)) - elif bytes > 1000: - return "{:.1f} kB".format(bytes / 1000.0) - else: - return "{} bytes".format(int(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 = None - - @classmethod - def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper": - cls.orig_stream = orig_stream - return cls() - - # compileall.compile_dir() needs stdout.encoding to print to stdout - # https://github.com/python/mypy/issues/4125 - @property - def encoding(self): # 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[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 "{user}{password}@{netloc}".format( - user=user, password=password, netloc=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[str, 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] - - -class HiddenText: - def __init__(self, secret: str, redacted: str) -> None: - self.secret = secret - self.redacted = redacted - - def __repr__(self) -> str: - return "".format(str(self)) - - 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 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 is_wheel_installed() -> bool: - """ - Return whether the wheel package is installed. - """ - try: - import wheel # noqa: F401 - except ImportError: - return False - - return True - - -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 ConfiguredPep517HookCaller(Pep517HookCaller): - 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, 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, 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, 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, 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, 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, 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, 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, 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/site-packages/pip/_internal/utils/models.py b/.venv/Lib/site-packages/pip/_internal/utils/models.py deleted file mode 100644 index b6bb21a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/models.py +++ /dev/null @@ -1,39 +0,0 @@ -"""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/site-packages/pip/_internal/utils/packaging.py b/.venv/Lib/site-packages/pip/_internal/utils/packaging.py deleted file mode 100644 index b9f6af4..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/packaging.py +++ /dev/null @@ -1,57 +0,0 @@ -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/site-packages/pip/_internal/utils/setuptools_build.py b/.venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py deleted file mode 100644 index 01ef4a4..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/setuptools_build.py +++ /dev/null @@ -1,195 +0,0 @@ -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], - install_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"] - - args += install_options - - 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 - - -def make_setuptools_install_args( - setup_py_path: str, - global_options: Sequence[str], - install_options: Sequence[str], - record_filename: str, - root: Optional[str], - prefix: Optional[str], - header_dir: Optional[str], - home: Optional[str], - use_user_site: bool, - no_user_config: bool, - pycompile: bool, -) -> List[str]: - assert not (use_user_site and prefix) - assert not (use_user_site and root) - - args = make_setuptools_shim_args( - setup_py_path, - global_options=global_options, - no_user_config=no_user_config, - unbuffered_output=True, - ) - args += ["install", "--record", record_filename] - args += ["--single-version-externally-managed"] - - if root is not None: - args += ["--root", root] - if prefix is not None: - args += ["--prefix", prefix] - if home is not None: - args += ["--home", home] - if use_user_site: - args += ["--user", "--prefix="] - - if pycompile: - args += ["--compile"] - else: - args += ["--no-compile"] - - if header_dir: - args += ["--install-headers", header_dir] - - args += install_options - - return args diff --git a/.venv/Lib/site-packages/pip/_internal/utils/subprocess.py b/.venv/Lib/site-packages/pip/_internal/utils/subprocess.py deleted file mode 100644 index cf5bf6b..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/subprocess.py +++ /dev/null @@ -1,260 +0,0 @@ -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("[present-rich] %s", error) - 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 pep517's Pep517HookCaller. Thus, the runner has - an API that matches what's expected by Pep517HookCaller.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/site-packages/pip/_internal/utils/temp_dir.py b/.venv/Lib/site-packages/pip/_internal/utils/temp_dir.py deleted file mode 100644 index 8ee8a1c..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/temp_dir.py +++ /dev/null @@ -1,246 +0,0 @@ -import errno -import itertools -import logging -import os.path -import tempfile -from contextlib import ExitStack, contextmanager -from typing import Any, Dict, Generator, 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, - ): - 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 - - 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 - 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/site-packages/pip/_internal/utils/unpacking.py b/.venv/Lib/site-packages/pip/_internal/utils/unpacking.py deleted file mode 100644 index 78b5c13..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/unpacking.py +++ /dev/null @@ -1,257 +0,0 @@ -"""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/site-packages/pip/_internal/utils/urls.py b/.venv/Lib/site-packages/pip/_internal/utils/urls.py deleted file mode 100644 index 6ba2e04..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/urls.py +++ /dev/null @@ -1,62 +0,0 @@ -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/site-packages/pip/_internal/utils/virtualenv.py b/.venv/Lib/site-packages/pip/_internal/utils/virtualenv.py deleted file mode 100644 index c926db4..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/virtualenv.py +++ /dev/null @@ -1,104 +0,0 @@ -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_regular_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: - """Return True if we're running inside a virtualenv, False otherwise.""" - return _running_under_venv() or _running_under_regular_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_regular_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_regular_virtualenv(): - return _no_global_under_regular_virtualenv() - - return False diff --git a/.venv/Lib/site-packages/pip/_internal/utils/wheel.py b/.venv/Lib/site-packages/pip/_internal/utils/wheel.py deleted file mode 100644 index e5e3f34..0000000 --- a/.venv/Lib/site-packages/pip/_internal/utils/wheel.py +++ /dev/null @@ -1,136 +0,0 @@ -"""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("{} has an invalid wheel, {}".format(name, 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( - ".dist-info directory {!r} does not start with {!r}".format( - info_dir, canonical_name - ) - ) - - 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/site-packages/pip/_internal/vcs/__init__.py b/.venv/Lib/site-packages/pip/_internal/vcs/__init__.py deleted file mode 100644 index b6beddb..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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/site-packages/pip/_internal/vcs/bazaar.py b/.venv/Lib/site-packages/pip/_internal/vcs/bazaar.py deleted file mode 100644 index 06c80e4..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/bazaar.py +++ /dev/null @@ -1,112 +0,0 @@ -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:// readd 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/site-packages/pip/_internal/vcs/git.py b/.venv/Lib/site-packages/pip/_internal/vcs/git.py deleted file mode 100644 index 8d1d499..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/git.py +++ /dev/null @@ -1,526 +0,0 @@ -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 tuple(int(c) for c in match.groups()) - - @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/site-packages/pip/_internal/vcs/mercurial.py b/.venv/Lib/site-packages/pip/_internal/vcs/mercurial.py deleted file mode 100644 index 2a005e0..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/mercurial.py +++ /dev/null @@ -1,163 +0,0 @@ -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 [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/site-packages/pip/_internal/vcs/subversion.py b/.venv/Lib/site-packages/pip/_internal/vcs/subversion.py deleted file mode 100644 index 2cd6f0a..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/subversion.py +++ /dev/null @@ -1,324 +0,0 @@ -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:// readd 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/site-packages/pip/_internal/vcs/versioncontrol.py b/.venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py deleted file mode 100644 index 02bbf68..0000000 --- a/.venv/Lib/site-packages/pip/_internal/vcs/versioncontrol.py +++ /dev/null @@ -1,705 +0,0 @@ -"""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( - "Sorry, {!r} is a malformed VCS url. " - "The format is +://, " - "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) - ) - # 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( - "The URL {!r} has an empty revision (after @) " - "which is not supported. Include a revision after @ " - "or remove @ from the URL.".format(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("What to do? {}".format(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/site-packages/pip/_internal/wheel_builder.py b/.venv/Lib/site-packages/pip/_internal/wheel_builder.py deleted file mode 100644 index 15b30af..0000000 --- a/.venv/Lib/site-packages/pip/_internal/wheel_builder.py +++ /dev/null @@ -1,382 +0,0 @@ -"""Orchestrator for building wheels from InstallRequirements. -""" - -import logging -import os.path -import re -import shutil -from typing import Callable, 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.deprecation import ( - LegacyInstallReasonMissingWheelPackage, - LegacyInstallReasonNoBinaryForcesSetuptoolsInstall, -) -from pip._internal.utils.logging import indent_log -from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed -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) - -BdistWheelAllowedPredicate = Callable[[InstallRequirement], bool] -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, - check_bdist_wheel: Optional[BdistWheelAllowedPredicate] = None, -) -> 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() - - if req.use_pep517: - return True - - assert check_bdist_wheel is not None - if not check_bdist_wheel(req): - # /!\ When we change this to unconditionally return True, we must also remove - # support for `--install-option`. Indeed, `--install-option` implies - # `--no-binary` so we can return False here and run `setup.py install`. - # `--global-option` and `--build-option` can remain until we drop support for - # building with `setup.py bdist_wheel`. - req.legacy_install_reason = LegacyInstallReasonNoBinaryForcesSetuptoolsInstall - return False - - if not is_wheel_installed(): - # we don't build legacy requirements if wheel is not installed - req.legacy_install_reason = LegacyInstallReasonMissingWheelPackage - return False - - 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, - check_bdist_wheel_allowed: BdistWheelAllowedPredicate, -) -> bool: - return _should_build( - req, need_wheel=False, check_bdist_wheel=check_bdist_wheel_allowed - ) - - -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( - "Wheel has unexpected file name: expected {!r}, " - "got {!r}".format(canonical_name, w.name), - ) - 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( - "Wheel has unexpected file name: expected {!r}, " - "got {!r}".format(dist_verstr, w.version), - ) - 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( - "Metadata 1.2 mandates PEP 440 version, " - "but {!r} is not".format(dist_verstr) - ) - - -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/site-packages/pip/_vendor/__init__.py b/.venv/Lib/site-packages/pip/_vendor/__init__.py deleted file mode 100644 index b22f7ab..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/__init__.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -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("urllib3") diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py deleted file mode 100644 index f631ae6..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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.12.11" - -from .wrapper import CacheControl -from .adapter import CacheControlAdapter -from .controller import CacheController - -import logging -logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py deleted file mode 100644 index 4266b5e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/_cmd.py +++ /dev/null @@ -1,61 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -import logging - -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 - -from argparse import ArgumentParser - - -def setup_logging(): - logger.setLevel(logging.DEBUG) - handler = logging.StreamHandler() - logger.addHandler(handler) - - -def get_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 - return sess - - -def get_args(): - parser = ArgumentParser() - parser.add_argument("url", help="The URL to try and cache") - return parser.parse_args() - - -def main(args=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 - sess.cache_controller.cache_response(resp.request, resp.raw) - - # Now try to get it - if sess.cache_controller.cached_request(resp.request): - print("Cached!") - else: - print("Not cached :(") - - -if __name__ == "__main__": - main() diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py deleted file mode 100644 index 94c75e1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/adapter.py +++ /dev/null @@ -1,137 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -import types -import functools -import zlib - -from pip._vendor.requests.adapters import HTTPAdapter - -from .controller import CacheController, PERMANENT_REDIRECT_STATUSES -from .cache import DictCache -from .filewrapper import CallbackFileWrapper - - -class CacheControlAdapter(HTTPAdapter): - invalidating_methods = {"PUT", "PATCH", "DELETE"} - - def __init__( - self, - cache=None, - cache_etags=True, - controller_class=None, - serializer=None, - heuristic=None, - cacheable_methods=None, - *args, - **kw - ): - super(CacheControlAdapter, self).__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, cacheable_methods=None, **kw): - """ - 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(CacheControlAdapter, self).send(request, **kw) - - return resp - - def build_response( - self, request, response, from_cache=False, cacheable_methods=None - ): - """ - 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( - response._fp, - functools.partial( - self.controller.cache_response, request, response - ), - ) - if response.chunked: - super_update_chunk_length = response._update_chunk_length - - def _update_chunk_length(self): - super_update_chunk_length() - if self.chunk_left == 0: - self._fp._close() - - response._update_chunk_length = types.MethodType( - _update_chunk_length, response - ) - - resp = super(CacheControlAdapter, self).build_response(request, response) - - # See if we should invalidate the cache. - if request.method in self.invalidating_methods and resp.ok: - 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 - - return resp - - def close(self): - self.cache.close() - super(CacheControlAdapter, self).close() diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py deleted file mode 100644 index 2a965f5..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/cache.py +++ /dev/null @@ -1,65 +0,0 @@ -# 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 threading import Lock - - -class BaseCache(object): - - def get(self, key): - raise NotImplementedError() - - def set(self, key, value, expires=None): - raise NotImplementedError() - - def delete(self, key): - raise NotImplementedError() - - def close(self): - pass - - -class DictCache(BaseCache): - - def __init__(self, init_dict=None): - self.lock = Lock() - self.data = init_dict or {} - - def get(self, key): - return self.data.get(key, None) - - def set(self, key, value, expires=None): - with self.lock: - self.data.update({key: value}) - - def delete(self, key): - 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, body): - raise NotImplementedError() - - def get_body(self, key): - """ - Return the body as file-like object. - """ - raise NotImplementedError() diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py deleted file mode 100644 index 3782729..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -from .file_cache import FileCache, SeparateBodyFileCache -from .redis_cache import RedisCache - - -__all__ = ["FileCache", "SeparateBodyFileCache", "RedisCache"] diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py deleted file mode 100644 index f1ddb2e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py +++ /dev/null @@ -1,188 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -import hashlib -import os -from textwrap import dedent - -from ..cache import BaseCache, SeparateBodyBaseCache -from ..controller import CacheController - -try: - FileNotFoundError -except NameError: - # py2.X - FileNotFoundError = (IOError, OSError) - - -def _secure_open_write(filename, fmode): - # 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 (IOError, 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, - forever=False, - filemode=0o0600, - dirmode=0o0700, - use_dir_lock=None, - lock_class=None, - ): - - if use_dir_lock is not None and lock_class is not None: - raise ValueError("Cannot use use_dir_lock and lock_class together") - - try: - from lockfile import LockFile - from lockfile.mkdirlockfile import MkdirLockFile - except ImportError: - notice = dedent( - """ - NOTE: In order to use the FileCache you must have - lockfile installed. You can install it via pip: - pip install lockfile - """ - ) - raise ImportError(notice) - - else: - if use_dir_lock: - lock_class = MkdirLockFile - - elif lock_class is None: - lock_class = LockFile - - self.directory = directory - self.forever = forever - self.filemode = filemode - self.dirmode = dirmode - self.lock_class = lock_class - - @staticmethod - def encode(x): - return hashlib.sha224(x.encode()).hexdigest() - - def _fn(self, name): - # 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): - name = self._fn(key) - try: - with open(name, "rb") as fh: - return fh.read() - - except FileNotFoundError: - return None - - def set(self, key, value, expires=None): - name = self._fn(key) - self._write(name, value) - - def _write(self, path, data: bytes): - """ - Safely write the data to the given path. - """ - # Make sure the directory exists - try: - os.makedirs(os.path.dirname(path), self.dirmode) - except (IOError, OSError): - pass - - with self.lock_class(path) as lock: - # Write our actual file - with _secure_open_write(lock.path, self.filemode) as fh: - fh.write(data) - - def _delete(self, key, suffix): - 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): - 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): - name = self._fn(key) + ".body" - try: - return open(name, "rb") - except FileNotFoundError: - return None - - def set_body(self, key, body): - name = self._fn(key) + ".body" - self._write(name, body) - - def delete(self, key): - self._delete(key, "") - self._delete(key, ".body") - - -def url_to_file_path(url, filecache): - """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/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py deleted file mode 100644 index 2cba4b0..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -from __future__ import division - -from datetime import datetime -from pip._vendor.cachecontrol.cache import BaseCache - - -class RedisCache(BaseCache): - - def __init__(self, conn): - self.conn = conn - - def get(self, key): - return self.conn.get(key) - - def set(self, key, value, expires=None): - if not expires: - self.conn.set(key, value) - elif isinstance(expires, datetime): - expires = expires - datetime.utcnow() - self.conn.setex(key, int(expires.total_seconds()), value) - else: - self.conn.setex(key, expires, value) - - def delete(self, key): - self.conn.delete(key) - - def clear(self): - """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): - """Redis uses connection pooling, no need to close the connection.""" - pass diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py deleted file mode 100644 index ccec937..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/compat.py +++ /dev/null @@ -1,32 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -try: - from urllib.parse import urljoin -except ImportError: - from urlparse import urljoin - - -try: - import cPickle as pickle -except ImportError: - import pickle - -# Handle the case where the requests module has been patched to not have -# urllib3 bundled as part of its source. -try: - from pip._vendor.requests.packages.urllib3.response import HTTPResponse -except ImportError: - from pip._vendor.urllib3.response import HTTPResponse - -try: - from pip._vendor.requests.packages.urllib3.util import is_fp_closed -except ImportError: - from pip._vendor.urllib3.util import is_fp_closed - -# Replicate some six behaviour -try: - text_type = unicode -except NameError: - text_type = str diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py deleted file mode 100644 index 7f23529..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/controller.py +++ /dev/null @@ -1,439 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -""" -The httplib2 algorithms ported for use with requests. -""" -import logging -import re -import calendar -import time -from email.utils import parsedate_tz - -from pip._vendor.requests.structures import CaseInsensitiveDict - -from .cache import DictCache, SeparateBodyBaseCache -from .serialize import Serializer - - -logger = logging.getLogger(__name__) - -URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") - -PERMANENT_REDIRECT_STATUSES = (301, 308) - - -def parse_uri(uri): - """Parses a URI using the regex given in Appendix B of RFC 3986. - - (scheme, authority, path, query, fragment) = parse_uri(uri) - """ - groups = URI.match(uri).groups() - return (groups[1], groups[3], groups[4], groups[6], groups[8]) - - -class CacheController(object): - """An interface to see if request should cached or not.""" - - def __init__( - self, cache=None, cache_etags=True, serializer=None, status_codes=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): - """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): - return cls._urlnorm(uri) - - def parse_cache_control(self, headers): - 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 = {} - - 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 cached_request(self, request): - """ - Return a cached response if it exists in the cache, otherwise - return False. - """ - 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 - - # Request allows serving from the cache, let's see if we find something - cache_data = self.cache.get(cache_url) - if cache_data is None: - logger.debug("No cache entry available") - return False - - if isinstance(self.cache, SeparateBodyBaseCache): - body_file = self.cache.get_body(cache_url) - else: - body_file = None - - # Check whether it can be deserialized - resp = self.serializer.loads(request, cache_data, body_file) - if not resp: - logger.warning("Cache entry deserialization failed, entry ignored") - 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(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() - date = calendar.timegm(parsedate_tz(headers["date"])) - 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 - if "max-age" in resp_cc: - freshness_lifetime = resp_cc["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) - 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. - if "max-age" in cc: - freshness_lifetime = cc["max-age"] - logger.debug( - "Freshness lifetime from request max-age: %i", freshness_lifetime - ) - - if "min-fresh" in cc: - min_fresh = cc["min-fresh"] - # 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): - cache_url = self.cache_url(request.url) - resp = self.serializer.loads(request, self.cache.get(cache_url)) - new_headers = {} - - if resp: - headers = 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, request, response, body=None, expires_time=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, - ) - 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, response, body=None, status_codes=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(response.headers) - - if "date" in response_headers: - date = calendar.timegm(parsedate_tz(response_headers["date"])) - 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) - - 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) - date - - expires_time = max(expires_time, 14 * 86400) - - logger.debug("etag object cached for {0} seconds".format(expires_time)) - 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: - date = calendar.timegm(parsedate_tz(response_headers["date"])) - # cache when there is a max-age > 0 - if "max-age" in cc and cc["max-age"] > 0: - logger.debug("Caching b/c date exists and max-age > 0") - expires_time = cc["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) - date - else: - expires_time = None - - logger.debug( - "Caching b/c of expires header. expires in {0} seconds".format( - expires_time - ) - ) - self._cache_set( - cache_url, - request, - response, - body, - expires_time, - ) - - def update_cached_response(self, request, response): - """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. - """ - cache_url = self.cache_url(request.url) - - cached_response = self.serializer.loads(request, self.cache.get(cache_url)) - - 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( - dict( - (k, v) - for k, v in response.headers.items() - 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/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py deleted file mode 100644 index f5ed5f6..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/filewrapper.py +++ /dev/null @@ -1,111 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -from tempfile import NamedTemporaryFile -import mmap - - -class CallbackFileWrapper(object): - """ - 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, callback): - self.__buf = NamedTemporaryFile("rb+", delete=True) - self.__fp = fp - self.__callback = callback - - def __getattr__(self, name): - # 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): - try: - return self.__fp.fp is None - - except AttributeError: - pass - - try: - return self.__fp.closed - - except AttributeError: - pass - - # We just don't cache it then. - # TODO: Add some logging here... - return False - - def _close(self): - 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=None): - data = 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): - data = self.__fp._safe_read(amt) - 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/site-packages/pip/_vendor/cachecontrol/heuristics.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py deleted file mode 100644 index ebe4a96..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/heuristics.py +++ /dev/null @@ -1,139 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -import calendar -import time - -from email.utils import formatdate, parsedate, parsedate_tz - -from datetime import datetime, timedelta - -TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" - - -def expire_after(delta, date=None): - date = date or datetime.utcnow() - return date + delta - - -def datetime_to_header(dt): - return formatdate(calendar.timegm(dt.timetuple())) - - -class BaseHeuristic(object): - - def warning(self, response): - """ - 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): - """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): - 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): - headers = {} - - if "expires" not in response.headers: - date = parsedate(response.headers["date"]) - expires = expire_after(timedelta(days=1), date=datetime(*date[:6])) - 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): - self.delta = timedelta(**kw) - - def update_headers(self, response): - expires = expire_after(self.delta) - return {"expires": datetime_to_header(expires), "cache-control": "public"} - - def warning(self, response): - 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): - headers = 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 {} - - date = calendar.timegm(parsedate_tz(headers["date"])) - last_modified = parsedate(headers["last-modified"]) - if date is None or 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): - return None diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py deleted file mode 100644 index 7fe1a3e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/serialize.py +++ /dev/null @@ -1,190 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -import base64 -import io -import json -import zlib - -from pip._vendor import msgpack -from pip._vendor.requests.structures import CaseInsensitiveDict - -from .compat import HTTPResponse, pickle, text_type - - -def _b64_decode_bytes(b): - return base64.b64decode(b.encode("ascii")) - - -def _b64_decode_str(s): - return _b64_decode_bytes(s).decode("utf8") - - -_default_body_read = object() - - -class Serializer(object): - def dumps(self, request, response, body=None): - response_headers = 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) - - # NOTE: This is all a bit weird, but it's really important that on - # Python 2.x these objects are unicode and not str, even when - # they contain only ascii. The problem here is that msgpack - # understands the difference between unicode and bytes and we - # have it set to differentiate between them, however Python 2 - # doesn't know the difference. Forcing these to unicode will be - # enough to have msgpack know the difference. - data = { - u"response": { - u"body": body, # Empty bytestring if body is stored separately - u"headers": dict( - (text_type(k), text_type(v)) for k, v in response.headers.items() - ), - u"status": response.status, - u"version": response.version, - u"reason": text_type(response.reason), - u"strict": response.strict, - u"decode_content": response.decode_content, - } - } - - # Construct our vary headers - data[u"vary"] = {} - if u"vary" in response_headers: - varied_headers = response_headers[u"vary"].split(",") - for header in varied_headers: - header = text_type(header).strip() - header_value = request.headers.get(header, None) - if header_value is not None: - header_value = text_type(header_value) - data[u"vary"][header] = header_value - - return b",".join([b"cc=4", msgpack.dumps(data, use_bin_type=True)]) - - def loads(self, request, data, body_file=None): - # Short circuit if we've been given an empty set of data - if not data: - return - - # 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 - ver = ver.split(b"=", 1)[-1].decode("ascii") - - # Dispatch to the actual load method for the given version - try: - return getattr(self, "_loads_v{}".format(ver))(request, data, body_file) - - 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 - - def prepare_response(self, request, cached, body_file=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 - - # 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 - - body_raw = cached["response"].pop("body") - - headers = CaseInsensitiveDict(data=cached["response"]["headers"]) - if headers.get("transfer-encoding", "") == "chunked": - headers.pop("transfer-encoding") - - cached["response"]["headers"] = headers - - try: - 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")) - - return HTTPResponse(body=body, preload_content=False, **cached["response"]) - - def _loads_v0(self, request, data, body_file=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 - - def _loads_v1(self, request, data, body_file=None): - try: - cached = pickle.loads(data) - except ValueError: - return - - return self.prepare_response(request, cached, body_file) - - def _loads_v2(self, request, data, body_file=None): - assert body_file is None - try: - cached = json.loads(zlib.decompress(data).decode("utf8")) - except (ValueError, zlib.error): - return - - # We need to decode the items that we've base64 encoded - cached["response"]["body"] = _b64_decode_bytes(cached["response"]["body"]) - cached["response"]["headers"] = dict( - (_b64_decode_str(k), _b64_decode_str(v)) - for k, v in cached["response"]["headers"].items() - ) - cached["response"]["reason"] = _b64_decode_str(cached["response"]["reason"]) - cached["vary"] = dict( - (_b64_decode_str(k), _b64_decode_str(v) if v is not None else v) - for k, v in cached["vary"].items() - ) - - return self.prepare_response(request, cached, body_file) - - def _loads_v3(self, request, data, body_file): - # 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 - - def _loads_v4(self, request, data, body_file=None): - try: - cached = msgpack.loads(data, raw=False) - except ValueError: - return - - return self.prepare_response(request, cached, body_file) diff --git a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py b/.venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py deleted file mode 100644 index b6ee7f2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/cachecontrol/wrapper.py +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: 2015 Eric Larson -# -# SPDX-License-Identifier: Apache-2.0 - -from .adapter import CacheControlAdapter -from .cache import DictCache - - -def CacheControl( - sess, - cache=None, - cache_etags=True, - serializer=None, - heuristic=None, - controller_class=None, - adapter_class=None, - cacheable_methods=None, -): - - 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/site-packages/pip/_vendor/certifi/__init__.py b/.venv/Lib/site-packages/pip/_vendor/certifi/__init__.py deleted file mode 100644 index af4bcc1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/certifi/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import contents, where - -__all__ = ["contents", "where"] -__version__ = "2022.09.24" diff --git a/.venv/Lib/site-packages/pip/_vendor/certifi/__main__.py b/.venv/Lib/site-packages/pip/_vendor/certifi/__main__.py deleted file mode 100644 index 0037634..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -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/site-packages/pip/_vendor/certifi/cacert.pem b/.venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem deleted file mode 100644 index 4005155..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/certifi/cacert.pem +++ /dev/null @@ -1,4708 +0,0 @@ - -# 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=Network Solutions Certificate Authority O=Network Solutions L.L.C. -# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. -# Label: "Network Solutions Certificate Authority" -# Serial: 116697915152937497490437556386812487904 -# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e -# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce -# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi -MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV -UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO -ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz -c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP -OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl -mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF -BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 -qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw -gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu -bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp -dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 -6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ -h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH -/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN -pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------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=Hongkong Post Root CA 1 O=Hongkong Post -# Subject: CN=Hongkong Post Root CA 1 O=Hongkong Post -# Label: "Hongkong Post Root CA 1" -# Serial: 1000 -# MD5 Fingerprint: a8:0d:6f:39:78:b9:43:6d:77:42:6d:98:5a:cc:23:ca -# SHA1 Fingerprint: d6:da:a8:20:8d:09:d2:15:4d:24:b5:2f:cb:34:6e:b2:58:b2:8a:58 -# SHA256 Fingerprint: f9:e6:7d:33:6c:51:00:2a:c0:54:c6:32:02:2d:66:dd:a2:e7:e3:ff:f1:0a:d0:61:ed:31:d8:bb:b4:10:cf:b2 ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx -FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg -Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG -A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr -b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ -jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn -PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh -ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 -nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h -q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED -MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC -mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 -7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB -oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs -EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO -fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi -AmvZWg== ------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=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi -# Subject: CN=E-Tugra Certification Authority O=E-Tu\u011fra EBG Bili\u015fim Teknolojileri ve Hizmetleri A.\u015e. OU=E-Tugra Sertifikasyon Merkezi -# Label: "E-Tugra Certification Authority" -# Serial: 7667447206703254355 -# MD5 Fingerprint: b8:a1:03:63:b0:bd:21:71:70:8a:6f:13:3a:bb:79:49 -# SHA1 Fingerprint: 51:c6:e7:08:49:06:6e:f3:92:d4:5c:a0:0d:6d:a3:62:8f:c3:52:39 -# SHA256 Fingerprint: b0:bf:d5:2b:b0:d7:d9:bd:92:bf:5d:4d:c1:3d:a2:55:c0:2c:54:2f:37:83:65:ea:89:39:11:f5:5e:55:f2:3c ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV -BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC -aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV -BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 -Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz -MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ -BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp -em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN -ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY -B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH -D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF -Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo -q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D -k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH -fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut -dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM -ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 -zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn -rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX -U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 -Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 -XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF -Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR -HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY -GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c -77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 -+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK -vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 -FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl -yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P -AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD -y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d -NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== ------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=Staat der Nederlanden EV Root CA O=Staat der Nederlanden -# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden -# Label: "Staat der Nederlanden EV Root CA" -# Serial: 10000013 -# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba -# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb -# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a ------BEGIN CERTIFICATE----- -MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y -MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg -TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS -b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS -M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC -UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d -Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p -rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l -pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb -j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC -KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS -/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X -cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH -1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP -px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 -MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI -eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u -2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS -v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC -wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy -CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e -vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 -Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa -Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL -eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 -FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc -7uzXLg== ------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=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor RootCert CA-1" -# Serial: 15752444095811006489 -# MD5 Fingerprint: 6e:85:f1:dc:1a:00:d3:22:d5:b2:b2:ac:6b:37:05:45 -# SHA1 Fingerprint: ff:bd:cd:e7:82:c8:43:5e:3c:6f:26:86:5c:ca:a8:3a:45:5b:c3:0a -# SHA256 Fingerprint: d4:0e:9c:86:cd:8f:e4:68:c1:77:69:59:f4:9e:a7:74:fa:54:86:84:b6:c4:06:f3:90:92:61:f4:dc:e2:57:5c ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y -IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB -pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h -IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG -A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU -cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid -RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V -seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme -9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV -EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW -hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ -DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD -ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I -/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf -ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ -yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts -L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN -zl/HHk484IkzlQsPpTLWPFp5LBk= ------END CERTIFICATE----- - -# Issuer: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor RootCert CA-2" -# Serial: 2711694510199101698 -# MD5 Fingerprint: a2:e1:f8:18:0b:ba:45:d5:c7:41:2a:bb:37:52:45:64 -# SHA1 Fingerprint: b8:be:6d:cb:56:f1:55:b9:63:d4:12:ca:4e:06:34:c7:94:b2:1c:c0 -# SHA256 Fingerprint: 07:53:e9:40:37:8c:1b:d5:e3:83:6e:39:5d:ae:a5:cb:83:9e:50:46:f1:bd:0e:ae:19:51:cf:10:fe:c7:c9:65 ------BEGIN CERTIFICATE----- -MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig -Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk -MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg -Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD -VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy -dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ -QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq -1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp -2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK -DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape -az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF -3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 -oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM -g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 -mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh -8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd -BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U -nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw -DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX -dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ -MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL -/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX -CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa -ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW -2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 -N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 -Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB -As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp -5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu -1uwJ ------END CERTIFICATE----- - -# Issuer: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor ECA-1" -# Serial: 9548242946988625984 -# MD5 Fingerprint: 27:92:23:1d:0a:f5:40:7c:e9:e6:6b:9d:d8:f5:e7:6c -# SHA1 Fingerprint: 58:d1:df:95:95:67:6b:63:c0:f0:5b:1c:17:4d:8b:84:0b:c8:78:bd -# SHA256 Fingerprint: 5a:88:5d:b1:9c:01:d9:12:c5:75:93:88:93:8c:af:bb:df:03:1a:b2:d4:8e:91:ee:15:58:9b:42:97:1d:03:9c ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y -IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig -RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb -3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA -BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 -3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou -owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ -wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF -ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf -BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ -MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv -civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 -AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F -hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 -soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI -WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi -tJ/X5g== ------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=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Subject: CN=E-Tugra Global Root CA RSA v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Label: "E-Tugra Global Root CA RSA v3" -# Serial: 75951268308633135324246244059508261641472512052 -# MD5 Fingerprint: 22:be:10:f6:c2:f8:03:88:73:5f:33:29:47:28:47:a4 -# SHA1 Fingerprint: e9:a8:5d:22:14:52:1c:5b:aa:0a:b4:be:24:6a:23:8a:c9:ba:e2:a9 -# SHA256 Fingerprint: ef:66:b0:b1:0a:3c:db:9f:2e:36:48:c7:6b:d2:af:18:ea:d2:bf:e6:f1:17:65:5e:28:c4:06:0d:a1:a3:f4:c2 ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -# Issuer: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Subject: CN=E-Tugra Global Root CA ECC v3 O=E-Tugra EBG A.S. OU=E-Tugra Trust Center -# Label: "E-Tugra Global Root CA ECC v3" -# Serial: 218504919822255052842371958738296604628416471745 -# MD5 Fingerprint: 46:bc:81:bb:f1:b5:1e:f7:4b:96:bc:14:e2:e7:27:64 -# SHA1 Fingerprint: 8a:2f:af:57:53:b1:b0:e6:a1:04:ec:5b:6a:69:71:6d:f6:1c:e2:84 -# SHA256 Fingerprint: 87:3f:46:85:fa:7f:56:36:25:25:2e:6d:36:bc:d7:f1:6f:c2:49:51:f2:64:e4:7e:1b:95:4f:49:08:cd:ca:13 ------BEGIN CERTIFICATE----- -MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMw -gYAxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVn -cmEgRUJHIEEuUy4xHTAbBgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYD -VQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENBIEVDQyB2MzAeFw0yMDAzMTgwOTQ2 -NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEPMA0GA1UEBxMGQW5r -YXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1Z3Jh -IFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBF -Q0MgdjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQ -KczLWYHMjLiSF4mDKpL2w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YK -fWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMB -Af8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQzPUwHQYDVR0OBBYEFP+C -MXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNp -ADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/6 -7W4WAie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFx -vmjkI6TZraE3 ------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----- diff --git a/.venv/Lib/site-packages/pip/_vendor/certifi/core.py b/.venv/Lib/site-packages/pip/_vendor/certifi/core.py deleted file mode 100644 index c3e5466..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/certifi/core.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -certifi.py -~~~~~~~~~~ - -This module returns the installation location of cacert.pem or its contents. -""" -import sys - - -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") diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/__init__.py b/.venv/Lib/site-packages/pip/_vendor/chardet/__init__.py deleted file mode 100644 index e91ad61..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/__init__.py +++ /dev/null @@ -1,93 +0,0 @@ -######################## 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 .enums import InputState -from .universaldetector import UniversalDetector -from .version import VERSION, __version__ - -__all__ = ["UniversalDetector", "detect", "detect_all", "__version__", "VERSION"] - - -def detect(byte_str): - """ - Detect the encoding of the given byte string. - - :param byte_str: The byte sequence to examine. - :type byte_str: ``bytes`` or ``bytearray`` - """ - 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() - detector.feed(byte_str) - return detector.close() - - -def detect_all(byte_str, ignore_threshold=False): - """ - 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`` - """ - 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() - detector.feed(byte_str) - detector.close() - - if detector.input_state == InputState.HIGH_BYTE: - results = [] - probers = [] - for prober in detector.charset_probers: - if hasattr(prober, "probers"): - 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 - ) - 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/site-packages/pip/_vendor/chardet/big5freq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py deleted file mode 100644 index 87d9f97..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/big5freq.py +++ /dev/null @@ -1,386 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/big5prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py deleted file mode 100644 index e4dfa7a..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/big5prober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## 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): - super().__init__() - self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) - self.distribution_analyzer = Big5DistributionAnalysis() - self.reset() - - @property - def charset_name(self): - return "Big5" - - @property - def language(self): - return "Chinese" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py b/.venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py deleted file mode 100644 index 27b4a29..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/chardistribution.py +++ /dev/null @@ -1,259 +0,0 @@ -######################## 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 .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): - # Mapping table to get frequency order from char order (get from - # GetOrder()) - self._char_to_freq_order = tuple() - self._table_size = None # 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 = None - self._done = None - self._total_chars = None - self._freq_chars = None - self.reset() - - def reset(self): - """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, char_len): - """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): - """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): - # 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, _): - # 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): - 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): - # 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): - 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): - # 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): - 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): - 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): - 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): - # 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): - 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): - # 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): - 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): - # 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): - 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): - # 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/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py deleted file mode 100644 index 778ff33..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py +++ /dev/null @@ -1,109 +0,0 @@ -######################## 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 .charsetprober import CharSetProber -from .enums import ProbingState - - -class CharSetGroupProber(CharSetProber): - def __init__(self, lang_filter=None): - super().__init__(lang_filter=lang_filter) - self._active_num = 0 - self.probers = [] - self._best_guess_prober = None - - def reset(self): - super().reset() - self._active_num = 0 - for prober in self.probers: - if prober: - prober.reset() - prober.active = True - self._active_num += 1 - self._best_guess_prober = None - - @property - def charset_name(self): - 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): - 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): - for prober in self.probers: - if not prober: - continue - 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): - 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: - continue - 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/site-packages/pip/_vendor/chardet/charsetprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py deleted file mode 100644 index 9f1afd9..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/charsetprober.py +++ /dev/null @@ -1,138 +0,0 @@ -######################## 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 .enums import 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=None): - self._state = None - self.lang_filter = lang_filter - self.logger = logging.getLogger(__name__) - - def reset(self): - self._state = ProbingState.DETECTING - - @property - def charset_name(self): - return None - - def feed(self, byte_str): - raise NotImplementedError - - @property - def state(self): - return self._state - - def get_confidence(self): - return 0.0 - - @staticmethod - def filter_high_byte_only(buf): - buf = re.sub(b"([\x00-\x7F])+", b" ", buf) - return buf - - @staticmethod - def filter_international_words(buf): - """ - 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): - """ - 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 - if buf_char == b">": - prev = curr + 1 - in_tag = False - elif buf_char == b"<": - 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/site-packages/pip/_vendor/chardet/cli/__init__.py b/.venv/Lib/site-packages/pip/_vendor/chardet/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py b/.venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py deleted file mode 100644 index 7926fa3..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -Script which takes one or more file paths and reports on their detected -encodings - -Example:: - - % chardetect somefile someotherfile - somefile: windows-1252 with confidence 0.5 - someotherfile: ascii with confidence 1.0 - -If no paths are provided, it takes its input from stdin. - -""" - - -import argparse -import sys - -from .. import __version__ -from ..universaldetector import UniversalDetector - - -def description_of(lines, name="stdin"): - """ - 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 - """ - u = UniversalDetector() - 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 result["encoding"]: - return f'{name}: {result["encoding"]} with confidence {result["confidence"]}' - return f"{name}: no result" - - -def main(argv=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( - "--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)) - - -if __name__ == "__main__": - main() diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py b/.venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py deleted file mode 100644 index d3e3e82..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py +++ /dev/null @@ -1,88 +0,0 @@ -######################## 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 .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): - self._model = sm - self._curr_byte_pos = 0 - self._curr_char_len = 0 - self._curr_state = None - self.logger = logging.getLogger(__name__) - self.reset() - - def reset(self): - self._curr_state = MachineState.START - - def next_state(self, c): - # 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): - return self._curr_char_len - - def get_coding_state_machine(self): - return self._model["name"] - - @property - def language(self): - return self._model["language"] diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py deleted file mode 100644 index 28a1f3d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/cp949prober.py +++ /dev/null @@ -1,49 +0,0 @@ -######################## 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): - 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): - return "CP949" - - @property - def language(self): - return "Korean" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/enums.py b/.venv/Lib/site-packages/pip/_vendor/chardet/enums.py deleted file mode 100644 index 32a77e7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/enums.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -All of the Enums that are used throughout the chardet package. - -:author: Dan Blanchard (dan.blanchard@gmail.com) -""" - - -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: - """ - This enum represents the different language filters we can apply to a - ``UniversalDetector``. - """ - - 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: - """ - 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): - """: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/site-packages/pip/_vendor/chardet/escprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/escprober.py deleted file mode 100644 index d992611..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/escprober.py +++ /dev/null @@ -1,102 +0,0 @@ -######################## 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 .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=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 = None - self._detected_charset = None - self._detected_language = None - self._state = None - self.reset() - - def reset(self): - super().reset() - for coding_sm in self.coding_sm: - if not coding_sm: - continue - 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): - return self._detected_charset - - @property - def language(self): - return self._detected_language - - def get_confidence(self): - return 0.99 if self._detected_charset else 0.00 - - def feed(self, byte_str): - for c in byte_str: - for coding_sm in self.coding_sm: - if not coding_sm or 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/site-packages/pip/_vendor/chardet/escsm.py b/.venv/Lib/site-packages/pip/_vendor/chardet/escsm.py deleted file mode 100644 index 3aa0f4d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/escsm.py +++ /dev/null @@ -1,260 +0,0 @@ -######################## 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 .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 = { - "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 = { - "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 = { - "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 = { - "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/site-packages/pip/_vendor/chardet/eucjpprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py deleted file mode 100644 index abf2e66..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py +++ /dev/null @@ -1,95 +0,0 @@ -######################## 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 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): - super().__init__() - self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) - self.distribution_analyzer = EUCJPDistributionAnalysis() - self.context_analyzer = EUCJPContextAnalysis() - self.reset() - - def reset(self): - super().reset() - self.context_analyzer.reset() - - @property - def charset_name(self): - return "EUC-JP" - - @property - def language(self): - return "Japanese" - - def feed(self, byte_str): - 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): - 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/site-packages/pip/_vendor/chardet/euckrfreq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py deleted file mode 100644 index 7dc3b10..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/euckrfreq.py +++ /dev/null @@ -1,196 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/euckrprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py deleted file mode 100644 index 154a6d2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/euckrprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## 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): - super().__init__() - self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) - self.distribution_analyzer = EUCKRDistributionAnalysis() - self.reset() - - @property - def charset_name(self): - return "EUC-KR" - - @property - def language(self): - return "Korean" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py deleted file mode 100644 index 4900ccc..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/euctwfreq.py +++ /dev/null @@ -1,388 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/euctwprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py deleted file mode 100644 index ca10a23..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/euctwprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## 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): - super().__init__() - self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) - self.distribution_analyzer = EUCTWDistributionAnalysis() - self.reset() - - @property - def charset_name(self): - return "EUC-TW" - - @property - def language(self): - return "Taiwan" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py deleted file mode 100644 index b32bfc7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/gb2312freq.py +++ /dev/null @@ -1,284 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/gb2312prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py deleted file mode 100644 index 251c042..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## 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): - super().__init__() - self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) - self.distribution_analyzer = GB2312DistributionAnalysis() - self.reset() - - @property - def charset_name(self): - return "GB2312" - - @property - def language(self): - return "Chinese" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py deleted file mode 100644 index 3ca634b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py +++ /dev/null @@ -1,302 +0,0 @@ -######################## 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 .charsetprober import CharSetProber -from .enums import ProbingState - -# 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): - # 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): - super().__init__() - self._final_char_logical_score = None - self._final_char_visual_score = None - self._prev = None - self._before_prev = None - self._logical_prober = None - self._visual_prober = None - self.reset() - - def reset(self): - 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._before_prev = " " - # These probers are owned by the group prober. - - def set_model_probers(self, logical_prober, visual_prober): - self._logical_prober = logical_prober - self._visual_prober = visual_prober - - def is_final(self, c): - return c in [ - self.FINAL_KAF, - self.FINAL_MEM, - self.FINAL_NUN, - self.FINAL_PE, - self.FINAL_TSADI, - ] - - def is_non_final(self, c): - # 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): - # 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 == " ": - # We stand on a space - a word just ended - if self._before_prev != " ": - # 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 == " ") - and (self.is_final(self._prev)) - and (cur != " ") - ): - # 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): - # 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): - return "Hebrew" - - @property - def state(self): - # 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/site-packages/pip/_vendor/chardet/jisfreq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py deleted file mode 100644 index 3293576..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/jisfreq.py +++ /dev/null @@ -1,325 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/johabfreq.py b/.venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py deleted file mode 100644 index c129699..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/johabfreq.py +++ /dev/null @@ -1,2382 +0,0 @@ -######################## 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/site-packages/pip/_vendor/chardet/johabprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py deleted file mode 100644 index 6f359d1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/johabprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## 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): - super().__init__() - self.coding_sm = CodingStateMachine(JOHAB_SM_MODEL) - self.distribution_analyzer = JOHABDistributionAnalysis() - self.reset() - - @property - def charset_name(self): - return "Johab" - - @property - def language(self): - return "Korean" diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py b/.venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py deleted file mode 100644 index 7a8e5be..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/jpcntx.py +++ /dev/null @@ -1,237 +0,0 @@ -######################## 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 ######################### - - -# 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): - self._total_rel = None - self._rel_sample = None - self._need_to_skip_char_num = None - self._last_char_order = None - self._done = None - self.reset() - - def reset(self): - 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, num_bytes): - 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): - return self._total_rel > self.ENOUGH_REL_THRESHOLD - - def get_confidence(self): - # 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, _): - return -1, 1 - - -class SJISContextAnalysis(JapaneseContextAnalysis): - def __init__(self): - super().__init__() - self._charset_name = "SHIFT_JIS" - - @property - def charset_name(self): - return self._charset_name - - def get_order(self, byte_str): - 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): - 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/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py deleted file mode 100644 index 9946682..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langbulgarianmodel.py +++ /dev/null @@ -1,4649 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langgreekmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py deleted file mode 100644 index cfb8639..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langgreekmodel.py +++ /dev/null @@ -1,4397 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py deleted file mode 100644 index 56d2975..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langhebrewmodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py deleted file mode 100644 index 09a0d32..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langhungarianmodel.py +++ /dev/null @@ -1,4649 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langrussianmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py deleted file mode 100644 index 39a5388..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langrussianmodel.py +++ /dev/null @@ -1,5725 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langthaimodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py deleted file mode 100644 index 489cad9..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langthaimodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -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/site-packages/pip/_vendor/chardet/langturkishmodel.py b/.venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py deleted file mode 100644 index 291857c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/langturkishmodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -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/site-packages/pip/_vendor/chardet/latin1prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py deleted file mode 100644 index 241f14a..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/latin1prober.py +++ /dev/null @@ -1,145 +0,0 @@ -######################## 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 .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): - super().__init__() - self._last_char_class = None - self._freq_counter = None - self.reset() - - def reset(self): - self._last_char_class = OTH - self._freq_counter = [0] * FREQ_CAT_NUM - super().reset() - - @property - def charset_name(self): - return "ISO-8859-1" - - @property - def language(self): - return "" - - def feed(self, byte_str): - 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): - 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/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py deleted file mode 100644 index bf96ad5..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py +++ /dev/null @@ -1,95 +0,0 @@ -######################## 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 .charsetprober import CharSetProber -from .enums import MachineState, ProbingState - - -class MultiByteCharSetProber(CharSetProber): - """ - MultiByteCharSetProber - """ - - def __init__(self, lang_filter=None): - super().__init__(lang_filter=lang_filter) - self.distribution_analyzer = None - self.coding_sm = None - self._last_char = [0, 0] - - def reset(self): - super().reset() - if self.coding_sm: - self.coding_sm.reset() - if self.distribution_analyzer: - self.distribution_analyzer.reset() - self._last_char = [0, 0] - - @property - def charset_name(self): - raise NotImplementedError - - @property - def language(self): - raise NotImplementedError - - def feed(self, byte_str): - 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): - return self.distribution_analyzer.get_confidence() diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py deleted file mode 100644 index 9448836..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +++ /dev/null @@ -1,56 +0,0 @@ -######################## 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 .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=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/site-packages/pip/_vendor/chardet/mbcssm.py b/.venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py deleted file mode 100644 index d3b9c4b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/mbcssm.py +++ /dev/null @@ -1,660 +0,0 @@ -######################## 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 .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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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 = { - "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/site-packages/pip/_vendor/chardet/metadata/__init__.py b/.venv/Lib/site-packages/pip/_vendor/chardet/metadata/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py b/.venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py deleted file mode 100644 index 1d37884..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py +++ /dev/null @@ -1,351 +0,0 @@ -""" -Metadata about languages used by our model training code for our -SingleByteCharSetProbers. Could be used for other things in the future. - -This code is based on the language metadata from the uchardet project. -""" - -from string import ascii_letters - -# TODO: Add Ukrainian (KOI8-U) - - -class Language: - """Metadata about a language useful for training models - - :ivar name: The human name for the language, in English. - :type name: str - :ivar iso_code: 2-letter ISO 639-1 if possible, 3-letter ISO code otherwise, - or use another catalog as a last resort. - :type iso_code: str - :ivar use_ascii: Whether or not ASCII letters should be included in trained - models. - :type use_ascii: bool - :ivar charsets: The charsets we want to support and create data for. - :type charsets: list of str - :ivar alphabet: The characters in the language's alphabet. If `use_ascii` is - `True`, you only need to add those not in the ASCII set. - :type alphabet: str - :ivar wiki_start_pages: The Wikipedia pages to start from if we're crawling - Wikipedia for training data. - :type wiki_start_pages: list of str - """ - - def __init__( - self, - name=None, - iso_code=None, - use_ascii=True, - charsets=None, - alphabet=None, - wiki_start_pages=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): - 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"], - alphabet="æøåÆØÅ", - wiki_start_pages=["Forside"], - ), - "German": Language( - name="German", - iso_code="de", - use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252"], - 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"], - 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"], - 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"], - 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"], - 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"], - 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"], - 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"], - 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/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py deleted file mode 100644 index 31d70e1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py +++ /dev/null @@ -1,160 +0,0 @@ -######################## 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 collections import namedtuple - -from .charsetprober import CharSetProber -from .enums import CharacterCategory, ProbingState, SequenceLikelihood - -SingleByteCharSetModel = namedtuple( - "SingleByteCharSetModel", - [ - "charset_name", - "language", - "char_to_order_map", - "language_model", - "typical_positive_ratio", - "keep_ascii_letters", - "alphabet", - ], -) - - -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, is_reversed=False, name_prober=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 = None - self._seq_counters = None - self._total_seqs = None - self._total_char = None - self._control_char = None - self._freq_char = None - self.reset() - - def reset(self): - 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): - if self._name_prober: - return self._name_prober.charset_name - return self._model.charset_name - - @property - def language(self): - if self._name_prober: - return self._name_prober.language - return self._model.language - - def feed(self, byte_str): - # 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): - 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/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py deleted file mode 100644 index cad001c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +++ /dev/null @@ -1,88 +0,0 @@ -######################## 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): - 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/site-packages/pip/_vendor/chardet/sjisprober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py deleted file mode 100644 index 3bcbdb7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/sjisprober.py +++ /dev/null @@ -1,98 +0,0 @@ -######################## 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 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): - super().__init__() - self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) - self.distribution_analyzer = SJISDistributionAnalysis() - self.context_analyzer = SJISContextAnalysis() - self.reset() - - def reset(self): - super().reset() - self.context_analyzer.reset() - - @property - def charset_name(self): - return self.context_analyzer.charset_name - - @property - def language(self): - return "Japanese" - - def feed(self, byte_str): - 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): - 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/site-packages/pip/_vendor/chardet/universaldetector.py b/.venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py deleted file mode 100644 index 22fcf82..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/universaldetector.py +++ /dev/null @@ -1,328 +0,0 @@ -######################## 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 .charsetgroupprober import CharSetGroupProber -from .enums import InputState, LanguageFilter, ProbingState -from .escprober import EscCharSetProber -from .latin1prober import Latin1Prober -from .mbcsgroupprober import MBCSGroupProber -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", - } - - def __init__(self, lang_filter=LanguageFilter.ALL): - self._esc_charset_prober = None - self._utf1632_prober = None - self._charset_probers = [] - self.result = None - self.done = None - self._got_data = None - self._input_state = None - self._last_char = None - self.lang_filter = lang_filter - self.logger = logging.getLogger(__name__) - self._has_win_bytes = None - self.reset() - - @property - def input_state(self): - return self._input_state - - @property - def has_win_bytes(self): - return self._has_win_bytes - - @property - def charset_probers(self): - return self._charset_probers - - def reset(self): - """ - 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): - """ - 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 = { - "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 = { - "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()) - 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): - """ - 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 - lower_charset_name = max_prober.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 - ) - 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/site-packages/pip/_vendor/chardet/utf1632prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py deleted file mode 100644 index 9fd1580..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py +++ /dev/null @@ -1,223 +0,0 @@ -######################## 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 .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): - 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): - 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): - 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): - return "" - - def approx_32bit_chars(self): - return max(1.0, self.position / 4.0) - - def approx_16bit_chars(self): - return max(1.0, self.position / 2.0) - - def is_likely_utf32be(self): - 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): - 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): - 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): - 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): - """ - 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): - """ - 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): - 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): - 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): - 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/site-packages/pip/_vendor/chardet/utf8prober.py b/.venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py deleted file mode 100644 index 3aae09e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/utf8prober.py +++ /dev/null @@ -1,80 +0,0 @@ -######################## 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 .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): - super().__init__() - self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) - self._num_mb_chars = None - self.reset() - - def reset(self): - super().reset() - self.coding_sm.reset() - self._num_mb_chars = 0 - - @property - def charset_name(self): - return "utf-8" - - @property - def language(self): - return "" - - def feed(self, byte_str): - 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): - 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/site-packages/pip/_vendor/chardet/version.py b/.venv/Lib/site-packages/pip/_vendor/chardet/version.py deleted file mode 100644 index a08a06b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/chardet/version.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -This module exists only to simplify retrieving the version number of chardet -from within setup.py and from chardet subpackages. - -:author: Dan Blanchard (dan.blanchard@gmail.com) -""" - -__version__ = "5.0.0" -VERSION = __version__.split(".") diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/__init__.py b/.venv/Lib/site-packages/pip/_vendor/colorama/__init__.py deleted file mode 100644 index 9138a8c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from .initialise import init, deinit, reinit, colorama_text -from .ansi import Fore, Back, Style, Cursor -from .ansitowin32 import AnsiToWin32 - -__version__ = '0.4.5' diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/ansi.py b/.venv/Lib/site-packages/pip/_vendor/colorama/ansi.py deleted file mode 100644 index 11ec695..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/ansi.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -''' -This module generates ANSI character codes to printing colors to terminals. -See: http://en.wikipedia.org/wiki/ANSI_escape_code -''' - -CSI = '\033[' -OSC = '\033]' -BEL = '\a' - - -def code_to_chars(code): - return CSI + str(code) + 'm' - -def set_title(title): - return OSC + '2;' + title + BEL - -def clear_screen(mode=2): - return CSI + str(mode) + 'J' - -def clear_line(mode=2): - return CSI + str(mode) + 'K' - - -class AnsiCodes(object): - def __init__(self): - # the subclasses declare class attributes which are numbers. - # Upon instantiation we define instance attributes, which are the same - # as the class attributes but wrapped with the ANSI escape sequence - for name in dir(self): - if not name.startswith('_'): - value = getattr(self, name) - setattr(self, name, code_to_chars(value)) - - -class AnsiCursor(object): - def UP(self, n=1): - return CSI + str(n) + 'A' - def DOWN(self, n=1): - return CSI + str(n) + 'B' - def FORWARD(self, n=1): - return CSI + str(n) + 'C' - def BACK(self, n=1): - return CSI + str(n) + 'D' - def POS(self, x=1, y=1): - return CSI + str(y) + ';' + str(x) + 'H' - - -class AnsiFore(AnsiCodes): - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - RESET = 39 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 90 - LIGHTRED_EX = 91 - LIGHTGREEN_EX = 92 - LIGHTYELLOW_EX = 93 - LIGHTBLUE_EX = 94 - LIGHTMAGENTA_EX = 95 - LIGHTCYAN_EX = 96 - LIGHTWHITE_EX = 97 - - -class AnsiBack(AnsiCodes): - BLACK = 40 - RED = 41 - GREEN = 42 - YELLOW = 43 - BLUE = 44 - MAGENTA = 45 - CYAN = 46 - WHITE = 47 - RESET = 49 - - # These are fairly well supported, but not part of the standard. - LIGHTBLACK_EX = 100 - LIGHTRED_EX = 101 - LIGHTGREEN_EX = 102 - LIGHTYELLOW_EX = 103 - LIGHTBLUE_EX = 104 - LIGHTMAGENTA_EX = 105 - LIGHTCYAN_EX = 106 - LIGHTWHITE_EX = 107 - - -class AnsiStyle(AnsiCodes): - BRIGHT = 1 - DIM = 2 - NORMAL = 22 - RESET_ALL = 0 - -Fore = AnsiFore() -Back = AnsiBack() -Style = AnsiStyle() -Cursor = AnsiCursor() diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py b/.venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py deleted file mode 100644 index 3db248b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import re -import sys -import os - -from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL -from .winterm import WinTerm, WinColor, WinStyle -from .win32 import windll, winapi_test - - -winterm = None -if windll is not None: - winterm = WinTerm() - - -class StreamWrapper(object): - ''' - Wraps a stream (such as stdout), acting as a transparent proxy for all - attribute access apart from method 'write()', which is delegated to our - Converter instance. - ''' - def __init__(self, wrapped, converter): - # double-underscore everything to prevent clashes with names of - # attributes on the wrapped stream object. - self.__wrapped = wrapped - self.__convertor = converter - - def __getattr__(self, name): - return getattr(self.__wrapped, name) - - def __enter__(self, *args, **kwargs): - # special method lookup bypasses __getattr__/__getattribute__, see - # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit - # thus, contextlib magic methods are not proxied via __getattr__ - return self.__wrapped.__enter__(*args, **kwargs) - - def __exit__(self, *args, **kwargs): - return self.__wrapped.__exit__(*args, **kwargs) - - def __setstate__(self, state): - self.__dict__ = state - - def __getstate__(self): - return self.__dict__ - - def write(self, text): - self.__convertor.write(text) - - def isatty(self): - stream = self.__wrapped - if 'PYCHARM_HOSTED' in os.environ: - if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): - return True - try: - stream_isatty = stream.isatty - except AttributeError: - return False - else: - return stream_isatty() - - @property - def closed(self): - stream = self.__wrapped - try: - return stream.closed - # AttributeError in the case that the stream doesn't support being closed - # ValueError for the case that the stream has already been detached when atexit runs - except (AttributeError, ValueError): - return True - - -class AnsiToWin32(object): - ''' - Implements a 'write()' method which, on Windows, will strip ANSI character - sequences from the text, and if outputting to a tty, will convert them into - win32 function calls. - ''' - ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer - ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command - - def __init__(self, wrapped, convert=None, strip=None, autoreset=False): - # The wrapped stream (normally sys.stdout or sys.stderr) - self.wrapped = wrapped - - # should we reset colors to defaults after every .write() - self.autoreset = autoreset - - # create the proxy wrapping our output stream - self.stream = StreamWrapper(wrapped, self) - - on_windows = os.name == 'nt' - # We test if the WinAPI works, because even if we are on Windows - # we may be using a terminal that doesn't support the WinAPI - # (e.g. Cygwin Terminal). In this case it's up to the terminal - # to support the ANSI codes. - conversion_supported = on_windows and winapi_test() - - # should we strip ANSI sequences from our output? - if strip is None: - strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) - self.strip = strip - - # should we should convert ANSI sequences into win32 calls? - if convert is None: - convert = conversion_supported and not self.stream.closed and self.stream.isatty() - self.convert = convert - - # dict of ansi codes to win32 functions and parameters - self.win32_calls = self.get_win32_calls() - - # are we wrapping stderr? - self.on_stderr = self.wrapped is sys.stderr - - def should_wrap(self): - ''' - True if this class is actually needed. If false, then the output - stream will not be affected, nor will win32 calls be issued, so - wrapping stdout is not actually required. This will generally be - False on non-Windows platforms, unless optional functionality like - autoreset has been requested using kwargs to init() - ''' - return self.convert or self.strip or self.autoreset - - def get_win32_calls(self): - if self.convert and winterm: - return { - AnsiStyle.RESET_ALL: (winterm.reset_all, ), - AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), - AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), - AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), - AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), - AnsiFore.RED: (winterm.fore, WinColor.RED), - AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), - AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), - AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), - AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), - AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), - AnsiFore.WHITE: (winterm.fore, WinColor.GREY), - AnsiFore.RESET: (winterm.fore, ), - AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), - AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), - AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), - AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), - AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), - AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), - AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), - AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), - AnsiBack.BLACK: (winterm.back, WinColor.BLACK), - AnsiBack.RED: (winterm.back, WinColor.RED), - AnsiBack.GREEN: (winterm.back, WinColor.GREEN), - AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), - AnsiBack.BLUE: (winterm.back, WinColor.BLUE), - AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), - AnsiBack.CYAN: (winterm.back, WinColor.CYAN), - AnsiBack.WHITE: (winterm.back, WinColor.GREY), - AnsiBack.RESET: (winterm.back, ), - AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), - AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), - AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), - AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), - AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), - AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), - AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), - AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), - } - return dict() - - def write(self, text): - if self.strip or self.convert: - self.write_and_convert(text) - else: - self.wrapped.write(text) - self.wrapped.flush() - if self.autoreset: - self.reset_all() - - - def reset_all(self): - if self.convert: - self.call_win32('m', (0,)) - elif not self.strip and not self.stream.closed: - self.wrapped.write(Style.RESET_ALL) - - - def write_and_convert(self, text): - ''' - Write the given text to our wrapped stream, stripping any ANSI - sequences from the text, and optionally converting them into win32 - calls. - ''' - cursor = 0 - text = self.convert_osc(text) - for match in self.ANSI_CSI_RE.finditer(text): - start, end = match.span() - self.write_plain_text(text, cursor, start) - self.convert_ansi(*match.groups()) - cursor = end - self.write_plain_text(text, cursor, len(text)) - - - def write_plain_text(self, text, start, end): - if start < end: - self.wrapped.write(text[start:end]) - self.wrapped.flush() - - - def convert_ansi(self, paramstring, command): - if self.convert: - params = self.extract_params(command, paramstring) - self.call_win32(command, params) - - - def extract_params(self, command, paramstring): - if command in 'Hf': - params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) - while len(params) < 2: - # defaults: - params = params + (1,) - else: - params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) - if len(params) == 0: - # defaults: - if command in 'JKm': - params = (0,) - elif command in 'ABCD': - params = (1,) - - return params - - - def call_win32(self, command, params): - if command == 'm': - for param in params: - if param in self.win32_calls: - func_args = self.win32_calls[param] - func = func_args[0] - args = func_args[1:] - kwargs = dict(on_stderr=self.on_stderr) - func(*args, **kwargs) - elif command in 'J': - winterm.erase_screen(params[0], on_stderr=self.on_stderr) - elif command in 'K': - winterm.erase_line(params[0], on_stderr=self.on_stderr) - elif command in 'Hf': # cursor position - absolute - winterm.set_cursor_position(params, on_stderr=self.on_stderr) - elif command in 'ABCD': # cursor position - relative - n = params[0] - # A - up, B - down, C - forward, D - back - x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] - winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) - - - def convert_osc(self, text): - for match in self.ANSI_OSC_RE.finditer(text): - start, end = match.span() - text = text[:start] + text[end:] - paramstring, command = match.groups() - if command == BEL: - if paramstring.count(";") == 1: - params = paramstring.split(";") - # 0 - change title and icon (we will only change title) - # 1 - change icon (we don't support this) - # 2 - change title - if params[0] in '02': - winterm.set_title(params[1]) - return text diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/initialise.py b/.venv/Lib/site-packages/pip/_vendor/colorama/initialise.py deleted file mode 100644 index 430d066..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/initialise.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -import atexit -import contextlib -import sys - -from .ansitowin32 import AnsiToWin32 - - -orig_stdout = None -orig_stderr = None - -wrapped_stdout = None -wrapped_stderr = None - -atexit_done = False - - -def reset_all(): - if AnsiToWin32 is not None: # Issue #74: objects might become None at exit - AnsiToWin32(orig_stdout).reset_all() - - -def init(autoreset=False, convert=None, strip=None, wrap=True): - - if not wrap and any([autoreset, convert, strip]): - raise ValueError('wrap=False conflicts with any other arg=True') - - global wrapped_stdout, wrapped_stderr - global orig_stdout, orig_stderr - - orig_stdout = sys.stdout - orig_stderr = sys.stderr - - if sys.stdout is None: - wrapped_stdout = None - else: - sys.stdout = wrapped_stdout = \ - wrap_stream(orig_stdout, convert, strip, autoreset, wrap) - if sys.stderr is None: - wrapped_stderr = None - else: - sys.stderr = wrapped_stderr = \ - wrap_stream(orig_stderr, convert, strip, autoreset, wrap) - - global atexit_done - if not atexit_done: - atexit.register(reset_all) - atexit_done = True - - -def deinit(): - if orig_stdout is not None: - sys.stdout = orig_stdout - if orig_stderr is not None: - sys.stderr = orig_stderr - - -@contextlib.contextmanager -def colorama_text(*args, **kwargs): - init(*args, **kwargs) - try: - yield - finally: - deinit() - - -def reinit(): - if wrapped_stdout is not None: - sys.stdout = wrapped_stdout - if wrapped_stderr is not None: - sys.stderr = wrapped_stderr - - -def wrap_stream(stream, convert, strip, autoreset, wrap): - if wrap: - wrapper = AnsiToWin32(stream, - convert=convert, strip=strip, autoreset=autoreset) - if wrapper.should_wrap(): - stream = wrapper.stream - return stream diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/win32.py b/.venv/Lib/site-packages/pip/_vendor/colorama/win32.py deleted file mode 100644 index c2d8360..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/win32.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. - -# from winbase.h -STDOUT = -11 -STDERR = -12 - -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 - - 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) diff --git a/.venv/Lib/site-packages/pip/_vendor/colorama/winterm.py b/.venv/Lib/site-packages/pip/_vendor/colorama/winterm.py deleted file mode 100644 index 0fdb4ec..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/colorama/winterm.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -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) diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/__init__.py b/.venv/Lib/site-packages/pip/_vendor/distlib/__init__.py deleted file mode 100644 index 962173c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012-2022 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -import logging - -__version__ = '0.3.6' - -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/site-packages/pip/_vendor/distlib/compat.py b/.venv/Lib/site-packages/pip/_vendor/distlib/compat.py deleted file mode 100644 index 1fe3d22..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/compat.py +++ /dev/null @@ -1,1116 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013-2017 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -from __future__ import absolute_import - -import os -import re -import sys - -try: - import ssl -except ImportError: # pragma: no cover - ssl = None - -if sys.version_info[0] < 3: # pragma: no cover - from StringIO import StringIO - string_types = basestring, - text_type = unicode - from types import FileType as file_type - import __builtin__ as builtins - import ConfigParser as configparser - from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit - from urllib import (urlretrieve, quote as _quote, unquote, url2pathname, - pathname2url, ContentTooShortError, splittype) - - def quote(s): - if isinstance(s, unicode): - s = s.encode('utf-8') - return _quote(s) - - import urllib2 - from urllib2 import (Request, urlopen, URLError, HTTPError, - HTTPBasicAuthHandler, HTTPPasswordMgr, - HTTPHandler, HTTPRedirectHandler, - build_opener) - if ssl: - from urllib2 import HTTPSHandler - import httplib - import xmlrpclib - import Queue as queue - from HTMLParser import HTMLParser - import htmlentitydefs - raw_input = raw_input - from itertools import ifilter as filter - from itertools import ifilterfalse as filterfalse - - # Leaving this around for now, in case it needs resurrecting in some way - # _userprog = None - # def splituser(host): - # """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - # global _userprog - # if _userprog is None: - # import re - # _userprog = re.compile('^(.*)@(.*)$') - - # match = _userprog.match(host) - # if match: return match.group(1, 2) - # return None, host - -else: # pragma: no cover - from io import StringIO - string_types = str, - text_type = str - from io import TextIOWrapper as file_type - import builtins - import configparser - import shutil - from urllib.parse import (urlparse, urlunparse, urljoin, quote, - unquote, urlsplit, urlunsplit, splittype) - from urllib.request import (urlopen, urlretrieve, Request, url2pathname, - pathname2url, - HTTPBasicAuthHandler, HTTPPasswordMgr, - HTTPHandler, HTTPRedirectHandler, - build_opener) - if ssl: - from urllib.request import HTTPSHandler - from urllib.error import HTTPError, URLError, ContentTooShortError - import http.client as httplib - import urllib.request as urllib2 - import xmlrpc.client as xmlrpclib - import queue - from html.parser import HTMLParser - import html.entities as htmlentitydefs - raw_input = input - from itertools import filterfalse - filter = filter - - -try: - from ssl import match_hostname, CertificateError -except ImportError: # pragma: no cover - class CertificateError(ValueError): - pass - - - def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - parts = dn.split('.') - leftmost, remainder = parts[0], parts[1:] - - wildcards = leftmost.count('*') - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - return pat.match(hostname) - - - def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED") - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError("hostname %r " - "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise CertificateError("hostname %r " - "doesn't match %r" - % (hostname, dnsnames[0])) - else: - raise CertificateError("no appropriate commonName or " - "subjectAltName fields were found") - - -try: - from types import SimpleNamespace as Container -except ImportError: # pragma: no cover - class Container(object): - """ - A generic container for when multiple values need to be returned - """ - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - -try: - from shutil import which -except ImportError: # pragma: no cover - # Implementation from Python 3.3 - def which(cmd, mode=os.F_OK | os.X_OK, path=None): - """Given a command, mode, and a PATH string, return the path which - conforms to the given mode on the PATH, or None if there is no such - file. - - `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result - of os.environ.get("PATH"), or can be overridden with a custom search - path. - - """ - # Check that a given file can be accessed with the correct mode. - # Additionally check that `file` is not a directory, as on Windows - # directories pass the os.access check. - def _access_check(fn, mode): - return (os.path.exists(fn) and os.access(fn, mode) - and not os.path.isdir(fn)) - - # If we're given a path with a directory part, look it up directly rather - # than referring to PATH directories. This includes checking relative to the - # current directory, e.g. ./script - if os.path.dirname(cmd): - if _access_check(cmd, mode): - return cmd - return None - - if path is None: - path = os.environ.get("PATH", os.defpath) - if not path: - return None - path = path.split(os.pathsep) - - if sys.platform == "win32": - # The current directory takes precedence on Windows. - if not os.curdir in path: - path.insert(0, os.curdir) - - # PATHEXT is necessary to check on Windows. - pathext = os.environ.get("PATHEXT", "").split(os.pathsep) - # See if the given file matches any of the expected path extensions. - # This will allow us to short circuit when given "python.exe". - # If it does match, only test that one, otherwise we have to try - # others. - if any(cmd.lower().endswith(ext.lower()) for ext in pathext): - files = [cmd] - else: - files = [cmd + ext for ext in pathext] - else: - # On other platforms you don't have things like PATHEXT to tell you - # what file suffixes are executable, so just pass on cmd as-is. - files = [cmd] - - seen = set() - for dir in path: - normdir = os.path.normcase(dir) - if not normdir in seen: - seen.add(normdir) - for thefile in files: - name = os.path.join(dir, thefile) - if _access_check(name, mode): - return name - return None - - -# ZipFile is a context manager in 2.7, but not in 2.6 - -from zipfile import ZipFile as BaseZipFile - -if hasattr(BaseZipFile, '__enter__'): # pragma: no cover - ZipFile = BaseZipFile -else: # pragma: no cover - from zipfile import ZipExtFile as BaseZipExtFile - - class ZipExtFile(BaseZipExtFile): - def __init__(self, base): - self.__dict__.update(base.__dict__) - - def __enter__(self): - return self - - def __exit__(self, *exc_info): - self.close() - # return None, so if an exception occurred, it will propagate - - class ZipFile(BaseZipFile): - def __enter__(self): - return self - - def __exit__(self, *exc_info): - self.close() - # return None, so if an exception occurred, it will propagate - - def open(self, *args, **kwargs): - base = BaseZipFile.open(self, *args, **kwargs) - return ZipExtFile(base) - -try: - from platform import python_implementation -except ImportError: # pragma: no cover - def python_implementation(): - """Return a string identifying the Python implementation.""" - if 'PyPy' in sys.version: - return 'PyPy' - if os.name == 'java': - return 'Jython' - if sys.version.startswith('IronPython'): - return 'IronPython' - return 'CPython' - -import shutil -import sysconfig - -try: - callable = callable -except NameError: # pragma: no cover - from collections.abc import Callable - - def callable(obj): - return isinstance(obj, Callable) - - -try: - fsencode = os.fsencode - fsdecode = os.fsdecode -except AttributeError: # pragma: no cover - # Issue #99: on some systems (e.g. containerised), - # sys.getfilesystemencoding() returns None, and we need a real value, - # so fall back to utf-8. From the CPython 2.7 docs relating to Unix and - # sys.getfilesystemencoding(): the return value is "the user’s preference - # according to the result of nl_langinfo(CODESET), or None if the - # nl_langinfo(CODESET) failed." - _fsencoding = sys.getfilesystemencoding() or 'utf-8' - if _fsencoding == 'mbcs': - _fserrors = 'strict' - else: - _fserrors = 'surrogateescape' - - def fsencode(filename): - if isinstance(filename, bytes): - return filename - elif isinstance(filename, text_type): - return filename.encode(_fsencoding, _fserrors) - else: - raise TypeError("expect bytes or str, not %s" % - type(filename).__name__) - - def fsdecode(filename): - if isinstance(filename, text_type): - return filename - elif isinstance(filename, bytes): - return filename.decode(_fsencoding, _fserrors) - else: - raise TypeError("expect bytes or str, not %s" % - type(filename).__name__) - -try: - from tokenize import detect_encoding -except ImportError: # pragma: no cover - from codecs import BOM_UTF8, lookup - import re - - cookie_re = re.compile(r"coding[:=]\s*([-\w.]+)") - - def _get_normal_name(orig_enc): - """Imitates get_normal_name in tokenizer.c.""" - # Only care about the first 12 characters. - enc = orig_enc[:12].lower().replace("_", "-") - if enc == "utf-8" or enc.startswith("utf-8-"): - return "utf-8" - if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ - enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): - return "iso-8859-1" - return orig_enc - - def detect_encoding(readline): - """ - The detect_encoding() function is used to detect the encoding that should - be used to decode a Python source file. It requires one argument, readline, - in the same way as the tokenize() generator. - - It will call readline a maximum of twice, and return the encoding used - (as a string) and a list of any lines (left as bytes) it has read in. - - It detects the encoding from the presence of a utf-8 bom or an encoding - cookie as specified in pep-0263. If both a bom and a cookie are present, - but disagree, a SyntaxError will be raised. If the encoding cookie is an - invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, - 'utf-8-sig' is returned. - - If no encoding is specified, then the default of 'utf-8' will be returned. - """ - try: - filename = readline.__self__.name - except AttributeError: - filename = None - bom_found = False - encoding = None - default = 'utf-8' - def read_or_stop(): - try: - return readline() - except StopIteration: - return b'' - - def find_cookie(line): - try: - # Decode as UTF-8. Either the line is an encoding declaration, - # in which case it should be pure ASCII, or it must be UTF-8 - # per default encoding. - line_string = line.decode('utf-8') - except UnicodeDecodeError: - msg = "invalid or missing encoding declaration" - if filename is not None: - msg = '{} for {!r}'.format(msg, filename) - raise SyntaxError(msg) - - matches = cookie_re.findall(line_string) - if not matches: - return None - encoding = _get_normal_name(matches[0]) - try: - codec = lookup(encoding) - except LookupError: - # This behaviour mimics the Python interpreter - if filename is None: - msg = "unknown encoding: " + encoding - else: - msg = "unknown encoding for {!r}: {}".format(filename, - encoding) - raise SyntaxError(msg) - - if bom_found: - if codec.name != 'utf-8': - # This behaviour mimics the Python interpreter - if filename is None: - msg = 'encoding problem: utf-8' - else: - msg = 'encoding problem for {!r}: utf-8'.format(filename) - raise SyntaxError(msg) - encoding += '-sig' - return encoding - - first = read_or_stop() - if first.startswith(BOM_UTF8): - bom_found = True - first = first[3:] - default = 'utf-8-sig' - if not first: - return default, [] - - encoding = find_cookie(first) - if encoding: - return encoding, [first] - - second = read_or_stop() - if not second: - return default, [first] - - encoding = find_cookie(second) - if encoding: - return encoding, [first, second] - - return default, [first, second] - -# For converting & <-> & etc. -try: - from html import escape -except ImportError: - from cgi import escape -if sys.version_info[:2] < (3, 4): - unescape = HTMLParser().unescape -else: - from html import unescape - -try: - from collections import ChainMap -except ImportError: # pragma: no cover - from collections import MutableMapping - - try: - from reprlib import recursive_repr as _recursive_repr - except ImportError: - def _recursive_repr(fillvalue='...'): - ''' - Decorator to make a repr function return fillvalue for a recursive - call - ''' - - def decorating_function(user_function): - repr_running = set() - - def wrapper(self): - key = id(self), get_ident() - if key in repr_running: - return fillvalue - repr_running.add(key) - try: - result = user_function(self) - finally: - repr_running.discard(key) - return result - - # Can't use functools.wraps() here because of bootstrap issues - wrapper.__module__ = getattr(user_function, '__module__') - wrapper.__doc__ = getattr(user_function, '__doc__') - wrapper.__name__ = getattr(user_function, '__name__') - wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) - return wrapper - - return decorating_function - - class ChainMap(MutableMapping): - ''' A ChainMap groups multiple dicts (or other mappings) together - to create a single, updateable view. - - The underlying mappings are stored in a list. That list is public and can - accessed or updated using the *maps* attribute. There is no other state. - - Lookups search the underlying mappings successively until a key is found. - In contrast, writes, updates, and deletions only operate on the first - mapping. - - ''' - - def __init__(self, *maps): - '''Initialize a ChainMap by setting *maps* to the given mappings. - If no mappings are provided, a single empty dictionary is used. - - ''' - self.maps = list(maps) or [{}] # always at least one map - - def __missing__(self, key): - raise KeyError(key) - - def __getitem__(self, key): - for mapping in self.maps: - try: - return mapping[key] # can't use 'key in mapping' with defaultdict - except KeyError: - pass - return self.__missing__(key) # support subclasses that define __missing__ - - def get(self, key, default=None): - return self[key] if key in self else default - - def __len__(self): - return len(set().union(*self.maps)) # reuses stored hash values if possible - - def __iter__(self): - return iter(set().union(*self.maps)) - - def __contains__(self, key): - return any(key in m for m in self.maps) - - def __bool__(self): - return any(self.maps) - - @_recursive_repr() - def __repr__(self): - return '{0.__class__.__name__}({1})'.format( - self, ', '.join(map(repr, self.maps))) - - @classmethod - def fromkeys(cls, iterable, *args): - 'Create a ChainMap with a single dict created from the iterable.' - return cls(dict.fromkeys(iterable, *args)) - - def copy(self): - 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' - return self.__class__(self.maps[0].copy(), *self.maps[1:]) - - __copy__ = copy - - def new_child(self): # like Django's Context.push() - 'New ChainMap with a new dict followed by all previous maps.' - return self.__class__({}, *self.maps) - - @property - def parents(self): # like Django's Context.pop() - 'New ChainMap from maps[1:].' - return self.__class__(*self.maps[1:]) - - def __setitem__(self, key, value): - self.maps[0][key] = value - - def __delitem__(self, key): - try: - del self.maps[0][key] - except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) - - def popitem(self): - 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' - try: - return self.maps[0].popitem() - except KeyError: - raise KeyError('No keys found in the first mapping.') - - def pop(self, key, *args): - 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' - try: - return self.maps[0].pop(key, *args) - except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) - - def clear(self): - 'Clear maps[0], leaving maps[1:] intact.' - self.maps[0].clear() - -try: - from importlib.util import cache_from_source # Python >= 3.4 -except ImportError: # pragma: no cover - def cache_from_source(path, debug_override=None): - assert path.endswith('.py') - if debug_override is None: - debug_override = __debug__ - if debug_override: - suffix = 'c' - else: - suffix = 'o' - return path + suffix - -try: - from collections import OrderedDict -except ImportError: # pragma: no cover -## {{{ http://code.activestate.com/recipes/576693/ (r9) -# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. -# Passes Python2.7's test suite and incorporates all the latest updates. - try: - from thread import get_ident as _get_ident - except ImportError: - from dummy_thread import get_ident as _get_ident - - try: - from _abcoll import KeysView, ValuesView, ItemsView - except ImportError: - pass - - - class OrderedDict(dict): - 'Dictionary that remembers insertion order' - # An inherited dict maps keys to values. - # The inherited dict provides __getitem__, __len__, __contains__, and get. - # The remaining methods are order-aware. - # Big-O running times for all methods are the same as for regular dictionaries. - - # The internal self.__map dictionary maps keys to links in a doubly linked list. - # The circular doubly linked list starts and ends with a sentinel element. - # The sentinel element never gets deleted (this simplifies the algorithm). - # Each link is stored as a list of length three: [PREV, NEXT, KEY]. - - def __init__(self, *args, **kwds): - '''Initialize an ordered dictionary. Signature is the same as for - regular dictionaries, but keyword arguments are not recommended - because their insertion order is arbitrary. - - ''' - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__root - except AttributeError: - self.__root = root = [] # sentinel node - root[:] = [root, root, None] - self.__map = {} - self.__update(*args, **kwds) - - def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' - # Setting a new item creates a new link which goes at the end of the linked - # list, and the inherited dictionary is updated with the new key/value pair. - if key not in self: - root = self.__root - last = root[0] - last[1] = root[0] = self.__map[key] = [last, root, key] - dict_setitem(self, key, value) - - def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' - # Deleting an existing item uses self.__map to find the link which is - # then removed by updating the links in the predecessor and successor nodes. - dict_delitem(self, key) - link_prev, link_next, key = self.__map.pop(key) - link_prev[1] = link_next - link_next[0] = link_prev - - def __iter__(self): - 'od.__iter__() <==> iter(od)' - root = self.__root - curr = root[1] - while curr is not root: - yield curr[2] - curr = curr[1] - - def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' - root = self.__root - curr = root[0] - while curr is not root: - yield curr[2] - curr = curr[0] - - def clear(self): - 'od.clear() -> None. Remove all items from od.' - try: - for node in self.__map.itervalues(): - del node[:] - root = self.__root - root[:] = [root, root, None] - self.__map.clear() - except AttributeError: - pass - dict.clear(self) - - def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. - Pairs are returned in LIFO order if last is true or FIFO order if false. - - ''' - if not self: - raise KeyError('dictionary is empty') - root = self.__root - if last: - link = root[0] - link_prev = link[0] - link_prev[1] = root - root[0] = link_prev - else: - link = root[1] - link_next = link[1] - root[1] = link_next - link_next[0] = root - key = link[2] - del self.__map[key] - value = dict.pop(self, key) - return key, value - - # -- the following methods do not depend on the internal structure -- - - def keys(self): - 'od.keys() -> list of keys in od' - return list(self) - - def values(self): - 'od.values() -> list of values in od' - return [self[key] for key in self] - - def items(self): - 'od.items() -> list of (key, value) pairs in od' - return [(key, self[key]) for key in self] - - def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' - return iter(self) - - def itervalues(self): - 'od.itervalues -> an iterator over the values in od' - for k in self: - yield self[k] - - def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' - for k in self: - yield (k, self[k]) - - def update(*args, **kwds): - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. - - If E is a dict instance, does: for k in E: od[k] = E[k] - If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] - Or if E is an iterable of items, does: for k, v in E: od[k] = v - In either case, this is followed by: for k, v in F.items(): od[k] = v - - ''' - if len(args) > 2: - raise TypeError('update() takes at most 2 positional ' - 'arguments (%d given)' % (len(args),)) - elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') - self = args[0] - # Make progressively weaker assumptions about "other" - other = () - if len(args) == 2: - other = args[1] - if isinstance(other, dict): - for key in other: - self[key] = other[key] - elif hasattr(other, 'keys'): - for key in other.keys(): - self[key] = other[key] - else: - for key, value in other: - self[key] = value - for key, value in kwds.items(): - self[key] = value - - __update = update # let subclasses override update without breaking __init__ - - __marker = object() - - def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. - If key is not found, d is returned if given, otherwise KeyError is raised. - - ''' - if key in self: - result = self[key] - del self[key] - return result - if default is self.__marker: - raise KeyError(key) - return default - - def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' - if key in self: - return self[key] - self[key] = default - return default - - def __repr__(self, _repr_running=None): - 'od.__repr__() <==> repr(od)' - if not _repr_running: _repr_running = {} - call_key = id(self), _get_ident() - if call_key in _repr_running: - return '...' - _repr_running[call_key] = 1 - try: - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - finally: - del _repr_running[call_key] - - def __reduce__(self): - 'Return state information for pickling' - items = [[k, self[k]] for k in self] - inst_dict = vars(self).copy() - for k in vars(OrderedDict()): - inst_dict.pop(k, None) - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def copy(self): - 'od.copy() -> a shallow copy of od' - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S - and values equal to v (which defaults to None). - - ''' - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - if isinstance(other, OrderedDict): - return len(self)==len(other) and self.items() == other.items() - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other - - # -- the following methods are only used in Python 2.7 -- - - def viewkeys(self): - "od.viewkeys() -> a set-like object providing a view on od's keys" - return KeysView(self) - - def viewvalues(self): - "od.viewvalues() -> an object providing a view on od's values" - return ValuesView(self) - - def viewitems(self): - "od.viewitems() -> a set-like object providing a view on od's items" - return ItemsView(self) - -try: - from logging.config import BaseConfigurator, valid_ident -except ImportError: # pragma: no cover - IDENTIFIER = re.compile('^[a-z_][a-z0-9_]*$', re.I) - - - def valid_ident(s): - m = IDENTIFIER.match(s) - if not m: - raise ValueError('Not a valid Python identifier: %r' % s) - return True - - - # The ConvertingXXX classes are wrappers around standard Python containers, - # and they serve to convert any suitable values in the container. The - # conversion converts base dicts, lists and tuples to their wrapped - # equivalents, whereas strings which match a conversion format are converted - # appropriately. - # - # Each wrapper should have a configurator attribute holding the actual - # configurator to use for conversion. - - class ConvertingDict(dict): - """A converting dictionary wrapper.""" - - def __getitem__(self, key): - value = dict.__getitem__(self, key) - result = self.configurator.convert(value) - #If the converted value is different, save for next time - if value is not result: - self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - result.key = key - return result - - def get(self, key, default=None): - value = dict.get(self, key, default) - result = self.configurator.convert(value) - #If the converted value is different, save for next time - if value is not result: - self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - result.key = key - return result - - def pop(self, key, default=None): - value = dict.pop(self, key, default) - result = self.configurator.convert(value) - if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - result.key = key - return result - - class ConvertingList(list): - """A converting list wrapper.""" - def __getitem__(self, key): - value = list.__getitem__(self, key) - result = self.configurator.convert(value) - #If the converted value is different, save for next time - if value is not result: - self[key] = result - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - result.key = key - return result - - def pop(self, idx=-1): - value = list.pop(self, idx) - result = self.configurator.convert(value) - if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - return result - - class ConvertingTuple(tuple): - """A converting tuple wrapper.""" - def __getitem__(self, key): - value = tuple.__getitem__(self, key) - result = self.configurator.convert(value) - if value is not result: - if type(result) in (ConvertingDict, ConvertingList, - ConvertingTuple): - result.parent = self - result.key = key - return result - - class BaseConfigurator(object): - """ - The configurator base class which defines some useful defaults. - """ - - CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$') - - WORD_PATTERN = re.compile(r'^\s*(\w+)\s*') - DOT_PATTERN = re.compile(r'^\.\s*(\w+)\s*') - INDEX_PATTERN = re.compile(r'^\[\s*(\w+)\s*\]\s*') - DIGIT_PATTERN = re.compile(r'^\d+$') - - value_converters = { - 'ext' : 'ext_convert', - 'cfg' : 'cfg_convert', - } - - # We might want to use a different one, e.g. importlib - importer = staticmethod(__import__) - - def __init__(self, config): - self.config = ConvertingDict(config) - self.config.configurator = self - - def resolve(self, s): - """ - Resolve strings to objects using standard import and attribute - syntax. - """ - name = s.split('.') - used = name.pop(0) - try: - found = self.importer(used) - for frag in name: - used += '.' + frag - try: - found = getattr(found, frag) - except AttributeError: - self.importer(used) - found = getattr(found, frag) - return found - except ImportError: - e, tb = sys.exc_info()[1:] - v = ValueError('Cannot resolve %r: %s' % (s, e)) - v.__cause__, v.__traceback__ = e, tb - raise v - - def ext_convert(self, value): - """Default converter for the ext:// protocol.""" - return self.resolve(value) - - def cfg_convert(self, value): - """Default converter for the cfg:// protocol.""" - rest = value - m = self.WORD_PATTERN.match(rest) - if m is None: - raise ValueError("Unable to convert %r" % value) - else: - rest = rest[m.end():] - d = self.config[m.groups()[0]] - #print d, rest - while rest: - m = self.DOT_PATTERN.match(rest) - if m: - d = d[m.groups()[0]] - else: - m = self.INDEX_PATTERN.match(rest) - if m: - idx = m.groups()[0] - if not self.DIGIT_PATTERN.match(idx): - d = d[idx] - else: - try: - n = int(idx) # try as number first (most likely) - d = d[n] - except TypeError: - d = d[idx] - if m: - rest = rest[m.end():] - else: - raise ValueError('Unable to convert ' - '%r at %r' % (value, rest)) - #rest should be empty - return d - - def convert(self, value): - """ - Convert values to an appropriate type. dicts, lists and tuples are - replaced by their converting alternatives. Strings are checked to - see if they have a conversion format and are converted if they do. - """ - if not isinstance(value, ConvertingDict) and isinstance(value, dict): - value = ConvertingDict(value) - value.configurator = self - elif not isinstance(value, ConvertingList) and isinstance(value, list): - value = ConvertingList(value) - value.configurator = self - elif not isinstance(value, ConvertingTuple) and\ - isinstance(value, tuple): - value = ConvertingTuple(value) - value.configurator = self - elif isinstance(value, string_types): - m = self.CONVERT_PATTERN.match(value) - if m: - d = m.groupdict() - prefix = d['prefix'] - converter = self.value_converters.get(prefix, None) - if converter: - suffix = d['suffix'] - converter = getattr(self, converter) - value = converter(suffix) - return value - - def configure_custom(self, config): - """Configure an object with a user-supplied factory.""" - c = config.pop('()') - if not callable(c): - c = self.resolve(c) - props = config.pop('.', None) - # Check for valid identifiers - kwargs = dict([(k, config[k]) for k in config if valid_ident(k)]) - result = c(**kwargs) - if props: - for name, value in props.items(): - setattr(result, name, value) - return result - - def as_tuple(self, value): - """Utility function which converts lists to tuples.""" - if isinstance(value, list): - value = tuple(value) - return value diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/database.py b/.venv/Lib/site-packages/pip/_vendor/distlib/database.py deleted file mode 100644 index 5db5d7f..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/database.py +++ /dev/null @@ -1,1350 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012-2017 The Python Software Foundation. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -"""PEP 376 implementation.""" - -from __future__ import unicode_literals - -import base64 -import codecs -import contextlib -import hashlib -import logging -import os -import posixpath -import sys -import zipimport - -from . import DistlibException, resources -from .compat import StringIO -from .version import get_scheme, UnsupportedVersionError -from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME) -from .util import (parse_requirement, cached_property, parse_name_and_version, - read_exports, write_exports, CSVReader, CSVWriter) - - -__all__ = ['Distribution', 'BaseInstalledDistribution', - 'InstalledDistribution', 'EggInfoDistribution', - 'DistributionPath'] - - -logger = logging.getLogger(__name__) - -EXPORTS_FILENAME = 'pydist-exports.json' -COMMANDS_FILENAME = 'pydist-commands.json' - -DIST_FILES = ('INSTALLER', METADATA_FILENAME, 'RECORD', 'REQUESTED', - 'RESOURCES', EXPORTS_FILENAME, 'SHARED') - -DISTINFO_EXT = '.dist-info' - - -class _Cache(object): - """ - A simple cache mapping names and .dist-info paths to distributions - """ - def __init__(self): - """ - Initialise an instance. There is normally one for each DistributionPath. - """ - self.name = {} - self.path = {} - self.generated = False - - def clear(self): - """ - Clear the cache, setting it to its initial state. - """ - self.name.clear() - self.path.clear() - self.generated = False - - def add(self, dist): - """ - Add a distribution to the cache. - :param dist: The distribution to add. - """ - if dist.path not in self.path: - self.path[dist.path] = dist - self.name.setdefault(dist.key, []).append(dist) - - -class DistributionPath(object): - """ - Represents a set of distributions installed on a path (typically sys.path). - """ - def __init__(self, path=None, include_egg=False): - """ - Create an instance from a path, optionally including legacy (distutils/ - setuptools/distribute) distributions. - :param path: The path to use, as a list of directories. If not specified, - sys.path is used. - :param include_egg: If True, this instance will look for and return legacy - distributions as well as those based on PEP 376. - """ - if path is None: - path = sys.path - self.path = path - self._include_dist = True - self._include_egg = include_egg - - self._cache = _Cache() - self._cache_egg = _Cache() - self._cache_enabled = True - self._scheme = get_scheme('default') - - def _get_cache_enabled(self): - return self._cache_enabled - - def _set_cache_enabled(self, value): - self._cache_enabled = value - - cache_enabled = property(_get_cache_enabled, _set_cache_enabled) - - def clear_cache(self): - """ - Clears the internal cache. - """ - self._cache.clear() - self._cache_egg.clear() - - - def _yield_distributions(self): - """ - Yield .dist-info and/or .egg(-info) distributions. - """ - # We need to check if we've seen some resources already, because on - # some Linux systems (e.g. some Debian/Ubuntu variants) there are - # symlinks which alias other files in the environment. - seen = set() - for path in self.path: - finder = resources.finder_for_path(path) - if finder is None: - continue - r = finder.find('') - if not r or not r.is_container: - continue - rset = sorted(r.resources) - for entry in rset: - r = finder.find(entry) - if not r or r.path in seen: - continue - try: - if self._include_dist and entry.endswith(DISTINFO_EXT): - possible_filenames = [METADATA_FILENAME, - WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME] - for metadata_filename in possible_filenames: - metadata_path = posixpath.join(entry, metadata_filename) - pydist = finder.find(metadata_path) - if pydist: - break - else: - continue - - with contextlib.closing(pydist.as_stream()) as stream: - metadata = Metadata(fileobj=stream, scheme='legacy') - logger.debug('Found %s', r.path) - seen.add(r.path) - yield new_dist_class(r.path, metadata=metadata, - env=self) - elif self._include_egg and entry.endswith(('.egg-info', - '.egg')): - logger.debug('Found %s', r.path) - seen.add(r.path) - yield old_dist_class(r.path, self) - except Exception as e: - msg = 'Unable to read distribution at %s, perhaps due to bad metadata: %s' - logger.warning(msg, r.path, e) - import warnings - warnings.warn(msg % (r.path, e), stacklevel=2) - - def _generate_cache(self): - """ - Scan the path for distributions and populate the cache with - those that are found. - """ - gen_dist = not self._cache.generated - gen_egg = self._include_egg and not self._cache_egg.generated - if gen_dist or gen_egg: - for dist in self._yield_distributions(): - if isinstance(dist, InstalledDistribution): - self._cache.add(dist) - else: - self._cache_egg.add(dist) - - if gen_dist: - self._cache.generated = True - if gen_egg: - self._cache_egg.generated = True - - @classmethod - def distinfo_dirname(cls, name, version): - """ - The *name* and *version* parameters are converted into their - filename-escaped form, i.e. any ``'-'`` characters are replaced - with ``'_'`` other than the one in ``'dist-info'`` and the one - separating the name from the version number. - - :parameter name: is converted to a standard distribution name by replacing - any runs of non- alphanumeric characters with a single - ``'-'``. - :type name: string - :parameter version: is converted to a standard version string. Spaces - become dots, and all other non-alphanumeric characters - (except dots) become dashes, with runs of multiple - dashes condensed to a single dash. - :type version: string - :returns: directory name - :rtype: string""" - name = name.replace('-', '_') - return '-'.join([name, version]) + DISTINFO_EXT - - def get_distributions(self): - """ - Provides an iterator that looks for distributions and returns - :class:`InstalledDistribution` or - :class:`EggInfoDistribution` instances for each one of them. - - :rtype: iterator of :class:`InstalledDistribution` and - :class:`EggInfoDistribution` instances - """ - if not self._cache_enabled: - for dist in self._yield_distributions(): - yield dist - else: - self._generate_cache() - - for dist in self._cache.path.values(): - yield dist - - if self._include_egg: - for dist in self._cache_egg.path.values(): - yield dist - - def get_distribution(self, name): - """ - Looks for a named distribution on the path. - - This function only returns the first result found, as no more than one - value is expected. If nothing is found, ``None`` is returned. - - :rtype: :class:`InstalledDistribution`, :class:`EggInfoDistribution` - or ``None`` - """ - result = None - name = name.lower() - if not self._cache_enabled: - for dist in self._yield_distributions(): - if dist.key == name: - result = dist - break - else: - self._generate_cache() - - if name in self._cache.name: - result = self._cache.name[name][0] - elif self._include_egg and name in self._cache_egg.name: - result = self._cache_egg.name[name][0] - return result - - def provides_distribution(self, name, version=None): - """ - Iterates over all distributions to find which distributions provide *name*. - If a *version* is provided, it will be used to filter the results. - - This function only returns the first result found, since no more than - one values are expected. If the directory is not found, returns ``None``. - - :parameter version: a version specifier that indicates the version - required, conforming to the format in ``PEP-345`` - - :type name: string - :type version: string - """ - matcher = None - if version is not None: - try: - matcher = self._scheme.matcher('%s (%s)' % (name, version)) - except ValueError: - raise DistlibException('invalid name or version: %r, %r' % - (name, version)) - - for dist in self.get_distributions(): - # We hit a problem on Travis where enum34 was installed and doesn't - # have a provides attribute ... - if not hasattr(dist, 'provides'): - logger.debug('No "provides": %s', dist) - else: - provided = dist.provides - - for p in provided: - p_name, p_ver = parse_name_and_version(p) - if matcher is None: - if p_name == name: - yield dist - break - else: - if p_name == name and matcher.match(p_ver): - yield dist - break - - def get_file_path(self, name, relative_path): - """ - Return the path to a resource file. - """ - dist = self.get_distribution(name) - if dist is None: - raise LookupError('no distribution named %r found' % name) - return dist.get_resource_path(relative_path) - - def get_exported_entries(self, category, name=None): - """ - Return all of the exported entries in a particular category. - - :param category: The category to search for entries. - :param name: If specified, only entries with that name are returned. - """ - for dist in self.get_distributions(): - r = dist.exports - if category in r: - d = r[category] - if name is not None: - if name in d: - yield d[name] - else: - for v in d.values(): - yield v - - -class Distribution(object): - """ - A base class for distributions, whether installed or from indexes. - Either way, it must have some metadata, so that's all that's needed - for construction. - """ - - build_time_dependency = False - """ - Set to True if it's known to be only a build-time dependency (i.e. - not needed after installation). - """ - - requested = False - """A boolean that indicates whether the ``REQUESTED`` metadata file is - present (in other words, whether the package was installed by user - request or it was installed as a dependency).""" - - def __init__(self, metadata): - """ - Initialise an instance. - :param metadata: The instance of :class:`Metadata` describing this - distribution. - """ - self.metadata = metadata - self.name = metadata.name - self.key = self.name.lower() # for case-insensitive comparisons - self.version = metadata.version - self.locator = None - self.digest = None - self.extras = None # additional features requested - self.context = None # environment marker overrides - self.download_urls = set() - self.digests = {} - - @property - def source_url(self): - """ - The source archive download URL for this distribution. - """ - return self.metadata.source_url - - download_url = source_url # Backward compatibility - - @property - def name_and_version(self): - """ - A utility property which displays the name and version in parentheses. - """ - return '%s (%s)' % (self.name, self.version) - - @property - def provides(self): - """ - A set of distribution names and versions provided by this distribution. - :return: A set of "name (version)" strings. - """ - plist = self.metadata.provides - s = '%s (%s)' % (self.name, self.version) - if s not in plist: - plist.append(s) - return plist - - def _get_requirements(self, req_attr): - md = self.metadata - reqts = getattr(md, req_attr) - logger.debug('%s: got requirements %r from metadata: %r', self.name, req_attr, - reqts) - return set(md.get_requirements(reqts, extras=self.extras, - env=self.context)) - - @property - def run_requires(self): - return self._get_requirements('run_requires') - - @property - def meta_requires(self): - return self._get_requirements('meta_requires') - - @property - def build_requires(self): - return self._get_requirements('build_requires') - - @property - def test_requires(self): - return self._get_requirements('test_requires') - - @property - def dev_requires(self): - return self._get_requirements('dev_requires') - - def matches_requirement(self, req): - """ - Say if this instance matches (fulfills) a requirement. - :param req: The requirement to match. - :rtype req: str - :return: True if it matches, else False. - """ - # Requirement may contain extras - parse to lose those - # from what's passed to the matcher - r = parse_requirement(req) - scheme = get_scheme(self.metadata.scheme) - try: - matcher = scheme.matcher(r.requirement) - except UnsupportedVersionError: - # XXX compat-mode if cannot read the version - logger.warning('could not read version %r - using name only', - req) - name = req.split()[0] - matcher = scheme.matcher(name) - - name = matcher.key # case-insensitive - - result = False - for p in self.provides: - p_name, p_ver = parse_name_and_version(p) - if p_name != name: - continue - try: - result = matcher.match(p_ver) - break - except UnsupportedVersionError: - pass - return result - - def __repr__(self): - """ - Return a textual representation of this instance, - """ - if self.source_url: - suffix = ' [%s]' % self.source_url - else: - suffix = '' - return '' % (self.name, self.version, suffix) - - def __eq__(self, other): - """ - See if this distribution is the same as another. - :param other: The distribution to compare with. To be equal to one - another. distributions must have the same type, name, - version and source_url. - :return: True if it is the same, else False. - """ - if type(other) is not type(self): - result = False - else: - result = (self.name == other.name and - self.version == other.version and - self.source_url == other.source_url) - return result - - def __hash__(self): - """ - Compute hash in a way which matches the equality test. - """ - return hash(self.name) + hash(self.version) + hash(self.source_url) - - -class BaseInstalledDistribution(Distribution): - """ - This is the base class for installed distributions (whether PEP 376 or - legacy). - """ - - hasher = None - - def __init__(self, metadata, path, env=None): - """ - Initialise an instance. - :param metadata: An instance of :class:`Metadata` which describes the - distribution. This will normally have been initialised - from a metadata file in the ``path``. - :param path: The path of the ``.dist-info`` or ``.egg-info`` - directory for the distribution. - :param env: This is normally the :class:`DistributionPath` - instance where this distribution was found. - """ - super(BaseInstalledDistribution, self).__init__(metadata) - self.path = path - self.dist_path = env - - def get_hash(self, data, hasher=None): - """ - Get the hash of some data, using a particular hash algorithm, if - specified. - - :param data: The data to be hashed. - :type data: bytes - :param hasher: The name of a hash implementation, supported by hashlib, - or ``None``. Examples of valid values are ``'sha1'``, - ``'sha224'``, ``'sha384'``, '``sha256'``, ``'md5'`` and - ``'sha512'``. If no hasher is specified, the ``hasher`` - attribute of the :class:`InstalledDistribution` instance - is used. If the hasher is determined to be ``None``, MD5 - is used as the hashing algorithm. - :returns: The hash of the data. If a hasher was explicitly specified, - the returned hash will be prefixed with the specified hasher - followed by '='. - :rtype: str - """ - if hasher is None: - hasher = self.hasher - if hasher is None: - hasher = hashlib.md5 - prefix = '' - else: - hasher = getattr(hashlib, hasher) - prefix = '%s=' % self.hasher - digest = hasher(data).digest() - digest = base64.urlsafe_b64encode(digest).rstrip(b'=').decode('ascii') - return '%s%s' % (prefix, digest) - - -class InstalledDistribution(BaseInstalledDistribution): - """ - Created with the *path* of the ``.dist-info`` directory provided to the - constructor. It reads the metadata contained in ``pydist.json`` when it is - instantiated., or uses a passed in Metadata instance (useful for when - dry-run mode is being used). - """ - - hasher = 'sha256' - - def __init__(self, path, metadata=None, env=None): - self.modules = [] - self.finder = finder = resources.finder_for_path(path) - if finder is None: - raise ValueError('finder unavailable for %s' % path) - if env and env._cache_enabled and path in env._cache.path: - metadata = env._cache.path[path].metadata - elif metadata is None: - r = finder.find(METADATA_FILENAME) - # Temporary - for Wheel 0.23 support - if r is None: - r = finder.find(WHEEL_METADATA_FILENAME) - # Temporary - for legacy support - if r is None: - r = finder.find(LEGACY_METADATA_FILENAME) - if r is None: - raise ValueError('no %s found in %s' % (METADATA_FILENAME, - path)) - with contextlib.closing(r.as_stream()) as stream: - metadata = Metadata(fileobj=stream, scheme='legacy') - - super(InstalledDistribution, self).__init__(metadata, path, env) - - if env and env._cache_enabled: - env._cache.add(self) - - r = finder.find('REQUESTED') - self.requested = r is not None - p = os.path.join(path, 'top_level.txt') - if os.path.exists(p): - with open(p, 'rb') as f: - data = f.read().decode('utf-8') - self.modules = data.splitlines() - - def __repr__(self): - return '' % ( - self.name, self.version, self.path) - - def __str__(self): - return "%s %s" % (self.name, self.version) - - def _get_records(self): - """ - Get the list of installed files for the distribution - :return: A list of tuples of path, hash and size. Note that hash and - size might be ``None`` for some entries. The path is exactly - as stored in the file (which is as in PEP 376). - """ - results = [] - r = self.get_distinfo_resource('RECORD') - with contextlib.closing(r.as_stream()) as stream: - with CSVReader(stream=stream) as record_reader: - # Base location is parent dir of .dist-info dir - #base_location = os.path.dirname(self.path) - #base_location = os.path.abspath(base_location) - for row in record_reader: - missing = [None for i in range(len(row), 3)] - path, checksum, size = row + missing - #if not os.path.isabs(path): - # path = path.replace('/', os.sep) - # path = os.path.join(base_location, path) - results.append((path, checksum, size)) - return results - - @cached_property - def exports(self): - """ - Return the information exported by this distribution. - :return: A dictionary of exports, mapping an export category to a dict - of :class:`ExportEntry` instances describing the individual - export entries, and keyed by name. - """ - result = {} - r = self.get_distinfo_resource(EXPORTS_FILENAME) - if r: - result = self.read_exports() - return result - - def read_exports(self): - """ - Read exports data from a file in .ini format. - - :return: A dictionary of exports, mapping an export category to a list - of :class:`ExportEntry` instances describing the individual - export entries. - """ - result = {} - r = self.get_distinfo_resource(EXPORTS_FILENAME) - if r: - with contextlib.closing(r.as_stream()) as stream: - result = read_exports(stream) - return result - - def write_exports(self, exports): - """ - Write a dictionary of exports to a file in .ini format. - :param exports: A dictionary of exports, mapping an export category to - a list of :class:`ExportEntry` instances describing the - individual export entries. - """ - rf = self.get_distinfo_file(EXPORTS_FILENAME) - with open(rf, 'w') as f: - write_exports(exports, f) - - def get_resource_path(self, relative_path): - """ - NOTE: This API may change in the future. - - Return the absolute path to a resource file with the given relative - path. - - :param relative_path: The path, relative to .dist-info, of the resource - of interest. - :return: The absolute path where the resource is to be found. - """ - r = self.get_distinfo_resource('RESOURCES') - with contextlib.closing(r.as_stream()) as stream: - with CSVReader(stream=stream) as resources_reader: - for relative, destination in resources_reader: - if relative == relative_path: - return destination - raise KeyError('no resource file with relative path %r ' - 'is installed' % relative_path) - - def list_installed_files(self): - """ - Iterates over the ``RECORD`` entries and returns a tuple - ``(path, hash, size)`` for each line. - - :returns: iterator of (path, hash, size) - """ - for result in self._get_records(): - yield result - - def write_installed_files(self, paths, prefix, dry_run=False): - """ - Writes the ``RECORD`` file, using the ``paths`` iterable passed in. Any - existing ``RECORD`` file is silently overwritten. - - prefix is used to determine when to write absolute paths. - """ - prefix = os.path.join(prefix, '') - base = os.path.dirname(self.path) - base_under_prefix = base.startswith(prefix) - base = os.path.join(base, '') - record_path = self.get_distinfo_file('RECORD') - logger.info('creating %s', record_path) - if dry_run: - return None - with CSVWriter(record_path) as writer: - for path in paths: - if os.path.isdir(path) or path.endswith(('.pyc', '.pyo')): - # do not put size and hash, as in PEP-376 - hash_value = size = '' - else: - size = '%d' % os.path.getsize(path) - with open(path, 'rb') as fp: - hash_value = self.get_hash(fp.read()) - if path.startswith(base) or (base_under_prefix and - path.startswith(prefix)): - path = os.path.relpath(path, base) - writer.writerow((path, hash_value, size)) - - # add the RECORD file itself - if record_path.startswith(base): - record_path = os.path.relpath(record_path, base) - writer.writerow((record_path, '', '')) - return record_path - - def check_installed_files(self): - """ - Checks that the hashes and sizes of the files in ``RECORD`` are - matched by the files themselves. Returns a (possibly empty) list of - mismatches. Each entry in the mismatch list will be a tuple consisting - of the path, 'exists', 'size' or 'hash' according to what didn't match - (existence is checked first, then size, then hash), the expected - value and the actual value. - """ - mismatches = [] - base = os.path.dirname(self.path) - record_path = self.get_distinfo_file('RECORD') - for path, hash_value, size in self.list_installed_files(): - if not os.path.isabs(path): - path = os.path.join(base, path) - if path == record_path: - continue - if not os.path.exists(path): - mismatches.append((path, 'exists', True, False)) - elif os.path.isfile(path): - actual_size = str(os.path.getsize(path)) - if size and actual_size != size: - mismatches.append((path, 'size', size, actual_size)) - elif hash_value: - if '=' in hash_value: - hasher = hash_value.split('=', 1)[0] - else: - hasher = None - - with open(path, 'rb') as f: - actual_hash = self.get_hash(f.read(), hasher) - if actual_hash != hash_value: - mismatches.append((path, 'hash', hash_value, actual_hash)) - return mismatches - - @cached_property - def shared_locations(self): - """ - A dictionary of shared locations whose keys are in the set 'prefix', - 'purelib', 'platlib', 'scripts', 'headers', 'data' and 'namespace'. - The corresponding value is the absolute path of that category for - this distribution, and takes into account any paths selected by the - user at installation time (e.g. via command-line arguments). In the - case of the 'namespace' key, this would be a list of absolute paths - for the roots of namespace packages in this distribution. - - The first time this property is accessed, the relevant information is - read from the SHARED file in the .dist-info directory. - """ - result = {} - shared_path = os.path.join(self.path, 'SHARED') - if os.path.isfile(shared_path): - with codecs.open(shared_path, 'r', encoding='utf-8') as f: - lines = f.read().splitlines() - for line in lines: - key, value = line.split('=', 1) - if key == 'namespace': - result.setdefault(key, []).append(value) - else: - result[key] = value - return result - - def write_shared_locations(self, paths, dry_run=False): - """ - Write shared location information to the SHARED file in .dist-info. - :param paths: A dictionary as described in the documentation for - :meth:`shared_locations`. - :param dry_run: If True, the action is logged but no file is actually - written. - :return: The path of the file written to. - """ - shared_path = os.path.join(self.path, 'SHARED') - logger.info('creating %s', shared_path) - if dry_run: - return None - lines = [] - for key in ('prefix', 'lib', 'headers', 'scripts', 'data'): - path = paths[key] - if os.path.isdir(paths[key]): - lines.append('%s=%s' % (key, path)) - for ns in paths.get('namespace', ()): - lines.append('namespace=%s' % ns) - - with codecs.open(shared_path, 'w', encoding='utf-8') as f: - f.write('\n'.join(lines)) - return shared_path - - def get_distinfo_resource(self, path): - if path not in DIST_FILES: - raise DistlibException('invalid path for a dist-info file: ' - '%r at %r' % (path, self.path)) - finder = resources.finder_for_path(self.path) - if finder is None: - raise DistlibException('Unable to get a finder for %s' % self.path) - return finder.find(path) - - def get_distinfo_file(self, path): - """ - Returns a path located under the ``.dist-info`` directory. Returns a - string representing the path. - - :parameter path: a ``'/'``-separated path relative to the - ``.dist-info`` directory or an absolute path; - If *path* is an absolute path and doesn't start - with the ``.dist-info`` directory path, - a :class:`DistlibException` is raised - :type path: str - :rtype: str - """ - # Check if it is an absolute path # XXX use relpath, add tests - if path.find(os.sep) >= 0: - # it's an absolute path? - distinfo_dirname, path = path.split(os.sep)[-2:] - if distinfo_dirname != self.path.split(os.sep)[-1]: - raise DistlibException( - 'dist-info file %r does not belong to the %r %s ' - 'distribution' % (path, self.name, self.version)) - - # The file must be relative - if path not in DIST_FILES: - raise DistlibException('invalid path for a dist-info file: ' - '%r at %r' % (path, self.path)) - - return os.path.join(self.path, path) - - def list_distinfo_files(self): - """ - Iterates over the ``RECORD`` entries and returns paths for each line if - the path is pointing to a file located in the ``.dist-info`` directory - or one of its subdirectories. - - :returns: iterator of paths - """ - base = os.path.dirname(self.path) - for path, checksum, size in self._get_records(): - # XXX add separator or use real relpath algo - if not os.path.isabs(path): - path = os.path.join(base, path) - if path.startswith(self.path): - yield path - - def __eq__(self, other): - return (isinstance(other, InstalledDistribution) and - self.path == other.path) - - # See http://docs.python.org/reference/datamodel#object.__hash__ - __hash__ = object.__hash__ - - -class EggInfoDistribution(BaseInstalledDistribution): - """Created with the *path* of the ``.egg-info`` directory or file provided - to the constructor. It reads the metadata contained in the file itself, or - if the given path happens to be a directory, the metadata is read from the - file ``PKG-INFO`` under that directory.""" - - requested = True # as we have no way of knowing, assume it was - shared_locations = {} - - def __init__(self, path, env=None): - def set_name_and_version(s, n, v): - s.name = n - s.key = n.lower() # for case-insensitive comparisons - s.version = v - - self.path = path - self.dist_path = env - if env and env._cache_enabled and path in env._cache_egg.path: - metadata = env._cache_egg.path[path].metadata - set_name_and_version(self, metadata.name, metadata.version) - else: - metadata = self._get_metadata(path) - - # Need to be set before caching - set_name_and_version(self, metadata.name, metadata.version) - - if env and env._cache_enabled: - env._cache_egg.add(self) - super(EggInfoDistribution, self).__init__(metadata, path, env) - - def _get_metadata(self, path): - requires = None - - def parse_requires_data(data): - """Create a list of dependencies from a requires.txt file. - - *data*: the contents of a setuptools-produced requires.txt file. - """ - reqs = [] - lines = data.splitlines() - for line in lines: - line = line.strip() - if line.startswith('['): - logger.warning('Unexpected line: quitting requirement scan: %r', - line) - break - r = parse_requirement(line) - if not r: - logger.warning('Not recognised as a requirement: %r', line) - continue - if r.extras: - logger.warning('extra requirements in requires.txt are ' - 'not supported') - if not r.constraints: - reqs.append(r.name) - else: - cons = ', '.join('%s%s' % c for c in r.constraints) - reqs.append('%s (%s)' % (r.name, cons)) - return reqs - - def parse_requires_path(req_path): - """Create a list of dependencies from a requires.txt file. - - *req_path*: the path to a setuptools-produced requires.txt file. - """ - - reqs = [] - try: - with codecs.open(req_path, 'r', 'utf-8') as fp: - reqs = parse_requires_data(fp.read()) - except IOError: - pass - return reqs - - tl_path = tl_data = None - if path.endswith('.egg'): - if os.path.isdir(path): - p = os.path.join(path, 'EGG-INFO') - meta_path = os.path.join(p, 'PKG-INFO') - metadata = Metadata(path=meta_path, scheme='legacy') - req_path = os.path.join(p, 'requires.txt') - tl_path = os.path.join(p, 'top_level.txt') - requires = parse_requires_path(req_path) - else: - # FIXME handle the case where zipfile is not available - zipf = zipimport.zipimporter(path) - fileobj = StringIO( - zipf.get_data('EGG-INFO/PKG-INFO').decode('utf8')) - metadata = Metadata(fileobj=fileobj, scheme='legacy') - try: - data = zipf.get_data('EGG-INFO/requires.txt') - tl_data = zipf.get_data('EGG-INFO/top_level.txt').decode('utf-8') - requires = parse_requires_data(data.decode('utf-8')) - except IOError: - requires = None - elif path.endswith('.egg-info'): - if os.path.isdir(path): - req_path = os.path.join(path, 'requires.txt') - requires = parse_requires_path(req_path) - path = os.path.join(path, 'PKG-INFO') - tl_path = os.path.join(path, 'top_level.txt') - metadata = Metadata(path=path, scheme='legacy') - else: - raise DistlibException('path must end with .egg-info or .egg, ' - 'got %r' % path) - - if requires: - metadata.add_requirements(requires) - # look for top-level modules in top_level.txt, if present - if tl_data is None: - if tl_path is not None and os.path.exists(tl_path): - with open(tl_path, 'rb') as f: - tl_data = f.read().decode('utf-8') - if not tl_data: - tl_data = [] - else: - tl_data = tl_data.splitlines() - self.modules = tl_data - return metadata - - def __repr__(self): - return '' % ( - self.name, self.version, self.path) - - def __str__(self): - return "%s %s" % (self.name, self.version) - - def check_installed_files(self): - """ - Checks that the hashes and sizes of the files in ``RECORD`` are - matched by the files themselves. Returns a (possibly empty) list of - mismatches. Each entry in the mismatch list will be a tuple consisting - of the path, 'exists', 'size' or 'hash' according to what didn't match - (existence is checked first, then size, then hash), the expected - value and the actual value. - """ - mismatches = [] - record_path = os.path.join(self.path, 'installed-files.txt') - if os.path.exists(record_path): - for path, _, _ in self.list_installed_files(): - if path == record_path: - continue - if not os.path.exists(path): - mismatches.append((path, 'exists', True, False)) - return mismatches - - def list_installed_files(self): - """ - Iterates over the ``installed-files.txt`` entries and returns a tuple - ``(path, hash, size)`` for each line. - - :returns: a list of (path, hash, size) - """ - - def _md5(path): - f = open(path, 'rb') - try: - content = f.read() - finally: - f.close() - return hashlib.md5(content).hexdigest() - - def _size(path): - return os.stat(path).st_size - - record_path = os.path.join(self.path, 'installed-files.txt') - result = [] - if os.path.exists(record_path): - with codecs.open(record_path, 'r', encoding='utf-8') as f: - for line in f: - line = line.strip() - p = os.path.normpath(os.path.join(self.path, line)) - # "./" is present as a marker between installed files - # and installation metadata files - if not os.path.exists(p): - logger.warning('Non-existent file: %s', p) - if p.endswith(('.pyc', '.pyo')): - continue - #otherwise fall through and fail - if not os.path.isdir(p): - result.append((p, _md5(p), _size(p))) - result.append((record_path, None, None)) - return result - - def list_distinfo_files(self, absolute=False): - """ - Iterates over the ``installed-files.txt`` entries and returns paths for - each line if the path is pointing to a file located in the - ``.egg-info`` directory or one of its subdirectories. - - :parameter absolute: If *absolute* is ``True``, each returned path is - transformed into a local absolute path. Otherwise the - raw value from ``installed-files.txt`` is returned. - :type absolute: boolean - :returns: iterator of paths - """ - record_path = os.path.join(self.path, 'installed-files.txt') - if os.path.exists(record_path): - skip = True - with codecs.open(record_path, 'r', encoding='utf-8') as f: - for line in f: - line = line.strip() - if line == './': - skip = False - continue - if not skip: - p = os.path.normpath(os.path.join(self.path, line)) - if p.startswith(self.path): - if absolute: - yield p - else: - yield line - - def __eq__(self, other): - return (isinstance(other, EggInfoDistribution) and - self.path == other.path) - - # See http://docs.python.org/reference/datamodel#object.__hash__ - __hash__ = object.__hash__ - -new_dist_class = InstalledDistribution -old_dist_class = EggInfoDistribution - - -class DependencyGraph(object): - """ - Represents a dependency graph between distributions. - - The dependency relationships are stored in an ``adjacency_list`` that maps - distributions to a list of ``(other, label)`` tuples where ``other`` - is a distribution and the edge is labeled with ``label`` (i.e. the version - specifier, if such was provided). Also, for more efficient traversal, for - every distribution ``x``, a list of predecessors is kept in - ``reverse_list[x]``. An edge from distribution ``a`` to - distribution ``b`` means that ``a`` depends on ``b``. If any missing - dependencies are found, they are stored in ``missing``, which is a - dictionary that maps distributions to a list of requirements that were not - provided by any other distributions. - """ - - def __init__(self): - self.adjacency_list = {} - self.reverse_list = {} - self.missing = {} - - def add_distribution(self, distribution): - """Add the *distribution* to the graph. - - :type distribution: :class:`distutils2.database.InstalledDistribution` - or :class:`distutils2.database.EggInfoDistribution` - """ - self.adjacency_list[distribution] = [] - self.reverse_list[distribution] = [] - #self.missing[distribution] = [] - - def add_edge(self, x, y, label=None): - """Add an edge from distribution *x* to distribution *y* with the given - *label*. - - :type x: :class:`distutils2.database.InstalledDistribution` or - :class:`distutils2.database.EggInfoDistribution` - :type y: :class:`distutils2.database.InstalledDistribution` or - :class:`distutils2.database.EggInfoDistribution` - :type label: ``str`` or ``None`` - """ - self.adjacency_list[x].append((y, label)) - # multiple edges are allowed, so be careful - if x not in self.reverse_list[y]: - self.reverse_list[y].append(x) - - def add_missing(self, distribution, requirement): - """ - Add a missing *requirement* for the given *distribution*. - - :type distribution: :class:`distutils2.database.InstalledDistribution` - or :class:`distutils2.database.EggInfoDistribution` - :type requirement: ``str`` - """ - logger.debug('%s missing %r', distribution, requirement) - self.missing.setdefault(distribution, []).append(requirement) - - def _repr_dist(self, dist): - return '%s %s' % (dist.name, dist.version) - - def repr_node(self, dist, level=1): - """Prints only a subgraph""" - output = [self._repr_dist(dist)] - for other, label in self.adjacency_list[dist]: - dist = self._repr_dist(other) - if label is not None: - dist = '%s [%s]' % (dist, label) - output.append(' ' * level + str(dist)) - suboutput = self.repr_node(other, level + 1) - subs = suboutput.split('\n') - output.extend(subs[1:]) - return '\n'.join(output) - - def to_dot(self, f, skip_disconnected=True): - """Writes a DOT output for the graph to the provided file *f*. - - If *skip_disconnected* is set to ``True``, then all distributions - that are not dependent on any other distribution are skipped. - - :type f: has to support ``file``-like operations - :type skip_disconnected: ``bool`` - """ - disconnected = [] - - f.write("digraph dependencies {\n") - for dist, adjs in self.adjacency_list.items(): - if len(adjs) == 0 and not skip_disconnected: - disconnected.append(dist) - for other, label in adjs: - if not label is None: - f.write('"%s" -> "%s" [label="%s"]\n' % - (dist.name, other.name, label)) - else: - f.write('"%s" -> "%s"\n' % (dist.name, other.name)) - if not skip_disconnected and len(disconnected) > 0: - f.write('subgraph disconnected {\n') - f.write('label = "Disconnected"\n') - f.write('bgcolor = red\n') - - for dist in disconnected: - f.write('"%s"' % dist.name) - f.write('\n') - f.write('}\n') - f.write('}\n') - - def topological_sort(self): - """ - Perform a topological sort of the graph. - :return: A tuple, the first element of which is a topologically sorted - list of distributions, and the second element of which is a - list of distributions that cannot be sorted because they have - circular dependencies and so form a cycle. - """ - result = [] - # Make a shallow copy of the adjacency list - alist = {} - for k, v in self.adjacency_list.items(): - alist[k] = v[:] - while True: - # See what we can remove in this run - to_remove = [] - for k, v in list(alist.items())[:]: - if not v: - to_remove.append(k) - del alist[k] - if not to_remove: - # What's left in alist (if anything) is a cycle. - break - # Remove from the adjacency list of others - for k, v in alist.items(): - alist[k] = [(d, r) for d, r in v if d not in to_remove] - logger.debug('Moving to result: %s', - ['%s (%s)' % (d.name, d.version) for d in to_remove]) - result.extend(to_remove) - return result, list(alist.keys()) - - def __repr__(self): - """Representation of the graph""" - output = [] - for dist, adjs in self.adjacency_list.items(): - output.append(self.repr_node(dist)) - return '\n'.join(output) - - -def make_graph(dists, scheme='default'): - """Makes a dependency graph from the given distributions. - - :parameter dists: a list of distributions - :type dists: list of :class:`distutils2.database.InstalledDistribution` and - :class:`distutils2.database.EggInfoDistribution` instances - :rtype: a :class:`DependencyGraph` instance - """ - scheme = get_scheme(scheme) - graph = DependencyGraph() - provided = {} # maps names to lists of (version, dist) tuples - - # first, build the graph and find out what's provided - for dist in dists: - graph.add_distribution(dist) - - for p in dist.provides: - name, version = parse_name_and_version(p) - logger.debug('Add to provided: %s, %s, %s', name, version, dist) - provided.setdefault(name, []).append((version, dist)) - - # now make the edges - for dist in dists: - requires = (dist.run_requires | dist.meta_requires | - dist.build_requires | dist.dev_requires) - for req in requires: - try: - matcher = scheme.matcher(req) - except UnsupportedVersionError: - # XXX compat-mode if cannot read the version - logger.warning('could not read version %r - using name only', - req) - name = req.split()[0] - matcher = scheme.matcher(name) - - name = matcher.key # case-insensitive - - matched = False - if name in provided: - for version, provider in provided[name]: - try: - match = matcher.match(version) - except UnsupportedVersionError: - match = False - - if match: - graph.add_edge(dist, provider, req) - matched = True - break - if not matched: - graph.add_missing(dist, req) - return graph - - -def get_dependent_dists(dists, dist): - """Recursively generate a list of distributions from *dists* that are - dependent on *dist*. - - :param dists: a list of distributions - :param dist: a distribution, member of *dists* for which we are interested - """ - if dist not in dists: - raise DistlibException('given distribution %r is not a member ' - 'of the list' % dist.name) - graph = make_graph(dists) - - dep = [dist] # dependent distributions - todo = graph.reverse_list[dist] # list of nodes we should inspect - - while todo: - d = todo.pop() - dep.append(d) - for succ in graph.reverse_list[d]: - if succ not in dep: - todo.append(succ) - - dep.pop(0) # remove dist from dep, was there to prevent infinite loops - return dep - - -def get_required_dists(dists, dist): - """Recursively generate a list of distributions from *dists* that are - required by *dist*. - - :param dists: a list of distributions - :param dist: a distribution, member of *dists* for which we are interested - in finding the dependencies. - """ - if dist not in dists: - raise DistlibException('given distribution %r is not a member ' - 'of the list' % dist.name) - graph = make_graph(dists) - - req = set() # required distributions - todo = graph.adjacency_list[dist] # list of nodes we should inspect - seen = set(t[0] for t in todo) # already added to todo - - while todo: - d = todo.pop()[0] - req.add(d) - pred_list = graph.adjacency_list[d] - for pred in pred_list: - d = pred[0] - if d not in req and d not in seen: - seen.add(d) - todo.append(pred) - return req - - -def make_dist(name, version, **kwargs): - """ - A convenience method for making a dist given just a name and version. - """ - summary = kwargs.pop('summary', 'Placeholder for summary') - md = Metadata(**kwargs) - md.name = name - md.version = version - md.summary = summary or 'Placeholder for summary' - return Distribution(md) diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/index.py b/.venv/Lib/site-packages/pip/_vendor/distlib/index.py deleted file mode 100644 index 9b6d129..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/index.py +++ /dev/null @@ -1,508 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -import hashlib -import logging -import os -import shutil -import subprocess -import tempfile -try: - from threading import Thread -except ImportError: # pragma: no cover - from dummy_threading import Thread - -from . import DistlibException -from .compat import (HTTPBasicAuthHandler, Request, HTTPPasswordMgr, - urlparse, build_opener, string_types) -from .util import zip_dir, ServerProxy - -logger = logging.getLogger(__name__) - -DEFAULT_INDEX = 'https://pypi.org/pypi' -DEFAULT_REALM = 'pypi' - -class PackageIndex(object): - """ - This class represents a package index compatible with PyPI, the Python - Package Index. - """ - - boundary = b'----------ThIs_Is_tHe_distlib_index_bouNdaRY_$' - - def __init__(self, url=None): - """ - Initialise an instance. - - :param url: The URL of the index. If not specified, the URL for PyPI is - used. - """ - self.url = url or DEFAULT_INDEX - self.read_configuration() - scheme, netloc, path, params, query, frag = urlparse(self.url) - if params or query or frag or scheme not in ('http', 'https'): - raise DistlibException('invalid repository: %s' % self.url) - self.password_handler = None - self.ssl_verifier = None - self.gpg = None - self.gpg_home = None - with open(os.devnull, 'w') as sink: - # Use gpg by default rather than gpg2, as gpg2 insists on - # prompting for passwords - for s in ('gpg', 'gpg2'): - try: - rc = subprocess.check_call([s, '--version'], stdout=sink, - stderr=sink) - if rc == 0: - self.gpg = s - break - except OSError: - pass - - def _get_pypirc_command(self): - """ - Get the distutils command for interacting with PyPI configurations. - :return: the command. - """ - from .util import _get_pypirc_command as cmd - return cmd() - - def read_configuration(self): - """ - Read the PyPI access configuration as supported by distutils. This populates - ``username``, ``password``, ``realm`` and ``url`` attributes from the - configuration. - """ - from .util import _load_pypirc - cfg = _load_pypirc(self) - self.username = cfg.get('username') - self.password = cfg.get('password') - self.realm = cfg.get('realm', 'pypi') - self.url = cfg.get('repository', self.url) - - def save_configuration(self): - """ - Save the PyPI access configuration. You must have set ``username`` and - ``password`` attributes before calling this method. - """ - self.check_credentials() - from .util import _store_pypirc - _store_pypirc(self) - - def check_credentials(self): - """ - Check that ``username`` and ``password`` have been set, and raise an - exception if not. - """ - if self.username is None or self.password is None: - raise DistlibException('username and password must be set') - pm = HTTPPasswordMgr() - _, netloc, _, _, _, _ = urlparse(self.url) - pm.add_password(self.realm, netloc, self.username, self.password) - self.password_handler = HTTPBasicAuthHandler(pm) - - def register(self, metadata): # pragma: no cover - """ - Register a distribution on PyPI, using the provided metadata. - - :param metadata: A :class:`Metadata` instance defining at least a name - and version number for the distribution to be - registered. - :return: The HTTP response received from PyPI upon submission of the - request. - """ - self.check_credentials() - metadata.validate() - d = metadata.todict() - d[':action'] = 'verify' - request = self.encode_request(d.items(), []) - response = self.send_request(request) - d[':action'] = 'submit' - request = self.encode_request(d.items(), []) - return self.send_request(request) - - def _reader(self, name, stream, outbuf): - """ - Thread runner for reading lines of from a subprocess into a buffer. - - :param name: The logical name of the stream (used for logging only). - :param stream: The stream to read from. This will typically a pipe - connected to the output stream of a subprocess. - :param outbuf: The list to append the read lines to. - """ - while True: - s = stream.readline() - if not s: - break - s = s.decode('utf-8').rstrip() - outbuf.append(s) - logger.debug('%s: %s' % (name, s)) - stream.close() - - def get_sign_command(self, filename, signer, sign_password, keystore=None): # pragma: no cover - """ - Return a suitable command for signing a file. - - :param filename: The pathname to the file to be signed. - :param signer: The identifier of the signer of the file. - :param sign_password: The passphrase for the signer's - private key used for signing. - :param keystore: The path to a directory which contains the keys - used in verification. If not specified, the - instance's ``gpg_home`` attribute is used instead. - :return: The signing command as a list suitable to be - passed to :class:`subprocess.Popen`. - """ - cmd = [self.gpg, '--status-fd', '2', '--no-tty'] - if keystore is None: - keystore = self.gpg_home - if keystore: - cmd.extend(['--homedir', keystore]) - if sign_password is not None: - cmd.extend(['--batch', '--passphrase-fd', '0']) - td = tempfile.mkdtemp() - sf = os.path.join(td, os.path.basename(filename) + '.asc') - cmd.extend(['--detach-sign', '--armor', '--local-user', - signer, '--output', sf, filename]) - logger.debug('invoking: %s', ' '.join(cmd)) - return cmd, sf - - def run_command(self, cmd, input_data=None): - """ - Run a command in a child process , passing it any input data specified. - - :param cmd: The command to run. - :param input_data: If specified, this must be a byte string containing - data to be sent to the child process. - :return: A tuple consisting of the subprocess' exit code, a list of - lines read from the subprocess' ``stdout``, and a list of - lines read from the subprocess' ``stderr``. - """ - kwargs = { - 'stdout': subprocess.PIPE, - 'stderr': subprocess.PIPE, - } - if input_data is not None: - kwargs['stdin'] = subprocess.PIPE - stdout = [] - stderr = [] - p = subprocess.Popen(cmd, **kwargs) - # We don't use communicate() here because we may need to - # get clever with interacting with the command - t1 = Thread(target=self._reader, args=('stdout', p.stdout, stdout)) - t1.start() - t2 = Thread(target=self._reader, args=('stderr', p.stderr, stderr)) - t2.start() - if input_data is not None: - p.stdin.write(input_data) - p.stdin.close() - - p.wait() - t1.join() - t2.join() - return p.returncode, stdout, stderr - - def sign_file(self, filename, signer, sign_password, keystore=None): # pragma: no cover - """ - Sign a file. - - :param filename: The pathname to the file to be signed. - :param signer: The identifier of the signer of the file. - :param sign_password: The passphrase for the signer's - private key used for signing. - :param keystore: The path to a directory which contains the keys - used in signing. If not specified, the instance's - ``gpg_home`` attribute is used instead. - :return: The absolute pathname of the file where the signature is - stored. - """ - cmd, sig_file = self.get_sign_command(filename, signer, sign_password, - keystore) - rc, stdout, stderr = self.run_command(cmd, - sign_password.encode('utf-8')) - if rc != 0: - raise DistlibException('sign command failed with error ' - 'code %s' % rc) - return sig_file - - def upload_file(self, metadata, filename, signer=None, sign_password=None, - filetype='sdist', pyversion='source', keystore=None): - """ - Upload a release file to the index. - - :param metadata: A :class:`Metadata` instance defining at least a name - and version number for the file to be uploaded. - :param filename: The pathname of the file to be uploaded. - :param signer: The identifier of the signer of the file. - :param sign_password: The passphrase for the signer's - private key used for signing. - :param filetype: The type of the file being uploaded. This is the - distutils command which produced that file, e.g. - ``sdist`` or ``bdist_wheel``. - :param pyversion: The version of Python which the release relates - to. For code compatible with any Python, this would - be ``source``, otherwise it would be e.g. ``3.2``. - :param keystore: The path to a directory which contains the keys - used in signing. If not specified, the instance's - ``gpg_home`` attribute is used instead. - :return: The HTTP response received from PyPI upon submission of the - request. - """ - self.check_credentials() - if not os.path.exists(filename): - raise DistlibException('not found: %s' % filename) - metadata.validate() - d = metadata.todict() - sig_file = None - if signer: - if not self.gpg: - logger.warning('no signing program available - not signed') - else: - sig_file = self.sign_file(filename, signer, sign_password, - keystore) - with open(filename, 'rb') as f: - file_data = f.read() - md5_digest = hashlib.md5(file_data).hexdigest() - sha256_digest = hashlib.sha256(file_data).hexdigest() - d.update({ - ':action': 'file_upload', - 'protocol_version': '1', - 'filetype': filetype, - 'pyversion': pyversion, - 'md5_digest': md5_digest, - 'sha256_digest': sha256_digest, - }) - files = [('content', os.path.basename(filename), file_data)] - if sig_file: - with open(sig_file, 'rb') as f: - sig_data = f.read() - files.append(('gpg_signature', os.path.basename(sig_file), - sig_data)) - shutil.rmtree(os.path.dirname(sig_file)) - request = self.encode_request(d.items(), files) - return self.send_request(request) - - def upload_documentation(self, metadata, doc_dir): # pragma: no cover - """ - Upload documentation to the index. - - :param metadata: A :class:`Metadata` instance defining at least a name - and version number for the documentation to be - uploaded. - :param doc_dir: The pathname of the directory which contains the - documentation. This should be the directory that - contains the ``index.html`` for the documentation. - :return: The HTTP response received from PyPI upon submission of the - request. - """ - self.check_credentials() - if not os.path.isdir(doc_dir): - raise DistlibException('not a directory: %r' % doc_dir) - fn = os.path.join(doc_dir, 'index.html') - if not os.path.exists(fn): - raise DistlibException('not found: %r' % fn) - metadata.validate() - name, version = metadata.name, metadata.version - zip_data = zip_dir(doc_dir).getvalue() - fields = [(':action', 'doc_upload'), - ('name', name), ('version', version)] - files = [('content', name, zip_data)] - request = self.encode_request(fields, files) - return self.send_request(request) - - def get_verify_command(self, signature_filename, data_filename, - keystore=None): - """ - Return a suitable command for verifying a file. - - :param signature_filename: The pathname to the file containing the - signature. - :param data_filename: The pathname to the file containing the - signed data. - :param keystore: The path to a directory which contains the keys - used in verification. If not specified, the - instance's ``gpg_home`` attribute is used instead. - :return: The verifying command as a list suitable to be - passed to :class:`subprocess.Popen`. - """ - cmd = [self.gpg, '--status-fd', '2', '--no-tty'] - if keystore is None: - keystore = self.gpg_home - if keystore: - cmd.extend(['--homedir', keystore]) - cmd.extend(['--verify', signature_filename, data_filename]) - logger.debug('invoking: %s', ' '.join(cmd)) - return cmd - - def verify_signature(self, signature_filename, data_filename, - keystore=None): - """ - Verify a signature for a file. - - :param signature_filename: The pathname to the file containing the - signature. - :param data_filename: The pathname to the file containing the - signed data. - :param keystore: The path to a directory which contains the keys - used in verification. If not specified, the - instance's ``gpg_home`` attribute is used instead. - :return: True if the signature was verified, else False. - """ - if not self.gpg: - raise DistlibException('verification unavailable because gpg ' - 'unavailable') - cmd = self.get_verify_command(signature_filename, data_filename, - keystore) - rc, stdout, stderr = self.run_command(cmd) - if rc not in (0, 1): - raise DistlibException('verify command failed with error ' - 'code %s' % rc) - return rc == 0 - - def download_file(self, url, destfile, digest=None, reporthook=None): - """ - This is a convenience method for downloading a file from an URL. - Normally, this will be a file from the index, though currently - no check is made for this (i.e. a file can be downloaded from - anywhere). - - The method is just like the :func:`urlretrieve` function in the - standard library, except that it allows digest computation to be - done during download and checking that the downloaded data - matched any expected value. - - :param url: The URL of the file to be downloaded (assumed to be - available via an HTTP GET request). - :param destfile: The pathname where the downloaded file is to be - saved. - :param digest: If specified, this must be a (hasher, value) - tuple, where hasher is the algorithm used (e.g. - ``'md5'``) and ``value`` is the expected value. - :param reporthook: The same as for :func:`urlretrieve` in the - standard library. - """ - if digest is None: - digester = None - logger.debug('No digest specified') - else: - if isinstance(digest, (list, tuple)): - hasher, digest = digest - else: - hasher = 'md5' - digester = getattr(hashlib, hasher)() - logger.debug('Digest specified: %s' % digest) - # The following code is equivalent to urlretrieve. - # We need to do it this way so that we can compute the - # digest of the file as we go. - with open(destfile, 'wb') as dfp: - # addinfourl is not a context manager on 2.x - # so we have to use try/finally - sfp = self.send_request(Request(url)) - try: - headers = sfp.info() - blocksize = 8192 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - if reporthook: - reporthook(blocknum, blocksize, size) - while True: - block = sfp.read(blocksize) - if not block: - break - read += len(block) - dfp.write(block) - if digester: - digester.update(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, blocksize, size) - finally: - sfp.close() - - # check that we got the whole file, if we can - if size >= 0 and read < size: - raise DistlibException( - 'retrieval incomplete: got only %d out of %d bytes' - % (read, size)) - # if we have a digest, it must match. - if digester: - actual = digester.hexdigest() - if digest != actual: - raise DistlibException('%s digest mismatch for %s: expected ' - '%s, got %s' % (hasher, destfile, - digest, actual)) - logger.debug('Digest verified: %s', digest) - - def send_request(self, req): - """ - Send a standard library :class:`Request` to PyPI and return its - response. - - :param req: The request to send. - :return: The HTTP response from PyPI (a standard library HTTPResponse). - """ - handlers = [] - if self.password_handler: - handlers.append(self.password_handler) - if self.ssl_verifier: - handlers.append(self.ssl_verifier) - opener = build_opener(*handlers) - return opener.open(req) - - def encode_request(self, fields, files): - """ - Encode fields and files for posting to an HTTP server. - - :param fields: The fields to send as a list of (fieldname, value) - tuples. - :param files: The files to send as a list of (fieldname, filename, - file_bytes) tuple. - """ - # Adapted from packaging, which in turn was adapted from - # http://code.activestate.com/recipes/146306 - - parts = [] - boundary = self.boundary - for k, values in fields: - if not isinstance(values, (list, tuple)): - values = [values] - - for v in values: - parts.extend(( - b'--' + boundary, - ('Content-Disposition: form-data; name="%s"' % - k).encode('utf-8'), - b'', - v.encode('utf-8'))) - for key, filename, value in files: - parts.extend(( - b'--' + boundary, - ('Content-Disposition: form-data; name="%s"; filename="%s"' % - (key, filename)).encode('utf-8'), - b'', - value)) - - parts.extend((b'--' + boundary + b'--', b'')) - - body = b'\r\n'.join(parts) - ct = b'multipart/form-data; boundary=' + boundary - headers = { - 'Content-type': ct, - 'Content-length': str(len(body)) - } - return Request(self.url, body, headers) - - def search(self, terms, operator=None): # pragma: no cover - if isinstance(terms, string_types): - terms = {'name': terms} - rpc_proxy = ServerProxy(self.url, timeout=3.0) - try: - return rpc_proxy.search(terms, operator or 'and') - finally: - rpc_proxy('close')() diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/locators.py b/.venv/Lib/site-packages/pip/_vendor/distlib/locators.py deleted file mode 100644 index 966ebc0..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/locators.py +++ /dev/null @@ -1,1300 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012-2015 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# - -import gzip -from io import BytesIO -import json -import logging -import os -import posixpath -import re -try: - import threading -except ImportError: # pragma: no cover - import dummy_threading as threading -import zlib - -from . import DistlibException -from .compat import (urljoin, urlparse, urlunparse, url2pathname, pathname2url, - queue, quote, unescape, build_opener, - HTTPRedirectHandler as BaseRedirectHandler, text_type, - Request, HTTPError, URLError) -from .database import Distribution, DistributionPath, make_dist -from .metadata import Metadata, MetadataInvalidError -from .util import (cached_property, ensure_slash, split_filename, get_project_data, - parse_requirement, parse_name_and_version, ServerProxy, - normalize_name) -from .version import get_scheme, UnsupportedVersionError -from .wheel import Wheel, is_compatible - -logger = logging.getLogger(__name__) - -HASHER_HASH = re.compile(r'^(\w+)=([a-f0-9]+)') -CHARSET = re.compile(r';\s*charset\s*=\s*(.*)\s*$', re.I) -HTML_CONTENT_TYPE = re.compile('text/html|application/x(ht)?ml') -DEFAULT_INDEX = 'https://pypi.org/pypi' - -def get_all_distribution_names(url=None): - """ - Return all distribution names known by an index. - :param url: The URL of the index. - :return: A list of all known distribution names. - """ - if url is None: - url = DEFAULT_INDEX - client = ServerProxy(url, timeout=3.0) - try: - return client.list_packages() - finally: - client('close')() - -class RedirectHandler(BaseRedirectHandler): - """ - A class to work around a bug in some Python 3.2.x releases. - """ - # There's a bug in the base version for some 3.2.x - # (e.g. 3.2.2 on Ubuntu Oneiric). If a Location header - # returns e.g. /abc, it bails because it says the scheme '' - # is bogus, when actually it should use the request's - # URL for the scheme. See Python issue #13696. - def http_error_302(self, req, fp, code, msg, headers): - # Some servers (incorrectly) return multiple Location headers - # (so probably same goes for URI). Use first header. - newurl = None - for key in ('location', 'uri'): - if key in headers: - newurl = headers[key] - break - if newurl is None: # pragma: no cover - return - urlparts = urlparse(newurl) - if urlparts.scheme == '': - newurl = urljoin(req.get_full_url(), newurl) - if hasattr(headers, 'replace_header'): - headers.replace_header(key, newurl) - else: - headers[key] = newurl - return BaseRedirectHandler.http_error_302(self, req, fp, code, msg, - headers) - - http_error_301 = http_error_303 = http_error_307 = http_error_302 - -class Locator(object): - """ - A base class for locators - things that locate distributions. - """ - source_extensions = ('.tar.gz', '.tar.bz2', '.tar', '.zip', '.tgz', '.tbz') - binary_extensions = ('.egg', '.exe', '.whl') - excluded_extensions = ('.pdf',) - - # A list of tags indicating which wheels you want to match. The default - # value of None matches against the tags compatible with the running - # Python. If you want to match other values, set wheel_tags on a locator - # instance to a list of tuples (pyver, abi, arch) which you want to match. - wheel_tags = None - - downloadable_extensions = source_extensions + ('.whl',) - - def __init__(self, scheme='default'): - """ - Initialise an instance. - :param scheme: Because locators look for most recent versions, they - need to know the version scheme to use. This specifies - the current PEP-recommended scheme - use ``'legacy'`` - if you need to support existing distributions on PyPI. - """ - self._cache = {} - self.scheme = scheme - # Because of bugs in some of the handlers on some of the platforms, - # we use our own opener rather than just using urlopen. - self.opener = build_opener(RedirectHandler()) - # If get_project() is called from locate(), the matcher instance - # is set from the requirement passed to locate(). See issue #18 for - # why this can be useful to know. - self.matcher = None - self.errors = queue.Queue() - - def get_errors(self): - """ - Return any errors which have occurred. - """ - result = [] - while not self.errors.empty(): # pragma: no cover - try: - e = self.errors.get(False) - result.append(e) - except self.errors.Empty: - continue - self.errors.task_done() - return result - - def clear_errors(self): - """ - Clear any errors which may have been logged. - """ - # Just get the errors and throw them away - self.get_errors() - - def clear_cache(self): - self._cache.clear() - - def _get_scheme(self): - return self._scheme - - def _set_scheme(self, value): - self._scheme = value - - scheme = property(_get_scheme, _set_scheme) - - def _get_project(self, name): - """ - For a given project, get a dictionary mapping available versions to Distribution - instances. - - This should be implemented in subclasses. - - If called from a locate() request, self.matcher will be set to a - matcher for the requirement to satisfy, otherwise it will be None. - """ - raise NotImplementedError('Please implement in the subclass') - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - raise NotImplementedError('Please implement in the subclass') - - def get_project(self, name): - """ - For a given project, get a dictionary mapping available versions to Distribution - instances. - - This calls _get_project to do all the work, and just implements a caching layer on top. - """ - if self._cache is None: # pragma: no cover - result = self._get_project(name) - elif name in self._cache: - result = self._cache[name] - else: - self.clear_errors() - result = self._get_project(name) - self._cache[name] = result - return result - - def score_url(self, url): - """ - Give an url a score which can be used to choose preferred URLs - for a given project release. - """ - t = urlparse(url) - basename = posixpath.basename(t.path) - compatible = True - is_wheel = basename.endswith('.whl') - is_downloadable = basename.endswith(self.downloadable_extensions) - if is_wheel: - compatible = is_compatible(Wheel(basename), self.wheel_tags) - return (t.scheme == 'https', 'pypi.org' in t.netloc, - is_downloadable, is_wheel, compatible, basename) - - def prefer_url(self, url1, url2): - """ - Choose one of two URLs where both are candidates for distribution - archives for the same version of a distribution (for example, - .tar.gz vs. zip). - - The current implementation favours https:// URLs over http://, archives - from PyPI over those from other locations, wheel compatibility (if a - wheel) and then the archive name. - """ - result = url2 - if url1: - s1 = self.score_url(url1) - s2 = self.score_url(url2) - if s1 > s2: - result = url1 - if result != url2: - logger.debug('Not replacing %r with %r', url1, url2) - else: - logger.debug('Replacing %r with %r', url1, url2) - return result - - def split_filename(self, filename, project_name): - """ - Attempt to split a filename in project name, version and Python version. - """ - return split_filename(filename, project_name) - - def convert_url_to_download_info(self, url, project_name): - """ - See if a URL is a candidate for a download URL for a project (the URL - has typically been scraped from an HTML page). - - If it is, a dictionary is returned with keys "name", "version", - "filename" and "url"; otherwise, None is returned. - """ - def same_project(name1, name2): - return normalize_name(name1) == normalize_name(name2) - - result = None - scheme, netloc, path, params, query, frag = urlparse(url) - if frag.lower().startswith('egg='): # pragma: no cover - logger.debug('%s: version hint in fragment: %r', - project_name, frag) - m = HASHER_HASH.match(frag) - if m: - algo, digest = m.groups() - else: - algo, digest = None, None - origpath = path - if path and path[-1] == '/': # pragma: no cover - path = path[:-1] - if path.endswith('.whl'): - try: - wheel = Wheel(path) - if not is_compatible(wheel, self.wheel_tags): - logger.debug('Wheel not compatible: %s', path) - else: - if project_name is None: - include = True - else: - include = same_project(wheel.name, project_name) - if include: - result = { - 'name': wheel.name, - 'version': wheel.version, - 'filename': wheel.filename, - 'url': urlunparse((scheme, netloc, origpath, - params, query, '')), - 'python-version': ', '.join( - ['.'.join(list(v[2:])) for v in wheel.pyver]), - } - except Exception as e: # pragma: no cover - logger.warning('invalid path for wheel: %s', path) - elif not path.endswith(self.downloadable_extensions): # pragma: no cover - logger.debug('Not downloadable: %s', path) - else: # downloadable extension - path = filename = posixpath.basename(path) - for ext in self.downloadable_extensions: - if path.endswith(ext): - path = path[:-len(ext)] - t = self.split_filename(path, project_name) - if not t: # pragma: no cover - logger.debug('No match for project/version: %s', path) - else: - name, version, pyver = t - if not project_name or same_project(project_name, name): - result = { - 'name': name, - 'version': version, - 'filename': filename, - 'url': urlunparse((scheme, netloc, origpath, - params, query, '')), - #'packagetype': 'sdist', - } - if pyver: # pragma: no cover - result['python-version'] = pyver - break - if result and algo: - result['%s_digest' % algo] = digest - return result - - def _get_digest(self, info): - """ - Get a digest from a dictionary by looking at a "digests" dictionary - or keys of the form 'algo_digest'. - - Returns a 2-tuple (algo, digest) if found, else None. Currently - looks only for SHA256, then MD5. - """ - result = None - if 'digests' in info: - digests = info['digests'] - for algo in ('sha256', 'md5'): - if algo in digests: - result = (algo, digests[algo]) - break - if not result: - for algo in ('sha256', 'md5'): - key = '%s_digest' % algo - if key in info: - result = (algo, info[key]) - break - return result - - def _update_version_data(self, result, info): - """ - Update a result dictionary (the final result from _get_project) with a - dictionary for a specific version, which typically holds information - gleaned from a filename or URL for an archive for the distribution. - """ - name = info.pop('name') - version = info.pop('version') - if version in result: - dist = result[version] - md = dist.metadata - else: - dist = make_dist(name, version, scheme=self.scheme) - md = dist.metadata - dist.digest = digest = self._get_digest(info) - url = info['url'] - result['digests'][url] = digest - if md.source_url != info['url']: - md.source_url = self.prefer_url(md.source_url, url) - result['urls'].setdefault(version, set()).add(url) - dist.locator = self - result[version] = dist - - def locate(self, requirement, prereleases=False): - """ - Find the most recent distribution which matches the given - requirement. - - :param requirement: A requirement of the form 'foo (1.0)' or perhaps - 'foo (>= 1.0, < 2.0, != 1.3)' - :param prereleases: If ``True``, allow pre-release versions - to be located. Otherwise, pre-release versions - are not returned. - :return: A :class:`Distribution` instance, or ``None`` if no such - distribution could be located. - """ - result = None - r = parse_requirement(requirement) - if r is None: # pragma: no cover - raise DistlibException('Not a valid requirement: %r' % requirement) - scheme = get_scheme(self.scheme) - self.matcher = matcher = scheme.matcher(r.requirement) - logger.debug('matcher: %s (%s)', matcher, type(matcher).__name__) - versions = self.get_project(r.name) - if len(versions) > 2: # urls and digests keys are present - # sometimes, versions are invalid - slist = [] - vcls = matcher.version_class - for k in versions: - if k in ('urls', 'digests'): - continue - try: - if not matcher.match(k): - pass # logger.debug('%s did not match %r', matcher, k) - else: - if prereleases or not vcls(k).is_prerelease: - slist.append(k) - # else: - # logger.debug('skipping pre-release ' - # 'version %s of %s', k, matcher.name) - except Exception: # pragma: no cover - logger.warning('error matching %s with %r', matcher, k) - pass # slist.append(k) - if len(slist) > 1: - slist = sorted(slist, key=scheme.key) - if slist: - logger.debug('sorted list: %s', slist) - version = slist[-1] - result = versions[version] - if result: - if r.extras: - result.extras = r.extras - result.download_urls = versions.get('urls', {}).get(version, set()) - d = {} - sd = versions.get('digests', {}) - for url in result.download_urls: - if url in sd: # pragma: no cover - d[url] = sd[url] - result.digests = d - self.matcher = None - return result - - -class PyPIRPCLocator(Locator): - """ - This locator uses XML-RPC to locate distributions. It therefore - cannot be used with simple mirrors (that only mirror file content). - """ - def __init__(self, url, **kwargs): - """ - Initialise an instance. - - :param url: The URL to use for XML-RPC. - :param kwargs: Passed to the superclass constructor. - """ - super(PyPIRPCLocator, self).__init__(**kwargs) - self.base_url = url - self.client = ServerProxy(url, timeout=3.0) - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - return set(self.client.list_packages()) - - def _get_project(self, name): - result = {'urls': {}, 'digests': {}} - versions = self.client.package_releases(name, True) - for v in versions: - urls = self.client.release_urls(name, v) - data = self.client.release_data(name, v) - metadata = Metadata(scheme=self.scheme) - metadata.name = data['name'] - metadata.version = data['version'] - metadata.license = data.get('license') - metadata.keywords = data.get('keywords', []) - metadata.summary = data.get('summary') - dist = Distribution(metadata) - if urls: - info = urls[0] - metadata.source_url = info['url'] - dist.digest = self._get_digest(info) - dist.locator = self - result[v] = dist - for info in urls: - url = info['url'] - digest = self._get_digest(info) - result['urls'].setdefault(v, set()).add(url) - result['digests'][url] = digest - return result - -class PyPIJSONLocator(Locator): - """ - This locator uses PyPI's JSON interface. It's very limited in functionality - and probably not worth using. - """ - def __init__(self, url, **kwargs): - super(PyPIJSONLocator, self).__init__(**kwargs) - self.base_url = ensure_slash(url) - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - raise NotImplementedError('Not available from this locator') - - def _get_project(self, name): - result = {'urls': {}, 'digests': {}} - url = urljoin(self.base_url, '%s/json' % quote(name)) - try: - resp = self.opener.open(url) - data = resp.read().decode() # for now - d = json.loads(data) - md = Metadata(scheme=self.scheme) - data = d['info'] - md.name = data['name'] - md.version = data['version'] - md.license = data.get('license') - md.keywords = data.get('keywords', []) - md.summary = data.get('summary') - dist = Distribution(md) - dist.locator = self - urls = d['urls'] - result[md.version] = dist - for info in d['urls']: - url = info['url'] - dist.download_urls.add(url) - dist.digests[url] = self._get_digest(info) - result['urls'].setdefault(md.version, set()).add(url) - result['digests'][url] = self._get_digest(info) - # Now get other releases - for version, infos in d['releases'].items(): - if version == md.version: - continue # already done - omd = Metadata(scheme=self.scheme) - omd.name = md.name - omd.version = version - odist = Distribution(omd) - odist.locator = self - result[version] = odist - for info in infos: - url = info['url'] - odist.download_urls.add(url) - odist.digests[url] = self._get_digest(info) - result['urls'].setdefault(version, set()).add(url) - result['digests'][url] = self._get_digest(info) -# for info in urls: -# md.source_url = info['url'] -# dist.digest = self._get_digest(info) -# dist.locator = self -# for info in urls: -# url = info['url'] -# result['urls'].setdefault(md.version, set()).add(url) -# result['digests'][url] = self._get_digest(info) - except Exception as e: - self.errors.put(text_type(e)) - logger.exception('JSON fetch failed: %s', e) - return result - - -class Page(object): - """ - This class represents a scraped HTML page. - """ - # The following slightly hairy-looking regex just looks for the contents of - # an anchor link, which has an attribute "href" either immediately preceded - # or immediately followed by a "rel" attribute. The attribute values can be - # declared with double quotes, single quotes or no quotes - which leads to - # the length of the expression. - _href = re.compile(""" -(rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*))\\s+)? -href\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)) -(\\s+rel\\s*=\\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\\s\n]*)))? -""", re.I | re.S | re.X) - _base = re.compile(r"""]+)""", re.I | re.S) - - def __init__(self, data, url): - """ - Initialise an instance with the Unicode page contents and the URL they - came from. - """ - self.data = data - self.base_url = self.url = url - m = self._base.search(self.data) - if m: - self.base_url = m.group(1) - - _clean_re = re.compile(r'[^a-z0-9$&+,/:;=?@.#%_\\|-]', re.I) - - @cached_property - def links(self): - """ - Return the URLs of all the links on a page together with information - about their "rel" attribute, for determining which ones to treat as - downloads and which ones to queue for further scraping. - """ - def clean(url): - "Tidy up an URL." - scheme, netloc, path, params, query, frag = urlparse(url) - return urlunparse((scheme, netloc, quote(path), - params, query, frag)) - - result = set() - for match in self._href.finditer(self.data): - d = match.groupdict('') - rel = (d['rel1'] or d['rel2'] or d['rel3'] or - d['rel4'] or d['rel5'] or d['rel6']) - url = d['url1'] or d['url2'] or d['url3'] - url = urljoin(self.base_url, url) - url = unescape(url) - url = self._clean_re.sub(lambda m: '%%%2x' % ord(m.group(0)), url) - result.add((url, rel)) - # We sort the result, hoping to bring the most recent versions - # to the front - result = sorted(result, key=lambda t: t[0], reverse=True) - return result - - -class SimpleScrapingLocator(Locator): - """ - A locator which scrapes HTML pages to locate downloads for a distribution. - This runs multiple threads to do the I/O; performance is at least as good - as pip's PackageFinder, which works in an analogous fashion. - """ - - # These are used to deal with various Content-Encoding schemes. - decoders = { - 'deflate': zlib.decompress, - 'gzip': lambda b: gzip.GzipFile(fileobj=BytesIO(b)).read(), - 'none': lambda b: b, - } - - def __init__(self, url, timeout=None, num_workers=10, **kwargs): - """ - Initialise an instance. - :param url: The root URL to use for scraping. - :param timeout: The timeout, in seconds, to be applied to requests. - This defaults to ``None`` (no timeout specified). - :param num_workers: The number of worker threads you want to do I/O, - This defaults to 10. - :param kwargs: Passed to the superclass. - """ - super(SimpleScrapingLocator, self).__init__(**kwargs) - self.base_url = ensure_slash(url) - self.timeout = timeout - self._page_cache = {} - self._seen = set() - self._to_fetch = queue.Queue() - self._bad_hosts = set() - self.skip_externals = False - self.num_workers = num_workers - self._lock = threading.RLock() - # See issue #45: we need to be resilient when the locator is used - # in a thread, e.g. with concurrent.futures. We can't use self._lock - # as it is for coordinating our internal threads - the ones created - # in _prepare_threads. - self._gplock = threading.RLock() - self.platform_check = False # See issue #112 - - def _prepare_threads(self): - """ - Threads are created only when get_project is called, and terminate - before it returns. They are there primarily to parallelise I/O (i.e. - fetching web pages). - """ - self._threads = [] - for i in range(self.num_workers): - t = threading.Thread(target=self._fetch) - t.daemon = True - t.start() - self._threads.append(t) - - def _wait_threads(self): - """ - Tell all the threads to terminate (by sending a sentinel value) and - wait for them to do so. - """ - # Note that you need two loops, since you can't say which - # thread will get each sentinel - for t in self._threads: - self._to_fetch.put(None) # sentinel - for t in self._threads: - t.join() - self._threads = [] - - def _get_project(self, name): - result = {'urls': {}, 'digests': {}} - with self._gplock: - self.result = result - self.project_name = name - url = urljoin(self.base_url, '%s/' % quote(name)) - self._seen.clear() - self._page_cache.clear() - self._prepare_threads() - try: - logger.debug('Queueing %s', url) - self._to_fetch.put(url) - self._to_fetch.join() - finally: - self._wait_threads() - del self.result - return result - - platform_dependent = re.compile(r'\b(linux_(i\d86|x86_64|arm\w+)|' - r'win(32|_amd64)|macosx_?\d+)\b', re.I) - - def _is_platform_dependent(self, url): - """ - Does an URL refer to a platform-specific download? - """ - return self.platform_dependent.search(url) - - def _process_download(self, url): - """ - See if an URL is a suitable download for a project. - - If it is, register information in the result dictionary (for - _get_project) about the specific version it's for. - - Note that the return value isn't actually used other than as a boolean - value. - """ - if self.platform_check and self._is_platform_dependent(url): - info = None - else: - info = self.convert_url_to_download_info(url, self.project_name) - logger.debug('process_download: %s -> %s', url, info) - if info: - with self._lock: # needed because self.result is shared - self._update_version_data(self.result, info) - return info - - def _should_queue(self, link, referrer, rel): - """ - Determine whether a link URL from a referring page and with a - particular "rel" attribute should be queued for scraping. - """ - scheme, netloc, path, _, _, _ = urlparse(link) - if path.endswith(self.source_extensions + self.binary_extensions + - self.excluded_extensions): - result = False - elif self.skip_externals and not link.startswith(self.base_url): - result = False - elif not referrer.startswith(self.base_url): - result = False - elif rel not in ('homepage', 'download'): - result = False - elif scheme not in ('http', 'https', 'ftp'): - result = False - elif self._is_platform_dependent(link): - result = False - else: - host = netloc.split(':', 1)[0] - if host.lower() == 'localhost': - result = False - else: - result = True - logger.debug('should_queue: %s (%s) from %s -> %s', link, rel, - referrer, result) - return result - - def _fetch(self): - """ - Get a URL to fetch from the work queue, get the HTML page, examine its - links for download candidates and candidates for further scraping. - - This is a handy method to run in a thread. - """ - while True: - url = self._to_fetch.get() - try: - if url: - page = self.get_page(url) - if page is None: # e.g. after an error - continue - for link, rel in page.links: - if link not in self._seen: - try: - self._seen.add(link) - if (not self._process_download(link) and - self._should_queue(link, url, rel)): - logger.debug('Queueing %s from %s', link, url) - self._to_fetch.put(link) - except MetadataInvalidError: # e.g. invalid versions - pass - except Exception as e: # pragma: no cover - self.errors.put(text_type(e)) - finally: - # always do this, to avoid hangs :-) - self._to_fetch.task_done() - if not url: - #logger.debug('Sentinel seen, quitting.') - break - - def get_page(self, url): - """ - Get the HTML for an URL, possibly from an in-memory cache. - - XXX TODO Note: this cache is never actually cleared. It's assumed that - the data won't get stale over the lifetime of a locator instance (not - necessarily true for the default_locator). - """ - # http://peak.telecommunity.com/DevCenter/EasyInstall#package-index-api - scheme, netloc, path, _, _, _ = urlparse(url) - if scheme == 'file' and os.path.isdir(url2pathname(path)): - url = urljoin(ensure_slash(url), 'index.html') - - if url in self._page_cache: - result = self._page_cache[url] - logger.debug('Returning %s from cache: %s', url, result) - else: - host = netloc.split(':', 1)[0] - result = None - if host in self._bad_hosts: - logger.debug('Skipping %s due to bad host %s', url, host) - else: - req = Request(url, headers={'Accept-encoding': 'identity'}) - try: - logger.debug('Fetching %s', url) - resp = self.opener.open(req, timeout=self.timeout) - logger.debug('Fetched %s', url) - headers = resp.info() - content_type = headers.get('Content-Type', '') - if HTML_CONTENT_TYPE.match(content_type): - final_url = resp.geturl() - data = resp.read() - encoding = headers.get('Content-Encoding') - if encoding: - decoder = self.decoders[encoding] # fail if not found - data = decoder(data) - encoding = 'utf-8' - m = CHARSET.search(content_type) - if m: - encoding = m.group(1) - try: - data = data.decode(encoding) - except UnicodeError: # pragma: no cover - data = data.decode('latin-1') # fallback - result = Page(data, final_url) - self._page_cache[final_url] = result - except HTTPError as e: - if e.code != 404: - logger.exception('Fetch failed: %s: %s', url, e) - except URLError as e: # pragma: no cover - logger.exception('Fetch failed: %s: %s', url, e) - with self._lock: - self._bad_hosts.add(host) - except Exception as e: # pragma: no cover - logger.exception('Fetch failed: %s: %s', url, e) - finally: - self._page_cache[url] = result # even if None (failure) - return result - - _distname_re = re.compile(']*>([^<]+)<') - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - result = set() - page = self.get_page(self.base_url) - if not page: - raise DistlibException('Unable to get %s' % self.base_url) - for match in self._distname_re.finditer(page.data): - result.add(match.group(1)) - return result - -class DirectoryLocator(Locator): - """ - This class locates distributions in a directory tree. - """ - - def __init__(self, path, **kwargs): - """ - Initialise an instance. - :param path: The root of the directory tree to search. - :param kwargs: Passed to the superclass constructor, - except for: - * recursive - if True (the default), subdirectories are - recursed into. If False, only the top-level directory - is searched, - """ - self.recursive = kwargs.pop('recursive', True) - super(DirectoryLocator, self).__init__(**kwargs) - path = os.path.abspath(path) - if not os.path.isdir(path): # pragma: no cover - raise DistlibException('Not a directory: %r' % path) - self.base_dir = path - - def should_include(self, filename, parent): - """ - Should a filename be considered as a candidate for a distribution - archive? As well as the filename, the directory which contains it - is provided, though not used by the current implementation. - """ - return filename.endswith(self.downloadable_extensions) - - def _get_project(self, name): - result = {'urls': {}, 'digests': {}} - for root, dirs, files in os.walk(self.base_dir): - for fn in files: - if self.should_include(fn, root): - fn = os.path.join(root, fn) - url = urlunparse(('file', '', - pathname2url(os.path.abspath(fn)), - '', '', '')) - info = self.convert_url_to_download_info(url, name) - if info: - self._update_version_data(result, info) - if not self.recursive: - break - return result - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - result = set() - for root, dirs, files in os.walk(self.base_dir): - for fn in files: - if self.should_include(fn, root): - fn = os.path.join(root, fn) - url = urlunparse(('file', '', - pathname2url(os.path.abspath(fn)), - '', '', '')) - info = self.convert_url_to_download_info(url, None) - if info: - result.add(info['name']) - if not self.recursive: - break - return result - -class JSONLocator(Locator): - """ - This locator uses special extended metadata (not available on PyPI) and is - the basis of performant dependency resolution in distlib. Other locators - require archive downloads before dependencies can be determined! As you - might imagine, that can be slow. - """ - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - raise NotImplementedError('Not available from this locator') - - def _get_project(self, name): - result = {'urls': {}, 'digests': {}} - data = get_project_data(name) - if data: - for info in data.get('files', []): - if info['ptype'] != 'sdist' or info['pyversion'] != 'source': - continue - # We don't store summary in project metadata as it makes - # the data bigger for no benefit during dependency - # resolution - dist = make_dist(data['name'], info['version'], - summary=data.get('summary', - 'Placeholder for summary'), - scheme=self.scheme) - md = dist.metadata - md.source_url = info['url'] - # TODO SHA256 digest - if 'digest' in info and info['digest']: - dist.digest = ('md5', info['digest']) - md.dependencies = info.get('requirements', {}) - dist.exports = info.get('exports', {}) - result[dist.version] = dist - result['urls'].setdefault(dist.version, set()).add(info['url']) - return result - -class DistPathLocator(Locator): - """ - This locator finds installed distributions in a path. It can be useful for - adding to an :class:`AggregatingLocator`. - """ - def __init__(self, distpath, **kwargs): - """ - Initialise an instance. - - :param distpath: A :class:`DistributionPath` instance to search. - """ - super(DistPathLocator, self).__init__(**kwargs) - assert isinstance(distpath, DistributionPath) - self.distpath = distpath - - def _get_project(self, name): - dist = self.distpath.get_distribution(name) - if dist is None: - result = {'urls': {}, 'digests': {}} - else: - result = { - dist.version: dist, - 'urls': {dist.version: set([dist.source_url])}, - 'digests': {dist.version: set([None])} - } - return result - - -class AggregatingLocator(Locator): - """ - This class allows you to chain and/or merge a list of locators. - """ - def __init__(self, *locators, **kwargs): - """ - Initialise an instance. - - :param locators: The list of locators to search. - :param kwargs: Passed to the superclass constructor, - except for: - * merge - if False (the default), the first successful - search from any of the locators is returned. If True, - the results from all locators are merged (this can be - slow). - """ - self.merge = kwargs.pop('merge', False) - self.locators = locators - super(AggregatingLocator, self).__init__(**kwargs) - - def clear_cache(self): - super(AggregatingLocator, self).clear_cache() - for locator in self.locators: - locator.clear_cache() - - def _set_scheme(self, value): - self._scheme = value - for locator in self.locators: - locator.scheme = value - - scheme = property(Locator.scheme.fget, _set_scheme) - - def _get_project(self, name): - result = {} - for locator in self.locators: - d = locator.get_project(name) - if d: - if self.merge: - files = result.get('urls', {}) - digests = result.get('digests', {}) - # next line could overwrite result['urls'], result['digests'] - result.update(d) - df = result.get('urls') - if files and df: - for k, v in files.items(): - if k in df: - df[k] |= v - else: - df[k] = v - dd = result.get('digests') - if digests and dd: - dd.update(digests) - else: - # See issue #18. If any dists are found and we're looking - # for specific constraints, we only return something if - # a match is found. For example, if a DirectoryLocator - # returns just foo (1.0) while we're looking for - # foo (>= 2.0), we'll pretend there was nothing there so - # that subsequent locators can be queried. Otherwise we - # would just return foo (1.0) which would then lead to a - # failure to find foo (>= 2.0), because other locators - # weren't searched. Note that this only matters when - # merge=False. - if self.matcher is None: - found = True - else: - found = False - for k in d: - if self.matcher.match(k): - found = True - break - if found: - result = d - break - return result - - def get_distribution_names(self): - """ - Return all the distribution names known to this locator. - """ - result = set() - for locator in self.locators: - try: - result |= locator.get_distribution_names() - except NotImplementedError: - pass - return result - - -# We use a legacy scheme simply because most of the dists on PyPI use legacy -# versions which don't conform to PEP 440. -default_locator = AggregatingLocator( - # JSONLocator(), # don't use as PEP 426 is withdrawn - SimpleScrapingLocator('https://pypi.org/simple/', - timeout=3.0), - scheme='legacy') - -locate = default_locator.locate - - -class DependencyFinder(object): - """ - Locate dependencies for distributions. - """ - - def __init__(self, locator=None): - """ - Initialise an instance, using the specified locator - to locate distributions. - """ - self.locator = locator or default_locator - self.scheme = get_scheme(self.locator.scheme) - - def add_distribution(self, dist): - """ - Add a distribution to the finder. This will update internal information - about who provides what. - :param dist: The distribution to add. - """ - logger.debug('adding distribution %s', dist) - name = dist.key - self.dists_by_name[name] = dist - self.dists[(name, dist.version)] = dist - for p in dist.provides: - name, version = parse_name_and_version(p) - logger.debug('Add to provided: %s, %s, %s', name, version, dist) - self.provided.setdefault(name, set()).add((version, dist)) - - def remove_distribution(self, dist): - """ - Remove a distribution from the finder. This will update internal - information about who provides what. - :param dist: The distribution to remove. - """ - logger.debug('removing distribution %s', dist) - name = dist.key - del self.dists_by_name[name] - del self.dists[(name, dist.version)] - for p in dist.provides: - name, version = parse_name_and_version(p) - logger.debug('Remove from provided: %s, %s, %s', name, version, dist) - s = self.provided[name] - s.remove((version, dist)) - if not s: - del self.provided[name] - - def get_matcher(self, reqt): - """ - Get a version matcher for a requirement. - :param reqt: The requirement - :type reqt: str - :return: A version matcher (an instance of - :class:`distlib.version.Matcher`). - """ - try: - matcher = self.scheme.matcher(reqt) - except UnsupportedVersionError: # pragma: no cover - # XXX compat-mode if cannot read the version - name = reqt.split()[0] - matcher = self.scheme.matcher(name) - return matcher - - def find_providers(self, reqt): - """ - Find the distributions which can fulfill a requirement. - - :param reqt: The requirement. - :type reqt: str - :return: A set of distribution which can fulfill the requirement. - """ - matcher = self.get_matcher(reqt) - name = matcher.key # case-insensitive - result = set() - provided = self.provided - if name in provided: - for version, provider in provided[name]: - try: - match = matcher.match(version) - except UnsupportedVersionError: - match = False - - if match: - result.add(provider) - break - return result - - def try_to_replace(self, provider, other, problems): - """ - Attempt to replace one provider with another. This is typically used - when resolving dependencies from multiple sources, e.g. A requires - (B >= 1.0) while C requires (B >= 1.1). - - For successful replacement, ``provider`` must meet all the requirements - which ``other`` fulfills. - - :param provider: The provider we are trying to replace with. - :param other: The provider we're trying to replace. - :param problems: If False is returned, this will contain what - problems prevented replacement. This is currently - a tuple of the literal string 'cantreplace', - ``provider``, ``other`` and the set of requirements - that ``provider`` couldn't fulfill. - :return: True if we can replace ``other`` with ``provider``, else - False. - """ - rlist = self.reqts[other] - unmatched = set() - for s in rlist: - matcher = self.get_matcher(s) - if not matcher.match(provider.version): - unmatched.add(s) - if unmatched: - # can't replace other with provider - problems.add(('cantreplace', provider, other, - frozenset(unmatched))) - result = False - else: - # can replace other with provider - self.remove_distribution(other) - del self.reqts[other] - for s in rlist: - self.reqts.setdefault(provider, set()).add(s) - self.add_distribution(provider) - result = True - return result - - def find(self, requirement, meta_extras=None, prereleases=False): - """ - Find a distribution and all distributions it depends on. - - :param requirement: The requirement specifying the distribution to - find, or a Distribution instance. - :param meta_extras: A list of meta extras such as :test:, :build: and - so on. - :param prereleases: If ``True``, allow pre-release versions to be - returned - otherwise, don't return prereleases - unless they're all that's available. - - Return a set of :class:`Distribution` instances and a set of - problems. - - The distributions returned should be such that they have the - :attr:`required` attribute set to ``True`` if they were - from the ``requirement`` passed to ``find()``, and they have the - :attr:`build_time_dependency` attribute set to ``True`` unless they - are post-installation dependencies of the ``requirement``. - - The problems should be a tuple consisting of the string - ``'unsatisfied'`` and the requirement which couldn't be satisfied - by any distribution known to the locator. - """ - - self.provided = {} - self.dists = {} - self.dists_by_name = {} - self.reqts = {} - - meta_extras = set(meta_extras or []) - if ':*:' in meta_extras: - meta_extras.remove(':*:') - # :meta: and :run: are implicitly included - meta_extras |= set([':test:', ':build:', ':dev:']) - - if isinstance(requirement, Distribution): - dist = odist = requirement - logger.debug('passed %s as requirement', odist) - else: - dist = odist = self.locator.locate(requirement, - prereleases=prereleases) - if dist is None: - raise DistlibException('Unable to locate %r' % requirement) - logger.debug('located %s', odist) - dist.requested = True - problems = set() - todo = set([dist]) - install_dists = set([odist]) - while todo: - dist = todo.pop() - name = dist.key # case-insensitive - if name not in self.dists_by_name: - self.add_distribution(dist) - else: - #import pdb; pdb.set_trace() - other = self.dists_by_name[name] - if other != dist: - self.try_to_replace(dist, other, problems) - - ireqts = dist.run_requires | dist.meta_requires - sreqts = dist.build_requires - ereqts = set() - if meta_extras and dist in install_dists: - for key in ('test', 'build', 'dev'): - e = ':%s:' % key - if e in meta_extras: - ereqts |= getattr(dist, '%s_requires' % key) - all_reqts = ireqts | sreqts | ereqts - for r in all_reqts: - providers = self.find_providers(r) - if not providers: - logger.debug('No providers found for %r', r) - provider = self.locator.locate(r, prereleases=prereleases) - # If no provider is found and we didn't consider - # prereleases, consider them now. - if provider is None and not prereleases: - provider = self.locator.locate(r, prereleases=True) - if provider is None: - logger.debug('Cannot satisfy %r', r) - problems.add(('unsatisfied', r)) - else: - n, v = provider.key, provider.version - if (n, v) not in self.dists: - todo.add(provider) - providers.add(provider) - if r in ireqts and dist in install_dists: - install_dists.add(provider) - logger.debug('Adding %s to install_dists', - provider.name_and_version) - for p in providers: - name = p.key - if name not in self.dists_by_name: - self.reqts.setdefault(p, set()).add(r) - else: - other = self.dists_by_name[name] - if other != p: - # see if other can be replaced by p - self.try_to_replace(p, other, problems) - - dists = set(self.dists.values()) - for dist in dists: - dist.build_time_dependency = dist not in install_dists - if dist.build_time_dependency: - logger.debug('%s is a build-time dependency only.', - dist.name_and_version) - logger.debug('find done for %s', odist) - return dists, problems diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/manifest.py b/.venv/Lib/site-packages/pip/_vendor/distlib/manifest.py deleted file mode 100644 index ca0fe44..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/manifest.py +++ /dev/null @@ -1,393 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012-2013 Python Software Foundation. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -""" -Class representing the list of files in a distribution. - -Equivalent to distutils.filelist, but fixes some problems. -""" -import fnmatch -import logging -import os -import re -import sys - -from . import DistlibException -from .compat import fsdecode -from .util import convert_path - - -__all__ = ['Manifest'] - -logger = logging.getLogger(__name__) - -# a \ followed by some spaces + EOL -_COLLAPSE_PATTERN = re.compile('\\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|\n(?=$)', re.M | re.S) - -# -# Due to the different results returned by fnmatch.translate, we need -# to do slightly different processing for Python 2.7 and 3.2 ... this needed -# to be brought in for Python 3.6 onwards. -# -_PYTHON_VERSION = sys.version_info[:2] - -class Manifest(object): - """A list of files built by on exploring the filesystem and filtered by - applying various patterns to what we find there. - """ - - def __init__(self, base=None): - """ - Initialise an instance. - - :param base: The base directory to explore under. - """ - self.base = os.path.abspath(os.path.normpath(base or os.getcwd())) - self.prefix = self.base + os.sep - self.allfiles = None - self.files = set() - - # - # Public API - # - - def findall(self): - """Find all files under the base and set ``allfiles`` to the absolute - pathnames of files found. - """ - from stat import S_ISREG, S_ISDIR, S_ISLNK - - self.allfiles = allfiles = [] - root = self.base - stack = [root] - pop = stack.pop - push = stack.append - - while stack: - root = pop() - names = os.listdir(root) - - for name in names: - fullname = os.path.join(root, name) - - # Avoid excess stat calls -- just one will do, thank you! - stat = os.stat(fullname) - mode = stat.st_mode - if S_ISREG(mode): - allfiles.append(fsdecode(fullname)) - elif S_ISDIR(mode) and not S_ISLNK(mode): - push(fullname) - - def add(self, item): - """ - Add a file to the manifest. - - :param item: The pathname to add. This can be relative to the base. - """ - if not item.startswith(self.prefix): - item = os.path.join(self.base, item) - self.files.add(os.path.normpath(item)) - - def add_many(self, items): - """ - Add a list of files to the manifest. - - :param items: The pathnames to add. These can be relative to the base. - """ - for item in items: - self.add(item) - - def sorted(self, wantdirs=False): - """ - Return sorted files in directory order - """ - - def add_dir(dirs, d): - dirs.add(d) - logger.debug('add_dir added %s', d) - if d != self.base: - parent, _ = os.path.split(d) - assert parent not in ('', '/') - add_dir(dirs, parent) - - result = set(self.files) # make a copy! - if wantdirs: - dirs = set() - for f in result: - add_dir(dirs, os.path.dirname(f)) - result |= dirs - return [os.path.join(*path_tuple) for path_tuple in - sorted(os.path.split(path) for path in result)] - - def clear(self): - """Clear all collected files.""" - self.files = set() - self.allfiles = [] - - def process_directive(self, directive): - """ - Process a directive which either adds some files from ``allfiles`` to - ``files``, or removes some files from ``files``. - - :param directive: The directive to process. This should be in a format - compatible with distutils ``MANIFEST.in`` files: - - http://docs.python.org/distutils/sourcedist.html#commands - """ - # Parse the line: split it up, make sure the right number of words - # is there, and return the relevant words. 'action' is always - # defined: it's the first word of the line. Which of the other - # three are defined depends on the action; it'll be either - # patterns, (dir and patterns), or (dirpattern). - action, patterns, thedir, dirpattern = self._parse_directive(directive) - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. - if action == 'include': - for pattern in patterns: - if not self._include_pattern(pattern, anchor=True): - logger.warning('no files found matching %r', pattern) - - elif action == 'exclude': - for pattern in patterns: - found = self._exclude_pattern(pattern, anchor=True) - #if not found: - # logger.warning('no previously-included files ' - # 'found matching %r', pattern) - - elif action == 'global-include': - for pattern in patterns: - if not self._include_pattern(pattern, anchor=False): - logger.warning('no files found matching %r ' - 'anywhere in distribution', pattern) - - elif action == 'global-exclude': - for pattern in patterns: - found = self._exclude_pattern(pattern, anchor=False) - #if not found: - # logger.warning('no previously-included files ' - # 'matching %r found anywhere in ' - # 'distribution', pattern) - - elif action == 'recursive-include': - for pattern in patterns: - if not self._include_pattern(pattern, prefix=thedir): - logger.warning('no files found matching %r ' - 'under directory %r', pattern, thedir) - - elif action == 'recursive-exclude': - for pattern in patterns: - found = self._exclude_pattern(pattern, prefix=thedir) - #if not found: - # logger.warning('no previously-included files ' - # 'matching %r found under directory %r', - # pattern, thedir) - - elif action == 'graft': - if not self._include_pattern(None, prefix=dirpattern): - logger.warning('no directories found matching %r', - dirpattern) - - elif action == 'prune': - if not self._exclude_pattern(None, prefix=dirpattern): - logger.warning('no previously-included directories found ' - 'matching %r', dirpattern) - else: # pragma: no cover - # This should never happen, as it should be caught in - # _parse_template_line - raise DistlibException( - 'invalid action %r' % action) - - # - # Private API - # - - def _parse_directive(self, directive): - """ - Validate a directive. - :param directive: The directive to validate. - :return: A tuple of action, patterns, thedir, dir_patterns - """ - words = directive.split() - if len(words) == 1 and words[0] not in ('include', 'exclude', - 'global-include', - 'global-exclude', - 'recursive-include', - 'recursive-exclude', - 'graft', 'prune'): - # no action given, let's use the default 'include' - words.insert(0, 'include') - - action = words[0] - patterns = thedir = dir_pattern = None - - if action in ('include', 'exclude', - 'global-include', 'global-exclude'): - if len(words) < 2: - raise DistlibException( - '%r expects ...' % action) - - patterns = [convert_path(word) for word in words[1:]] - - elif action in ('recursive-include', 'recursive-exclude'): - if len(words) < 3: - raise DistlibException( - '%r expects ...' % action) - - thedir = convert_path(words[1]) - patterns = [convert_path(word) for word in words[2:]] - - elif action in ('graft', 'prune'): - if len(words) != 2: - raise DistlibException( - '%r expects a single ' % action) - - dir_pattern = convert_path(words[1]) - - else: - raise DistlibException('unknown action %r' % action) - - return action, patterns, thedir, dir_pattern - - def _include_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): - """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. - - Patterns are not quite the same as implemented by the 'fnmatch' - module: '*' and '?' match non-special characters, where "special" - is platform-dependent: slash on Unix; colon, slash, and backslash on - DOS/Windows; and colon on Mac OS. - - If 'anchor' is true (the default), then the pattern match is more - stringent: "*.py" will match "foo.py" but not "foo/bar.py". If - 'anchor' is false, both of these will match. - - If 'prefix' is supplied, then only filenames starting with 'prefix' - (itself a pattern) and ending with 'pattern', with anything in between - them, will match. 'anchor' is ignored in this case. - - If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and - 'pattern' is assumed to be either a string containing a regex or a - regex object -- no translation is done, the regex is just compiled - and used as-is. - - Selected strings will be added to self.files. - - Return True if files are found. - """ - # XXX docstring lying about what the special chars are? - found = False - pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) - - # delayed loading of allfiles list - if self.allfiles is None: - self.findall() - - for name in self.allfiles: - if pattern_re.search(name): - self.files.add(name) - found = True - return found - - def _exclude_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): - """Remove strings (presumably filenames) from 'files' that match - 'pattern'. - - Other parameters are the same as for 'include_pattern()', above. - The list 'self.files' is modified in place. Return True if files are - found. - - This API is public to allow e.g. exclusion of SCM subdirs, e.g. when - packaging source distributions - """ - found = False - pattern_re = self._translate_pattern(pattern, anchor, prefix, is_regex) - for f in list(self.files): - if pattern_re.search(f): - self.files.remove(f) - found = True - return found - - def _translate_pattern(self, pattern, anchor=True, prefix=None, - is_regex=False): - """Translate a shell-like wildcard pattern to a compiled regular - expression. - - Return the compiled regex. If 'is_regex' true, - then 'pattern' is directly compiled to a regex (if it's a string) - or just returned as-is (assumes it's a regex object). - """ - if is_regex: - if isinstance(pattern, str): - return re.compile(pattern) - else: - return pattern - - if _PYTHON_VERSION > (3, 2): - # ditch start and end characters - start, _, end = self._glob_to_re('_').partition('_') - - if pattern: - pattern_re = self._glob_to_re(pattern) - if _PYTHON_VERSION > (3, 2): - assert pattern_re.startswith(start) and pattern_re.endswith(end) - else: - pattern_re = '' - - base = re.escape(os.path.join(self.base, '')) - if prefix is not None: - # ditch end of pattern character - if _PYTHON_VERSION <= (3, 2): - empty_pattern = self._glob_to_re('') - prefix_re = self._glob_to_re(prefix)[:-len(empty_pattern)] - else: - prefix_re = self._glob_to_re(prefix) - assert prefix_re.startswith(start) and prefix_re.endswith(end) - prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] - sep = os.sep - if os.sep == '\\': - sep = r'\\' - if _PYTHON_VERSION <= (3, 2): - pattern_re = '^' + base + sep.join((prefix_re, - '.*' + pattern_re)) - else: - pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] - pattern_re = r'%s%s%s%s.*%s%s' % (start, base, prefix_re, sep, - pattern_re, end) - else: # no prefix -- respect anchor flag - if anchor: - if _PYTHON_VERSION <= (3, 2): - pattern_re = '^' + base + pattern_re - else: - pattern_re = r'%s%s%s' % (start, base, pattern_re[len(start):]) - - return re.compile(pattern_re) - - def _glob_to_re(self, pattern): - """Translate a shell-like glob pattern to a regular expression. - - Return a string containing the regex. Differs from - 'fnmatch.translate()' in that '*' does not match "special characters" - (which are platform-specific). - """ - pattern_re = fnmatch.translate(pattern) - - # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which - # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, - # and by extension they shouldn't match such "special characters" under - # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters (currently: just os.sep). - sep = os.sep - if os.sep == '\\': - # we're using a regex to manipulate a regex, so we need - # to escape the backslash twice - sep = r'\\\\' - escaped = r'\1[^%s]' % sep - pattern_re = re.sub(r'((? y, - '!=': lambda x, y: x != y, - '<': lambda x, y: x < y, - '<=': lambda x, y: x == y or x < y, - '>': lambda x, y: x > y, - '>=': lambda x, y: x == y or x > y, - 'and': lambda x, y: x and y, - 'or': lambda x, y: x or y, - 'in': lambda x, y: x in y, - 'not in': lambda x, y: x not in y, - } - - def evaluate(self, expr, context): - """ - Evaluate a marker expression returned by the :func:`parse_requirement` - function in the specified context. - """ - if isinstance(expr, string_types): - if expr[0] in '\'"': - result = expr[1:-1] - else: - if expr not in context: - raise SyntaxError('unknown variable: %s' % expr) - result = context[expr] - else: - assert isinstance(expr, dict) - op = expr['op'] - if op not in self.operations: - raise NotImplementedError('op not implemented: %s' % op) - elhs = expr['lhs'] - erhs = expr['rhs'] - if _is_literal(expr['lhs']) and _is_literal(expr['rhs']): - raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs)) - - lhs = self.evaluate(elhs, context) - rhs = self.evaluate(erhs, context) - if ((elhs == 'python_version' or erhs == 'python_version') and - op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')): - lhs = NV(lhs) - rhs = NV(rhs) - elif elhs == 'python_version' and op in ('in', 'not in'): - lhs = NV(lhs) - rhs = _get_versions(rhs) - result = self.operations[op](lhs, rhs) - return result - -_DIGITS = re.compile(r'\d+\.\d+') - -def default_context(): - def format_full_version(info): - version = '%s.%s.%s' % (info.major, info.minor, info.micro) - kind = info.releaselevel - if kind != 'final': - version += kind[0] + str(info.serial) - return version - - if hasattr(sys, 'implementation'): - implementation_version = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - else: - implementation_version = '0' - implementation_name = '' - - ppv = platform.python_version() - m = _DIGITS.match(ppv) - pv = m.group(0) - result = { - 'implementation_name': implementation_name, - 'implementation_version': implementation_version, - 'os_name': os.name, - 'platform_machine': platform.machine(), - 'platform_python_implementation': platform.python_implementation(), - 'platform_release': platform.release(), - 'platform_system': platform.system(), - 'platform_version': platform.version(), - 'platform_in_venv': str(in_venv()), - 'python_full_version': ppv, - 'python_version': pv, - 'sys_platform': sys.platform, - } - return result - -DEFAULT_CONTEXT = default_context() -del default_context - -evaluator = Evaluator() - -def interpret(marker, execution_context=None): - """ - Interpret a marker and return a result depending on environment. - - :param marker: The marker to interpret. - :type marker: str - :param execution_context: The context used for name lookup. - :type execution_context: mapping - """ - try: - expr, rest = parse_marker(marker) - except Exception as e: - raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e)) - if rest and rest[0] != '#': - raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest)) - context = dict(DEFAULT_CONTEXT) - if execution_context: - context.update(execution_context) - return evaluator.evaluate(expr, context) diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/metadata.py b/.venv/Lib/site-packages/pip/_vendor/distlib/metadata.py deleted file mode 100644 index c329e19..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/metadata.py +++ /dev/null @@ -1,1076 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012 The Python Software Foundation. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -"""Implementation of the Metadata for Python packages PEPs. - -Supports all metadata formats (1.0, 1.1, 1.2, 1.3/2.1 and 2.2). -""" -from __future__ import unicode_literals - -import codecs -from email import message_from_file -import json -import logging -import re - - -from . import DistlibException, __version__ -from .compat import StringIO, string_types, text_type -from .markers import interpret -from .util import extract_by_key, get_extras -from .version import get_scheme, PEP440_VERSION_RE - -logger = logging.getLogger(__name__) - - -class MetadataMissingError(DistlibException): - """A required metadata is missing""" - - -class MetadataConflictError(DistlibException): - """Attempt to read or write metadata fields that are conflictual.""" - - -class MetadataUnrecognizedVersionError(DistlibException): - """Unknown metadata version number.""" - - -class MetadataInvalidError(DistlibException): - """A metadata value is invalid""" - -# public API of this module -__all__ = ['Metadata', 'PKG_INFO_ENCODING', 'PKG_INFO_PREFERRED_VERSION'] - -# Encoding used for the PKG-INFO files -PKG_INFO_ENCODING = 'utf-8' - -# preferred version. Hopefully will be changed -# to 1.2 once PEP 345 is supported everywhere -PKG_INFO_PREFERRED_VERSION = '1.1' - -_LINE_PREFIX_1_2 = re.compile('\n \\|') -_LINE_PREFIX_PRE_1_2 = re.compile('\n ') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'License') - -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'License', 'Classifier', 'Download-URL', 'Obsoletes', - 'Provides', 'Requires') - -_314_MARKERS = ('Obsoletes', 'Provides', 'Requires', 'Classifier', - 'Download-URL') - -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'Maintainer', 'Maintainer-email', 'License', - 'Classifier', 'Download-URL', 'Obsoletes-Dist', - 'Project-URL', 'Provides-Dist', 'Requires-Dist', - 'Requires-Python', 'Requires-External') - -_345_MARKERS = ('Provides-Dist', 'Requires-Dist', 'Requires-Python', - 'Obsoletes-Dist', 'Requires-External', 'Maintainer', - 'Maintainer-email', 'Project-URL') - -_426_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', - 'Supported-Platform', 'Summary', 'Description', - 'Keywords', 'Home-page', 'Author', 'Author-email', - 'Maintainer', 'Maintainer-email', 'License', - 'Classifier', 'Download-URL', 'Obsoletes-Dist', - 'Project-URL', 'Provides-Dist', 'Requires-Dist', - 'Requires-Python', 'Requires-External', 'Private-Version', - 'Obsoleted-By', 'Setup-Requires-Dist', 'Extension', - 'Provides-Extra') - -_426_MARKERS = ('Private-Version', 'Provides-Extra', 'Obsoleted-By', - 'Setup-Requires-Dist', 'Extension') - -# See issue #106: Sometimes 'Requires' and 'Provides' occur wrongly in -# the metadata. Include them in the tuple literal below to allow them -# (for now). -# Ditto for Obsoletes - see issue #140. -_566_FIELDS = _426_FIELDS + ('Description-Content-Type', - 'Requires', 'Provides', 'Obsoletes') - -_566_MARKERS = ('Description-Content-Type',) - -_643_MARKERS = ('Dynamic', 'License-File') - -_643_FIELDS = _566_FIELDS + _643_MARKERS - -_ALL_FIELDS = set() -_ALL_FIELDS.update(_241_FIELDS) -_ALL_FIELDS.update(_314_FIELDS) -_ALL_FIELDS.update(_345_FIELDS) -_ALL_FIELDS.update(_426_FIELDS) -_ALL_FIELDS.update(_566_FIELDS) -_ALL_FIELDS.update(_643_FIELDS) - -EXTRA_RE = re.compile(r'''extra\s*==\s*("([^"]+)"|'([^']+)')''') - - -def _version2fieldlist(version): - if version == '1.0': - return _241_FIELDS - elif version == '1.1': - return _314_FIELDS - elif version == '1.2': - return _345_FIELDS - elif version in ('1.3', '2.1'): - # avoid adding field names if already there - return _345_FIELDS + tuple(f for f in _566_FIELDS if f not in _345_FIELDS) - elif version == '2.0': - raise ValueError('Metadata 2.0 is withdrawn and not supported') - # return _426_FIELDS - elif version == '2.2': - return _643_FIELDS - raise MetadataUnrecognizedVersionError(version) - - -def _best_version(fields): - """Detect the best version depending on the fields used.""" - def _has_marker(keys, markers): - for marker in markers: - if marker in keys: - return True - return False - - keys = [] - for key, value in fields.items(): - if value in ([], 'UNKNOWN', None): - continue - keys.append(key) - - possible_versions = ['1.0', '1.1', '1.2', '1.3', '2.1', '2.2'] # 2.0 removed - - # first let's try to see if a field is not part of one of the version - for key in keys: - if key not in _241_FIELDS and '1.0' in possible_versions: - possible_versions.remove('1.0') - logger.debug('Removed 1.0 due to %s', key) - if key not in _314_FIELDS and '1.1' in possible_versions: - possible_versions.remove('1.1') - logger.debug('Removed 1.1 due to %s', key) - if key not in _345_FIELDS and '1.2' in possible_versions: - possible_versions.remove('1.2') - logger.debug('Removed 1.2 due to %s', key) - if key not in _566_FIELDS and '1.3' in possible_versions: - possible_versions.remove('1.3') - logger.debug('Removed 1.3 due to %s', key) - if key not in _566_FIELDS and '2.1' in possible_versions: - if key != 'Description': # In 2.1, description allowed after headers - possible_versions.remove('2.1') - logger.debug('Removed 2.1 due to %s', key) - if key not in _643_FIELDS and '2.2' in possible_versions: - possible_versions.remove('2.2') - logger.debug('Removed 2.2 due to %s', key) - # if key not in _426_FIELDS and '2.0' in possible_versions: - # possible_versions.remove('2.0') - # logger.debug('Removed 2.0 due to %s', key) - - # possible_version contains qualified versions - if len(possible_versions) == 1: - return possible_versions[0] # found ! - elif len(possible_versions) == 0: - logger.debug('Out of options - unknown metadata set: %s', fields) - raise MetadataConflictError('Unknown metadata set') - - # let's see if one unique marker is found - is_1_1 = '1.1' in possible_versions and _has_marker(keys, _314_MARKERS) - is_1_2 = '1.2' in possible_versions and _has_marker(keys, _345_MARKERS) - is_2_1 = '2.1' in possible_versions and _has_marker(keys, _566_MARKERS) - # is_2_0 = '2.0' in possible_versions and _has_marker(keys, _426_MARKERS) - is_2_2 = '2.2' in possible_versions and _has_marker(keys, _643_MARKERS) - if int(is_1_1) + int(is_1_2) + int(is_2_1) + int(is_2_2) > 1: - raise MetadataConflictError('You used incompatible 1.1/1.2/2.1/2.2 fields') - - # we have the choice, 1.0, or 1.2, 2.1 or 2.2 - # - 1.0 has a broken Summary field but works with all tools - # - 1.1 is to avoid - # - 1.2 fixes Summary but has little adoption - # - 2.1 adds more features - # - 2.2 is the latest - if not is_1_1 and not is_1_2 and not is_2_1 and not is_2_2: - # we couldn't find any specific marker - if PKG_INFO_PREFERRED_VERSION in possible_versions: - return PKG_INFO_PREFERRED_VERSION - if is_1_1: - return '1.1' - if is_1_2: - return '1.2' - if is_2_1: - return '2.1' - # if is_2_2: - # return '2.2' - - return '2.2' - -# This follows the rules about transforming keys as described in -# https://www.python.org/dev/peps/pep-0566/#id17 -_ATTR2FIELD = { - name.lower().replace("-", "_"): name for name in _ALL_FIELDS -} -_FIELD2ATTR = {field: attr for attr, field in _ATTR2FIELD.items()} - -_PREDICATE_FIELDS = ('Requires-Dist', 'Obsoletes-Dist', 'Provides-Dist') -_VERSIONS_FIELDS = ('Requires-Python',) -_VERSION_FIELDS = ('Version',) -_LISTFIELDS = ('Platform', 'Classifier', 'Obsoletes', - 'Requires', 'Provides', 'Obsoletes-Dist', - 'Provides-Dist', 'Requires-Dist', 'Requires-External', - 'Project-URL', 'Supported-Platform', 'Setup-Requires-Dist', - 'Provides-Extra', 'Extension', 'License-File') -_LISTTUPLEFIELDS = ('Project-URL',) - -_ELEMENTSFIELD = ('Keywords',) - -_UNICODEFIELDS = ('Author', 'Maintainer', 'Summary', 'Description') - -_MISSING = object() - -_FILESAFE = re.compile('[^A-Za-z0-9.]+') - - -def _get_name_and_version(name, version, for_filename=False): - """Return the distribution name with version. - - If for_filename is true, return a filename-escaped form.""" - if for_filename: - # For both name and version any runs of non-alphanumeric or '.' - # characters are replaced with a single '-'. Additionally any - # spaces in the version string become '.' - name = _FILESAFE.sub('-', name) - version = _FILESAFE.sub('-', version.replace(' ', '.')) - return '%s-%s' % (name, version) - - -class LegacyMetadata(object): - """The legacy metadata of a release. - - Supports versions 1.0, 1.1, 1.2, 2.0 and 1.3/2.1 (auto-detected). You can - instantiate the class with one of these arguments (or none): - - *path*, the path to a metadata file - - *fileobj* give a file-like object with metadata as content - - *mapping* is a dict-like object - - *scheme* is a version scheme name - """ - # TODO document the mapping API and UNKNOWN default key - - def __init__(self, path=None, fileobj=None, mapping=None, - scheme='default'): - if [path, fileobj, mapping].count(None) < 2: - raise TypeError('path, fileobj and mapping are exclusive') - self._fields = {} - self.requires_files = [] - self._dependencies = None - self.scheme = scheme - if path is not None: - self.read(path) - elif fileobj is not None: - self.read_file(fileobj) - elif mapping is not None: - self.update(mapping) - self.set_metadata_version() - - def set_metadata_version(self): - self._fields['Metadata-Version'] = _best_version(self._fields) - - def _write_field(self, fileobj, name, value): - fileobj.write('%s: %s\n' % (name, value)) - - def __getitem__(self, name): - return self.get(name) - - def __setitem__(self, name, value): - return self.set(name, value) - - def __delitem__(self, name): - field_name = self._convert_name(name) - try: - del self._fields[field_name] - except KeyError: - raise KeyError(name) - - def __contains__(self, name): - return (name in self._fields or - self._convert_name(name) in self._fields) - - def _convert_name(self, name): - if name in _ALL_FIELDS: - return name - name = name.replace('-', '_').lower() - return _ATTR2FIELD.get(name, name) - - def _default_value(self, name): - if name in _LISTFIELDS or name in _ELEMENTSFIELD: - return [] - return 'UNKNOWN' - - def _remove_line_prefix(self, value): - if self.metadata_version in ('1.0', '1.1'): - return _LINE_PREFIX_PRE_1_2.sub('\n', value) - else: - return _LINE_PREFIX_1_2.sub('\n', value) - - def __getattr__(self, name): - if name in _ATTR2FIELD: - return self[name] - raise AttributeError(name) - - # - # Public API - # - -# dependencies = property(_get_dependencies, _set_dependencies) - - def get_fullname(self, filesafe=False): - """Return the distribution name with version. - - If filesafe is true, return a filename-escaped form.""" - return _get_name_and_version(self['Name'], self['Version'], filesafe) - - def is_field(self, name): - """return True if name is a valid metadata key""" - name = self._convert_name(name) - return name in _ALL_FIELDS - - def is_multi_field(self, name): - name = self._convert_name(name) - return name in _LISTFIELDS - - def read(self, filepath): - """Read the metadata values from a file path.""" - fp = codecs.open(filepath, 'r', encoding='utf-8') - try: - self.read_file(fp) - finally: - fp.close() - - def read_file(self, fileob): - """Read the metadata values from a file object.""" - msg = message_from_file(fileob) - self._fields['Metadata-Version'] = msg['metadata-version'] - - # When reading, get all the fields we can - for field in _ALL_FIELDS: - if field not in msg: - continue - if field in _LISTFIELDS: - # we can have multiple lines - values = msg.get_all(field) - if field in _LISTTUPLEFIELDS and values is not None: - values = [tuple(value.split(',')) for value in values] - self.set(field, values) - else: - # single line - value = msg[field] - if value is not None and value != 'UNKNOWN': - self.set(field, value) - - # PEP 566 specifies that the body be used for the description, if - # available - body = msg.get_payload() - self["Description"] = body if body else self["Description"] - # logger.debug('Attempting to set metadata for %s', self) - # self.set_metadata_version() - - def write(self, filepath, skip_unknown=False): - """Write the metadata fields to filepath.""" - fp = codecs.open(filepath, 'w', encoding='utf-8') - try: - self.write_file(fp, skip_unknown) - finally: - fp.close() - - def write_file(self, fileobject, skip_unknown=False): - """Write the PKG-INFO format data to a file object.""" - self.set_metadata_version() - - for field in _version2fieldlist(self['Metadata-Version']): - values = self.get(field) - if skip_unknown and values in ('UNKNOWN', [], ['UNKNOWN']): - continue - if field in _ELEMENTSFIELD: - self._write_field(fileobject, field, ','.join(values)) - continue - if field not in _LISTFIELDS: - if field == 'Description': - if self.metadata_version in ('1.0', '1.1'): - values = values.replace('\n', '\n ') - else: - values = values.replace('\n', '\n |') - values = [values] - - if field in _LISTTUPLEFIELDS: - values = [','.join(value) for value in values] - - for value in values: - self._write_field(fileobject, field, value) - - def update(self, other=None, **kwargs): - """Set metadata values from the given iterable `other` and kwargs. - - Behavior is like `dict.update`: If `other` has a ``keys`` method, - they are looped over and ``self[key]`` is assigned ``other[key]``. - Else, ``other`` is an iterable of ``(key, value)`` iterables. - - Keys that don't match a metadata field or that have an empty value are - dropped. - """ - def _set(key, value): - if key in _ATTR2FIELD and value: - self.set(self._convert_name(key), value) - - if not other: - # other is None or empty container - pass - elif hasattr(other, 'keys'): - for k in other.keys(): - _set(k, other[k]) - else: - for k, v in other: - _set(k, v) - - if kwargs: - for k, v in kwargs.items(): - _set(k, v) - - def set(self, name, value): - """Control then set a metadata field.""" - name = self._convert_name(name) - - if ((name in _ELEMENTSFIELD or name == 'Platform') and - not isinstance(value, (list, tuple))): - if isinstance(value, string_types): - value = [v.strip() for v in value.split(',')] - else: - value = [] - elif (name in _LISTFIELDS and - not isinstance(value, (list, tuple))): - if isinstance(value, string_types): - value = [value] - else: - value = [] - - if logger.isEnabledFor(logging.WARNING): - project_name = self['Name'] - - scheme = get_scheme(self.scheme) - if name in _PREDICATE_FIELDS and value is not None: - for v in value: - # check that the values are valid - if not scheme.is_valid_matcher(v.split(';')[0]): - logger.warning( - "'%s': '%s' is not valid (field '%s')", - project_name, v, name) - # FIXME this rejects UNKNOWN, is that right? - elif name in _VERSIONS_FIELDS and value is not None: - if not scheme.is_valid_constraint_list(value): - logger.warning("'%s': '%s' is not a valid version (field '%s')", - project_name, value, name) - elif name in _VERSION_FIELDS and value is not None: - if not scheme.is_valid_version(value): - logger.warning("'%s': '%s' is not a valid version (field '%s')", - project_name, value, name) - - if name in _UNICODEFIELDS: - if name == 'Description': - value = self._remove_line_prefix(value) - - self._fields[name] = value - - def get(self, name, default=_MISSING): - """Get a metadata field.""" - name = self._convert_name(name) - if name not in self._fields: - if default is _MISSING: - default = self._default_value(name) - return default - if name in _UNICODEFIELDS: - value = self._fields[name] - return value - elif name in _LISTFIELDS: - value = self._fields[name] - if value is None: - return [] - res = [] - for val in value: - if name not in _LISTTUPLEFIELDS: - res.append(val) - else: - # That's for Project-URL - res.append((val[0], val[1])) - return res - - elif name in _ELEMENTSFIELD: - value = self._fields[name] - if isinstance(value, string_types): - return value.split(',') - return self._fields[name] - - def check(self, strict=False): - """Check if the metadata is compliant. If strict is True then raise if - no Name or Version are provided""" - self.set_metadata_version() - - # XXX should check the versions (if the file was loaded) - missing, warnings = [], [] - - for attr in ('Name', 'Version'): # required by PEP 345 - if attr not in self: - missing.append(attr) - - if strict and missing != []: - msg = 'missing required metadata: %s' % ', '.join(missing) - raise MetadataMissingError(msg) - - for attr in ('Home-page', 'Author'): - if attr not in self: - missing.append(attr) - - # checking metadata 1.2 (XXX needs to check 1.1, 1.0) - if self['Metadata-Version'] != '1.2': - return missing, warnings - - scheme = get_scheme(self.scheme) - - def are_valid_constraints(value): - for v in value: - if not scheme.is_valid_matcher(v.split(';')[0]): - return False - return True - - for fields, controller in ((_PREDICATE_FIELDS, are_valid_constraints), - (_VERSIONS_FIELDS, - scheme.is_valid_constraint_list), - (_VERSION_FIELDS, - scheme.is_valid_version)): - for field in fields: - value = self.get(field, None) - if value is not None and not controller(value): - warnings.append("Wrong value for '%s': %s" % (field, value)) - - return missing, warnings - - def todict(self, skip_missing=False): - """Return fields as a dict. - - Field names will be converted to use the underscore-lowercase style - instead of hyphen-mixed case (i.e. home_page instead of Home-page). - This is as per https://www.python.org/dev/peps/pep-0566/#id17. - """ - self.set_metadata_version() - - fields = _version2fieldlist(self['Metadata-Version']) - - data = {} - - for field_name in fields: - if not skip_missing or field_name in self._fields: - key = _FIELD2ATTR[field_name] - if key != 'project_url': - data[key] = self[field_name] - else: - data[key] = [','.join(u) for u in self[field_name]] - - return data - - def add_requirements(self, requirements): - if self['Metadata-Version'] == '1.1': - # we can't have 1.1 metadata *and* Setuptools requires - for field in ('Obsoletes', 'Requires', 'Provides'): - if field in self: - del self[field] - self['Requires-Dist'] += requirements - - # Mapping API - # TODO could add iter* variants - - def keys(self): - return list(_version2fieldlist(self['Metadata-Version'])) - - def __iter__(self): - for key in self.keys(): - yield key - - def values(self): - return [self[key] for key in self.keys()] - - def items(self): - return [(key, self[key]) for key in self.keys()] - - def __repr__(self): - return '<%s %s %s>' % (self.__class__.__name__, self.name, - self.version) - - -METADATA_FILENAME = 'pydist.json' -WHEEL_METADATA_FILENAME = 'metadata.json' -LEGACY_METADATA_FILENAME = 'METADATA' - - -class Metadata(object): - """ - The metadata of a release. This implementation uses 2.1 - metadata where possible. If not possible, it wraps a LegacyMetadata - instance which handles the key-value metadata format. - """ - - METADATA_VERSION_MATCHER = re.compile(r'^\d+(\.\d+)*$') - - NAME_MATCHER = re.compile('^[0-9A-Z]([0-9A-Z_.-]*[0-9A-Z])?$', re.I) - - FIELDNAME_MATCHER = re.compile('^[A-Z]([0-9A-Z-]*[0-9A-Z])?$', re.I) - - VERSION_MATCHER = PEP440_VERSION_RE - - SUMMARY_MATCHER = re.compile('.{1,2047}') - - METADATA_VERSION = '2.0' - - GENERATOR = 'distlib (%s)' % __version__ - - MANDATORY_KEYS = { - 'name': (), - 'version': (), - 'summary': ('legacy',), - } - - INDEX_KEYS = ('name version license summary description author ' - 'author_email keywords platform home_page classifiers ' - 'download_url') - - DEPENDENCY_KEYS = ('extras run_requires test_requires build_requires ' - 'dev_requires provides meta_requires obsoleted_by ' - 'supports_environments') - - SYNTAX_VALIDATORS = { - 'metadata_version': (METADATA_VERSION_MATCHER, ()), - 'name': (NAME_MATCHER, ('legacy',)), - 'version': (VERSION_MATCHER, ('legacy',)), - 'summary': (SUMMARY_MATCHER, ('legacy',)), - 'dynamic': (FIELDNAME_MATCHER, ('legacy',)), - } - - __slots__ = ('_legacy', '_data', 'scheme') - - def __init__(self, path=None, fileobj=None, mapping=None, - scheme='default'): - if [path, fileobj, mapping].count(None) < 2: - raise TypeError('path, fileobj and mapping are exclusive') - self._legacy = None - self._data = None - self.scheme = scheme - #import pdb; pdb.set_trace() - if mapping is not None: - try: - self._validate_mapping(mapping, scheme) - self._data = mapping - except MetadataUnrecognizedVersionError: - self._legacy = LegacyMetadata(mapping=mapping, scheme=scheme) - self.validate() - else: - data = None - if path: - with open(path, 'rb') as f: - data = f.read() - elif fileobj: - data = fileobj.read() - if data is None: - # Initialised with no args - to be added - self._data = { - 'metadata_version': self.METADATA_VERSION, - 'generator': self.GENERATOR, - } - else: - if not isinstance(data, text_type): - data = data.decode('utf-8') - try: - self._data = json.loads(data) - self._validate_mapping(self._data, scheme) - except ValueError: - # Note: MetadataUnrecognizedVersionError does not - # inherit from ValueError (it's a DistlibException, - # which should not inherit from ValueError). - # The ValueError comes from the json.load - if that - # succeeds and we get a validation error, we want - # that to propagate - self._legacy = LegacyMetadata(fileobj=StringIO(data), - scheme=scheme) - self.validate() - - common_keys = set(('name', 'version', 'license', 'keywords', 'summary')) - - none_list = (None, list) - none_dict = (None, dict) - - mapped_keys = { - 'run_requires': ('Requires-Dist', list), - 'build_requires': ('Setup-Requires-Dist', list), - 'dev_requires': none_list, - 'test_requires': none_list, - 'meta_requires': none_list, - 'extras': ('Provides-Extra', list), - 'modules': none_list, - 'namespaces': none_list, - 'exports': none_dict, - 'commands': none_dict, - 'classifiers': ('Classifier', list), - 'source_url': ('Download-URL', None), - 'metadata_version': ('Metadata-Version', None), - } - - del none_list, none_dict - - def __getattribute__(self, key): - common = object.__getattribute__(self, 'common_keys') - mapped = object.__getattribute__(self, 'mapped_keys') - if key in mapped: - lk, maker = mapped[key] - if self._legacy: - if lk is None: - result = None if maker is None else maker() - else: - result = self._legacy.get(lk) - else: - value = None if maker is None else maker() - if key not in ('commands', 'exports', 'modules', 'namespaces', - 'classifiers'): - result = self._data.get(key, value) - else: - # special cases for PEP 459 - sentinel = object() - result = sentinel - d = self._data.get('extensions') - if d: - if key == 'commands': - result = d.get('python.commands', value) - elif key == 'classifiers': - d = d.get('python.details') - if d: - result = d.get(key, value) - else: - d = d.get('python.exports') - if not d: - d = self._data.get('python.exports') - if d: - result = d.get(key, value) - if result is sentinel: - result = value - elif key not in common: - result = object.__getattribute__(self, key) - elif self._legacy: - result = self._legacy.get(key) - else: - result = self._data.get(key) - return result - - def _validate_value(self, key, value, scheme=None): - if key in self.SYNTAX_VALIDATORS: - pattern, exclusions = self.SYNTAX_VALIDATORS[key] - if (scheme or self.scheme) not in exclusions: - m = pattern.match(value) - if not m: - raise MetadataInvalidError("'%s' is an invalid value for " - "the '%s' property" % (value, - key)) - - def __setattr__(self, key, value): - self._validate_value(key, value) - common = object.__getattribute__(self, 'common_keys') - mapped = object.__getattribute__(self, 'mapped_keys') - if key in mapped: - lk, _ = mapped[key] - if self._legacy: - if lk is None: - raise NotImplementedError - self._legacy[lk] = value - elif key not in ('commands', 'exports', 'modules', 'namespaces', - 'classifiers'): - self._data[key] = value - else: - # special cases for PEP 459 - d = self._data.setdefault('extensions', {}) - if key == 'commands': - d['python.commands'] = value - elif key == 'classifiers': - d = d.setdefault('python.details', {}) - d[key] = value - else: - d = d.setdefault('python.exports', {}) - d[key] = value - elif key not in common: - object.__setattr__(self, key, value) - else: - if key == 'keywords': - if isinstance(value, string_types): - value = value.strip() - if value: - value = value.split() - else: - value = [] - if self._legacy: - self._legacy[key] = value - else: - self._data[key] = value - - @property - def name_and_version(self): - return _get_name_and_version(self.name, self.version, True) - - @property - def provides(self): - if self._legacy: - result = self._legacy['Provides-Dist'] - else: - result = self._data.setdefault('provides', []) - s = '%s (%s)' % (self.name, self.version) - if s not in result: - result.append(s) - return result - - @provides.setter - def provides(self, value): - if self._legacy: - self._legacy['Provides-Dist'] = value - else: - self._data['provides'] = value - - def get_requirements(self, reqts, extras=None, env=None): - """ - Base method to get dependencies, given a set of extras - to satisfy and an optional environment context. - :param reqts: A list of sometimes-wanted dependencies, - perhaps dependent on extras and environment. - :param extras: A list of optional components being requested. - :param env: An optional environment for marker evaluation. - """ - if self._legacy: - result = reqts - else: - result = [] - extras = get_extras(extras or [], self.extras) - for d in reqts: - if 'extra' not in d and 'environment' not in d: - # unconditional - include = True - else: - if 'extra' not in d: - # Not extra-dependent - only environment-dependent - include = True - else: - include = d.get('extra') in extras - if include: - # Not excluded because of extras, check environment - marker = d.get('environment') - if marker: - include = interpret(marker, env) - if include: - result.extend(d['requires']) - for key in ('build', 'dev', 'test'): - e = ':%s:' % key - if e in extras: - extras.remove(e) - # A recursive call, but it should terminate since 'test' - # has been removed from the extras - reqts = self._data.get('%s_requires' % key, []) - result.extend(self.get_requirements(reqts, extras=extras, - env=env)) - return result - - @property - def dictionary(self): - if self._legacy: - return self._from_legacy() - return self._data - - @property - def dependencies(self): - if self._legacy: - raise NotImplementedError - else: - return extract_by_key(self._data, self.DEPENDENCY_KEYS) - - @dependencies.setter - def dependencies(self, value): - if self._legacy: - raise NotImplementedError - else: - self._data.update(value) - - def _validate_mapping(self, mapping, scheme): - if mapping.get('metadata_version') != self.METADATA_VERSION: - raise MetadataUnrecognizedVersionError() - missing = [] - for key, exclusions in self.MANDATORY_KEYS.items(): - if key not in mapping: - if scheme not in exclusions: - missing.append(key) - if missing: - msg = 'Missing metadata items: %s' % ', '.join(missing) - raise MetadataMissingError(msg) - for k, v in mapping.items(): - self._validate_value(k, v, scheme) - - def validate(self): - if self._legacy: - missing, warnings = self._legacy.check(True) - if missing or warnings: - logger.warning('Metadata: missing: %s, warnings: %s', - missing, warnings) - else: - self._validate_mapping(self._data, self.scheme) - - def todict(self): - if self._legacy: - return self._legacy.todict(True) - else: - result = extract_by_key(self._data, self.INDEX_KEYS) - return result - - def _from_legacy(self): - assert self._legacy and not self._data - result = { - 'metadata_version': self.METADATA_VERSION, - 'generator': self.GENERATOR, - } - lmd = self._legacy.todict(True) # skip missing ones - for k in ('name', 'version', 'license', 'summary', 'description', - 'classifier'): - if k in lmd: - if k == 'classifier': - nk = 'classifiers' - else: - nk = k - result[nk] = lmd[k] - kw = lmd.get('Keywords', []) - if kw == ['']: - kw = [] - result['keywords'] = kw - keys = (('requires_dist', 'run_requires'), - ('setup_requires_dist', 'build_requires')) - for ok, nk in keys: - if ok in lmd and lmd[ok]: - result[nk] = [{'requires': lmd[ok]}] - result['provides'] = self.provides - author = {} - maintainer = {} - return result - - LEGACY_MAPPING = { - 'name': 'Name', - 'version': 'Version', - ('extensions', 'python.details', 'license'): 'License', - 'summary': 'Summary', - 'description': 'Description', - ('extensions', 'python.project', 'project_urls', 'Home'): 'Home-page', - ('extensions', 'python.project', 'contacts', 0, 'name'): 'Author', - ('extensions', 'python.project', 'contacts', 0, 'email'): 'Author-email', - 'source_url': 'Download-URL', - ('extensions', 'python.details', 'classifiers'): 'Classifier', - } - - def _to_legacy(self): - def process_entries(entries): - reqts = set() - for e in entries: - extra = e.get('extra') - env = e.get('environment') - rlist = e['requires'] - for r in rlist: - if not env and not extra: - reqts.add(r) - else: - marker = '' - if extra: - marker = 'extra == "%s"' % extra - if env: - if marker: - marker = '(%s) and %s' % (env, marker) - else: - marker = env - reqts.add(';'.join((r, marker))) - return reqts - - assert self._data and not self._legacy - result = LegacyMetadata() - nmd = self._data - # import pdb; pdb.set_trace() - for nk, ok in self.LEGACY_MAPPING.items(): - if not isinstance(nk, tuple): - if nk in nmd: - result[ok] = nmd[nk] - else: - d = nmd - found = True - for k in nk: - try: - d = d[k] - except (KeyError, IndexError): - found = False - break - if found: - result[ok] = d - r1 = process_entries(self.run_requires + self.meta_requires) - r2 = process_entries(self.build_requires + self.dev_requires) - if self.extras: - result['Provides-Extra'] = sorted(self.extras) - result['Requires-Dist'] = sorted(r1) - result['Setup-Requires-Dist'] = sorted(r2) - # TODO: any other fields wanted - return result - - def write(self, path=None, fileobj=None, legacy=False, skip_unknown=True): - if [path, fileobj].count(None) != 1: - raise ValueError('Exactly one of path and fileobj is needed') - self.validate() - if legacy: - if self._legacy: - legacy_md = self._legacy - else: - legacy_md = self._to_legacy() - if path: - legacy_md.write(path, skip_unknown=skip_unknown) - else: - legacy_md.write_file(fileobj, skip_unknown=skip_unknown) - else: - if self._legacy: - d = self._from_legacy() - else: - d = self._data - if fileobj: - json.dump(d, fileobj, ensure_ascii=True, indent=2, - sort_keys=True) - else: - with codecs.open(path, 'w', 'utf-8') as f: - json.dump(d, f, ensure_ascii=True, indent=2, - sort_keys=True) - - def add_requirements(self, requirements): - if self._legacy: - self._legacy.add_requirements(requirements) - else: - run_requires = self._data.setdefault('run_requires', []) - always = None - for entry in run_requires: - if 'environment' not in entry and 'extra' not in entry: - always = entry - break - if always is None: - always = { 'requires': requirements } - run_requires.insert(0, always) - else: - rset = set(always['requires']) | set(requirements) - always['requires'] = sorted(rset) - - def __repr__(self): - name = self.name or '(no name)' - version = self.version or 'no version' - return '<%s %s %s (%s)>' % (self.__class__.__name__, - self.metadata_version, name, version) diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/resources.py b/.venv/Lib/site-packages/pip/_vendor/distlib/resources.py deleted file mode 100644 index fef52aa..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/resources.py +++ /dev/null @@ -1,358 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013-2017 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -from __future__ import unicode_literals - -import bisect -import io -import logging -import os -import pkgutil -import sys -import types -import zipimport - -from . import DistlibException -from .util import cached_property, get_cache_base, Cache - -logger = logging.getLogger(__name__) - - -cache = None # created when needed - - -class ResourceCache(Cache): - def __init__(self, base=None): - if base is None: - # Use native string to avoid issues on 2.x: see Python #20140. - base = os.path.join(get_cache_base(), str('resource-cache')) - super(ResourceCache, self).__init__(base) - - def is_stale(self, resource, path): - """ - Is the cache stale for the given resource? - - :param resource: The :class:`Resource` being cached. - :param path: The path of the resource in the cache. - :return: True if the cache is stale. - """ - # Cache invalidation is a hard problem :-) - return True - - def get(self, resource): - """ - Get a resource into the cache, - - :param resource: A :class:`Resource` instance. - :return: The pathname of the resource in the cache. - """ - prefix, path = resource.finder.get_cache_info(resource) - if prefix is None: - result = path - else: - result = os.path.join(self.base, self.prefix_to_dir(prefix), path) - dirname = os.path.dirname(result) - if not os.path.isdir(dirname): - os.makedirs(dirname) - if not os.path.exists(result): - stale = True - else: - stale = self.is_stale(resource, path) - if stale: - # write the bytes of the resource to the cache location - with open(result, 'wb') as f: - f.write(resource.bytes) - return result - - -class ResourceBase(object): - def __init__(self, finder, name): - self.finder = finder - self.name = name - - -class Resource(ResourceBase): - """ - A class representing an in-package resource, such as a data file. This is - not normally instantiated by user code, but rather by a - :class:`ResourceFinder` which manages the resource. - """ - is_container = False # Backwards compatibility - - def as_stream(self): - """ - Get the resource as a stream. - - This is not a property to make it obvious that it returns a new stream - each time. - """ - return self.finder.get_stream(self) - - @cached_property - def file_path(self): - global cache - if cache is None: - cache = ResourceCache() - return cache.get(self) - - @cached_property - def bytes(self): - return self.finder.get_bytes(self) - - @cached_property - def size(self): - return self.finder.get_size(self) - - -class ResourceContainer(ResourceBase): - is_container = True # Backwards compatibility - - @cached_property - def resources(self): - return self.finder.get_resources(self) - - -class ResourceFinder(object): - """ - Resource finder for file system resources. - """ - - if sys.platform.startswith('java'): - skipped_extensions = ('.pyc', '.pyo', '.class') - else: - skipped_extensions = ('.pyc', '.pyo') - - def __init__(self, module): - self.module = module - self.loader = getattr(module, '__loader__', None) - self.base = os.path.dirname(getattr(module, '__file__', '')) - - def _adjust_path(self, path): - return os.path.realpath(path) - - def _make_path(self, resource_name): - # Issue #50: need to preserve type of path on Python 2.x - # like os.path._get_sep - if isinstance(resource_name, bytes): # should only happen on 2.x - sep = b'/' - else: - sep = '/' - parts = resource_name.split(sep) - parts.insert(0, self.base) - result = os.path.join(*parts) - return self._adjust_path(result) - - def _find(self, path): - return os.path.exists(path) - - def get_cache_info(self, resource): - return None, resource.path - - def find(self, resource_name): - path = self._make_path(resource_name) - if not self._find(path): - result = None - else: - if self._is_directory(path): - result = ResourceContainer(self, resource_name) - else: - result = Resource(self, resource_name) - result.path = path - return result - - def get_stream(self, resource): - return open(resource.path, 'rb') - - def get_bytes(self, resource): - with open(resource.path, 'rb') as f: - return f.read() - - def get_size(self, resource): - return os.path.getsize(resource.path) - - def get_resources(self, resource): - def allowed(f): - return (f != '__pycache__' and not - f.endswith(self.skipped_extensions)) - return set([f for f in os.listdir(resource.path) if allowed(f)]) - - def is_container(self, resource): - return self._is_directory(resource.path) - - _is_directory = staticmethod(os.path.isdir) - - def iterator(self, resource_name): - resource = self.find(resource_name) - if resource is not None: - todo = [resource] - while todo: - resource = todo.pop(0) - yield resource - if resource.is_container: - rname = resource.name - for name in resource.resources: - if not rname: - new_name = name - else: - new_name = '/'.join([rname, name]) - child = self.find(new_name) - if child.is_container: - todo.append(child) - else: - yield child - - -class ZipResourceFinder(ResourceFinder): - """ - Resource finder for resources in .zip files. - """ - def __init__(self, module): - super(ZipResourceFinder, self).__init__(module) - archive = self.loader.archive - self.prefix_len = 1 + len(archive) - # PyPy doesn't have a _files attr on zipimporter, and you can't set one - if hasattr(self.loader, '_files'): - self._files = self.loader._files - else: - self._files = zipimport._zip_directory_cache[archive] - self.index = sorted(self._files) - - def _adjust_path(self, path): - return path - - def _find(self, path): - path = path[self.prefix_len:] - if path in self._files: - result = True - else: - if path and path[-1] != os.sep: - path = path + os.sep - i = bisect.bisect(self.index, path) - try: - result = self.index[i].startswith(path) - except IndexError: - result = False - if not result: - logger.debug('_find failed: %r %r', path, self.loader.prefix) - else: - logger.debug('_find worked: %r %r', path, self.loader.prefix) - return result - - def get_cache_info(self, resource): - prefix = self.loader.archive - path = resource.path[1 + len(prefix):] - return prefix, path - - def get_bytes(self, resource): - return self.loader.get_data(resource.path) - - def get_stream(self, resource): - return io.BytesIO(self.get_bytes(resource)) - - def get_size(self, resource): - path = resource.path[self.prefix_len:] - return self._files[path][3] - - def get_resources(self, resource): - path = resource.path[self.prefix_len:] - if path and path[-1] != os.sep: - path += os.sep - plen = len(path) - result = set() - i = bisect.bisect(self.index, path) - while i < len(self.index): - if not self.index[i].startswith(path): - break - s = self.index[i][plen:] - result.add(s.split(os.sep, 1)[0]) # only immediate children - i += 1 - return result - - def _is_directory(self, path): - path = path[self.prefix_len:] - if path and path[-1] != os.sep: - path += os.sep - i = bisect.bisect(self.index, path) - try: - result = self.index[i].startswith(path) - except IndexError: - result = False - return result - - -_finder_registry = { - type(None): ResourceFinder, - zipimport.zipimporter: ZipResourceFinder -} - -try: - # In Python 3.6, _frozen_importlib -> _frozen_importlib_external - try: - import _frozen_importlib_external as _fi - except ImportError: - import _frozen_importlib as _fi - _finder_registry[_fi.SourceFileLoader] = ResourceFinder - _finder_registry[_fi.FileFinder] = ResourceFinder - # See issue #146 - _finder_registry[_fi.SourcelessFileLoader] = ResourceFinder - del _fi -except (ImportError, AttributeError): - pass - - -def register_finder(loader, finder_maker): - _finder_registry[type(loader)] = finder_maker - - -_finder_cache = {} - - -def finder(package): - """ - Return a resource finder for a package. - :param package: The name of the package. - :return: A :class:`ResourceFinder` instance for the package. - """ - if package in _finder_cache: - result = _finder_cache[package] - else: - if package not in sys.modules: - __import__(package) - module = sys.modules[package] - path = getattr(module, '__path__', None) - if path is None: - raise DistlibException('You cannot get a finder for a module, ' - 'only for a package') - loader = getattr(module, '__loader__', None) - finder_maker = _finder_registry.get(type(loader)) - if finder_maker is None: - raise DistlibException('Unable to locate finder for %r' % package) - result = finder_maker(module) - _finder_cache[package] = result - return result - - -_dummy_module = types.ModuleType(str('__dummy__')) - - -def finder_for_path(path): - """ - Return a resource finder for a path, which should represent a container. - - :param path: The path. - :return: A :class:`ResourceFinder` instance for the path. - """ - result = None - # calls any path hooks, gets importer into cache - pkgutil.get_importer(path) - loader = sys.path_importer_cache.get(path) - finder = _finder_registry.get(type(loader)) - if finder: - module = _dummy_module - module.__file__ = os.path.join(path, '') - module.__loader__ = loader - result = finder(module) - return result diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/scripts.py b/.venv/Lib/site-packages/pip/_vendor/distlib/scripts.py deleted file mode 100644 index d270624..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/scripts.py +++ /dev/null @@ -1,437 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013-2015 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -from io import BytesIO -import logging -import os -import re -import struct -import sys -import time -from zipfile import ZipInfo - -from .compat import sysconfig, detect_encoding, ZipFile -from .resources import finder -from .util import (FileOperator, get_export_entry, convert_path, - get_executable, get_platform, in_venv) - -logger = logging.getLogger(__name__) - -_DEFAULT_MANIFEST = ''' - - - - - - - - - - - - -'''.strip() - -# check if Python is called on the first line with this expression -FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') -SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- -import re -import sys -from %(module)s import %(import_name)s -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(%(func)s()) -''' - - -def enquote_executable(executable): - if ' ' in executable: - # make sure we quote only the executable in case of env - # for example /usr/bin/env "/dir with spaces/bin/jython" - # instead of "/usr/bin/env /dir with spaces/bin/jython" - # otherwise whole - if executable.startswith('/usr/bin/env '): - env, _executable = executable.split(' ', 1) - if ' ' in _executable and not _executable.startswith('"'): - executable = '%s "%s"' % (env, _executable) - else: - if not executable.startswith('"'): - executable = '"%s"' % executable - return executable - -# Keep the old name around (for now), as there is at least one project using it! -_enquote_executable = enquote_executable - -class ScriptMaker(object): - """ - A class to copy or create scripts from source scripts or callable - specifications. - """ - script_template = SCRIPT_TEMPLATE - - executable = None # for shebangs - - def __init__(self, source_dir, target_dir, add_launchers=True, - dry_run=False, fileop=None): - self.source_dir = source_dir - self.target_dir = target_dir - self.add_launchers = add_launchers - self.force = False - self.clobber = False - # It only makes sense to set mode bits on POSIX. - self.set_mode = (os.name == 'posix') or (os.name == 'java' and - os._name == 'posix') - self.variants = set(('', 'X.Y')) - self._fileop = fileop or FileOperator(dry_run) - - self._is_nt = os.name == 'nt' or ( - os.name == 'java' and os._name == 'nt') - self.version_info = sys.version_info - - def _get_alternate_executable(self, executable, options): - if options.get('gui', False) and self._is_nt: # pragma: no cover - dn, fn = os.path.split(executable) - fn = fn.replace('python', 'pythonw') - executable = os.path.join(dn, fn) - return executable - - if sys.platform.startswith('java'): # pragma: no cover - def _is_shell(self, executable): - """ - Determine if the specified executable is a script - (contains a #! line) - """ - try: - with open(executable) as fp: - return fp.read(2) == '#!' - except (OSError, IOError): - logger.warning('Failed to open %s', executable) - return False - - def _fix_jython_executable(self, executable): - if self._is_shell(executable): - # Workaround for Jython is not needed on Linux systems. - import java - - if java.lang.System.getProperty('os.name') == 'Linux': - return executable - elif executable.lower().endswith('jython.exe'): - # Use wrapper exe for Jython on Windows - return executable - return '/usr/bin/env %s' % executable - - def _build_shebang(self, executable, post_interp): - """ - Build a shebang line. In the simple case (on Windows, or a shebang line - which is not too long or contains spaces) use a simple formulation for - the shebang. Otherwise, use /bin/sh as the executable, with a contrived - shebang which allows the script to run either under Python or sh, using - suitable quoting. Thanks to Harald Nordgren for his input. - - See also: http://www.in-ulm.de/~mascheck/various/shebang/#length - https://hg.mozilla.org/mozilla-central/file/tip/mach - """ - if os.name != 'posix': - simple_shebang = True - else: - # Add 3 for '#!' prefix and newline suffix. - shebang_length = len(executable) + len(post_interp) + 3 - if sys.platform == 'darwin': - max_shebang_length = 512 - else: - max_shebang_length = 127 - simple_shebang = ((b' ' not in executable) and - (shebang_length <= max_shebang_length)) - - if simple_shebang: - result = b'#!' + executable + post_interp + b'\n' - else: - result = b'#!/bin/sh\n' - result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' - result += b"' '''" - return result - - def _get_shebang(self, encoding, post_interp=b'', options=None): - enquote = True - if self.executable: - executable = self.executable - enquote = False # assume this will be taken care of - elif not sysconfig.is_python_build(): - executable = get_executable() - elif in_venv(): # pragma: no cover - executable = os.path.join(sysconfig.get_path('scripts'), - 'python%s' % sysconfig.get_config_var('EXE')) - else: # pragma: no cover - executable = os.path.join( - sysconfig.get_config_var('BINDIR'), - 'python%s%s' % (sysconfig.get_config_var('VERSION'), - sysconfig.get_config_var('EXE'))) - if not os.path.isfile(executable): - # for Python builds from source on Windows, no Python executables with - # a version suffix are created, so we use python.exe - executable = os.path.join(sysconfig.get_config_var('BINDIR'), - 'python%s' % (sysconfig.get_config_var('EXE'))) - if options: - executable = self._get_alternate_executable(executable, options) - - if sys.platform.startswith('java'): # pragma: no cover - executable = self._fix_jython_executable(executable) - - # Normalise case for Windows - COMMENTED OUT - # executable = os.path.normcase(executable) - # N.B. The normalising operation above has been commented out: See - # issue #124. Although paths in Windows are generally case-insensitive, - # they aren't always. For example, a path containing a ẞ (which is a - # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a - # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by - # Windows as equivalent in path names. - - # If the user didn't specify an executable, it may be necessary to - # cater for executable paths with spaces (not uncommon on Windows) - if enquote: - executable = enquote_executable(executable) - # Issue #51: don't use fsencode, since we later try to - # check that the shebang is decodable using utf-8. - executable = executable.encode('utf-8') - # in case of IronPython, play safe and enable frames support - if (sys.platform == 'cli' and '-X:Frames' not in post_interp - and '-X:FullFrames' not in post_interp): # pragma: no cover - post_interp += b' -X:Frames' - shebang = self._build_shebang(executable, post_interp) - # Python parser starts to read a script using UTF-8 until - # it gets a #coding:xxx cookie. The shebang has to be the - # first line of a file, the #coding:xxx cookie cannot be - # written before. So the shebang has to be decodable from - # UTF-8. - try: - shebang.decode('utf-8') - except UnicodeDecodeError: # pragma: no cover - raise ValueError( - 'The shebang (%r) is not decodable from utf-8' % shebang) - # If the script is encoded to a custom encoding (use a - # #coding:xxx cookie), the shebang has to be decodable from - # the script encoding too. - if encoding != 'utf-8': - try: - shebang.decode(encoding) - except UnicodeDecodeError: # pragma: no cover - raise ValueError( - 'The shebang (%r) is not decodable ' - 'from the script encoding (%r)' % (shebang, encoding)) - return shebang - - def _get_script_text(self, entry): - return self.script_template % dict(module=entry.prefix, - import_name=entry.suffix.split('.')[0], - func=entry.suffix) - - manifest = _DEFAULT_MANIFEST - - def get_manifest(self, exename): - base = os.path.basename(exename) - return self.manifest % base - - def _write_script(self, names, shebang, script_bytes, filenames, ext): - use_launcher = self.add_launchers and self._is_nt - linesep = os.linesep.encode('utf-8') - if not shebang.endswith(linesep): - shebang += linesep - if not use_launcher: - script_bytes = shebang + script_bytes - else: # pragma: no cover - if ext == 'py': - launcher = self._get_launcher('t') - else: - launcher = self._get_launcher('w') - stream = BytesIO() - with ZipFile(stream, 'w') as zf: - source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH') - if source_date_epoch: - date_time = time.gmtime(int(source_date_epoch))[:6] - zinfo = ZipInfo(filename='__main__.py', date_time=date_time) - zf.writestr(zinfo, script_bytes) - else: - zf.writestr('__main__.py', script_bytes) - zip_data = stream.getvalue() - script_bytes = launcher + shebang + zip_data - for name in names: - outname = os.path.join(self.target_dir, name) - if use_launcher: # pragma: no cover - n, e = os.path.splitext(outname) - if e.startswith('.py'): - outname = n - outname = '%s.exe' % outname - try: - self._fileop.write_binary_file(outname, script_bytes) - except Exception: - # Failed writing an executable - it might be in use. - logger.warning('Failed to write executable - trying to ' - 'use .deleteme logic') - dfname = '%s.deleteme' % outname - if os.path.exists(dfname): - os.remove(dfname) # Not allowed to fail here - os.rename(outname, dfname) # nor here - self._fileop.write_binary_file(outname, script_bytes) - logger.debug('Able to replace executable using ' - '.deleteme logic') - try: - os.remove(dfname) - except Exception: - pass # still in use - ignore error - else: - if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover - outname = '%s.%s' % (outname, ext) - if os.path.exists(outname) and not self.clobber: - logger.warning('Skipping existing file %s', outname) - continue - self._fileop.write_binary_file(outname, script_bytes) - if self.set_mode: - self._fileop.set_executable_mode([outname]) - filenames.append(outname) - - variant_separator = '-' - - def get_script_filenames(self, name): - result = set() - if '' in self.variants: - result.add(name) - if 'X' in self.variants: - result.add('%s%s' % (name, self.version_info[0])) - if 'X.Y' in self.variants: - result.add('%s%s%s.%s' % (name, self.variant_separator, - self.version_info[0], self.version_info[1])) - return result - - def _make_script(self, entry, filenames, options=None): - post_interp = b'' - if options: - args = options.get('interpreter_args', []) - if args: - args = ' %s' % ' '.join(args) - post_interp = args.encode('utf-8') - shebang = self._get_shebang('utf-8', post_interp, options=options) - script = self._get_script_text(entry).encode('utf-8') - scriptnames = self.get_script_filenames(entry.name) - if options and options.get('gui', False): - ext = 'pyw' - else: - ext = 'py' - self._write_script(scriptnames, shebang, script, filenames, ext) - - def _copy_script(self, script, filenames): - adjust = False - script = os.path.join(self.source_dir, convert_path(script)) - outname = os.path.join(self.target_dir, os.path.basename(script)) - if not self.force and not self._fileop.newer(script, outname): - logger.debug('not copying %s (up-to-date)', script) - return - - # Always open the file, but ignore failures in dry-run mode -- - # that way, we'll get accurate feedback if we can read the - # script. - try: - f = open(script, 'rb') - except IOError: # pragma: no cover - if not self.dry_run: - raise - f = None - else: - first_line = f.readline() - if not first_line: # pragma: no cover - logger.warning('%s is an empty file (skipping)', script) - return - - match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) - if match: - adjust = True - post_interp = match.group(1) or b'' - - if not adjust: - if f: - f.close() - self._fileop.copy_file(script, outname) - if self.set_mode: - self._fileop.set_executable_mode([outname]) - filenames.append(outname) - else: - logger.info('copying and adjusting %s -> %s', script, - self.target_dir) - if not self._fileop.dry_run: - encoding, lines = detect_encoding(f.readline) - f.seek(0) - shebang = self._get_shebang(encoding, post_interp) - if b'pythonw' in first_line: # pragma: no cover - ext = 'pyw' - else: - ext = 'py' - n = os.path.basename(outname) - self._write_script([n], shebang, f.read(), filenames, ext) - if f: - f.close() - - @property - def dry_run(self): - return self._fileop.dry_run - - @dry_run.setter - def dry_run(self, value): - self._fileop.dry_run = value - - if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover - # Executable launcher support. - # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ - - def _get_launcher(self, kind): - if struct.calcsize('P') == 8: # 64-bit - bits = '64' - else: - bits = '32' - platform_suffix = '-arm' if get_platform() == 'win-arm64' else '' - name = '%s%s%s.exe' % (kind, bits, platform_suffix) - # Issue 31: don't hardcode an absolute package name, but - # determine it relative to the current package - distlib_package = __name__.rsplit('.', 1)[0] - resource = finder(distlib_package).find(name) - if not resource: - msg = ('Unable to find resource %s in package %s' % (name, - distlib_package)) - raise ValueError(msg) - return resource.bytes - - # Public API follows - - def make(self, specification, options=None): - """ - Make a script. - - :param specification: The specification, which is either a valid export - entry specification (to make a script from a - callable) or a filename (to make a script by - copying from a source location). - :param options: A dictionary of options controlling script generation. - :return: A list of all absolute pathnames written to. - """ - filenames = [] - entry = get_export_entry(specification) - if entry is None: - self._copy_script(specification, filenames) - else: - self._make_script(entry, filenames, options=options) - return filenames - - def make_multiple(self, specifications, options=None): - """ - Take a list of specifications and make scripts from them, - :param specifications: A list of specifications. - :return: A list of all absolute pathnames written to, - """ - filenames = [] - for specification in specifications: - filenames.extend(self.make(specification, options)) - return filenames diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/t32.exe b/.venv/Lib/site-packages/pip/_vendor/distlib/t32.exe deleted file mode 100644 index 52154f0be32cc2bdbf98af131d477900667d0abd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97792 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

XMwe#*Lv$g8jIpIBEV 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 w+IyK^N3WCD*(>mhAj=irKyR>D9b+D<@QBhV6U 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

qx{CqlSNQ_YhQ()VtG-D; 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 z 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 bB!6W7g#4-b3-g!cLzth+PZapSNrC?c%T_@< diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/wheel.py b/.venv/Lib/site-packages/pip/_vendor/distlib/wheel.py deleted file mode 100644 index 028c2d9..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/wheel.py +++ /dev/null @@ -1,1082 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2013-2020 Vinay Sajip. -# Licensed to the Python Software Foundation under a contributor agreement. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -from __future__ import unicode_literals - -import base64 -import codecs -import datetime -from email import message_from_file -import hashlib -import json -import logging -import os -import posixpath -import re -import shutil -import sys -import tempfile -import zipfile - -from . import __version__, DistlibException -from .compat import sysconfig, ZipFile, fsdecode, text_type, filter -from .database import InstalledDistribution -from .metadata import (Metadata, METADATA_FILENAME, WHEEL_METADATA_FILENAME, - LEGACY_METADATA_FILENAME) -from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, - cached_property, get_cache_base, read_exports, tempdir, - get_platform) -from .version import NormalizedVersion, UnsupportedVersionError - -logger = logging.getLogger(__name__) - -cache = None # created when needed - -if hasattr(sys, 'pypy_version_info'): # pragma: no cover - IMP_PREFIX = 'pp' -elif sys.platform.startswith('java'): # pragma: no cover - IMP_PREFIX = 'jy' -elif sys.platform == 'cli': # pragma: no cover - IMP_PREFIX = 'ip' -else: - IMP_PREFIX = 'cp' - -VER_SUFFIX = sysconfig.get_config_var('py_version_nodot') -if not VER_SUFFIX: # pragma: no cover - VER_SUFFIX = '%s%s' % sys.version_info[:2] -PYVER = 'py' + VER_SUFFIX -IMPVER = IMP_PREFIX + VER_SUFFIX - -ARCH = get_platform().replace('-', '_').replace('.', '_') - -ABI = sysconfig.get_config_var('SOABI') -if ABI and ABI.startswith('cpython-'): - ABI = ABI.replace('cpython-', 'cp').split('-')[0] -else: - def _derive_abi(): - parts = ['cp', VER_SUFFIX] - if sysconfig.get_config_var('Py_DEBUG'): - parts.append('d') - if IMP_PREFIX == 'cp': - vi = sys.version_info[:2] - if vi < (3, 8): - wpm = sysconfig.get_config_var('WITH_PYMALLOC') - if wpm is None: - wpm = True - if wpm: - parts.append('m') - if vi < (3, 3): - us = sysconfig.get_config_var('Py_UNICODE_SIZE') - if us == 4 or (us is None and sys.maxunicode == 0x10FFFF): - parts.append('u') - return ''.join(parts) - ABI = _derive_abi() - del _derive_abi - -FILENAME_RE = re.compile(r''' -(?P[^-]+) --(?P\d+[^-]*) -(-(?P\d+[^-]*))? --(?P\w+\d+(\.\w+\d+)*) --(?P\w+) --(?P\w+(\.\w+)*) -\.whl$ -''', re.IGNORECASE | re.VERBOSE) - -NAME_VERSION_RE = re.compile(r''' -(?P[^-]+) --(?P\d+[^-]*) -(-(?P\d+[^-]*))?$ -''', re.IGNORECASE | re.VERBOSE) - -SHEBANG_RE = re.compile(br'\s*#![^\r\n]*') -SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$') -SHEBANG_PYTHON = b'#!python' -SHEBANG_PYTHONW = b'#!pythonw' - -if os.sep == '/': - to_posix = lambda o: o -else: - to_posix = lambda o: o.replace(os.sep, '/') - -if sys.version_info[0] < 3: - import imp -else: - imp = None - import importlib.machinery - import importlib.util - -def _get_suffixes(): - if imp: - return [s[0] for s in imp.get_suffixes()] - else: - return importlib.machinery.EXTENSION_SUFFIXES - -def _load_dynamic(name, path): - # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly - if imp: - return imp.load_dynamic(name, path) - else: - spec = importlib.util.spec_from_file_location(name, path) - module = importlib.util.module_from_spec(spec) - sys.modules[name] = module - spec.loader.exec_module(module) - return module - -class Mounter(object): - def __init__(self): - self.impure_wheels = {} - self.libs = {} - - def add(self, pathname, extensions): - self.impure_wheels[pathname] = extensions - self.libs.update(extensions) - - def remove(self, pathname): - extensions = self.impure_wheels.pop(pathname) - for k, v in extensions: - if k in self.libs: - del self.libs[k] - - def find_module(self, fullname, path=None): - if fullname in self.libs: - result = self - else: - result = None - return result - - def load_module(self, fullname): - if fullname in sys.modules: - result = sys.modules[fullname] - else: - if fullname not in self.libs: - raise ImportError('unable to find extension for %s' % fullname) - result = _load_dynamic(fullname, self.libs[fullname]) - result.__loader__ = self - parts = fullname.rsplit('.', 1) - if len(parts) > 1: - result.__package__ = parts[0] - return result - -_hook = Mounter() - - -class Wheel(object): - """ - Class to build and install from Wheel files (PEP 427). - """ - - wheel_version = (1, 1) - hash_kind = 'sha256' - - def __init__(self, filename=None, sign=False, verify=False): - """ - Initialise an instance using a (valid) filename. - """ - self.sign = sign - self.should_verify = verify - self.buildver = '' - self.pyver = [PYVER] - self.abi = ['none'] - self.arch = ['any'] - self.dirname = os.getcwd() - if filename is None: - self.name = 'dummy' - self.version = '0.1' - self._filename = self.filename - else: - m = NAME_VERSION_RE.match(filename) - if m: - info = m.groupdict('') - self.name = info['nm'] - # Reinstate the local version separator - self.version = info['vn'].replace('_', '-') - self.buildver = info['bn'] - self._filename = self.filename - else: - dirname, filename = os.path.split(filename) - m = FILENAME_RE.match(filename) - if not m: - raise DistlibException('Invalid name or ' - 'filename: %r' % filename) - if dirname: - self.dirname = os.path.abspath(dirname) - self._filename = filename - info = m.groupdict('') - self.name = info['nm'] - self.version = info['vn'] - self.buildver = info['bn'] - self.pyver = info['py'].split('.') - self.abi = info['bi'].split('.') - self.arch = info['ar'].split('.') - - @property - def filename(self): - """ - Build and return a filename from the various components. - """ - if self.buildver: - buildver = '-' + self.buildver - else: - buildver = '' - pyver = '.'.join(self.pyver) - abi = '.'.join(self.abi) - arch = '.'.join(self.arch) - # replace - with _ as a local version separator - version = self.version.replace('-', '_') - return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, - pyver, abi, arch) - - @property - def exists(self): - path = os.path.join(self.dirname, self.filename) - return os.path.isfile(path) - - @property - def tags(self): - for pyver in self.pyver: - for abi in self.abi: - for arch in self.arch: - yield pyver, abi, arch - - @cached_property - def metadata(self): - pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) - info_dir = '%s.dist-info' % name_ver - wrapper = codecs.getreader('utf-8') - with ZipFile(pathname, 'r') as zf: - wheel_metadata = self.get_wheel_metadata(zf) - wv = wheel_metadata['Wheel-Version'].split('.', 1) - file_version = tuple([int(i) for i in wv]) - # if file_version < (1, 1): - # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME, - # LEGACY_METADATA_FILENAME] - # else: - # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME] - fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME] - result = None - for fn in fns: - try: - metadata_filename = posixpath.join(info_dir, fn) - with zf.open(metadata_filename) as bf: - wf = wrapper(bf) - result = Metadata(fileobj=wf) - if result: - break - except KeyError: - pass - if not result: - raise ValueError('Invalid wheel, because metadata is ' - 'missing: looked in %s' % ', '.join(fns)) - return result - - def get_wheel_metadata(self, zf): - name_ver = '%s-%s' % (self.name, self.version) - info_dir = '%s.dist-info' % name_ver - metadata_filename = posixpath.join(info_dir, 'WHEEL') - with zf.open(metadata_filename) as bf: - wf = codecs.getreader('utf-8')(bf) - message = message_from_file(wf) - return dict(message) - - @cached_property - def info(self): - pathname = os.path.join(self.dirname, self.filename) - with ZipFile(pathname, 'r') as zf: - result = self.get_wheel_metadata(zf) - return result - - def process_shebang(self, data): - m = SHEBANG_RE.match(data) - if m: - end = m.end() - shebang, data_after_shebang = data[:end], data[end:] - # Preserve any arguments after the interpreter - if b'pythonw' in shebang.lower(): - shebang_python = SHEBANG_PYTHONW - else: - shebang_python = SHEBANG_PYTHON - m = SHEBANG_DETAIL_RE.match(shebang) - if m: - args = b' ' + m.groups()[-1] - else: - args = b'' - shebang = shebang_python + args - data = shebang + data_after_shebang - else: - cr = data.find(b'\r') - lf = data.find(b'\n') - if cr < 0 or cr > lf: - term = b'\n' - else: - if data[cr:cr + 2] == b'\r\n': - term = b'\r\n' - else: - term = b'\r' - data = SHEBANG_PYTHON + term + data - return data - - def get_hash(self, data, hash_kind=None): - if hash_kind is None: - hash_kind = self.hash_kind - try: - hasher = getattr(hashlib, hash_kind) - except AttributeError: - raise DistlibException('Unsupported hash algorithm: %r' % hash_kind) - result = hasher(data).digest() - result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii') - return hash_kind, result - - def write_record(self, records, record_path, archive_record_path): - records = list(records) # make a copy, as mutated - records.append((archive_record_path, '', '')) - with CSVWriter(record_path) as writer: - for row in records: - writer.writerow(row) - - def write_records(self, info, libdir, archive_paths): - records = [] - distinfo, info_dir = info - hasher = getattr(hashlib, self.hash_kind) - for ap, p in archive_paths: - with open(p, 'rb') as f: - data = f.read() - digest = '%s=%s' % self.get_hash(data) - size = os.path.getsize(p) - records.append((ap, digest, size)) - - p = os.path.join(distinfo, 'RECORD') - ap = to_posix(os.path.join(info_dir, 'RECORD')) - self.write_record(records, p, ap) - archive_paths.append((ap, p)) - - def build_zip(self, pathname, archive_paths): - with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf: - for ap, p in archive_paths: - logger.debug('Wrote %s to %s in wheel', p, ap) - zf.write(p, ap) - - def build(self, paths, tags=None, wheel_version=None): - """ - Build a wheel from files in specified paths, and use any specified tags - when determining the name of the wheel. - """ - if tags is None: - tags = {} - - libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0] - if libkey == 'platlib': - is_pure = 'false' - default_pyver = [IMPVER] - default_abi = [ABI] - default_arch = [ARCH] - else: - is_pure = 'true' - default_pyver = [PYVER] - default_abi = ['none'] - default_arch = ['any'] - - self.pyver = tags.get('pyver', default_pyver) - self.abi = tags.get('abi', default_abi) - self.arch = tags.get('arch', default_arch) - - libdir = paths[libkey] - - name_ver = '%s-%s' % (self.name, self.version) - data_dir = '%s.data' % name_ver - info_dir = '%s.dist-info' % name_ver - - archive_paths = [] - - # First, stuff which is not in site-packages - for key in ('data', 'headers', 'scripts'): - if key not in paths: - continue - path = paths[key] - if os.path.isdir(path): - for root, dirs, files in os.walk(path): - for fn in files: - p = fsdecode(os.path.join(root, fn)) - rp = os.path.relpath(p, path) - ap = to_posix(os.path.join(data_dir, key, rp)) - archive_paths.append((ap, p)) - if key == 'scripts' and not p.endswith('.exe'): - with open(p, 'rb') as f: - data = f.read() - data = self.process_shebang(data) - with open(p, 'wb') as f: - f.write(data) - - # Now, stuff which is in site-packages, other than the - # distinfo stuff. - path = libdir - distinfo = None - for root, dirs, files in os.walk(path): - if root == path: - # At the top level only, save distinfo for later - # and skip it for now - for i, dn in enumerate(dirs): - dn = fsdecode(dn) - if dn.endswith('.dist-info'): - distinfo = os.path.join(root, dn) - del dirs[i] - break - assert distinfo, '.dist-info directory expected, not found' - - for fn in files: - # comment out next suite to leave .pyc files in - if fsdecode(fn).endswith(('.pyc', '.pyo')): - continue - p = os.path.join(root, fn) - rp = to_posix(os.path.relpath(p, path)) - archive_paths.append((rp, p)) - - # Now distinfo. Assumed to be flat, i.e. os.listdir is enough. - files = os.listdir(distinfo) - for fn in files: - if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'): - p = fsdecode(os.path.join(distinfo, fn)) - ap = to_posix(os.path.join(info_dir, fn)) - archive_paths.append((ap, p)) - - wheel_metadata = [ - 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version), - 'Generator: distlib %s' % __version__, - 'Root-Is-Purelib: %s' % is_pure, - ] - for pyver, abi, arch in self.tags: - wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch)) - p = os.path.join(distinfo, 'WHEEL') - with open(p, 'w') as f: - f.write('\n'.join(wheel_metadata)) - ap = to_posix(os.path.join(info_dir, 'WHEEL')) - archive_paths.append((ap, p)) - - # sort the entries by archive path. Not needed by any spec, but it - # keeps the archive listing and RECORD tidier than they would otherwise - # be. Use the number of path segments to keep directory entries together, - # and keep the dist-info stuff at the end. - def sorter(t): - ap = t[0] - n = ap.count('/') - if '.dist-info' in ap: - n += 10000 - return (n, ap) - archive_paths = sorted(archive_paths, key=sorter) - - # Now, at last, RECORD. - # Paths in here are archive paths - nothing else makes sense. - self.write_records((distinfo, info_dir), libdir, archive_paths) - # Now, ready to build the zip file - pathname = os.path.join(self.dirname, self.filename) - self.build_zip(pathname, archive_paths) - return pathname - - def skip_entry(self, arcname): - """ - Determine whether an archive entry should be skipped when verifying - or installing. - """ - # The signature file won't be in RECORD, - # and we don't currently don't do anything with it - # We also skip directories, as they won't be in RECORD - # either. See: - # - # https://github.com/pypa/wheel/issues/294 - # https://github.com/pypa/wheel/issues/287 - # https://github.com/pypa/wheel/pull/289 - # - return arcname.endswith(('/', '/RECORD.jws')) - - def install(self, paths, maker, **kwargs): - """ - Install a wheel to the specified paths. If kwarg ``warner`` is - specified, it should be a callable, which will be called with two - tuples indicating the wheel version of this software and the wheel - version in the file, if there is a discrepancy in the versions. - This can be used to issue any warnings to raise any exceptions. - If kwarg ``lib_only`` is True, only the purelib/platlib files are - installed, and the headers, scripts, data and dist-info metadata are - not written. If kwarg ``bytecode_hashed_invalidation`` is True, written - bytecode will try to use file-hash based invalidation (PEP-552) on - supported interpreter versions (CPython 2.7+). - - The return value is a :class:`InstalledDistribution` instance unless - ``options.lib_only`` is True, in which case the return value is ``None``. - """ - - dry_run = maker.dry_run - warner = kwargs.get('warner') - lib_only = kwargs.get('lib_only', False) - bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False) - - pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) - data_dir = '%s.data' % name_ver - info_dir = '%s.dist-info' % name_ver - - metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) - wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') - record_name = posixpath.join(info_dir, 'RECORD') - - wrapper = codecs.getreader('utf-8') - - with ZipFile(pathname, 'r') as zf: - with zf.open(wheel_metadata_name) as bwf: - wf = wrapper(bwf) - message = message_from_file(wf) - wv = message['Wheel-Version'].split('.', 1) - file_version = tuple([int(i) for i in wv]) - if (file_version != self.wheel_version) and warner: - warner(self.wheel_version, file_version) - - if message['Root-Is-Purelib'] == 'true': - libdir = paths['purelib'] - else: - libdir = paths['platlib'] - - records = {} - with zf.open(record_name) as bf: - with CSVReader(stream=bf) as reader: - for row in reader: - p = row[0] - records[p] = row - - data_pfx = posixpath.join(data_dir, '') - info_pfx = posixpath.join(info_dir, '') - script_pfx = posixpath.join(data_dir, 'scripts', '') - - # make a new instance rather than a copy of maker's, - # as we mutate it - fileop = FileOperator(dry_run=dry_run) - fileop.record = True # so we can rollback if needed - - bc = not sys.dont_write_bytecode # Double negatives. Lovely! - - outfiles = [] # for RECORD writing - - # for script copying/shebang processing - workdir = tempfile.mkdtemp() - # set target dir later - # we default add_launchers to False, as the - # Python Launcher should be used instead - maker.source_dir = workdir - maker.target_dir = None - try: - for zinfo in zf.infolist(): - arcname = zinfo.filename - if isinstance(arcname, text_type): - u_arcname = arcname - else: - u_arcname = arcname.decode('utf-8') - if self.skip_entry(u_arcname): - continue - row = records[u_arcname] - if row[2] and str(zinfo.file_size) != row[2]: - raise DistlibException('size mismatch for ' - '%s' % u_arcname) - if row[1]: - kind, value = row[1].split('=', 1) - with zf.open(arcname) as bf: - data = bf.read() - _, digest = self.get_hash(data, kind) - if digest != value: - raise DistlibException('digest mismatch for ' - '%s' % arcname) - - if lib_only and u_arcname.startswith((info_pfx, data_pfx)): - logger.debug('lib_only: skipping %s', u_arcname) - continue - is_script = (u_arcname.startswith(script_pfx) - and not u_arcname.endswith('.exe')) - - if u_arcname.startswith(data_pfx): - _, where, rp = u_arcname.split('/', 2) - outfile = os.path.join(paths[where], convert_path(rp)) - else: - # meant for site-packages. - if u_arcname in (wheel_metadata_name, record_name): - continue - outfile = os.path.join(libdir, convert_path(u_arcname)) - if not is_script: - with zf.open(arcname) as bf: - fileop.copy_stream(bf, outfile) - # Issue #147: permission bits aren't preserved. Using - # zf.extract(zinfo, libdir) should have worked, but didn't, - # see https://www.thetopsites.net/article/53834422.shtml - # So ... manually preserve permission bits as given in zinfo - if os.name == 'posix': - # just set the normal permission bits - os.chmod(outfile, (zinfo.external_attr >> 16) & 0x1FF) - outfiles.append(outfile) - # Double check the digest of the written file - if not dry_run and row[1]: - with open(outfile, 'rb') as bf: - data = bf.read() - _, newdigest = self.get_hash(data, kind) - if newdigest != digest: - raise DistlibException('digest mismatch ' - 'on write for ' - '%s' % outfile) - if bc and outfile.endswith('.py'): - try: - pyc = fileop.byte_compile(outfile, - hashed_invalidation=bc_hashed_invalidation) - outfiles.append(pyc) - except Exception: - # Don't give up if byte-compilation fails, - # but log it and perhaps warn the user - logger.warning('Byte-compilation failed', - exc_info=True) - else: - fn = os.path.basename(convert_path(arcname)) - workname = os.path.join(workdir, fn) - with zf.open(arcname) as bf: - fileop.copy_stream(bf, workname) - - dn, fn = os.path.split(outfile) - maker.target_dir = dn - filenames = maker.make(fn) - fileop.set_executable_mode(filenames) - outfiles.extend(filenames) - - if lib_only: - logger.debug('lib_only: returning None') - dist = None - else: - # Generate scripts - - # Try to get pydist.json so we can see if there are - # any commands to generate. If this fails (e.g. because - # of a legacy wheel), log a warning but don't give up. - commands = None - file_version = self.info['Wheel-Version'] - if file_version == '1.0': - # Use legacy info - ep = posixpath.join(info_dir, 'entry_points.txt') - try: - with zf.open(ep) as bwf: - epdata = read_exports(bwf) - commands = {} - for key in ('console', 'gui'): - k = '%s_scripts' % key - if k in epdata: - commands['wrap_%s' % key] = d = {} - for v in epdata[k].values(): - s = '%s:%s' % (v.prefix, v.suffix) - if v.flags: - s += ' [%s]' % ','.join(v.flags) - d[v.name] = s - except Exception: - logger.warning('Unable to read legacy script ' - 'metadata, so cannot generate ' - 'scripts') - else: - try: - with zf.open(metadata_name) as bwf: - wf = wrapper(bwf) - commands = json.load(wf).get('extensions') - if commands: - commands = commands.get('python.commands') - except Exception: - logger.warning('Unable to read JSON metadata, so ' - 'cannot generate scripts') - if commands: - console_scripts = commands.get('wrap_console', {}) - gui_scripts = commands.get('wrap_gui', {}) - if console_scripts or gui_scripts: - script_dir = paths.get('scripts', '') - if not os.path.isdir(script_dir): - raise ValueError('Valid script path not ' - 'specified') - maker.target_dir = script_dir - for k, v in console_scripts.items(): - script = '%s = %s' % (k, v) - filenames = maker.make(script) - fileop.set_executable_mode(filenames) - - if gui_scripts: - options = {'gui': True } - for k, v in gui_scripts.items(): - script = '%s = %s' % (k, v) - filenames = maker.make(script, options) - fileop.set_executable_mode(filenames) - - p = os.path.join(libdir, info_dir) - dist = InstalledDistribution(p) - - # Write SHARED - paths = dict(paths) # don't change passed in dict - del paths['purelib'] - del paths['platlib'] - paths['lib'] = libdir - p = dist.write_shared_locations(paths, dry_run) - if p: - outfiles.append(p) - - # Write RECORD - dist.write_installed_files(outfiles, paths['prefix'], - dry_run) - return dist - except Exception: # pragma: no cover - logger.exception('installation failed.') - fileop.rollback() - raise - finally: - shutil.rmtree(workdir) - - def _get_dylib_cache(self): - global cache - if cache is None: - # Use native string to avoid issues on 2.x: see Python #20140. - base = os.path.join(get_cache_base(), str('dylib-cache'), - '%s.%s' % sys.version_info[:2]) - cache = Cache(base) - return cache - - def _get_extensions(self): - pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) - info_dir = '%s.dist-info' % name_ver - arcname = posixpath.join(info_dir, 'EXTENSIONS') - wrapper = codecs.getreader('utf-8') - result = [] - with ZipFile(pathname, 'r') as zf: - try: - with zf.open(arcname) as bf: - wf = wrapper(bf) - extensions = json.load(wf) - cache = self._get_dylib_cache() - prefix = cache.prefix_to_dir(pathname) - cache_base = os.path.join(cache.base, prefix) - if not os.path.isdir(cache_base): - os.makedirs(cache_base) - for name, relpath in extensions.items(): - dest = os.path.join(cache_base, convert_path(relpath)) - if not os.path.exists(dest): - extract = True - else: - file_time = os.stat(dest).st_mtime - file_time = datetime.datetime.fromtimestamp(file_time) - info = zf.getinfo(relpath) - wheel_time = datetime.datetime(*info.date_time) - extract = wheel_time > file_time - if extract: - zf.extract(relpath, cache_base) - result.append((name, dest)) - except KeyError: - pass - return result - - def is_compatible(self): - """ - Determine if a wheel is compatible with the running system. - """ - return is_compatible(self) - - def is_mountable(self): - """ - Determine if a wheel is asserted as mountable by its metadata. - """ - return True # for now - metadata details TBD - - def mount(self, append=False): - pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) - if not self.is_compatible(): - msg = 'Wheel %s not compatible with this Python.' % pathname - raise DistlibException(msg) - if not self.is_mountable(): - msg = 'Wheel %s is marked as not mountable.' % pathname - raise DistlibException(msg) - if pathname in sys.path: - logger.debug('%s already in path', pathname) - else: - if append: - sys.path.append(pathname) - else: - sys.path.insert(0, pathname) - extensions = self._get_extensions() - if extensions: - if _hook not in sys.meta_path: - sys.meta_path.append(_hook) - _hook.add(pathname, extensions) - - def unmount(self): - pathname = os.path.abspath(os.path.join(self.dirname, self.filename)) - if pathname not in sys.path: - logger.debug('%s not in path', pathname) - else: - sys.path.remove(pathname) - if pathname in _hook.impure_wheels: - _hook.remove(pathname) - if not _hook.impure_wheels: - if _hook in sys.meta_path: - sys.meta_path.remove(_hook) - - def verify(self): - pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) - data_dir = '%s.data' % name_ver - info_dir = '%s.dist-info' % name_ver - - metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME) - wheel_metadata_name = posixpath.join(info_dir, 'WHEEL') - record_name = posixpath.join(info_dir, 'RECORD') - - wrapper = codecs.getreader('utf-8') - - with ZipFile(pathname, 'r') as zf: - with zf.open(wheel_metadata_name) as bwf: - wf = wrapper(bwf) - message = message_from_file(wf) - wv = message['Wheel-Version'].split('.', 1) - file_version = tuple([int(i) for i in wv]) - # TODO version verification - - records = {} - with zf.open(record_name) as bf: - with CSVReader(stream=bf) as reader: - for row in reader: - p = row[0] - records[p] = row - - for zinfo in zf.infolist(): - arcname = zinfo.filename - if isinstance(arcname, text_type): - u_arcname = arcname - else: - u_arcname = arcname.decode('utf-8') - # See issue #115: some wheels have .. in their entries, but - # in the filename ... e.g. __main__..py ! So the check is - # updated to look for .. in the directory portions - p = u_arcname.split('/') - if '..' in p: - raise DistlibException('invalid entry in ' - 'wheel: %r' % u_arcname) - - if self.skip_entry(u_arcname): - continue - row = records[u_arcname] - if row[2] and str(zinfo.file_size) != row[2]: - raise DistlibException('size mismatch for ' - '%s' % u_arcname) - if row[1]: - kind, value = row[1].split('=', 1) - with zf.open(arcname) as bf: - data = bf.read() - _, digest = self.get_hash(data, kind) - if digest != value: - raise DistlibException('digest mismatch for ' - '%s' % arcname) - - def update(self, modifier, dest_dir=None, **kwargs): - """ - Update the contents of a wheel in a generic way. The modifier should - be a callable which expects a dictionary argument: its keys are - archive-entry paths, and its values are absolute filesystem paths - where the contents the corresponding archive entries can be found. The - modifier is free to change the contents of the files pointed to, add - new entries and remove entries, before returning. This method will - extract the entire contents of the wheel to a temporary location, call - the modifier, and then use the passed (and possibly updated) - dictionary to write a new wheel. If ``dest_dir`` is specified, the new - wheel is written there -- otherwise, the original wheel is overwritten. - - The modifier should return True if it updated the wheel, else False. - This method returns the same value the modifier returns. - """ - - def get_version(path_map, info_dir): - version = path = None - key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME) - if key not in path_map: - key = '%s/PKG-INFO' % info_dir - if key in path_map: - path = path_map[key] - version = Metadata(path=path).version - return version, path - - def update_version(version, path): - updated = None - try: - v = NormalizedVersion(version) - i = version.find('-') - if i < 0: - updated = '%s+1' % version - else: - parts = [int(s) for s in version[i + 1:].split('.')] - parts[-1] += 1 - updated = '%s+%s' % (version[:i], - '.'.join(str(i) for i in parts)) - except UnsupportedVersionError: - logger.debug('Cannot update non-compliant (PEP-440) ' - 'version %r', version) - if updated: - md = Metadata(path=path) - md.version = updated - legacy = path.endswith(LEGACY_METADATA_FILENAME) - md.write(path=path, legacy=legacy) - logger.debug('Version updated from %r to %r', version, - updated) - - pathname = os.path.join(self.dirname, self.filename) - name_ver = '%s-%s' % (self.name, self.version) - info_dir = '%s.dist-info' % name_ver - record_name = posixpath.join(info_dir, 'RECORD') - with tempdir() as workdir: - with ZipFile(pathname, 'r') as zf: - path_map = {} - for zinfo in zf.infolist(): - arcname = zinfo.filename - if isinstance(arcname, text_type): - u_arcname = arcname - else: - u_arcname = arcname.decode('utf-8') - if u_arcname == record_name: - continue - if '..' in u_arcname: - raise DistlibException('invalid entry in ' - 'wheel: %r' % u_arcname) - zf.extract(zinfo, workdir) - path = os.path.join(workdir, convert_path(u_arcname)) - path_map[u_arcname] = path - - # Remember the version. - original_version, _ = get_version(path_map, info_dir) - # Files extracted. Call the modifier. - modified = modifier(path_map, **kwargs) - if modified: - # Something changed - need to build a new wheel. - current_version, path = get_version(path_map, info_dir) - if current_version and (current_version == original_version): - # Add or update local version to signify changes. - update_version(current_version, path) - # Decide where the new wheel goes. - if dest_dir is None: - fd, newpath = tempfile.mkstemp(suffix='.whl', - prefix='wheel-update-', - dir=workdir) - os.close(fd) - else: - if not os.path.isdir(dest_dir): - raise DistlibException('Not a directory: %r' % dest_dir) - newpath = os.path.join(dest_dir, self.filename) - archive_paths = list(path_map.items()) - distinfo = os.path.join(workdir, info_dir) - info = distinfo, info_dir - self.write_records(info, workdir, archive_paths) - self.build_zip(newpath, archive_paths) - if dest_dir is None: - shutil.copyfile(newpath, pathname) - return modified - -def _get_glibc_version(): - import platform - ver = platform.libc_ver() - result = [] - if ver[0] == 'glibc': - for s in ver[1].split('.'): - result.append(int(s) if s.isdigit() else 0) - result = tuple(result) - return result - -def compatible_tags(): - """ - Return (pyver, abi, arch) tuples compatible with this Python. - """ - versions = [VER_SUFFIX] - major = VER_SUFFIX[0] - for minor in range(sys.version_info[1] - 1, - 1, -1): - versions.append(''.join([major, str(minor)])) - - abis = [] - for suffix in _get_suffixes(): - if suffix.startswith('.abi'): - abis.append(suffix.split('.', 2)[1]) - abis.sort() - if ABI != 'none': - abis.insert(0, ABI) - abis.append('none') - result = [] - - arches = [ARCH] - if sys.platform == 'darwin': - m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH) - if m: - name, major, minor, arch = m.groups() - minor = int(minor) - matches = [arch] - if arch in ('i386', 'ppc'): - matches.append('fat') - if arch in ('i386', 'ppc', 'x86_64'): - matches.append('fat3') - if arch in ('ppc64', 'x86_64'): - matches.append('fat64') - if arch in ('i386', 'x86_64'): - matches.append('intel') - if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'): - matches.append('universal') - while minor >= 0: - for match in matches: - s = '%s_%s_%s_%s' % (name, major, minor, match) - if s != ARCH: # already there - arches.append(s) - minor -= 1 - - # Most specific - our Python version, ABI and arch - for abi in abis: - for arch in arches: - result.append((''.join((IMP_PREFIX, versions[0])), abi, arch)) - # manylinux - if abi != 'none' and sys.platform.startswith('linux'): - arch = arch.replace('linux_', '') - parts = _get_glibc_version() - if len(parts) == 2: - if parts >= (2, 5): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux1_%s' % arch)) - if parts >= (2, 12): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux2010_%s' % arch)) - if parts >= (2, 17): - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux2014_%s' % arch)) - result.append((''.join((IMP_PREFIX, versions[0])), abi, - 'manylinux_%s_%s_%s' % (parts[0], parts[1], - arch))) - - # where no ABI / arch dependency, but IMP_PREFIX dependency - for i, version in enumerate(versions): - result.append((''.join((IMP_PREFIX, version)), 'none', 'any')) - if i == 0: - result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any')) - - # no IMP_PREFIX, ABI or arch dependency - for i, version in enumerate(versions): - result.append((''.join(('py', version)), 'none', 'any')) - if i == 0: - result.append((''.join(('py', version[0])), 'none', 'any')) - - return set(result) - - -COMPATIBLE_TAGS = compatible_tags() - -del compatible_tags - - -def is_compatible(wheel, tags=None): - if not isinstance(wheel, Wheel): - wheel = Wheel(wheel) # assume it's a filename - result = False - if tags is None: - tags = COMPATIBLE_TAGS - for ver, abi, arch in tags: - if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch: - result = True - break - return result diff --git a/.venv/Lib/site-packages/pip/_vendor/distro/__init__.py b/.venv/Lib/site-packages/pip/_vendor/distro/__init__.py deleted file mode 100644 index 7686fe8..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distro/__init__.py +++ /dev/null @@ -1,54 +0,0 @@ -from .distro import ( - NORMALIZED_DISTRO_ID, - NORMALIZED_LSB_ID, - NORMALIZED_OS_ID, - LinuxDistribution, - __version__, - build_number, - codename, - distro_release_attr, - distro_release_info, - id, - info, - like, - linux_distribution, - lsb_release_attr, - lsb_release_info, - major_version, - minor_version, - name, - os_release_attr, - os_release_info, - uname_attr, - uname_info, - version, - version_parts, -) - -__all__ = [ - "NORMALIZED_DISTRO_ID", - "NORMALIZED_LSB_ID", - "NORMALIZED_OS_ID", - "LinuxDistribution", - "build_number", - "codename", - "distro_release_attr", - "distro_release_info", - "id", - "info", - "like", - "linux_distribution", - "lsb_release_attr", - "lsb_release_info", - "major_version", - "minor_version", - "name", - "os_release_attr", - "os_release_info", - "uname_attr", - "uname_info", - "version", - "version_parts", -] - -__version__ = __version__ diff --git a/.venv/Lib/site-packages/pip/_vendor/distro/__main__.py b/.venv/Lib/site-packages/pip/_vendor/distro/__main__.py deleted file mode 100644 index 0c01d5b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distro/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .distro import main - -if __name__ == "__main__": - main() diff --git a/.venv/Lib/site-packages/pip/_vendor/distro/distro.py b/.venv/Lib/site-packages/pip/_vendor/distro/distro.py deleted file mode 100644 index 49066ae..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distro/distro.py +++ /dev/null @@ -1,1374 +0,0 @@ -#!/usr/bin/env python -# Copyright 2015,2016,2017 Nir Cohen -# -# 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. - -""" -The ``distro`` package (``distro`` stands for Linux Distribution) provides -information about the Linux distribution it runs on, such as a reliable -machine-readable distro ID, or version information. - -It is the recommended replacement for Python's original -:py:func:`platform.linux_distribution` function, but it provides much more -functionality. An alternative implementation became necessary because Python -3.5 deprecated this function, and Python 3.8 removed it altogether. Its -predecessor function :py:func:`platform.dist` was already deprecated since -Python 2.6 and removed in Python 3.8. Still, there are many cases in which -access to OS distribution information is needed. See `Python issue 1322 -`_ for more information. -""" - -import argparse -import json -import logging -import os -import re -import shlex -import subprocess -import sys -import warnings -from typing import ( - Any, - Callable, - Dict, - Iterable, - Optional, - Sequence, - TextIO, - Tuple, - Type, -) - -try: - from typing import TypedDict -except ImportError: - # Python 3.7 - TypedDict = dict - -__version__ = "1.7.0" - - -class VersionDict(TypedDict): - major: str - minor: str - build_number: str - - -class InfoDict(TypedDict): - id: str - version: str - version_parts: VersionDict - like: str - codename: str - - -_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc") -_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib") -_OS_RELEASE_BASENAME = "os-release" - -#: Translation table for normalizing the "ID" attribute defined in os-release -#: files, for use by the :func:`distro.id` method. -#: -#: * Key: Value as defined in the os-release file, translated to lower case, -#: with blanks translated to underscores. -#: -#: * Value: Normalized value. -NORMALIZED_OS_ID = { - "ol": "oracle", # Oracle Linux - "opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap -} - -#: Translation table for normalizing the "Distributor ID" attribute returned by -#: the lsb_release command, for use by the :func:`distro.id` method. -#: -#: * Key: Value as returned by the lsb_release command, translated to lower -#: case, with blanks translated to underscores. -#: -#: * Value: Normalized value. -NORMALIZED_LSB_ID = { - "enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4 - "enterpriseenterpriseserver": "oracle", # Oracle Linux 5 - "redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation - "redhatenterpriseserver": "rhel", # RHEL 6, 7 Server - "redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode -} - -#: Translation table for normalizing the distro ID derived from the file name -#: of distro release files, for use by the :func:`distro.id` method. -#: -#: * Key: Value as derived from the file name of a distro release file, -#: translated to lower case, with blanks translated to underscores. -#: -#: * Value: Normalized value. -NORMALIZED_DISTRO_ID = { - "redhat": "rhel", # RHEL 6.x, 7.x -} - -# Pattern for content of distro release file (reversed) -_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile( - r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)" -) - -# Pattern for base file name of distro release file -_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") - -# Base file names to be ignored when searching for distro release file -_DISTRO_RELEASE_IGNORE_BASENAMES = ( - "debian_version", - "lsb-release", - "oem-release", - _OS_RELEASE_BASENAME, - "system-release", - "plesk-release", - "iredmail-release", -) - - -def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]: - """ - .. deprecated:: 1.6.0 - - :func:`distro.linux_distribution()` is deprecated. It should only be - used as a compatibility shim with Python's - :py:func:`platform.linux_distribution()`. Please use :func:`distro.id`, - :func:`distro.version` and :func:`distro.name` instead. - - Return information about the current OS distribution as a tuple - ``(id_name, version, codename)`` with items as follows: - - * ``id_name``: If *full_distribution_name* is false, the result of - :func:`distro.id`. Otherwise, the result of :func:`distro.name`. - - * ``version``: The result of :func:`distro.version`. - - * ``codename``: The extra item (usually in parentheses) after the - os-release version number, or the result of :func:`distro.codename`. - - The interface of this function is compatible with the original - :py:func:`platform.linux_distribution` function, supporting a subset of - its parameters. - - The data it returns may not exactly be the same, because it uses more data - sources than the original function, and that may lead to different data if - the OS distribution is not consistent across multiple data sources it - provides (there are indeed such distributions ...). - - Another reason for differences is the fact that the :func:`distro.id` - method normalizes the distro ID string to a reliable machine-readable value - for a number of popular OS distributions. - """ - warnings.warn( - "distro.linux_distribution() is deprecated. It should only be used as a " - "compatibility shim with Python's platform.linux_distribution(). Please use " - "distro.id(), distro.version() and distro.name() instead.", - DeprecationWarning, - stacklevel=2, - ) - return _distro.linux_distribution(full_distribution_name) - - -def id() -> str: - """ - Return the distro ID of the current distribution, as a - machine-readable string. - - For a number of OS distributions, the returned distro ID value is - *reliable*, in the sense that it is documented and that it does not change - across releases of the distribution. - - This package maintains the following reliable distro ID values: - - ============== ========================================= - Distro ID Distribution - ============== ========================================= - "ubuntu" Ubuntu - "debian" Debian - "rhel" RedHat Enterprise Linux - "centos" CentOS - "fedora" Fedora - "sles" SUSE Linux Enterprise Server - "opensuse" openSUSE - "amzn" Amazon Linux - "arch" Arch Linux - "cloudlinux" CloudLinux OS - "exherbo" Exherbo Linux - "gentoo" GenToo Linux - "ibm_powerkvm" IBM PowerKVM - "kvmibm" KVM for IBM z Systems - "linuxmint" Linux Mint - "mageia" Mageia - "mandriva" Mandriva Linux - "parallels" Parallels - "pidora" Pidora - "raspbian" Raspbian - "oracle" Oracle Linux (and Oracle Enterprise Linux) - "scientific" Scientific Linux - "slackware" Slackware - "xenserver" XenServer - "openbsd" OpenBSD - "netbsd" NetBSD - "freebsd" FreeBSD - "midnightbsd" MidnightBSD - "rocky" Rocky Linux - "aix" AIX - ============== ========================================= - - If you have a need to get distros for reliable IDs added into this set, - or if you find that the :func:`distro.id` function returns a different - distro ID for one of the listed distros, please create an issue in the - `distro issue tracker`_. - - **Lookup hierarchy and transformations:** - - First, the ID is obtained from the following sources, in the specified - order. The first available and non-empty value is used: - - * the value of the "ID" attribute of the os-release file, - - * the value of the "Distributor ID" attribute returned by the lsb_release - command, - - * the first part of the file name of the distro release file, - - The so determined ID value then passes the following transformations, - before it is returned by this method: - - * it is translated to lower case, - - * blanks (which should not be there anyway) are translated to underscores, - - * a normalization of the ID is performed, based upon - `normalization tables`_. The purpose of this normalization is to ensure - that the ID is as reliable as possible, even across incompatible changes - in the OS distributions. A common reason for an incompatible change is - the addition of an os-release file, or the addition of the lsb_release - command, with ID values that differ from what was previously determined - from the distro release file name. - """ - return _distro.id() - - -def name(pretty: bool = False) -> str: - """ - Return the name of the current OS distribution, as a human-readable - string. - - If *pretty* is false, the name is returned without version or codename. - (e.g. "CentOS Linux") - - If *pretty* is true, the version and codename are appended. - (e.g. "CentOS Linux 7.1.1503 (Core)") - - **Lookup hierarchy:** - - The name is obtained from the following sources, in the specified order. - The first available and non-empty value is used: - - * If *pretty* is false: - - - the value of the "NAME" attribute of the os-release file, - - - the value of the "Distributor ID" attribute returned by the lsb_release - command, - - - the value of the "" field of the distro release file. - - * If *pretty* is true: - - - the value of the "PRETTY_NAME" attribute of the os-release file, - - - the value of the "Description" attribute returned by the lsb_release - command, - - - the value of the "" field of the distro release file, appended - with the value of the pretty version ("" and "" - fields) of the distro release file, if available. - """ - return _distro.name(pretty) - - -def version(pretty: bool = False, best: bool = False) -> str: - """ - Return the version of the current OS distribution, as a human-readable - string. - - If *pretty* is false, the version is returned without codename (e.g. - "7.0"). - - If *pretty* is true, the codename in parenthesis is appended, if the - codename is non-empty (e.g. "7.0 (Maipo)"). - - Some distributions provide version numbers with different precisions in - the different sources of distribution information. Examining the different - sources in a fixed priority order does not always yield the most precise - version (e.g. for Debian 8.2, or CentOS 7.1). - - Some other distributions may not provide this kind of information. In these - cases, an empty string would be returned. This behavior can be observed - with rolling releases distributions (e.g. Arch Linux). - - The *best* parameter can be used to control the approach for the returned - version: - - If *best* is false, the first non-empty version number in priority order of - the examined sources is returned. - - If *best* is true, the most precise version number out of all examined - sources is returned. - - **Lookup hierarchy:** - - In all cases, the version number is obtained from the following sources. - If *best* is false, this order represents the priority order: - - * the value of the "VERSION_ID" attribute of the os-release file, - * the value of the "Release" attribute returned by the lsb_release - command, - * the version number parsed from the "" field of the first line - of the distro release file, - * the version number parsed from the "PRETTY_NAME" attribute of the - os-release file, if it follows the format of the distro release files. - * the version number parsed from the "Description" attribute returned by - the lsb_release command, if it follows the format of the distro release - files. - """ - return _distro.version(pretty, best) - - -def version_parts(best: bool = False) -> Tuple[str, str, str]: - """ - Return the version of the current OS distribution as a tuple - ``(major, minor, build_number)`` with items as follows: - - * ``major``: The result of :func:`distro.major_version`. - - * ``minor``: The result of :func:`distro.minor_version`. - - * ``build_number``: The result of :func:`distro.build_number`. - - For a description of the *best* parameter, see the :func:`distro.version` - method. - """ - return _distro.version_parts(best) - - -def major_version(best: bool = False) -> str: - """ - Return the major version of the current OS distribution, as a string, - if provided. - Otherwise, the empty string is returned. The major version is the first - part of the dot-separated version string. - - For a description of the *best* parameter, see the :func:`distro.version` - method. - """ - return _distro.major_version(best) - - -def minor_version(best: bool = False) -> str: - """ - Return the minor version of the current OS distribution, as a string, - if provided. - Otherwise, the empty string is returned. The minor version is the second - part of the dot-separated version string. - - For a description of the *best* parameter, see the :func:`distro.version` - method. - """ - return _distro.minor_version(best) - - -def build_number(best: bool = False) -> str: - """ - Return the build number of the current OS distribution, as a string, - if provided. - Otherwise, the empty string is returned. The build number is the third part - of the dot-separated version string. - - For a description of the *best* parameter, see the :func:`distro.version` - method. - """ - return _distro.build_number(best) - - -def like() -> str: - """ - Return a space-separated list of distro IDs of distributions that are - closely related to the current OS distribution in regards to packaging - and programming interfaces, for example distributions the current - distribution is a derivative from. - - **Lookup hierarchy:** - - This information item is only provided by the os-release file. - For details, see the description of the "ID_LIKE" attribute in the - `os-release man page - `_. - """ - return _distro.like() - - -def codename() -> str: - """ - Return the codename for the release of the current OS distribution, - as a string. - - If the distribution does not have a codename, an empty string is returned. - - Note that the returned codename is not always really a codename. For - example, openSUSE returns "x86_64". This function does not handle such - cases in any special way and just returns the string it finds, if any. - - **Lookup hierarchy:** - - * the codename within the "VERSION" attribute of the os-release file, if - provided, - - * the value of the "Codename" attribute returned by the lsb_release - command, - - * the value of the "" field of the distro release file. - """ - return _distro.codename() - - -def info(pretty: bool = False, best: bool = False) -> InfoDict: - """ - Return certain machine-readable information items about the current OS - distribution in a dictionary, as shown in the following example: - - .. sourcecode:: python - - { - 'id': 'rhel', - 'version': '7.0', - 'version_parts': { - 'major': '7', - 'minor': '0', - 'build_number': '' - }, - 'like': 'fedora', - 'codename': 'Maipo' - } - - The dictionary structure and keys are always the same, regardless of which - information items are available in the underlying data sources. The values - for the various keys are as follows: - - * ``id``: The result of :func:`distro.id`. - - * ``version``: The result of :func:`distro.version`. - - * ``version_parts -> major``: The result of :func:`distro.major_version`. - - * ``version_parts -> minor``: The result of :func:`distro.minor_version`. - - * ``version_parts -> build_number``: The result of - :func:`distro.build_number`. - - * ``like``: The result of :func:`distro.like`. - - * ``codename``: The result of :func:`distro.codename`. - - For a description of the *pretty* and *best* parameters, see the - :func:`distro.version` method. - """ - return _distro.info(pretty, best) - - -def os_release_info() -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information items - from the os-release file data source of the current OS distribution. - - See `os-release file`_ for details about these information items. - """ - return _distro.os_release_info() - - -def lsb_release_info() -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information items - from the lsb_release command data source of the current OS distribution. - - See `lsb_release command output`_ for details about these information - items. - """ - return _distro.lsb_release_info() - - -def distro_release_info() -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information items - from the distro release file data source of the current OS distribution. - - See `distro release file`_ for details about these information items. - """ - return _distro.distro_release_info() - - -def uname_info() -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information items - from the distro release file data source of the current OS distribution. - """ - return _distro.uname_info() - - -def os_release_attr(attribute: str) -> str: - """ - Return a single named information item from the os-release file data source - of the current OS distribution. - - Parameters: - - * ``attribute`` (string): Key of the information item. - - Returns: - - * (string): Value of the information item, if the item exists. - The empty string, if the item does not exist. - - See `os-release file`_ for details about these information items. - """ - return _distro.os_release_attr(attribute) - - -def lsb_release_attr(attribute: str) -> str: - """ - Return a single named information item from the lsb_release command output - data source of the current OS distribution. - - Parameters: - - * ``attribute`` (string): Key of the information item. - - Returns: - - * (string): Value of the information item, if the item exists. - The empty string, if the item does not exist. - - See `lsb_release command output`_ for details about these information - items. - """ - return _distro.lsb_release_attr(attribute) - - -def distro_release_attr(attribute: str) -> str: - """ - Return a single named information item from the distro release file - data source of the current OS distribution. - - Parameters: - - * ``attribute`` (string): Key of the information item. - - Returns: - - * (string): Value of the information item, if the item exists. - The empty string, if the item does not exist. - - See `distro release file`_ for details about these information items. - """ - return _distro.distro_release_attr(attribute) - - -def uname_attr(attribute: str) -> str: - """ - Return a single named information item from the distro release file - data source of the current OS distribution. - - Parameters: - - * ``attribute`` (string): Key of the information item. - - Returns: - - * (string): Value of the information item, if the item exists. - The empty string, if the item does not exist. - """ - return _distro.uname_attr(attribute) - - -try: - from functools import cached_property -except ImportError: - # Python < 3.8 - class cached_property: # type: ignore - """A version of @property which caches the value. On access, it calls the - underlying function and sets the value in `__dict__` so future accesses - will not re-call the property. - """ - - def __init__(self, f: Callable[[Any], Any]) -> None: - self._fname = f.__name__ - self._f = f - - def __get__(self, obj: Any, owner: Type[Any]) -> Any: - assert obj is not None, f"call {self._fname} on an instance" - ret = obj.__dict__[self._fname] = self._f(obj) - return ret - - -class LinuxDistribution: - """ - Provides information about a OS distribution. - - This package creates a private module-global instance of this class with - default initialization arguments, that is used by the - `consolidated accessor functions`_ and `single source accessor functions`_. - By using default initialization arguments, that module-global instance - returns data about the current OS distribution (i.e. the distro this - package runs on). - - Normally, it is not necessary to create additional instances of this class. - However, in situations where control is needed over the exact data sources - that are used, instances of this class can be created with a specific - distro release file, or a specific os-release file, or without invoking the - lsb_release command. - """ - - def __init__( - self, - include_lsb: Optional[bool] = None, - os_release_file: str = "", - distro_release_file: str = "", - include_uname: Optional[bool] = None, - root_dir: Optional[str] = None, - include_oslevel: Optional[bool] = None, - ) -> None: - """ - The initialization method of this class gathers information from the - available data sources, and stores that in private instance attributes. - Subsequent access to the information items uses these private instance - attributes, so that the data sources are read only once. - - Parameters: - - * ``include_lsb`` (bool): Controls whether the - `lsb_release command output`_ is included as a data source. - - If the lsb_release command is not available in the program execution - path, the data source for the lsb_release command will be empty. - - * ``os_release_file`` (string): The path name of the - `os-release file`_ that is to be used as a data source. - - An empty string (the default) will cause the default path name to - be used (see `os-release file`_ for details). - - If the specified or defaulted os-release file does not exist, the - data source for the os-release file will be empty. - - * ``distro_release_file`` (string): The path name of the - `distro release file`_ that is to be used as a data source. - - An empty string (the default) will cause a default search algorithm - to be used (see `distro release file`_ for details). - - If the specified distro release file does not exist, or if no default - distro release file can be found, the data source for the distro - release file will be empty. - - * ``include_uname`` (bool): Controls whether uname command output is - included as a data source. If the uname command is not available in - the program execution path the data source for the uname command will - be empty. - - * ``root_dir`` (string): The absolute path to the root directory to use - to find distro-related information files. Note that ``include_*`` - parameters must not be enabled in combination with ``root_dir``. - - * ``include_oslevel`` (bool): Controls whether (AIX) oslevel command - output is included as a data source. If the oslevel command is not - available in the program execution path the data source will be - empty. - - Public instance attributes: - - * ``os_release_file`` (string): The path name of the - `os-release file`_ that is actually used as a data source. The - empty string if no distro release file is used as a data source. - - * ``distro_release_file`` (string): The path name of the - `distro release file`_ that is actually used as a data source. The - empty string if no distro release file is used as a data source. - - * ``include_lsb`` (bool): The result of the ``include_lsb`` parameter. - This controls whether the lsb information will be loaded. - - * ``include_uname`` (bool): The result of the ``include_uname`` - parameter. This controls whether the uname information will - be loaded. - - * ``include_oslevel`` (bool): The result of the ``include_oslevel`` - parameter. This controls whether (AIX) oslevel information will be - loaded. - - * ``root_dir`` (string): The result of the ``root_dir`` parameter. - The absolute path to the root directory to use to find distro-related - information files. - - Raises: - - * :py:exc:`ValueError`: Initialization parameters combination is not - supported. - - * :py:exc:`OSError`: Some I/O issue with an os-release file or distro - release file. - - * :py:exc:`UnicodeError`: A data source has unexpected characters or - uses an unexpected encoding. - """ - self.root_dir = root_dir - self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR - self.usr_lib_dir = ( - os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR - ) - - if os_release_file: - self.os_release_file = os_release_file - else: - etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME) - usr_lib_os_release_file = os.path.join( - self.usr_lib_dir, _OS_RELEASE_BASENAME - ) - - # NOTE: The idea is to respect order **and** have it set - # at all times for API backwards compatibility. - if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile( - usr_lib_os_release_file - ): - self.os_release_file = etc_dir_os_release_file - else: - self.os_release_file = usr_lib_os_release_file - - self.distro_release_file = distro_release_file or "" # updated later - - is_root_dir_defined = root_dir is not None - if is_root_dir_defined and (include_lsb or include_uname or include_oslevel): - raise ValueError( - "Including subprocess data sources from specific root_dir is disallowed" - " to prevent false information" - ) - self.include_lsb = ( - include_lsb if include_lsb is not None else not is_root_dir_defined - ) - self.include_uname = ( - include_uname if include_uname is not None else not is_root_dir_defined - ) - self.include_oslevel = ( - include_oslevel if include_oslevel is not None else not is_root_dir_defined - ) - - def __repr__(self) -> str: - """Return repr of all info""" - return ( - "LinuxDistribution(" - "os_release_file={self.os_release_file!r}, " - "distro_release_file={self.distro_release_file!r}, " - "include_lsb={self.include_lsb!r}, " - "include_uname={self.include_uname!r}, " - "include_oslevel={self.include_oslevel!r}, " - "root_dir={self.root_dir!r}, " - "_os_release_info={self._os_release_info!r}, " - "_lsb_release_info={self._lsb_release_info!r}, " - "_distro_release_info={self._distro_release_info!r}, " - "_uname_info={self._uname_info!r}, " - "_oslevel_info={self._oslevel_info!r})".format(self=self) - ) - - def linux_distribution( - self, full_distribution_name: bool = True - ) -> Tuple[str, str, str]: - """ - Return information about the OS distribution that is compatible - with Python's :func:`platform.linux_distribution`, supporting a subset - of its parameters. - - For details, see :func:`distro.linux_distribution`. - """ - return ( - self.name() if full_distribution_name else self.id(), - self.version(), - self._os_release_info.get("release_codename") or self.codename(), - ) - - def id(self) -> str: - """Return the distro ID of the OS distribution, as a string. - - For details, see :func:`distro.id`. - """ - - def normalize(distro_id: str, table: Dict[str, str]) -> str: - distro_id = distro_id.lower().replace(" ", "_") - return table.get(distro_id, distro_id) - - distro_id = self.os_release_attr("id") - if distro_id: - return normalize(distro_id, NORMALIZED_OS_ID) - - distro_id = self.lsb_release_attr("distributor_id") - if distro_id: - return normalize(distro_id, NORMALIZED_LSB_ID) - - distro_id = self.distro_release_attr("id") - if distro_id: - return normalize(distro_id, NORMALIZED_DISTRO_ID) - - distro_id = self.uname_attr("id") - if distro_id: - return normalize(distro_id, NORMALIZED_DISTRO_ID) - - return "" - - def name(self, pretty: bool = False) -> str: - """ - Return the name of the OS distribution, as a string. - - For details, see :func:`distro.name`. - """ - name = ( - self.os_release_attr("name") - or self.lsb_release_attr("distributor_id") - or self.distro_release_attr("name") - or self.uname_attr("name") - ) - if pretty: - name = self.os_release_attr("pretty_name") or self.lsb_release_attr( - "description" - ) - if not name: - name = self.distro_release_attr("name") or self.uname_attr("name") - version = self.version(pretty=True) - if version: - name = f"{name} {version}" - return name or "" - - def version(self, pretty: bool = False, best: bool = False) -> str: - """ - Return the version of the OS distribution, as a string. - - For details, see :func:`distro.version`. - """ - versions = [ - self.os_release_attr("version_id"), - self.lsb_release_attr("release"), - self.distro_release_attr("version_id"), - self._parse_distro_release_content(self.os_release_attr("pretty_name")).get( - "version_id", "" - ), - self._parse_distro_release_content( - self.lsb_release_attr("description") - ).get("version_id", ""), - self.uname_attr("release"), - ] - if self.uname_attr("id").startswith("aix"): - # On AIX platforms, prefer oslevel command output. - versions.insert(0, self.oslevel_info()) - version = "" - if best: - # This algorithm uses the last version in priority order that has - # the best precision. If the versions are not in conflict, that - # does not matter; otherwise, using the last one instead of the - # first one might be considered a surprise. - for v in versions: - if v.count(".") > version.count(".") or version == "": - version = v - else: - for v in versions: - if v != "": - version = v - break - if pretty and version and self.codename(): - version = f"{version} ({self.codename()})" - return version - - def version_parts(self, best: bool = False) -> Tuple[str, str, str]: - """ - Return the version of the OS distribution, as a tuple of version - numbers. - - For details, see :func:`distro.version_parts`. - """ - version_str = self.version(best=best) - if version_str: - version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?") - matches = version_regex.match(version_str) - if matches: - major, minor, build_number = matches.groups() - return major, minor or "", build_number or "" - return "", "", "" - - def major_version(self, best: bool = False) -> str: - """ - Return the major version number of the current distribution. - - For details, see :func:`distro.major_version`. - """ - return self.version_parts(best)[0] - - def minor_version(self, best: bool = False) -> str: - """ - Return the minor version number of the current distribution. - - For details, see :func:`distro.minor_version`. - """ - return self.version_parts(best)[1] - - def build_number(self, best: bool = False) -> str: - """ - Return the build number of the current distribution. - - For details, see :func:`distro.build_number`. - """ - return self.version_parts(best)[2] - - def like(self) -> str: - """ - Return the IDs of distributions that are like the OS distribution. - - For details, see :func:`distro.like`. - """ - return self.os_release_attr("id_like") or "" - - def codename(self) -> str: - """ - Return the codename of the OS distribution. - - For details, see :func:`distro.codename`. - """ - try: - # Handle os_release specially since distros might purposefully set - # this to empty string to have no codename - return self._os_release_info["codename"] - except KeyError: - return ( - self.lsb_release_attr("codename") - or self.distro_release_attr("codename") - or "" - ) - - def info(self, pretty: bool = False, best: bool = False) -> InfoDict: - """ - Return certain machine-readable information about the OS - distribution. - - For details, see :func:`distro.info`. - """ - return dict( - id=self.id(), - version=self.version(pretty, best), - version_parts=dict( - major=self.major_version(best), - minor=self.minor_version(best), - build_number=self.build_number(best), - ), - like=self.like(), - codename=self.codename(), - ) - - def os_release_info(self) -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information - items from the os-release file data source of the OS distribution. - - For details, see :func:`distro.os_release_info`. - """ - return self._os_release_info - - def lsb_release_info(self) -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information - items from the lsb_release command data source of the OS - distribution. - - For details, see :func:`distro.lsb_release_info`. - """ - return self._lsb_release_info - - def distro_release_info(self) -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information - items from the distro release file data source of the OS - distribution. - - For details, see :func:`distro.distro_release_info`. - """ - return self._distro_release_info - - def uname_info(self) -> Dict[str, str]: - """ - Return a dictionary containing key-value pairs for the information - items from the uname command data source of the OS distribution. - - For details, see :func:`distro.uname_info`. - """ - return self._uname_info - - def oslevel_info(self) -> str: - """ - Return AIX' oslevel command output. - """ - return self._oslevel_info - - def os_release_attr(self, attribute: str) -> str: - """ - Return a single named information item from the os-release file data - source of the OS distribution. - - For details, see :func:`distro.os_release_attr`. - """ - return self._os_release_info.get(attribute, "") - - def lsb_release_attr(self, attribute: str) -> str: - """ - Return a single named information item from the lsb_release command - output data source of the OS distribution. - - For details, see :func:`distro.lsb_release_attr`. - """ - return self._lsb_release_info.get(attribute, "") - - def distro_release_attr(self, attribute: str) -> str: - """ - Return a single named information item from the distro release file - data source of the OS distribution. - - For details, see :func:`distro.distro_release_attr`. - """ - return self._distro_release_info.get(attribute, "") - - def uname_attr(self, attribute: str) -> str: - """ - Return a single named information item from the uname command - output data source of the OS distribution. - - For details, see :func:`distro.uname_attr`. - """ - return self._uname_info.get(attribute, "") - - @cached_property - def _os_release_info(self) -> Dict[str, str]: - """ - Get the information items from the specified os-release file. - - Returns: - A dictionary containing all information items. - """ - if os.path.isfile(self.os_release_file): - with open(self.os_release_file, encoding="utf-8") as release_file: - return self._parse_os_release_content(release_file) - return {} - - @staticmethod - def _parse_os_release_content(lines: TextIO) -> Dict[str, str]: - """ - Parse the lines of an os-release file. - - Parameters: - - * lines: Iterable through the lines in the os-release file. - Each line must be a unicode string or a UTF-8 encoded byte - string. - - Returns: - A dictionary containing all information items. - """ - props = {} - lexer = shlex.shlex(lines, posix=True) - lexer.whitespace_split = True - - tokens = list(lexer) - for token in tokens: - # At this point, all shell-like parsing has been done (i.e. - # comments processed, quotes and backslash escape sequences - # processed, multi-line values assembled, trailing newlines - # stripped, etc.), so the tokens are now either: - # * variable assignments: var=value - # * commands or their arguments (not allowed in os-release) - # Ignore any tokens that are not variable assignments - if "=" in token: - k, v = token.split("=", 1) - props[k.lower()] = v - - if "version" in props: - # extract release codename (if any) from version attribute - match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"]) - if match: - release_codename = match.group(1) or match.group(2) - props["codename"] = props["release_codename"] = release_codename - - if "version_codename" in props: - # os-release added a version_codename field. Use that in - # preference to anything else Note that some distros purposefully - # do not have code names. They should be setting - # version_codename="" - props["codename"] = props["version_codename"] - elif "ubuntu_codename" in props: - # Same as above but a non-standard field name used on older Ubuntus - props["codename"] = props["ubuntu_codename"] - - return props - - @cached_property - def _lsb_release_info(self) -> Dict[str, str]: - """ - Get the information items from the lsb_release command output. - - Returns: - A dictionary containing all information items. - """ - if not self.include_lsb: - return {} - try: - cmd = ("lsb_release", "-a") - stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) - # Command not found or lsb_release returned error - except (OSError, subprocess.CalledProcessError): - return {} - content = self._to_str(stdout).splitlines() - return self._parse_lsb_release_content(content) - - @staticmethod - def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]: - """ - Parse the output of the lsb_release command. - - Parameters: - - * lines: Iterable through the lines of the lsb_release output. - Each line must be a unicode string or a UTF-8 encoded byte - string. - - Returns: - A dictionary containing all information items. - """ - props = {} - for line in lines: - kv = line.strip("\n").split(":", 1) - if len(kv) != 2: - # Ignore lines without colon. - continue - k, v = kv - props.update({k.replace(" ", "_").lower(): v.strip()}) - return props - - @cached_property - def _uname_info(self) -> Dict[str, str]: - if not self.include_uname: - return {} - try: - cmd = ("uname", "-rs") - stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) - except OSError: - return {} - content = self._to_str(stdout).splitlines() - return self._parse_uname_content(content) - - @cached_property - def _oslevel_info(self) -> str: - if not self.include_oslevel: - return "" - try: - stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL) - except (OSError, subprocess.CalledProcessError): - return "" - return self._to_str(stdout).strip() - - @staticmethod - def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: - if not lines: - return {} - props = {} - match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip()) - if match: - name, version = match.groups() - - # This is to prevent the Linux kernel version from - # appearing as the 'best' version on otherwise - # identifiable distributions. - if name == "Linux": - return {} - props["id"] = name.lower() - props["name"] = name - props["release"] = version - return props - - @staticmethod - def _to_str(bytestring: bytes) -> str: - encoding = sys.getfilesystemencoding() - return bytestring.decode(encoding) - - @cached_property - def _distro_release_info(self) -> Dict[str, str]: - """ - Get the information items from the specified distro release file. - - Returns: - A dictionary containing all information items. - """ - if self.distro_release_file: - # If it was specified, we use it and parse what we can, even if - # its file name or content does not match the expected pattern. - distro_info = self._parse_distro_release_file(self.distro_release_file) - basename = os.path.basename(self.distro_release_file) - # The file name pattern for user-specified distro release files - # is somewhat more tolerant (compared to when searching for the - # file), because we want to use what was specified as best as - # possible. - match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if "name" in distro_info and "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" - elif match: - distro_info["id"] = match.group(1) - return distro_info - else: - try: - basenames = os.listdir(self.etc_dir) - # We sort for repeatability in cases where there are multiple - # distro specific files; e.g. CentOS, Oracle, Enterprise all - # containing `redhat-release` on top of their own. - basenames.sort() - except OSError: - # This may occur when /etc is not readable but we can't be - # sure about the *-release files. Check common entries of - # /etc for information. If they turn out to not be there the - # error is handled in `_parse_distro_release_file()`. - basenames = [ - "SuSE-release", - "arch-release", - "base-release", - "centos-release", - "fedora-release", - "gentoo-release", - "mageia-release", - "mandrake-release", - "mandriva-release", - "mandrivalinux-release", - "manjaro-release", - "oracle-release", - "redhat-release", - "rocky-release", - "sl-release", - "slackware-version", - ] - for basename in basenames: - if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: - continue - match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if match: - filepath = os.path.join(self.etc_dir, basename) - distro_info = self._parse_distro_release_file(filepath) - if "name" in distro_info: - # The name is always present if the pattern matches - self.distro_release_file = filepath - distro_info["id"] = match.group(1) - if "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" - return distro_info - return {} - - def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: - """ - Parse a distro release file. - - Parameters: - - * filepath: Path name of the distro release file. - - Returns: - A dictionary containing all information items. - """ - try: - with open(filepath, encoding="utf-8") as fp: - # Only parse the first line. For instance, on SLES there - # are multiple lines. We don't want them... - return self._parse_distro_release_content(fp.readline()) - except OSError: - # Ignore not being able to read a specific, seemingly version - # related file. - # See https://github.com/python-distro/distro/issues/162 - return {} - - @staticmethod - def _parse_distro_release_content(line: str) -> Dict[str, str]: - """ - Parse a line from a distro release file. - - Parameters: - * line: Line from the distro release file. Must be a unicode string - or a UTF-8 encoded byte string. - - Returns: - A dictionary containing all information items. - """ - matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1]) - distro_info = {} - if matches: - # regexp ensures non-None - distro_info["name"] = matches.group(3)[::-1] - if matches.group(2): - distro_info["version_id"] = matches.group(2)[::-1] - if matches.group(1): - distro_info["codename"] = matches.group(1)[::-1] - elif line: - distro_info["name"] = line.strip() - return distro_info - - -_distro = LinuxDistribution() - - -def main() -> None: - logger = logging.getLogger(__name__) - logger.setLevel(logging.DEBUG) - logger.addHandler(logging.StreamHandler(sys.stdout)) - - parser = argparse.ArgumentParser(description="OS distro info tool") - parser.add_argument( - "--json", "-j", help="Output in machine readable format", action="store_true" - ) - - parser.add_argument( - "--root-dir", - "-r", - type=str, - dest="root_dir", - help="Path to the root filesystem directory (defaults to /)", - ) - - args = parser.parse_args() - - if args.root_dir: - dist = LinuxDistribution( - include_lsb=False, - include_uname=False, - include_oslevel=False, - root_dir=args.root_dir, - ) - else: - dist = _distro - - if args.json: - logger.info(json.dumps(dist.info(), indent=4, sort_keys=True)) - else: - logger.info("Name: %s", dist.name(pretty=True)) - distribution_version = dist.version(pretty=True) - logger.info("Version: %s", distribution_version) - distribution_codename = dist.codename() - logger.info("Codename: %s", distribution_codename) - - -if __name__ == "__main__": - main() diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/__init__.py b/.venv/Lib/site-packages/pip/_vendor/idna/__init__.py deleted file mode 100644 index a40eeaf..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -from .package_data import __version__ -from .core import ( - IDNABidiError, - IDNAError, - InvalidCodepoint, - InvalidCodepointContext, - alabel, - check_bidi, - check_hyphen_ok, - check_initial_combiner, - check_label, - check_nfc, - decode, - encode, - ulabel, - uts46_remap, - valid_contextj, - valid_contexto, - valid_label_length, - valid_string_length, -) -from .intranges import intranges_contain - -__all__ = [ - "IDNABidiError", - "IDNAError", - "InvalidCodepoint", - "InvalidCodepointContext", - "alabel", - "check_bidi", - "check_hyphen_ok", - "check_initial_combiner", - "check_label", - "check_nfc", - "decode", - "encode", - "intranges_contain", - "ulabel", - "uts46_remap", - "valid_contextj", - "valid_contexto", - "valid_label_length", - "valid_string_length", -] diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/codec.py b/.venv/Lib/site-packages/pip/_vendor/idna/codec.py deleted file mode 100644 index 1ca9ba6..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/codec.py +++ /dev/null @@ -1,112 +0,0 @@ -from .core import encode, decode, alabel, ulabel, IDNAError -import codecs -import re -from typing import Tuple, Optional - -_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') - -class Codec(codecs.Codec): - - def encode(self, data: str, errors: str = 'strict') -> Tuple[bytes, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return b"", 0 - - return encode(data), len(data) - - def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return '', 0 - - return decode(data), len(data) - -class IncrementalEncoder(codecs.BufferedIncrementalEncoder): - def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return "", 0 - - labels = _unicode_dots_re.split(data) - trailing_dot = '' - if labels: - if not labels[-1]: - trailing_dot = '.' - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = '.' - - result = [] - size = 0 - for label in labels: - result.append(alabel(label)) - if size: - size += 1 - size += len(label) - - # Join with U+002E - result_str = '.'.join(result) + trailing_dot # type: ignore - size += len(trailing_dot) - return result_str, size - -class IncrementalDecoder(codecs.BufferedIncrementalDecoder): - def _buffer_decode(self, data: str, errors: str, final: bool) -> Tuple[str, int]: # type: ignore - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return ('', 0) - - labels = _unicode_dots_re.split(data) - trailing_dot = '' - if labels: - if not labels[-1]: - trailing_dot = '.' - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = '.' - - result = [] - size = 0 - for label in labels: - result.append(ulabel(label)) - if size: - size += 1 - size += len(label) - - result_str = '.'.join(result) + trailing_dot - size += len(trailing_dot) - return (result_str, size) - - -class StreamWriter(Codec, codecs.StreamWriter): - pass - - -class StreamReader(Codec, codecs.StreamReader): - pass - - -def getregentry() -> codecs.CodecInfo: - # Compatibility as a search_function for codecs.register() - return codecs.CodecInfo( - name='idna', - encode=Codec().encode, # type: ignore - decode=Codec().decode, # type: ignore - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamwriter=StreamWriter, - streamreader=StreamReader, - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/compat.py b/.venv/Lib/site-packages/pip/_vendor/idna/compat.py deleted file mode 100644 index 786e6bd..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/compat.py +++ /dev/null @@ -1,13 +0,0 @@ -from .core import * -from .codec import * -from typing import Any, Union - -def ToASCII(label: str) -> bytes: - return encode(label) - -def ToUnicode(label: Union[bytes, bytearray]) -> str: - return decode(label) - -def nameprep(s: Any) -> None: - raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol') - diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/core.py b/.venv/Lib/site-packages/pip/_vendor/idna/core.py deleted file mode 100644 index 4f30037..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/core.py +++ /dev/null @@ -1,400 +0,0 @@ -from . import idnadata -import bisect -import unicodedata -import re -from typing import Union, Optional -from .intranges import intranges_contain - -_virama_combining_class = 9 -_alabel_prefix = b'xn--' -_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') - -class IDNAError(UnicodeError): - """ Base exception for all IDNA-encoding related problems """ - pass - - -class IDNABidiError(IDNAError): - """ Exception when bidirectional requirements are not satisfied """ - pass - - -class InvalidCodepoint(IDNAError): - """ Exception when a disallowed or unallocated codepoint is used """ - pass - - -class InvalidCodepointContext(IDNAError): - """ Exception when the codepoint is not valid in the context it is used """ - pass - - -def _combining_class(cp: int) -> int: - v = unicodedata.combining(chr(cp)) - if v == 0: - if not unicodedata.name(chr(cp)): - raise ValueError('Unknown character in unicodedata') - return v - -def _is_script(cp: str, script: str) -> bool: - return intranges_contain(ord(cp), idnadata.scripts[script]) - -def _punycode(s: str) -> bytes: - return s.encode('punycode') - -def _unot(s: int) -> str: - return 'U+{:04X}'.format(s) - - -def valid_label_length(label: Union[bytes, str]) -> bool: - if len(label) > 63: - return False - return True - - -def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: - if len(label) > (254 if trailing_dot else 253): - return False - return True - - -def check_bidi(label: str, check_ltr: bool = False) -> bool: - # Bidi rules should only be applied if string contains RTL characters - bidi_label = False - for (idx, cp) in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - if direction == '': - # String likely comes from a newer version of Unicode - raise IDNABidiError('Unknown directionality in label {} at position {}'.format(repr(label), idx)) - if direction in ['R', 'AL', 'AN']: - bidi_label = True - if not bidi_label and not check_ltr: - return True - - # Bidi rule 1 - direction = unicodedata.bidirectional(label[0]) - if direction in ['R', 'AL']: - rtl = True - elif direction == 'L': - rtl = False - else: - raise IDNABidiError('First codepoint in label {} must be directionality L, R or AL'.format(repr(label))) - - valid_ending = False - number_type = None # type: Optional[str] - for (idx, cp) in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - - if rtl: - # Bidi rule 2 - if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {} in a right-to-left label'.format(idx)) - # Bidi rule 3 - if direction in ['R', 'AL', 'EN', 'AN']: - valid_ending = True - elif direction != 'NSM': - valid_ending = False - # Bidi rule 4 - if direction in ['AN', 'EN']: - if not number_type: - number_type = direction - else: - if number_type != direction: - raise IDNABidiError('Can not mix numeral types in a right-to-left label') - else: - # Bidi rule 5 - if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {} in a left-to-right label'.format(idx)) - # Bidi rule 6 - if direction in ['L', 'EN']: - valid_ending = True - elif direction != 'NSM': - valid_ending = False - - if not valid_ending: - raise IDNABidiError('Label ends with illegal codepoint directionality') - - return True - - -def check_initial_combiner(label: str) -> bool: - if unicodedata.category(label[0])[0] == 'M': - raise IDNAError('Label begins with an illegal combining character') - return True - - -def check_hyphen_ok(label: str) -> bool: - if label[2:4] == '--': - raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') - if label[0] == '-' or label[-1] == '-': - raise IDNAError('Label must not start or end with a hyphen') - return True - - -def check_nfc(label: str) -> None: - if unicodedata.normalize('NFC', label) != label: - raise IDNAError('Label must be in Normalization Form C') - - -def valid_contextj(label: str, pos: int) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x200c: - - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - - ok = False - for i in range(pos-1, -1, -1): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord('T'): - continue - if joining_type in [ord('L'), ord('D')]: - ok = True - break - - if not ok: - return False - - ok = False - for i in range(pos+1, len(label)): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord('T'): - continue - if joining_type in [ord('R'), ord('D')]: - ok = True - break - return ok - - if cp_value == 0x200d: - - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - return False - - else: - - return False - - -def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x00b7: - if 0 < pos < len(label)-1: - if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c: - return True - return False - - elif cp_value == 0x0375: - if pos < len(label)-1 and len(label) > 1: - return _is_script(label[pos + 1], 'Greek') - return False - - elif cp_value == 0x05f3 or cp_value == 0x05f4: - if pos > 0: - return _is_script(label[pos - 1], 'Hebrew') - return False - - elif cp_value == 0x30fb: - for cp in label: - if cp == '\u30fb': - continue - if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): - return True - return False - - elif 0x660 <= cp_value <= 0x669: - for cp in label: - if 0x6f0 <= ord(cp) <= 0x06f9: - return False - return True - - elif 0x6f0 <= cp_value <= 0x6f9: - for cp in label: - if 0x660 <= ord(cp) <= 0x0669: - return False - return True - - return False - - -def check_label(label: Union[str, bytes, bytearray]) -> None: - if isinstance(label, (bytes, bytearray)): - label = label.decode('utf-8') - if len(label) == 0: - raise IDNAError('Empty Label') - - check_nfc(label) - check_hyphen_ok(label) - check_initial_combiner(label) - - for (pos, cp) in enumerate(label): - cp_value = ord(cp) - if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']): - continue - elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): - try: - if not valid_contextj(label, pos): - raise InvalidCodepointContext('Joiner {} not allowed at position {} in {}'.format( - _unot(cp_value), pos+1, repr(label))) - except ValueError: - raise IDNAError('Unknown codepoint adjacent to joiner {} at position {} in {}'.format( - _unot(cp_value), pos+1, repr(label))) - elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): - if not valid_contexto(label, pos): - raise InvalidCodepointContext('Codepoint {} not allowed at position {} in {}'.format(_unot(cp_value), pos+1, repr(label))) - else: - raise InvalidCodepoint('Codepoint {} at position {} of {} not allowed'.format(_unot(cp_value), pos+1, repr(label))) - - check_bidi(label) - - -def alabel(label: str) -> bytes: - try: - label_bytes = label.encode('ascii') - ulabel(label_bytes) - if not valid_label_length(label_bytes): - raise IDNAError('Label too long') - return label_bytes - except UnicodeEncodeError: - pass - - if not label: - raise IDNAError('No Input') - - label = str(label) - check_label(label) - label_bytes = _punycode(label) - label_bytes = _alabel_prefix + label_bytes - - if not valid_label_length(label_bytes): - raise IDNAError('Label too long') - - return label_bytes - - -def ulabel(label: Union[str, bytes, bytearray]) -> str: - if not isinstance(label, (bytes, bytearray)): - try: - label_bytes = label.encode('ascii') - except UnicodeEncodeError: - check_label(label) - return label - else: - label_bytes = label - - label_bytes = label_bytes.lower() - if label_bytes.startswith(_alabel_prefix): - label_bytes = label_bytes[len(_alabel_prefix):] - if not label_bytes: - raise IDNAError('Malformed A-label, no Punycode eligible content found') - if label_bytes.decode('ascii')[-1] == '-': - raise IDNAError('A-label must not end with a hyphen') - else: - check_label(label_bytes) - return label_bytes.decode('ascii') - - try: - label = label_bytes.decode('punycode') - except UnicodeError: - raise IDNAError('Invalid A-label') - check_label(label) - return label - - -def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: - """Re-map the characters in the string according to UTS46 processing.""" - from .uts46data import uts46data - output = '' - - for pos, char in enumerate(domain): - code_point = ord(char) - try: - uts46row = uts46data[code_point if code_point < 256 else - bisect.bisect_left(uts46data, (code_point, 'Z')) - 1] - status = uts46row[1] - replacement = None # type: Optional[str] - if len(uts46row) == 3: - replacement = uts46row[2] # type: ignore - if (status == 'V' or - (status == 'D' and not transitional) or - (status == '3' and not std3_rules and replacement is None)): - output += char - elif replacement is not None and (status == 'M' or - (status == '3' and not std3_rules) or - (status == 'D' and transitional)): - output += replacement - elif status != 'I': - raise IndexError() - except IndexError: - raise InvalidCodepoint( - 'Codepoint {} not allowed at position {} in {}'.format( - _unot(code_point), pos + 1, repr(domain))) - - return unicodedata.normalize('NFC', output) - - -def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False, transitional: bool = False) -> bytes: - if isinstance(s, (bytes, bytearray)): - try: - s = s.decode('ascii') - except UnicodeDecodeError: - raise IDNAError('should pass a unicode string to the function rather than a byte string.') - if uts46: - s = uts46_remap(s, std3_rules, transitional) - trailing_dot = False - result = [] - if strict: - labels = s.split('.') - else: - labels = _unicode_dots_re.split(s) - if not labels or labels == ['']: - raise IDNAError('Empty domain') - if labels[-1] == '': - del labels[-1] - trailing_dot = True - for label in labels: - s = alabel(label) - if s: - result.append(s) - else: - raise IDNAError('Empty label') - if trailing_dot: - result.append(b'') - s = b'.'.join(result) - if not valid_string_length(s, trailing_dot): - raise IDNAError('Domain too long') - return s - - -def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False) -> str: - try: - if isinstance(s, (bytes, bytearray)): - s = s.decode('ascii') - except UnicodeDecodeError: - raise IDNAError('Invalid ASCII in A-label') - if uts46: - s = uts46_remap(s, std3_rules, False) - trailing_dot = False - result = [] - if not strict: - labels = _unicode_dots_re.split(s) - else: - labels = s.split('.') - if not labels or labels == ['']: - raise IDNAError('Empty domain') - if not labels[-1]: - del labels[-1] - trailing_dot = True - for label in labels: - s = ulabel(label) - if s: - result.append(s) - else: - raise IDNAError('Empty label') - if trailing_dot: - result.append('') - return '.'.join(result) diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/idnadata.py b/.venv/Lib/site-packages/pip/_vendor/idna/idnadata.py deleted file mode 100644 index 67db462..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/idnadata.py +++ /dev/null @@ -1,2151 +0,0 @@ -# This file is automatically generated by tools/idna-data - -__version__ = '15.0.0' -scripts = { - 'Greek': ( - 0x37000000374, - 0x37500000378, - 0x37a0000037e, - 0x37f00000380, - 0x38400000385, - 0x38600000387, - 0x3880000038b, - 0x38c0000038d, - 0x38e000003a2, - 0x3a3000003e2, - 0x3f000000400, - 0x1d2600001d2b, - 0x1d5d00001d62, - 0x1d6600001d6b, - 0x1dbf00001dc0, - 0x1f0000001f16, - 0x1f1800001f1e, - 0x1f2000001f46, - 0x1f4800001f4e, - 0x1f5000001f58, - 0x1f5900001f5a, - 0x1f5b00001f5c, - 0x1f5d00001f5e, - 0x1f5f00001f7e, - 0x1f8000001fb5, - 0x1fb600001fc5, - 0x1fc600001fd4, - 0x1fd600001fdc, - 0x1fdd00001ff0, - 0x1ff200001ff5, - 0x1ff600001fff, - 0x212600002127, - 0xab650000ab66, - 0x101400001018f, - 0x101a0000101a1, - 0x1d2000001d246, - ), - 'Han': ( - 0x2e8000002e9a, - 0x2e9b00002ef4, - 0x2f0000002fd6, - 0x300500003006, - 0x300700003008, - 0x30210000302a, - 0x30380000303c, - 0x340000004dc0, - 0x4e000000a000, - 0xf9000000fa6e, - 0xfa700000fada, - 0x16fe200016fe4, - 0x16ff000016ff2, - 0x200000002a6e0, - 0x2a7000002b73a, - 0x2b7400002b81e, - 0x2b8200002cea2, - 0x2ceb00002ebe1, - 0x2f8000002fa1e, - 0x300000003134b, - 0x31350000323b0, - ), - 'Hebrew': ( - 0x591000005c8, - 0x5d0000005eb, - 0x5ef000005f5, - 0xfb1d0000fb37, - 0xfb380000fb3d, - 0xfb3e0000fb3f, - 0xfb400000fb42, - 0xfb430000fb45, - 0xfb460000fb50, - ), - 'Hiragana': ( - 0x304100003097, - 0x309d000030a0, - 0x1b0010001b120, - 0x1b1320001b133, - 0x1b1500001b153, - 0x1f2000001f201, - ), - 'Katakana': ( - 0x30a1000030fb, - 0x30fd00003100, - 0x31f000003200, - 0x32d0000032ff, - 0x330000003358, - 0xff660000ff70, - 0xff710000ff9e, - 0x1aff00001aff4, - 0x1aff50001affc, - 0x1affd0001afff, - 0x1b0000001b001, - 0x1b1200001b123, - 0x1b1550001b156, - 0x1b1640001b168, - ), -} -joining_types = { - 0x600: 85, - 0x601: 85, - 0x602: 85, - 0x603: 85, - 0x604: 85, - 0x605: 85, - 0x608: 85, - 0x60b: 85, - 0x620: 68, - 0x621: 85, - 0x622: 82, - 0x623: 82, - 0x624: 82, - 0x625: 82, - 0x626: 68, - 0x627: 82, - 0x628: 68, - 0x629: 82, - 0x62a: 68, - 0x62b: 68, - 0x62c: 68, - 0x62d: 68, - 0x62e: 68, - 0x62f: 82, - 0x630: 82, - 0x631: 82, - 0x632: 82, - 0x633: 68, - 0x634: 68, - 0x635: 68, - 0x636: 68, - 0x637: 68, - 0x638: 68, - 0x639: 68, - 0x63a: 68, - 0x63b: 68, - 0x63c: 68, - 0x63d: 68, - 0x63e: 68, - 0x63f: 68, - 0x640: 67, - 0x641: 68, - 0x642: 68, - 0x643: 68, - 0x644: 68, - 0x645: 68, - 0x646: 68, - 0x647: 68, - 0x648: 82, - 0x649: 68, - 0x64a: 68, - 0x66e: 68, - 0x66f: 68, - 0x671: 82, - 0x672: 82, - 0x673: 82, - 0x674: 85, - 0x675: 82, - 0x676: 82, - 0x677: 82, - 0x678: 68, - 0x679: 68, - 0x67a: 68, - 0x67b: 68, - 0x67c: 68, - 0x67d: 68, - 0x67e: 68, - 0x67f: 68, - 0x680: 68, - 0x681: 68, - 0x682: 68, - 0x683: 68, - 0x684: 68, - 0x685: 68, - 0x686: 68, - 0x687: 68, - 0x688: 82, - 0x689: 82, - 0x68a: 82, - 0x68b: 82, - 0x68c: 82, - 0x68d: 82, - 0x68e: 82, - 0x68f: 82, - 0x690: 82, - 0x691: 82, - 0x692: 82, - 0x693: 82, - 0x694: 82, - 0x695: 82, - 0x696: 82, - 0x697: 82, - 0x698: 82, - 0x699: 82, - 0x69a: 68, - 0x69b: 68, - 0x69c: 68, - 0x69d: 68, - 0x69e: 68, - 0x69f: 68, - 0x6a0: 68, - 0x6a1: 68, - 0x6a2: 68, - 0x6a3: 68, - 0x6a4: 68, - 0x6a5: 68, - 0x6a6: 68, - 0x6a7: 68, - 0x6a8: 68, - 0x6a9: 68, - 0x6aa: 68, - 0x6ab: 68, - 0x6ac: 68, - 0x6ad: 68, - 0x6ae: 68, - 0x6af: 68, - 0x6b0: 68, - 0x6b1: 68, - 0x6b2: 68, - 0x6b3: 68, - 0x6b4: 68, - 0x6b5: 68, - 0x6b6: 68, - 0x6b7: 68, - 0x6b8: 68, - 0x6b9: 68, - 0x6ba: 68, - 0x6bb: 68, - 0x6bc: 68, - 0x6bd: 68, - 0x6be: 68, - 0x6bf: 68, - 0x6c0: 82, - 0x6c1: 68, - 0x6c2: 68, - 0x6c3: 82, - 0x6c4: 82, - 0x6c5: 82, - 0x6c6: 82, - 0x6c7: 82, - 0x6c8: 82, - 0x6c9: 82, - 0x6ca: 82, - 0x6cb: 82, - 0x6cc: 68, - 0x6cd: 82, - 0x6ce: 68, - 0x6cf: 82, - 0x6d0: 68, - 0x6d1: 68, - 0x6d2: 82, - 0x6d3: 82, - 0x6d5: 82, - 0x6dd: 85, - 0x6ee: 82, - 0x6ef: 82, - 0x6fa: 68, - 0x6fb: 68, - 0x6fc: 68, - 0x6ff: 68, - 0x70f: 84, - 0x710: 82, - 0x712: 68, - 0x713: 68, - 0x714: 68, - 0x715: 82, - 0x716: 82, - 0x717: 82, - 0x718: 82, - 0x719: 82, - 0x71a: 68, - 0x71b: 68, - 0x71c: 68, - 0x71d: 68, - 0x71e: 82, - 0x71f: 68, - 0x720: 68, - 0x721: 68, - 0x722: 68, - 0x723: 68, - 0x724: 68, - 0x725: 68, - 0x726: 68, - 0x727: 68, - 0x728: 82, - 0x729: 68, - 0x72a: 82, - 0x72b: 68, - 0x72c: 82, - 0x72d: 68, - 0x72e: 68, - 0x72f: 82, - 0x74d: 82, - 0x74e: 68, - 0x74f: 68, - 0x750: 68, - 0x751: 68, - 0x752: 68, - 0x753: 68, - 0x754: 68, - 0x755: 68, - 0x756: 68, - 0x757: 68, - 0x758: 68, - 0x759: 82, - 0x75a: 82, - 0x75b: 82, - 0x75c: 68, - 0x75d: 68, - 0x75e: 68, - 0x75f: 68, - 0x760: 68, - 0x761: 68, - 0x762: 68, - 0x763: 68, - 0x764: 68, - 0x765: 68, - 0x766: 68, - 0x767: 68, - 0x768: 68, - 0x769: 68, - 0x76a: 68, - 0x76b: 82, - 0x76c: 82, - 0x76d: 68, - 0x76e: 68, - 0x76f: 68, - 0x770: 68, - 0x771: 82, - 0x772: 68, - 0x773: 82, - 0x774: 82, - 0x775: 68, - 0x776: 68, - 0x777: 68, - 0x778: 82, - 0x779: 82, - 0x77a: 68, - 0x77b: 68, - 0x77c: 68, - 0x77d: 68, - 0x77e: 68, - 0x77f: 68, - 0x7ca: 68, - 0x7cb: 68, - 0x7cc: 68, - 0x7cd: 68, - 0x7ce: 68, - 0x7cf: 68, - 0x7d0: 68, - 0x7d1: 68, - 0x7d2: 68, - 0x7d3: 68, - 0x7d4: 68, - 0x7d5: 68, - 0x7d6: 68, - 0x7d7: 68, - 0x7d8: 68, - 0x7d9: 68, - 0x7da: 68, - 0x7db: 68, - 0x7dc: 68, - 0x7dd: 68, - 0x7de: 68, - 0x7df: 68, - 0x7e0: 68, - 0x7e1: 68, - 0x7e2: 68, - 0x7e3: 68, - 0x7e4: 68, - 0x7e5: 68, - 0x7e6: 68, - 0x7e7: 68, - 0x7e8: 68, - 0x7e9: 68, - 0x7ea: 68, - 0x7fa: 67, - 0x840: 82, - 0x841: 68, - 0x842: 68, - 0x843: 68, - 0x844: 68, - 0x845: 68, - 0x846: 82, - 0x847: 82, - 0x848: 68, - 0x849: 82, - 0x84a: 68, - 0x84b: 68, - 0x84c: 68, - 0x84d: 68, - 0x84e: 68, - 0x84f: 68, - 0x850: 68, - 0x851: 68, - 0x852: 68, - 0x853: 68, - 0x854: 82, - 0x855: 68, - 0x856: 82, - 0x857: 82, - 0x858: 82, - 0x860: 68, - 0x861: 85, - 0x862: 68, - 0x863: 68, - 0x864: 68, - 0x865: 68, - 0x866: 85, - 0x867: 82, - 0x868: 68, - 0x869: 82, - 0x86a: 82, - 0x870: 82, - 0x871: 82, - 0x872: 82, - 0x873: 82, - 0x874: 82, - 0x875: 82, - 0x876: 82, - 0x877: 82, - 0x878: 82, - 0x879: 82, - 0x87a: 82, - 0x87b: 82, - 0x87c: 82, - 0x87d: 82, - 0x87e: 82, - 0x87f: 82, - 0x880: 82, - 0x881: 82, - 0x882: 82, - 0x883: 67, - 0x884: 67, - 0x885: 67, - 0x886: 68, - 0x887: 85, - 0x888: 85, - 0x889: 68, - 0x88a: 68, - 0x88b: 68, - 0x88c: 68, - 0x88d: 68, - 0x88e: 82, - 0x890: 85, - 0x891: 85, - 0x8a0: 68, - 0x8a1: 68, - 0x8a2: 68, - 0x8a3: 68, - 0x8a4: 68, - 0x8a5: 68, - 0x8a6: 68, - 0x8a7: 68, - 0x8a8: 68, - 0x8a9: 68, - 0x8aa: 82, - 0x8ab: 82, - 0x8ac: 82, - 0x8ad: 85, - 0x8ae: 82, - 0x8af: 68, - 0x8b0: 68, - 0x8b1: 82, - 0x8b2: 82, - 0x8b3: 68, - 0x8b4: 68, - 0x8b5: 68, - 0x8b6: 68, - 0x8b7: 68, - 0x8b8: 68, - 0x8b9: 82, - 0x8ba: 68, - 0x8bb: 68, - 0x8bc: 68, - 0x8bd: 68, - 0x8be: 68, - 0x8bf: 68, - 0x8c0: 68, - 0x8c1: 68, - 0x8c2: 68, - 0x8c3: 68, - 0x8c4: 68, - 0x8c5: 68, - 0x8c6: 68, - 0x8c7: 68, - 0x8c8: 68, - 0x8e2: 85, - 0x1806: 85, - 0x1807: 68, - 0x180a: 67, - 0x180e: 85, - 0x1820: 68, - 0x1821: 68, - 0x1822: 68, - 0x1823: 68, - 0x1824: 68, - 0x1825: 68, - 0x1826: 68, - 0x1827: 68, - 0x1828: 68, - 0x1829: 68, - 0x182a: 68, - 0x182b: 68, - 0x182c: 68, - 0x182d: 68, - 0x182e: 68, - 0x182f: 68, - 0x1830: 68, - 0x1831: 68, - 0x1832: 68, - 0x1833: 68, - 0x1834: 68, - 0x1835: 68, - 0x1836: 68, - 0x1837: 68, - 0x1838: 68, - 0x1839: 68, - 0x183a: 68, - 0x183b: 68, - 0x183c: 68, - 0x183d: 68, - 0x183e: 68, - 0x183f: 68, - 0x1840: 68, - 0x1841: 68, - 0x1842: 68, - 0x1843: 68, - 0x1844: 68, - 0x1845: 68, - 0x1846: 68, - 0x1847: 68, - 0x1848: 68, - 0x1849: 68, - 0x184a: 68, - 0x184b: 68, - 0x184c: 68, - 0x184d: 68, - 0x184e: 68, - 0x184f: 68, - 0x1850: 68, - 0x1851: 68, - 0x1852: 68, - 0x1853: 68, - 0x1854: 68, - 0x1855: 68, - 0x1856: 68, - 0x1857: 68, - 0x1858: 68, - 0x1859: 68, - 0x185a: 68, - 0x185b: 68, - 0x185c: 68, - 0x185d: 68, - 0x185e: 68, - 0x185f: 68, - 0x1860: 68, - 0x1861: 68, - 0x1862: 68, - 0x1863: 68, - 0x1864: 68, - 0x1865: 68, - 0x1866: 68, - 0x1867: 68, - 0x1868: 68, - 0x1869: 68, - 0x186a: 68, - 0x186b: 68, - 0x186c: 68, - 0x186d: 68, - 0x186e: 68, - 0x186f: 68, - 0x1870: 68, - 0x1871: 68, - 0x1872: 68, - 0x1873: 68, - 0x1874: 68, - 0x1875: 68, - 0x1876: 68, - 0x1877: 68, - 0x1878: 68, - 0x1880: 85, - 0x1881: 85, - 0x1882: 85, - 0x1883: 85, - 0x1884: 85, - 0x1885: 84, - 0x1886: 84, - 0x1887: 68, - 0x1888: 68, - 0x1889: 68, - 0x188a: 68, - 0x188b: 68, - 0x188c: 68, - 0x188d: 68, - 0x188e: 68, - 0x188f: 68, - 0x1890: 68, - 0x1891: 68, - 0x1892: 68, - 0x1893: 68, - 0x1894: 68, - 0x1895: 68, - 0x1896: 68, - 0x1897: 68, - 0x1898: 68, - 0x1899: 68, - 0x189a: 68, - 0x189b: 68, - 0x189c: 68, - 0x189d: 68, - 0x189e: 68, - 0x189f: 68, - 0x18a0: 68, - 0x18a1: 68, - 0x18a2: 68, - 0x18a3: 68, - 0x18a4: 68, - 0x18a5: 68, - 0x18a6: 68, - 0x18a7: 68, - 0x18a8: 68, - 0x18aa: 68, - 0x200c: 85, - 0x200d: 67, - 0x202f: 85, - 0x2066: 85, - 0x2067: 85, - 0x2068: 85, - 0x2069: 85, - 0xa840: 68, - 0xa841: 68, - 0xa842: 68, - 0xa843: 68, - 0xa844: 68, - 0xa845: 68, - 0xa846: 68, - 0xa847: 68, - 0xa848: 68, - 0xa849: 68, - 0xa84a: 68, - 0xa84b: 68, - 0xa84c: 68, - 0xa84d: 68, - 0xa84e: 68, - 0xa84f: 68, - 0xa850: 68, - 0xa851: 68, - 0xa852: 68, - 0xa853: 68, - 0xa854: 68, - 0xa855: 68, - 0xa856: 68, - 0xa857: 68, - 0xa858: 68, - 0xa859: 68, - 0xa85a: 68, - 0xa85b: 68, - 0xa85c: 68, - 0xa85d: 68, - 0xa85e: 68, - 0xa85f: 68, - 0xa860: 68, - 0xa861: 68, - 0xa862: 68, - 0xa863: 68, - 0xa864: 68, - 0xa865: 68, - 0xa866: 68, - 0xa867: 68, - 0xa868: 68, - 0xa869: 68, - 0xa86a: 68, - 0xa86b: 68, - 0xa86c: 68, - 0xa86d: 68, - 0xa86e: 68, - 0xa86f: 68, - 0xa870: 68, - 0xa871: 68, - 0xa872: 76, - 0xa873: 85, - 0x10ac0: 68, - 0x10ac1: 68, - 0x10ac2: 68, - 0x10ac3: 68, - 0x10ac4: 68, - 0x10ac5: 82, - 0x10ac6: 85, - 0x10ac7: 82, - 0x10ac8: 85, - 0x10ac9: 82, - 0x10aca: 82, - 0x10acb: 85, - 0x10acc: 85, - 0x10acd: 76, - 0x10ace: 82, - 0x10acf: 82, - 0x10ad0: 82, - 0x10ad1: 82, - 0x10ad2: 82, - 0x10ad3: 68, - 0x10ad4: 68, - 0x10ad5: 68, - 0x10ad6: 68, - 0x10ad7: 76, - 0x10ad8: 68, - 0x10ad9: 68, - 0x10ada: 68, - 0x10adb: 68, - 0x10adc: 68, - 0x10add: 82, - 0x10ade: 68, - 0x10adf: 68, - 0x10ae0: 68, - 0x10ae1: 82, - 0x10ae2: 85, - 0x10ae3: 85, - 0x10ae4: 82, - 0x10aeb: 68, - 0x10aec: 68, - 0x10aed: 68, - 0x10aee: 68, - 0x10aef: 82, - 0x10b80: 68, - 0x10b81: 82, - 0x10b82: 68, - 0x10b83: 82, - 0x10b84: 82, - 0x10b85: 82, - 0x10b86: 68, - 0x10b87: 68, - 0x10b88: 68, - 0x10b89: 82, - 0x10b8a: 68, - 0x10b8b: 68, - 0x10b8c: 82, - 0x10b8d: 68, - 0x10b8e: 82, - 0x10b8f: 82, - 0x10b90: 68, - 0x10b91: 82, - 0x10ba9: 82, - 0x10baa: 82, - 0x10bab: 82, - 0x10bac: 82, - 0x10bad: 68, - 0x10bae: 68, - 0x10baf: 85, - 0x10d00: 76, - 0x10d01: 68, - 0x10d02: 68, - 0x10d03: 68, - 0x10d04: 68, - 0x10d05: 68, - 0x10d06: 68, - 0x10d07: 68, - 0x10d08: 68, - 0x10d09: 68, - 0x10d0a: 68, - 0x10d0b: 68, - 0x10d0c: 68, - 0x10d0d: 68, - 0x10d0e: 68, - 0x10d0f: 68, - 0x10d10: 68, - 0x10d11: 68, - 0x10d12: 68, - 0x10d13: 68, - 0x10d14: 68, - 0x10d15: 68, - 0x10d16: 68, - 0x10d17: 68, - 0x10d18: 68, - 0x10d19: 68, - 0x10d1a: 68, - 0x10d1b: 68, - 0x10d1c: 68, - 0x10d1d: 68, - 0x10d1e: 68, - 0x10d1f: 68, - 0x10d20: 68, - 0x10d21: 68, - 0x10d22: 82, - 0x10d23: 68, - 0x10f30: 68, - 0x10f31: 68, - 0x10f32: 68, - 0x10f33: 82, - 0x10f34: 68, - 0x10f35: 68, - 0x10f36: 68, - 0x10f37: 68, - 0x10f38: 68, - 0x10f39: 68, - 0x10f3a: 68, - 0x10f3b: 68, - 0x10f3c: 68, - 0x10f3d: 68, - 0x10f3e: 68, - 0x10f3f: 68, - 0x10f40: 68, - 0x10f41: 68, - 0x10f42: 68, - 0x10f43: 68, - 0x10f44: 68, - 0x10f45: 85, - 0x10f51: 68, - 0x10f52: 68, - 0x10f53: 68, - 0x10f54: 82, - 0x10f70: 68, - 0x10f71: 68, - 0x10f72: 68, - 0x10f73: 68, - 0x10f74: 82, - 0x10f75: 82, - 0x10f76: 68, - 0x10f77: 68, - 0x10f78: 68, - 0x10f79: 68, - 0x10f7a: 68, - 0x10f7b: 68, - 0x10f7c: 68, - 0x10f7d: 68, - 0x10f7e: 68, - 0x10f7f: 68, - 0x10f80: 68, - 0x10f81: 68, - 0x10fb0: 68, - 0x10fb1: 85, - 0x10fb2: 68, - 0x10fb3: 68, - 0x10fb4: 82, - 0x10fb5: 82, - 0x10fb6: 82, - 0x10fb7: 85, - 0x10fb8: 68, - 0x10fb9: 82, - 0x10fba: 82, - 0x10fbb: 68, - 0x10fbc: 68, - 0x10fbd: 82, - 0x10fbe: 68, - 0x10fbf: 68, - 0x10fc0: 85, - 0x10fc1: 68, - 0x10fc2: 82, - 0x10fc3: 82, - 0x10fc4: 68, - 0x10fc5: 85, - 0x10fc6: 85, - 0x10fc7: 85, - 0x10fc8: 85, - 0x10fc9: 82, - 0x10fca: 68, - 0x10fcb: 76, - 0x110bd: 85, - 0x110cd: 85, - 0x1e900: 68, - 0x1e901: 68, - 0x1e902: 68, - 0x1e903: 68, - 0x1e904: 68, - 0x1e905: 68, - 0x1e906: 68, - 0x1e907: 68, - 0x1e908: 68, - 0x1e909: 68, - 0x1e90a: 68, - 0x1e90b: 68, - 0x1e90c: 68, - 0x1e90d: 68, - 0x1e90e: 68, - 0x1e90f: 68, - 0x1e910: 68, - 0x1e911: 68, - 0x1e912: 68, - 0x1e913: 68, - 0x1e914: 68, - 0x1e915: 68, - 0x1e916: 68, - 0x1e917: 68, - 0x1e918: 68, - 0x1e919: 68, - 0x1e91a: 68, - 0x1e91b: 68, - 0x1e91c: 68, - 0x1e91d: 68, - 0x1e91e: 68, - 0x1e91f: 68, - 0x1e920: 68, - 0x1e921: 68, - 0x1e922: 68, - 0x1e923: 68, - 0x1e924: 68, - 0x1e925: 68, - 0x1e926: 68, - 0x1e927: 68, - 0x1e928: 68, - 0x1e929: 68, - 0x1e92a: 68, - 0x1e92b: 68, - 0x1e92c: 68, - 0x1e92d: 68, - 0x1e92e: 68, - 0x1e92f: 68, - 0x1e930: 68, - 0x1e931: 68, - 0x1e932: 68, - 0x1e933: 68, - 0x1e934: 68, - 0x1e935: 68, - 0x1e936: 68, - 0x1e937: 68, - 0x1e938: 68, - 0x1e939: 68, - 0x1e93a: 68, - 0x1e93b: 68, - 0x1e93c: 68, - 0x1e93d: 68, - 0x1e93e: 68, - 0x1e93f: 68, - 0x1e940: 68, - 0x1e941: 68, - 0x1e942: 68, - 0x1e943: 68, - 0x1e94b: 84, -} -codepoint_classes = { - 'PVALID': ( - 0x2d0000002e, - 0x300000003a, - 0x610000007b, - 0xdf000000f7, - 0xf800000100, - 0x10100000102, - 0x10300000104, - 0x10500000106, - 0x10700000108, - 0x1090000010a, - 0x10b0000010c, - 0x10d0000010e, - 0x10f00000110, - 0x11100000112, - 0x11300000114, - 0x11500000116, - 0x11700000118, - 0x1190000011a, - 0x11b0000011c, - 0x11d0000011e, - 0x11f00000120, - 0x12100000122, - 0x12300000124, - 0x12500000126, - 0x12700000128, - 0x1290000012a, - 0x12b0000012c, - 0x12d0000012e, - 0x12f00000130, - 0x13100000132, - 0x13500000136, - 0x13700000139, - 0x13a0000013b, - 0x13c0000013d, - 0x13e0000013f, - 0x14200000143, - 0x14400000145, - 0x14600000147, - 0x14800000149, - 0x14b0000014c, - 0x14d0000014e, - 0x14f00000150, - 0x15100000152, - 0x15300000154, - 0x15500000156, - 0x15700000158, - 0x1590000015a, - 0x15b0000015c, - 0x15d0000015e, - 0x15f00000160, - 0x16100000162, - 0x16300000164, - 0x16500000166, - 0x16700000168, - 0x1690000016a, - 0x16b0000016c, - 0x16d0000016e, - 0x16f00000170, - 0x17100000172, - 0x17300000174, - 0x17500000176, - 0x17700000178, - 0x17a0000017b, - 0x17c0000017d, - 0x17e0000017f, - 0x18000000181, - 0x18300000184, - 0x18500000186, - 0x18800000189, - 0x18c0000018e, - 0x19200000193, - 0x19500000196, - 0x1990000019c, - 0x19e0000019f, - 0x1a1000001a2, - 0x1a3000001a4, - 0x1a5000001a6, - 0x1a8000001a9, - 0x1aa000001ac, - 0x1ad000001ae, - 0x1b0000001b1, - 0x1b4000001b5, - 0x1b6000001b7, - 0x1b9000001bc, - 0x1bd000001c4, - 0x1ce000001cf, - 0x1d0000001d1, - 0x1d2000001d3, - 0x1d4000001d5, - 0x1d6000001d7, - 0x1d8000001d9, - 0x1da000001db, - 0x1dc000001de, - 0x1df000001e0, - 0x1e1000001e2, - 0x1e3000001e4, - 0x1e5000001e6, - 0x1e7000001e8, - 0x1e9000001ea, - 0x1eb000001ec, - 0x1ed000001ee, - 0x1ef000001f1, - 0x1f5000001f6, - 0x1f9000001fa, - 0x1fb000001fc, - 0x1fd000001fe, - 0x1ff00000200, - 0x20100000202, - 0x20300000204, - 0x20500000206, - 0x20700000208, - 0x2090000020a, - 0x20b0000020c, - 0x20d0000020e, - 0x20f00000210, - 0x21100000212, - 0x21300000214, - 0x21500000216, - 0x21700000218, - 0x2190000021a, - 0x21b0000021c, - 0x21d0000021e, - 0x21f00000220, - 0x22100000222, - 0x22300000224, - 0x22500000226, - 0x22700000228, - 0x2290000022a, - 0x22b0000022c, - 0x22d0000022e, - 0x22f00000230, - 0x23100000232, - 0x2330000023a, - 0x23c0000023d, - 0x23f00000241, - 0x24200000243, - 0x24700000248, - 0x2490000024a, - 0x24b0000024c, - 0x24d0000024e, - 0x24f000002b0, - 0x2b9000002c2, - 0x2c6000002d2, - 0x2ec000002ed, - 0x2ee000002ef, - 0x30000000340, - 0x34200000343, - 0x3460000034f, - 0x35000000370, - 0x37100000372, - 0x37300000374, - 0x37700000378, - 0x37b0000037e, - 0x39000000391, - 0x3ac000003cf, - 0x3d7000003d8, - 0x3d9000003da, - 0x3db000003dc, - 0x3dd000003de, - 0x3df000003e0, - 0x3e1000003e2, - 0x3e3000003e4, - 0x3e5000003e6, - 0x3e7000003e8, - 0x3e9000003ea, - 0x3eb000003ec, - 0x3ed000003ee, - 0x3ef000003f0, - 0x3f3000003f4, - 0x3f8000003f9, - 0x3fb000003fd, - 0x43000000460, - 0x46100000462, - 0x46300000464, - 0x46500000466, - 0x46700000468, - 0x4690000046a, - 0x46b0000046c, - 0x46d0000046e, - 0x46f00000470, - 0x47100000472, - 0x47300000474, - 0x47500000476, - 0x47700000478, - 0x4790000047a, - 0x47b0000047c, - 0x47d0000047e, - 0x47f00000480, - 0x48100000482, - 0x48300000488, - 0x48b0000048c, - 0x48d0000048e, - 0x48f00000490, - 0x49100000492, - 0x49300000494, - 0x49500000496, - 0x49700000498, - 0x4990000049a, - 0x49b0000049c, - 0x49d0000049e, - 0x49f000004a0, - 0x4a1000004a2, - 0x4a3000004a4, - 0x4a5000004a6, - 0x4a7000004a8, - 0x4a9000004aa, - 0x4ab000004ac, - 0x4ad000004ae, - 0x4af000004b0, - 0x4b1000004b2, - 0x4b3000004b4, - 0x4b5000004b6, - 0x4b7000004b8, - 0x4b9000004ba, - 0x4bb000004bc, - 0x4bd000004be, - 0x4bf000004c0, - 0x4c2000004c3, - 0x4c4000004c5, - 0x4c6000004c7, - 0x4c8000004c9, - 0x4ca000004cb, - 0x4cc000004cd, - 0x4ce000004d0, - 0x4d1000004d2, - 0x4d3000004d4, - 0x4d5000004d6, - 0x4d7000004d8, - 0x4d9000004da, - 0x4db000004dc, - 0x4dd000004de, - 0x4df000004e0, - 0x4e1000004e2, - 0x4e3000004e4, - 0x4e5000004e6, - 0x4e7000004e8, - 0x4e9000004ea, - 0x4eb000004ec, - 0x4ed000004ee, - 0x4ef000004f0, - 0x4f1000004f2, - 0x4f3000004f4, - 0x4f5000004f6, - 0x4f7000004f8, - 0x4f9000004fa, - 0x4fb000004fc, - 0x4fd000004fe, - 0x4ff00000500, - 0x50100000502, - 0x50300000504, - 0x50500000506, - 0x50700000508, - 0x5090000050a, - 0x50b0000050c, - 0x50d0000050e, - 0x50f00000510, - 0x51100000512, - 0x51300000514, - 0x51500000516, - 0x51700000518, - 0x5190000051a, - 0x51b0000051c, - 0x51d0000051e, - 0x51f00000520, - 0x52100000522, - 0x52300000524, - 0x52500000526, - 0x52700000528, - 0x5290000052a, - 0x52b0000052c, - 0x52d0000052e, - 0x52f00000530, - 0x5590000055a, - 0x56000000587, - 0x58800000589, - 0x591000005be, - 0x5bf000005c0, - 0x5c1000005c3, - 0x5c4000005c6, - 0x5c7000005c8, - 0x5d0000005eb, - 0x5ef000005f3, - 0x6100000061b, - 0x62000000640, - 0x64100000660, - 0x66e00000675, - 0x679000006d4, - 0x6d5000006dd, - 0x6df000006e9, - 0x6ea000006f0, - 0x6fa00000700, - 0x7100000074b, - 0x74d000007b2, - 0x7c0000007f6, - 0x7fd000007fe, - 0x8000000082e, - 0x8400000085c, - 0x8600000086b, - 0x87000000888, - 0x8890000088f, - 0x898000008e2, - 0x8e300000958, - 0x96000000964, - 0x96600000970, - 0x97100000984, - 0x9850000098d, - 0x98f00000991, - 0x993000009a9, - 0x9aa000009b1, - 0x9b2000009b3, - 0x9b6000009ba, - 0x9bc000009c5, - 0x9c7000009c9, - 0x9cb000009cf, - 0x9d7000009d8, - 0x9e0000009e4, - 0x9e6000009f2, - 0x9fc000009fd, - 0x9fe000009ff, - 0xa0100000a04, - 0xa0500000a0b, - 0xa0f00000a11, - 0xa1300000a29, - 0xa2a00000a31, - 0xa3200000a33, - 0xa3500000a36, - 0xa3800000a3a, - 0xa3c00000a3d, - 0xa3e00000a43, - 0xa4700000a49, - 0xa4b00000a4e, - 0xa5100000a52, - 0xa5c00000a5d, - 0xa6600000a76, - 0xa8100000a84, - 0xa8500000a8e, - 0xa8f00000a92, - 0xa9300000aa9, - 0xaaa00000ab1, - 0xab200000ab4, - 0xab500000aba, - 0xabc00000ac6, - 0xac700000aca, - 0xacb00000ace, - 0xad000000ad1, - 0xae000000ae4, - 0xae600000af0, - 0xaf900000b00, - 0xb0100000b04, - 0xb0500000b0d, - 0xb0f00000b11, - 0xb1300000b29, - 0xb2a00000b31, - 0xb3200000b34, - 0xb3500000b3a, - 0xb3c00000b45, - 0xb4700000b49, - 0xb4b00000b4e, - 0xb5500000b58, - 0xb5f00000b64, - 0xb6600000b70, - 0xb7100000b72, - 0xb8200000b84, - 0xb8500000b8b, - 0xb8e00000b91, - 0xb9200000b96, - 0xb9900000b9b, - 0xb9c00000b9d, - 0xb9e00000ba0, - 0xba300000ba5, - 0xba800000bab, - 0xbae00000bba, - 0xbbe00000bc3, - 0xbc600000bc9, - 0xbca00000bce, - 0xbd000000bd1, - 0xbd700000bd8, - 0xbe600000bf0, - 0xc0000000c0d, - 0xc0e00000c11, - 0xc1200000c29, - 0xc2a00000c3a, - 0xc3c00000c45, - 0xc4600000c49, - 0xc4a00000c4e, - 0xc5500000c57, - 0xc5800000c5b, - 0xc5d00000c5e, - 0xc6000000c64, - 0xc6600000c70, - 0xc8000000c84, - 0xc8500000c8d, - 0xc8e00000c91, - 0xc9200000ca9, - 0xcaa00000cb4, - 0xcb500000cba, - 0xcbc00000cc5, - 0xcc600000cc9, - 0xcca00000cce, - 0xcd500000cd7, - 0xcdd00000cdf, - 0xce000000ce4, - 0xce600000cf0, - 0xcf100000cf4, - 0xd0000000d0d, - 0xd0e00000d11, - 0xd1200000d45, - 0xd4600000d49, - 0xd4a00000d4f, - 0xd5400000d58, - 0xd5f00000d64, - 0xd6600000d70, - 0xd7a00000d80, - 0xd8100000d84, - 0xd8500000d97, - 0xd9a00000db2, - 0xdb300000dbc, - 0xdbd00000dbe, - 0xdc000000dc7, - 0xdca00000dcb, - 0xdcf00000dd5, - 0xdd600000dd7, - 0xdd800000de0, - 0xde600000df0, - 0xdf200000df4, - 0xe0100000e33, - 0xe3400000e3b, - 0xe4000000e4f, - 0xe5000000e5a, - 0xe8100000e83, - 0xe8400000e85, - 0xe8600000e8b, - 0xe8c00000ea4, - 0xea500000ea6, - 0xea700000eb3, - 0xeb400000ebe, - 0xec000000ec5, - 0xec600000ec7, - 0xec800000ecf, - 0xed000000eda, - 0xede00000ee0, - 0xf0000000f01, - 0xf0b00000f0c, - 0xf1800000f1a, - 0xf2000000f2a, - 0xf3500000f36, - 0xf3700000f38, - 0xf3900000f3a, - 0xf3e00000f43, - 0xf4400000f48, - 0xf4900000f4d, - 0xf4e00000f52, - 0xf5300000f57, - 0xf5800000f5c, - 0xf5d00000f69, - 0xf6a00000f6d, - 0xf7100000f73, - 0xf7400000f75, - 0xf7a00000f81, - 0xf8200000f85, - 0xf8600000f93, - 0xf9400000f98, - 0xf9900000f9d, - 0xf9e00000fa2, - 0xfa300000fa7, - 0xfa800000fac, - 0xfad00000fb9, - 0xfba00000fbd, - 0xfc600000fc7, - 0x10000000104a, - 0x10500000109e, - 0x10d0000010fb, - 0x10fd00001100, - 0x120000001249, - 0x124a0000124e, - 0x125000001257, - 0x125800001259, - 0x125a0000125e, - 0x126000001289, - 0x128a0000128e, - 0x1290000012b1, - 0x12b2000012b6, - 0x12b8000012bf, - 0x12c0000012c1, - 0x12c2000012c6, - 0x12c8000012d7, - 0x12d800001311, - 0x131200001316, - 0x13180000135b, - 0x135d00001360, - 0x138000001390, - 0x13a0000013f6, - 0x14010000166d, - 0x166f00001680, - 0x16810000169b, - 0x16a0000016eb, - 0x16f1000016f9, - 0x170000001716, - 0x171f00001735, - 0x174000001754, - 0x17600000176d, - 0x176e00001771, - 0x177200001774, - 0x1780000017b4, - 0x17b6000017d4, - 0x17d7000017d8, - 0x17dc000017de, - 0x17e0000017ea, - 0x18100000181a, - 0x182000001879, - 0x1880000018ab, - 0x18b0000018f6, - 0x19000000191f, - 0x19200000192c, - 0x19300000193c, - 0x19460000196e, - 0x197000001975, - 0x1980000019ac, - 0x19b0000019ca, - 0x19d0000019da, - 0x1a0000001a1c, - 0x1a2000001a5f, - 0x1a6000001a7d, - 0x1a7f00001a8a, - 0x1a9000001a9a, - 0x1aa700001aa8, - 0x1ab000001abe, - 0x1abf00001acf, - 0x1b0000001b4d, - 0x1b5000001b5a, - 0x1b6b00001b74, - 0x1b8000001bf4, - 0x1c0000001c38, - 0x1c4000001c4a, - 0x1c4d00001c7e, - 0x1cd000001cd3, - 0x1cd400001cfb, - 0x1d0000001d2c, - 0x1d2f00001d30, - 0x1d3b00001d3c, - 0x1d4e00001d4f, - 0x1d6b00001d78, - 0x1d7900001d9b, - 0x1dc000001e00, - 0x1e0100001e02, - 0x1e0300001e04, - 0x1e0500001e06, - 0x1e0700001e08, - 0x1e0900001e0a, - 0x1e0b00001e0c, - 0x1e0d00001e0e, - 0x1e0f00001e10, - 0x1e1100001e12, - 0x1e1300001e14, - 0x1e1500001e16, - 0x1e1700001e18, - 0x1e1900001e1a, - 0x1e1b00001e1c, - 0x1e1d00001e1e, - 0x1e1f00001e20, - 0x1e2100001e22, - 0x1e2300001e24, - 0x1e2500001e26, - 0x1e2700001e28, - 0x1e2900001e2a, - 0x1e2b00001e2c, - 0x1e2d00001e2e, - 0x1e2f00001e30, - 0x1e3100001e32, - 0x1e3300001e34, - 0x1e3500001e36, - 0x1e3700001e38, - 0x1e3900001e3a, - 0x1e3b00001e3c, - 0x1e3d00001e3e, - 0x1e3f00001e40, - 0x1e4100001e42, - 0x1e4300001e44, - 0x1e4500001e46, - 0x1e4700001e48, - 0x1e4900001e4a, - 0x1e4b00001e4c, - 0x1e4d00001e4e, - 0x1e4f00001e50, - 0x1e5100001e52, - 0x1e5300001e54, - 0x1e5500001e56, - 0x1e5700001e58, - 0x1e5900001e5a, - 0x1e5b00001e5c, - 0x1e5d00001e5e, - 0x1e5f00001e60, - 0x1e6100001e62, - 0x1e6300001e64, - 0x1e6500001e66, - 0x1e6700001e68, - 0x1e6900001e6a, - 0x1e6b00001e6c, - 0x1e6d00001e6e, - 0x1e6f00001e70, - 0x1e7100001e72, - 0x1e7300001e74, - 0x1e7500001e76, - 0x1e7700001e78, - 0x1e7900001e7a, - 0x1e7b00001e7c, - 0x1e7d00001e7e, - 0x1e7f00001e80, - 0x1e8100001e82, - 0x1e8300001e84, - 0x1e8500001e86, - 0x1e8700001e88, - 0x1e8900001e8a, - 0x1e8b00001e8c, - 0x1e8d00001e8e, - 0x1e8f00001e90, - 0x1e9100001e92, - 0x1e9300001e94, - 0x1e9500001e9a, - 0x1e9c00001e9e, - 0x1e9f00001ea0, - 0x1ea100001ea2, - 0x1ea300001ea4, - 0x1ea500001ea6, - 0x1ea700001ea8, - 0x1ea900001eaa, - 0x1eab00001eac, - 0x1ead00001eae, - 0x1eaf00001eb0, - 0x1eb100001eb2, - 0x1eb300001eb4, - 0x1eb500001eb6, - 0x1eb700001eb8, - 0x1eb900001eba, - 0x1ebb00001ebc, - 0x1ebd00001ebe, - 0x1ebf00001ec0, - 0x1ec100001ec2, - 0x1ec300001ec4, - 0x1ec500001ec6, - 0x1ec700001ec8, - 0x1ec900001eca, - 0x1ecb00001ecc, - 0x1ecd00001ece, - 0x1ecf00001ed0, - 0x1ed100001ed2, - 0x1ed300001ed4, - 0x1ed500001ed6, - 0x1ed700001ed8, - 0x1ed900001eda, - 0x1edb00001edc, - 0x1edd00001ede, - 0x1edf00001ee0, - 0x1ee100001ee2, - 0x1ee300001ee4, - 0x1ee500001ee6, - 0x1ee700001ee8, - 0x1ee900001eea, - 0x1eeb00001eec, - 0x1eed00001eee, - 0x1eef00001ef0, - 0x1ef100001ef2, - 0x1ef300001ef4, - 0x1ef500001ef6, - 0x1ef700001ef8, - 0x1ef900001efa, - 0x1efb00001efc, - 0x1efd00001efe, - 0x1eff00001f08, - 0x1f1000001f16, - 0x1f2000001f28, - 0x1f3000001f38, - 0x1f4000001f46, - 0x1f5000001f58, - 0x1f6000001f68, - 0x1f7000001f71, - 0x1f7200001f73, - 0x1f7400001f75, - 0x1f7600001f77, - 0x1f7800001f79, - 0x1f7a00001f7b, - 0x1f7c00001f7d, - 0x1fb000001fb2, - 0x1fb600001fb7, - 0x1fc600001fc7, - 0x1fd000001fd3, - 0x1fd600001fd8, - 0x1fe000001fe3, - 0x1fe400001fe8, - 0x1ff600001ff7, - 0x214e0000214f, - 0x218400002185, - 0x2c3000002c60, - 0x2c6100002c62, - 0x2c6500002c67, - 0x2c6800002c69, - 0x2c6a00002c6b, - 0x2c6c00002c6d, - 0x2c7100002c72, - 0x2c7300002c75, - 0x2c7600002c7c, - 0x2c8100002c82, - 0x2c8300002c84, - 0x2c8500002c86, - 0x2c8700002c88, - 0x2c8900002c8a, - 0x2c8b00002c8c, - 0x2c8d00002c8e, - 0x2c8f00002c90, - 0x2c9100002c92, - 0x2c9300002c94, - 0x2c9500002c96, - 0x2c9700002c98, - 0x2c9900002c9a, - 0x2c9b00002c9c, - 0x2c9d00002c9e, - 0x2c9f00002ca0, - 0x2ca100002ca2, - 0x2ca300002ca4, - 0x2ca500002ca6, - 0x2ca700002ca8, - 0x2ca900002caa, - 0x2cab00002cac, - 0x2cad00002cae, - 0x2caf00002cb0, - 0x2cb100002cb2, - 0x2cb300002cb4, - 0x2cb500002cb6, - 0x2cb700002cb8, - 0x2cb900002cba, - 0x2cbb00002cbc, - 0x2cbd00002cbe, - 0x2cbf00002cc0, - 0x2cc100002cc2, - 0x2cc300002cc4, - 0x2cc500002cc6, - 0x2cc700002cc8, - 0x2cc900002cca, - 0x2ccb00002ccc, - 0x2ccd00002cce, - 0x2ccf00002cd0, - 0x2cd100002cd2, - 0x2cd300002cd4, - 0x2cd500002cd6, - 0x2cd700002cd8, - 0x2cd900002cda, - 0x2cdb00002cdc, - 0x2cdd00002cde, - 0x2cdf00002ce0, - 0x2ce100002ce2, - 0x2ce300002ce5, - 0x2cec00002ced, - 0x2cee00002cf2, - 0x2cf300002cf4, - 0x2d0000002d26, - 0x2d2700002d28, - 0x2d2d00002d2e, - 0x2d3000002d68, - 0x2d7f00002d97, - 0x2da000002da7, - 0x2da800002daf, - 0x2db000002db7, - 0x2db800002dbf, - 0x2dc000002dc7, - 0x2dc800002dcf, - 0x2dd000002dd7, - 0x2dd800002ddf, - 0x2de000002e00, - 0x2e2f00002e30, - 0x300500003008, - 0x302a0000302e, - 0x303c0000303d, - 0x304100003097, - 0x30990000309b, - 0x309d0000309f, - 0x30a1000030fb, - 0x30fc000030ff, - 0x310500003130, - 0x31a0000031c0, - 0x31f000003200, - 0x340000004dc0, - 0x4e000000a48d, - 0xa4d00000a4fe, - 0xa5000000a60d, - 0xa6100000a62c, - 0xa6410000a642, - 0xa6430000a644, - 0xa6450000a646, - 0xa6470000a648, - 0xa6490000a64a, - 0xa64b0000a64c, - 0xa64d0000a64e, - 0xa64f0000a650, - 0xa6510000a652, - 0xa6530000a654, - 0xa6550000a656, - 0xa6570000a658, - 0xa6590000a65a, - 0xa65b0000a65c, - 0xa65d0000a65e, - 0xa65f0000a660, - 0xa6610000a662, - 0xa6630000a664, - 0xa6650000a666, - 0xa6670000a668, - 0xa6690000a66a, - 0xa66b0000a66c, - 0xa66d0000a670, - 0xa6740000a67e, - 0xa67f0000a680, - 0xa6810000a682, - 0xa6830000a684, - 0xa6850000a686, - 0xa6870000a688, - 0xa6890000a68a, - 0xa68b0000a68c, - 0xa68d0000a68e, - 0xa68f0000a690, - 0xa6910000a692, - 0xa6930000a694, - 0xa6950000a696, - 0xa6970000a698, - 0xa6990000a69a, - 0xa69b0000a69c, - 0xa69e0000a6e6, - 0xa6f00000a6f2, - 0xa7170000a720, - 0xa7230000a724, - 0xa7250000a726, - 0xa7270000a728, - 0xa7290000a72a, - 0xa72b0000a72c, - 0xa72d0000a72e, - 0xa72f0000a732, - 0xa7330000a734, - 0xa7350000a736, - 0xa7370000a738, - 0xa7390000a73a, - 0xa73b0000a73c, - 0xa73d0000a73e, - 0xa73f0000a740, - 0xa7410000a742, - 0xa7430000a744, - 0xa7450000a746, - 0xa7470000a748, - 0xa7490000a74a, - 0xa74b0000a74c, - 0xa74d0000a74e, - 0xa74f0000a750, - 0xa7510000a752, - 0xa7530000a754, - 0xa7550000a756, - 0xa7570000a758, - 0xa7590000a75a, - 0xa75b0000a75c, - 0xa75d0000a75e, - 0xa75f0000a760, - 0xa7610000a762, - 0xa7630000a764, - 0xa7650000a766, - 0xa7670000a768, - 0xa7690000a76a, - 0xa76b0000a76c, - 0xa76d0000a76e, - 0xa76f0000a770, - 0xa7710000a779, - 0xa77a0000a77b, - 0xa77c0000a77d, - 0xa77f0000a780, - 0xa7810000a782, - 0xa7830000a784, - 0xa7850000a786, - 0xa7870000a789, - 0xa78c0000a78d, - 0xa78e0000a790, - 0xa7910000a792, - 0xa7930000a796, - 0xa7970000a798, - 0xa7990000a79a, - 0xa79b0000a79c, - 0xa79d0000a79e, - 0xa79f0000a7a0, - 0xa7a10000a7a2, - 0xa7a30000a7a4, - 0xa7a50000a7a6, - 0xa7a70000a7a8, - 0xa7a90000a7aa, - 0xa7af0000a7b0, - 0xa7b50000a7b6, - 0xa7b70000a7b8, - 0xa7b90000a7ba, - 0xa7bb0000a7bc, - 0xa7bd0000a7be, - 0xa7bf0000a7c0, - 0xa7c10000a7c2, - 0xa7c30000a7c4, - 0xa7c80000a7c9, - 0xa7ca0000a7cb, - 0xa7d10000a7d2, - 0xa7d30000a7d4, - 0xa7d50000a7d6, - 0xa7d70000a7d8, - 0xa7d90000a7da, - 0xa7f20000a7f5, - 0xa7f60000a7f8, - 0xa7fa0000a828, - 0xa82c0000a82d, - 0xa8400000a874, - 0xa8800000a8c6, - 0xa8d00000a8da, - 0xa8e00000a8f8, - 0xa8fb0000a8fc, - 0xa8fd0000a92e, - 0xa9300000a954, - 0xa9800000a9c1, - 0xa9cf0000a9da, - 0xa9e00000a9ff, - 0xaa000000aa37, - 0xaa400000aa4e, - 0xaa500000aa5a, - 0xaa600000aa77, - 0xaa7a0000aac3, - 0xaadb0000aade, - 0xaae00000aaf0, - 0xaaf20000aaf7, - 0xab010000ab07, - 0xab090000ab0f, - 0xab110000ab17, - 0xab200000ab27, - 0xab280000ab2f, - 0xab300000ab5b, - 0xab600000ab69, - 0xabc00000abeb, - 0xabec0000abee, - 0xabf00000abfa, - 0xac000000d7a4, - 0xfa0e0000fa10, - 0xfa110000fa12, - 0xfa130000fa15, - 0xfa1f0000fa20, - 0xfa210000fa22, - 0xfa230000fa25, - 0xfa270000fa2a, - 0xfb1e0000fb1f, - 0xfe200000fe30, - 0xfe730000fe74, - 0x100000001000c, - 0x1000d00010027, - 0x100280001003b, - 0x1003c0001003e, - 0x1003f0001004e, - 0x100500001005e, - 0x10080000100fb, - 0x101fd000101fe, - 0x102800001029d, - 0x102a0000102d1, - 0x102e0000102e1, - 0x1030000010320, - 0x1032d00010341, - 0x103420001034a, - 0x103500001037b, - 0x103800001039e, - 0x103a0000103c4, - 0x103c8000103d0, - 0x104280001049e, - 0x104a0000104aa, - 0x104d8000104fc, - 0x1050000010528, - 0x1053000010564, - 0x10597000105a2, - 0x105a3000105b2, - 0x105b3000105ba, - 0x105bb000105bd, - 0x1060000010737, - 0x1074000010756, - 0x1076000010768, - 0x1078000010786, - 0x10787000107b1, - 0x107b2000107bb, - 0x1080000010806, - 0x1080800010809, - 0x1080a00010836, - 0x1083700010839, - 0x1083c0001083d, - 0x1083f00010856, - 0x1086000010877, - 0x108800001089f, - 0x108e0000108f3, - 0x108f4000108f6, - 0x1090000010916, - 0x109200001093a, - 0x10980000109b8, - 0x109be000109c0, - 0x10a0000010a04, - 0x10a0500010a07, - 0x10a0c00010a14, - 0x10a1500010a18, - 0x10a1900010a36, - 0x10a3800010a3b, - 0x10a3f00010a40, - 0x10a6000010a7d, - 0x10a8000010a9d, - 0x10ac000010ac8, - 0x10ac900010ae7, - 0x10b0000010b36, - 0x10b4000010b56, - 0x10b6000010b73, - 0x10b8000010b92, - 0x10c0000010c49, - 0x10cc000010cf3, - 0x10d0000010d28, - 0x10d3000010d3a, - 0x10e8000010eaa, - 0x10eab00010ead, - 0x10eb000010eb2, - 0x10efd00010f1d, - 0x10f2700010f28, - 0x10f3000010f51, - 0x10f7000010f86, - 0x10fb000010fc5, - 0x10fe000010ff7, - 0x1100000011047, - 0x1106600011076, - 0x1107f000110bb, - 0x110c2000110c3, - 0x110d0000110e9, - 0x110f0000110fa, - 0x1110000011135, - 0x1113600011140, - 0x1114400011148, - 0x1115000011174, - 0x1117600011177, - 0x11180000111c5, - 0x111c9000111cd, - 0x111ce000111db, - 0x111dc000111dd, - 0x1120000011212, - 0x1121300011238, - 0x1123e00011242, - 0x1128000011287, - 0x1128800011289, - 0x1128a0001128e, - 0x1128f0001129e, - 0x1129f000112a9, - 0x112b0000112eb, - 0x112f0000112fa, - 0x1130000011304, - 0x113050001130d, - 0x1130f00011311, - 0x1131300011329, - 0x1132a00011331, - 0x1133200011334, - 0x113350001133a, - 0x1133b00011345, - 0x1134700011349, - 0x1134b0001134e, - 0x1135000011351, - 0x1135700011358, - 0x1135d00011364, - 0x113660001136d, - 0x1137000011375, - 0x114000001144b, - 0x114500001145a, - 0x1145e00011462, - 0x11480000114c6, - 0x114c7000114c8, - 0x114d0000114da, - 0x11580000115b6, - 0x115b8000115c1, - 0x115d8000115de, - 0x1160000011641, - 0x1164400011645, - 0x116500001165a, - 0x11680000116b9, - 0x116c0000116ca, - 0x117000001171b, - 0x1171d0001172c, - 0x117300001173a, - 0x1174000011747, - 0x118000001183b, - 0x118c0000118ea, - 0x118ff00011907, - 0x119090001190a, - 0x1190c00011914, - 0x1191500011917, - 0x1191800011936, - 0x1193700011939, - 0x1193b00011944, - 0x119500001195a, - 0x119a0000119a8, - 0x119aa000119d8, - 0x119da000119e2, - 0x119e3000119e5, - 0x11a0000011a3f, - 0x11a4700011a48, - 0x11a5000011a9a, - 0x11a9d00011a9e, - 0x11ab000011af9, - 0x11c0000011c09, - 0x11c0a00011c37, - 0x11c3800011c41, - 0x11c5000011c5a, - 0x11c7200011c90, - 0x11c9200011ca8, - 0x11ca900011cb7, - 0x11d0000011d07, - 0x11d0800011d0a, - 0x11d0b00011d37, - 0x11d3a00011d3b, - 0x11d3c00011d3e, - 0x11d3f00011d48, - 0x11d5000011d5a, - 0x11d6000011d66, - 0x11d6700011d69, - 0x11d6a00011d8f, - 0x11d9000011d92, - 0x11d9300011d99, - 0x11da000011daa, - 0x11ee000011ef7, - 0x11f0000011f11, - 0x11f1200011f3b, - 0x11f3e00011f43, - 0x11f5000011f5a, - 0x11fb000011fb1, - 0x120000001239a, - 0x1248000012544, - 0x12f9000012ff1, - 0x1300000013430, - 0x1344000013456, - 0x1440000014647, - 0x1680000016a39, - 0x16a4000016a5f, - 0x16a6000016a6a, - 0x16a7000016abf, - 0x16ac000016aca, - 0x16ad000016aee, - 0x16af000016af5, - 0x16b0000016b37, - 0x16b4000016b44, - 0x16b5000016b5a, - 0x16b6300016b78, - 0x16b7d00016b90, - 0x16e6000016e80, - 0x16f0000016f4b, - 0x16f4f00016f88, - 0x16f8f00016fa0, - 0x16fe000016fe2, - 0x16fe300016fe5, - 0x16ff000016ff2, - 0x17000000187f8, - 0x1880000018cd6, - 0x18d0000018d09, - 0x1aff00001aff4, - 0x1aff50001affc, - 0x1affd0001afff, - 0x1b0000001b123, - 0x1b1320001b133, - 0x1b1500001b153, - 0x1b1550001b156, - 0x1b1640001b168, - 0x1b1700001b2fc, - 0x1bc000001bc6b, - 0x1bc700001bc7d, - 0x1bc800001bc89, - 0x1bc900001bc9a, - 0x1bc9d0001bc9f, - 0x1cf000001cf2e, - 0x1cf300001cf47, - 0x1da000001da37, - 0x1da3b0001da6d, - 0x1da750001da76, - 0x1da840001da85, - 0x1da9b0001daa0, - 0x1daa10001dab0, - 0x1df000001df1f, - 0x1df250001df2b, - 0x1e0000001e007, - 0x1e0080001e019, - 0x1e01b0001e022, - 0x1e0230001e025, - 0x1e0260001e02b, - 0x1e0300001e06e, - 0x1e08f0001e090, - 0x1e1000001e12d, - 0x1e1300001e13e, - 0x1e1400001e14a, - 0x1e14e0001e14f, - 0x1e2900001e2af, - 0x1e2c00001e2fa, - 0x1e4d00001e4fa, - 0x1e7e00001e7e7, - 0x1e7e80001e7ec, - 0x1e7ed0001e7ef, - 0x1e7f00001e7ff, - 0x1e8000001e8c5, - 0x1e8d00001e8d7, - 0x1e9220001e94c, - 0x1e9500001e95a, - 0x200000002a6e0, - 0x2a7000002b73a, - 0x2b7400002b81e, - 0x2b8200002cea2, - 0x2ceb00002ebe1, - 0x300000003134b, - 0x31350000323b0, - ), - 'CONTEXTJ': ( - 0x200c0000200e, - ), - 'CONTEXTO': ( - 0xb7000000b8, - 0x37500000376, - 0x5f3000005f5, - 0x6600000066a, - 0x6f0000006fa, - 0x30fb000030fc, - ), -} diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/intranges.py b/.venv/Lib/site-packages/pip/_vendor/idna/intranges.py deleted file mode 100644 index 6a43b04..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/intranges.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Given a list of integers, made up of (hopefully) a small number of long runs -of consecutive integers, compute a representation of the form -((start1, end1), (start2, end2) ...). Then answer the question "was x present -in the original list?" in time O(log(# runs)). -""" - -import bisect -from typing import List, Tuple - -def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: - """Represent a list of integers as a sequence of ranges: - ((start_0, end_0), (start_1, end_1), ...), such that the original - integers are exactly those x such that start_i <= x < end_i for some i. - - Ranges are encoded as single integers (start << 32 | end), not as tuples. - """ - - sorted_list = sorted(list_) - ranges = [] - last_write = -1 - for i in range(len(sorted_list)): - if i+1 < len(sorted_list): - if sorted_list[i] == sorted_list[i+1]-1: - continue - current_range = sorted_list[last_write+1:i+1] - ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) - last_write = i - - return tuple(ranges) - -def _encode_range(start: int, end: int) -> int: - return (start << 32) | end - -def _decode_range(r: int) -> Tuple[int, int]: - return (r >> 32), (r & ((1 << 32) - 1)) - - -def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: - """Determine if `int_` falls into one of the ranges in `ranges`.""" - tuple_ = _encode_range(int_, 0) - pos = bisect.bisect_left(ranges, tuple_) - # we could be immediately ahead of a tuple (start, end) - # with start < int_ <= end - if pos > 0: - left, right = _decode_range(ranges[pos-1]) - if left <= int_ < right: - return True - # or we could be immediately behind a tuple (int_, end) - if pos < len(ranges): - left, _ = _decode_range(ranges[pos]) - if left == int_: - return True - return False diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/package_data.py b/.venv/Lib/site-packages/pip/_vendor/idna/package_data.py deleted file mode 100644 index 8501893..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/package_data.py +++ /dev/null @@ -1,2 +0,0 @@ -__version__ = '3.4' - diff --git a/.venv/Lib/site-packages/pip/_vendor/idna/uts46data.py b/.venv/Lib/site-packages/pip/_vendor/idna/uts46data.py deleted file mode 100644 index 186796c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/idna/uts46data.py +++ /dev/null @@ -1,8600 +0,0 @@ -# This file is automatically generated by tools/idna-data -# vim: set fileencoding=utf-8 : - -from typing import List, Tuple, Union - - -"""IDNA Mapping Table from UTS46.""" - - -__version__ = '15.0.0' -def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x0, '3'), - (0x1, '3'), - (0x2, '3'), - (0x3, '3'), - (0x4, '3'), - (0x5, '3'), - (0x6, '3'), - (0x7, '3'), - (0x8, '3'), - (0x9, '3'), - (0xA, '3'), - (0xB, '3'), - (0xC, '3'), - (0xD, '3'), - (0xE, '3'), - (0xF, '3'), - (0x10, '3'), - (0x11, '3'), - (0x12, '3'), - (0x13, '3'), - (0x14, '3'), - (0x15, '3'), - (0x16, '3'), - (0x17, '3'), - (0x18, '3'), - (0x19, '3'), - (0x1A, '3'), - (0x1B, '3'), - (0x1C, '3'), - (0x1D, '3'), - (0x1E, '3'), - (0x1F, '3'), - (0x20, '3'), - (0x21, '3'), - (0x22, '3'), - (0x23, '3'), - (0x24, '3'), - (0x25, '3'), - (0x26, '3'), - (0x27, '3'), - (0x28, '3'), - (0x29, '3'), - (0x2A, '3'), - (0x2B, '3'), - (0x2C, '3'), - (0x2D, 'V'), - (0x2E, 'V'), - (0x2F, '3'), - (0x30, 'V'), - (0x31, 'V'), - (0x32, 'V'), - (0x33, 'V'), - (0x34, 'V'), - (0x35, 'V'), - (0x36, 'V'), - (0x37, 'V'), - (0x38, 'V'), - (0x39, 'V'), - (0x3A, '3'), - (0x3B, '3'), - (0x3C, '3'), - (0x3D, '3'), - (0x3E, '3'), - (0x3F, '3'), - (0x40, '3'), - (0x41, 'M', 'a'), - (0x42, 'M', 'b'), - (0x43, 'M', 'c'), - (0x44, 'M', 'd'), - (0x45, 'M', 'e'), - (0x46, 'M', 'f'), - (0x47, 'M', 'g'), - (0x48, 'M', 'h'), - (0x49, 'M', 'i'), - (0x4A, 'M', 'j'), - (0x4B, 'M', 'k'), - (0x4C, 'M', 'l'), - (0x4D, 'M', 'm'), - (0x4E, 'M', 'n'), - (0x4F, 'M', 'o'), - (0x50, 'M', 'p'), - (0x51, 'M', 'q'), - (0x52, 'M', 'r'), - (0x53, 'M', 's'), - (0x54, 'M', 't'), - (0x55, 'M', 'u'), - (0x56, 'M', 'v'), - (0x57, 'M', 'w'), - (0x58, 'M', 'x'), - (0x59, 'M', 'y'), - (0x5A, 'M', 'z'), - (0x5B, '3'), - (0x5C, '3'), - (0x5D, '3'), - (0x5E, '3'), - (0x5F, '3'), - (0x60, '3'), - (0x61, 'V'), - (0x62, 'V'), - (0x63, 'V'), - ] - -def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x64, 'V'), - (0x65, 'V'), - (0x66, 'V'), - (0x67, 'V'), - (0x68, 'V'), - (0x69, 'V'), - (0x6A, 'V'), - (0x6B, 'V'), - (0x6C, 'V'), - (0x6D, 'V'), - (0x6E, 'V'), - (0x6F, 'V'), - (0x70, 'V'), - (0x71, 'V'), - (0x72, 'V'), - (0x73, 'V'), - (0x74, 'V'), - (0x75, 'V'), - (0x76, 'V'), - (0x77, 'V'), - (0x78, 'V'), - (0x79, 'V'), - (0x7A, 'V'), - (0x7B, '3'), - (0x7C, '3'), - (0x7D, '3'), - (0x7E, '3'), - (0x7F, '3'), - (0x80, 'X'), - (0x81, 'X'), - (0x82, 'X'), - (0x83, 'X'), - (0x84, 'X'), - (0x85, 'X'), - (0x86, 'X'), - (0x87, 'X'), - (0x88, 'X'), - (0x89, 'X'), - (0x8A, 'X'), - (0x8B, 'X'), - (0x8C, 'X'), - (0x8D, 'X'), - (0x8E, 'X'), - (0x8F, 'X'), - (0x90, 'X'), - (0x91, 'X'), - (0x92, 'X'), - (0x93, 'X'), - (0x94, 'X'), - (0x95, 'X'), - (0x96, 'X'), - (0x97, 'X'), - (0x98, 'X'), - (0x99, 'X'), - (0x9A, 'X'), - (0x9B, 'X'), - (0x9C, 'X'), - (0x9D, 'X'), - (0x9E, 'X'), - (0x9F, 'X'), - (0xA0, '3', ' '), - (0xA1, 'V'), - (0xA2, 'V'), - (0xA3, 'V'), - (0xA4, 'V'), - (0xA5, 'V'), - (0xA6, 'V'), - (0xA7, 'V'), - (0xA8, '3', ' ̈'), - (0xA9, 'V'), - (0xAA, 'M', 'a'), - (0xAB, 'V'), - (0xAC, 'V'), - (0xAD, 'I'), - (0xAE, 'V'), - (0xAF, '3', ' ̄'), - (0xB0, 'V'), - (0xB1, 'V'), - (0xB2, 'M', '2'), - (0xB3, 'M', '3'), - (0xB4, '3', ' ́'), - (0xB5, 'M', 'μ'), - (0xB6, 'V'), - (0xB7, 'V'), - (0xB8, '3', ' ̧'), - (0xB9, 'M', '1'), - (0xBA, 'M', 'o'), - (0xBB, 'V'), - (0xBC, 'M', '1⁄4'), - (0xBD, 'M', '1⁄2'), - (0xBE, 'M', '3⁄4'), - (0xBF, 'V'), - (0xC0, 'M', 'à'), - (0xC1, 'M', 'á'), - (0xC2, 'M', 'â'), - (0xC3, 'M', 'ã'), - (0xC4, 'M', 'ä'), - (0xC5, 'M', 'å'), - (0xC6, 'M', 'æ'), - (0xC7, 'M', 'ç'), - ] - -def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC8, 'M', 'è'), - (0xC9, 'M', 'é'), - (0xCA, 'M', 'ê'), - (0xCB, 'M', 'ë'), - (0xCC, 'M', 'ì'), - (0xCD, 'M', 'í'), - (0xCE, 'M', 'î'), - (0xCF, 'M', 'ï'), - (0xD0, 'M', 'ð'), - (0xD1, 'M', 'ñ'), - (0xD2, 'M', 'ò'), - (0xD3, 'M', 'ó'), - (0xD4, 'M', 'ô'), - (0xD5, 'M', 'õ'), - (0xD6, 'M', 'ö'), - (0xD7, 'V'), - (0xD8, 'M', 'ø'), - (0xD9, 'M', 'ù'), - (0xDA, 'M', 'ú'), - (0xDB, 'M', 'û'), - (0xDC, 'M', 'ü'), - (0xDD, 'M', 'ý'), - (0xDE, 'M', 'þ'), - (0xDF, 'D', 'ss'), - (0xE0, 'V'), - (0xE1, 'V'), - (0xE2, 'V'), - (0xE3, 'V'), - (0xE4, 'V'), - (0xE5, 'V'), - (0xE6, 'V'), - (0xE7, 'V'), - (0xE8, 'V'), - (0xE9, 'V'), - (0xEA, 'V'), - (0xEB, 'V'), - (0xEC, 'V'), - (0xED, 'V'), - (0xEE, 'V'), - (0xEF, 'V'), - (0xF0, 'V'), - (0xF1, 'V'), - (0xF2, 'V'), - (0xF3, 'V'), - (0xF4, 'V'), - (0xF5, 'V'), - (0xF6, 'V'), - (0xF7, 'V'), - (0xF8, 'V'), - (0xF9, 'V'), - (0xFA, 'V'), - (0xFB, 'V'), - (0xFC, 'V'), - (0xFD, 'V'), - (0xFE, 'V'), - (0xFF, 'V'), - (0x100, 'M', 'ā'), - (0x101, 'V'), - (0x102, 'M', 'ă'), - (0x103, 'V'), - (0x104, 'M', 'ą'), - (0x105, 'V'), - (0x106, 'M', 'ć'), - (0x107, 'V'), - (0x108, 'M', 'ĉ'), - (0x109, 'V'), - (0x10A, 'M', 'ċ'), - (0x10B, 'V'), - (0x10C, 'M', 'č'), - (0x10D, 'V'), - (0x10E, 'M', 'ď'), - (0x10F, 'V'), - (0x110, 'M', 'đ'), - (0x111, 'V'), - (0x112, 'M', 'ē'), - (0x113, 'V'), - (0x114, 'M', 'ĕ'), - (0x115, 'V'), - (0x116, 'M', 'ė'), - (0x117, 'V'), - (0x118, 'M', 'ę'), - (0x119, 'V'), - (0x11A, 'M', 'ě'), - (0x11B, 'V'), - (0x11C, 'M', 'ĝ'), - (0x11D, 'V'), - (0x11E, 'M', 'ğ'), - (0x11F, 'V'), - (0x120, 'M', 'ġ'), - (0x121, 'V'), - (0x122, 'M', 'ģ'), - (0x123, 'V'), - (0x124, 'M', 'ĥ'), - (0x125, 'V'), - (0x126, 'M', 'ħ'), - (0x127, 'V'), - (0x128, 'M', 'ĩ'), - (0x129, 'V'), - (0x12A, 'M', 'ī'), - (0x12B, 'V'), - ] - -def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x12C, 'M', 'ĭ'), - (0x12D, 'V'), - (0x12E, 'M', 'į'), - (0x12F, 'V'), - (0x130, 'M', 'i̇'), - (0x131, 'V'), - (0x132, 'M', 'ij'), - (0x134, 'M', 'ĵ'), - (0x135, 'V'), - (0x136, 'M', 'ķ'), - (0x137, 'V'), - (0x139, 'M', 'ĺ'), - (0x13A, 'V'), - (0x13B, 'M', 'ļ'), - (0x13C, 'V'), - (0x13D, 'M', 'ľ'), - (0x13E, 'V'), - (0x13F, 'M', 'l·'), - (0x141, 'M', 'ł'), - (0x142, 'V'), - (0x143, 'M', 'ń'), - (0x144, 'V'), - (0x145, 'M', 'ņ'), - (0x146, 'V'), - (0x147, 'M', 'ň'), - (0x148, 'V'), - (0x149, 'M', 'ʼn'), - (0x14A, 'M', 'ŋ'), - (0x14B, 'V'), - (0x14C, 'M', 'ō'), - (0x14D, 'V'), - (0x14E, 'M', 'ŏ'), - (0x14F, 'V'), - (0x150, 'M', 'ő'), - (0x151, 'V'), - (0x152, 'M', 'œ'), - (0x153, 'V'), - (0x154, 'M', 'ŕ'), - (0x155, 'V'), - (0x156, 'M', 'ŗ'), - (0x157, 'V'), - (0x158, 'M', 'ř'), - (0x159, 'V'), - (0x15A, 'M', 'ś'), - (0x15B, 'V'), - (0x15C, 'M', 'ŝ'), - (0x15D, 'V'), - (0x15E, 'M', 'ş'), - (0x15F, 'V'), - (0x160, 'M', 'š'), - (0x161, 'V'), - (0x162, 'M', 'ţ'), - (0x163, 'V'), - (0x164, 'M', 'ť'), - (0x165, 'V'), - (0x166, 'M', 'ŧ'), - (0x167, 'V'), - (0x168, 'M', 'ũ'), - (0x169, 'V'), - (0x16A, 'M', 'ū'), - (0x16B, 'V'), - (0x16C, 'M', 'ŭ'), - (0x16D, 'V'), - (0x16E, 'M', 'ů'), - (0x16F, 'V'), - (0x170, 'M', 'ű'), - (0x171, 'V'), - (0x172, 'M', 'ų'), - (0x173, 'V'), - (0x174, 'M', 'ŵ'), - (0x175, 'V'), - (0x176, 'M', 'ŷ'), - (0x177, 'V'), - (0x178, 'M', 'ÿ'), - (0x179, 'M', 'ź'), - (0x17A, 'V'), - (0x17B, 'M', 'ż'), - (0x17C, 'V'), - (0x17D, 'M', 'ž'), - (0x17E, 'V'), - (0x17F, 'M', 's'), - (0x180, 'V'), - (0x181, 'M', 'ɓ'), - (0x182, 'M', 'ƃ'), - (0x183, 'V'), - (0x184, 'M', 'ƅ'), - (0x185, 'V'), - (0x186, 'M', 'ɔ'), - (0x187, 'M', 'ƈ'), - (0x188, 'V'), - (0x189, 'M', 'ɖ'), - (0x18A, 'M', 'ɗ'), - (0x18B, 'M', 'ƌ'), - (0x18C, 'V'), - (0x18E, 'M', 'ǝ'), - (0x18F, 'M', 'ə'), - (0x190, 'M', 'ɛ'), - (0x191, 'M', 'ƒ'), - (0x192, 'V'), - (0x193, 'M', 'ɠ'), - ] - -def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x194, 'M', 'ɣ'), - (0x195, 'V'), - (0x196, 'M', 'ɩ'), - (0x197, 'M', 'ɨ'), - (0x198, 'M', 'ƙ'), - (0x199, 'V'), - (0x19C, 'M', 'ɯ'), - (0x19D, 'M', 'ɲ'), - (0x19E, 'V'), - (0x19F, 'M', 'ɵ'), - (0x1A0, 'M', 'ơ'), - (0x1A1, 'V'), - (0x1A2, 'M', 'ƣ'), - (0x1A3, 'V'), - (0x1A4, 'M', 'ƥ'), - (0x1A5, 'V'), - (0x1A6, 'M', 'ʀ'), - (0x1A7, 'M', 'ƨ'), - (0x1A8, 'V'), - (0x1A9, 'M', 'ʃ'), - (0x1AA, 'V'), - (0x1AC, 'M', 'ƭ'), - (0x1AD, 'V'), - (0x1AE, 'M', 'ʈ'), - (0x1AF, 'M', 'ư'), - (0x1B0, 'V'), - (0x1B1, 'M', 'ʊ'), - (0x1B2, 'M', 'ʋ'), - (0x1B3, 'M', 'ƴ'), - (0x1B4, 'V'), - (0x1B5, 'M', 'ƶ'), - (0x1B6, 'V'), - (0x1B7, 'M', 'ʒ'), - (0x1B8, 'M', 'ƹ'), - (0x1B9, 'V'), - (0x1BC, 'M', 'ƽ'), - (0x1BD, 'V'), - (0x1C4, 'M', 'dž'), - (0x1C7, 'M', 'lj'), - (0x1CA, 'M', 'nj'), - (0x1CD, 'M', 'ǎ'), - (0x1CE, 'V'), - (0x1CF, 'M', 'ǐ'), - (0x1D0, 'V'), - (0x1D1, 'M', 'ǒ'), - (0x1D2, 'V'), - (0x1D3, 'M', 'ǔ'), - (0x1D4, 'V'), - (0x1D5, 'M', 'ǖ'), - (0x1D6, 'V'), - (0x1D7, 'M', 'ǘ'), - (0x1D8, 'V'), - (0x1D9, 'M', 'ǚ'), - (0x1DA, 'V'), - (0x1DB, 'M', 'ǜ'), - (0x1DC, 'V'), - (0x1DE, 'M', 'ǟ'), - (0x1DF, 'V'), - (0x1E0, 'M', 'ǡ'), - (0x1E1, 'V'), - (0x1E2, 'M', 'ǣ'), - (0x1E3, 'V'), - (0x1E4, 'M', 'ǥ'), - (0x1E5, 'V'), - (0x1E6, 'M', 'ǧ'), - (0x1E7, 'V'), - (0x1E8, 'M', 'ǩ'), - (0x1E9, 'V'), - (0x1EA, 'M', 'ǫ'), - (0x1EB, 'V'), - (0x1EC, 'M', 'ǭ'), - (0x1ED, 'V'), - (0x1EE, 'M', 'ǯ'), - (0x1EF, 'V'), - (0x1F1, 'M', 'dz'), - (0x1F4, 'M', 'ǵ'), - (0x1F5, 'V'), - (0x1F6, 'M', 'ƕ'), - (0x1F7, 'M', 'ƿ'), - (0x1F8, 'M', 'ǹ'), - (0x1F9, 'V'), - (0x1FA, 'M', 'ǻ'), - (0x1FB, 'V'), - (0x1FC, 'M', 'ǽ'), - (0x1FD, 'V'), - (0x1FE, 'M', 'ǿ'), - (0x1FF, 'V'), - (0x200, 'M', 'ȁ'), - (0x201, 'V'), - (0x202, 'M', 'ȃ'), - (0x203, 'V'), - (0x204, 'M', 'ȅ'), - (0x205, 'V'), - (0x206, 'M', 'ȇ'), - (0x207, 'V'), - (0x208, 'M', 'ȉ'), - (0x209, 'V'), - (0x20A, 'M', 'ȋ'), - (0x20B, 'V'), - (0x20C, 'M', 'ȍ'), - ] - -def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x20D, 'V'), - (0x20E, 'M', 'ȏ'), - (0x20F, 'V'), - (0x210, 'M', 'ȑ'), - (0x211, 'V'), - (0x212, 'M', 'ȓ'), - (0x213, 'V'), - (0x214, 'M', 'ȕ'), - (0x215, 'V'), - (0x216, 'M', 'ȗ'), - (0x217, 'V'), - (0x218, 'M', 'ș'), - (0x219, 'V'), - (0x21A, 'M', 'ț'), - (0x21B, 'V'), - (0x21C, 'M', 'ȝ'), - (0x21D, 'V'), - (0x21E, 'M', 'ȟ'), - (0x21F, 'V'), - (0x220, 'M', 'ƞ'), - (0x221, 'V'), - (0x222, 'M', 'ȣ'), - (0x223, 'V'), - (0x224, 'M', 'ȥ'), - (0x225, 'V'), - (0x226, 'M', 'ȧ'), - (0x227, 'V'), - (0x228, 'M', 'ȩ'), - (0x229, 'V'), - (0x22A, 'M', 'ȫ'), - (0x22B, 'V'), - (0x22C, 'M', 'ȭ'), - (0x22D, 'V'), - (0x22E, 'M', 'ȯ'), - (0x22F, 'V'), - (0x230, 'M', 'ȱ'), - (0x231, 'V'), - (0x232, 'M', 'ȳ'), - (0x233, 'V'), - (0x23A, 'M', 'ⱥ'), - (0x23B, 'M', 'ȼ'), - (0x23C, 'V'), - (0x23D, 'M', 'ƚ'), - (0x23E, 'M', 'ⱦ'), - (0x23F, 'V'), - (0x241, 'M', 'ɂ'), - (0x242, 'V'), - (0x243, 'M', 'ƀ'), - (0x244, 'M', 'ʉ'), - (0x245, 'M', 'ʌ'), - (0x246, 'M', 'ɇ'), - (0x247, 'V'), - (0x248, 'M', 'ɉ'), - (0x249, 'V'), - (0x24A, 'M', 'ɋ'), - (0x24B, 'V'), - (0x24C, 'M', 'ɍ'), - (0x24D, 'V'), - (0x24E, 'M', 'ɏ'), - (0x24F, 'V'), - (0x2B0, 'M', 'h'), - (0x2B1, 'M', 'ɦ'), - (0x2B2, 'M', 'j'), - (0x2B3, 'M', 'r'), - (0x2B4, 'M', 'ɹ'), - (0x2B5, 'M', 'ɻ'), - (0x2B6, 'M', 'ʁ'), - (0x2B7, 'M', 'w'), - (0x2B8, 'M', 'y'), - (0x2B9, 'V'), - (0x2D8, '3', ' ̆'), - (0x2D9, '3', ' ̇'), - (0x2DA, '3', ' ̊'), - (0x2DB, '3', ' ̨'), - (0x2DC, '3', ' ̃'), - (0x2DD, '3', ' ̋'), - (0x2DE, 'V'), - (0x2E0, 'M', 'ɣ'), - (0x2E1, 'M', 'l'), - (0x2E2, 'M', 's'), - (0x2E3, 'M', 'x'), - (0x2E4, 'M', 'ʕ'), - (0x2E5, 'V'), - (0x340, 'M', '̀'), - (0x341, 'M', '́'), - (0x342, 'V'), - (0x343, 'M', '̓'), - (0x344, 'M', '̈́'), - (0x345, 'M', 'ι'), - (0x346, 'V'), - (0x34F, 'I'), - (0x350, 'V'), - (0x370, 'M', 'ͱ'), - (0x371, 'V'), - (0x372, 'M', 'ͳ'), - (0x373, 'V'), - (0x374, 'M', 'ʹ'), - (0x375, 'V'), - (0x376, 'M', 'ͷ'), - (0x377, 'V'), - ] - -def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x378, 'X'), - (0x37A, '3', ' ι'), - (0x37B, 'V'), - (0x37E, '3', ';'), - (0x37F, 'M', 'ϳ'), - (0x380, 'X'), - (0x384, '3', ' ́'), - (0x385, '3', ' ̈́'), - (0x386, 'M', 'ά'), - (0x387, 'M', '·'), - (0x388, 'M', 'έ'), - (0x389, 'M', 'ή'), - (0x38A, 'M', 'ί'), - (0x38B, 'X'), - (0x38C, 'M', 'ό'), - (0x38D, 'X'), - (0x38E, 'M', 'ύ'), - (0x38F, 'M', 'ώ'), - (0x390, 'V'), - (0x391, 'M', 'α'), - (0x392, 'M', 'β'), - (0x393, 'M', 'γ'), - (0x394, 'M', 'δ'), - (0x395, 'M', 'ε'), - (0x396, 'M', 'ζ'), - (0x397, 'M', 'η'), - (0x398, 'M', 'θ'), - (0x399, 'M', 'ι'), - (0x39A, 'M', 'κ'), - (0x39B, 'M', 'λ'), - (0x39C, 'M', 'μ'), - (0x39D, 'M', 'ν'), - (0x39E, 'M', 'ξ'), - (0x39F, 'M', 'ο'), - (0x3A0, 'M', 'π'), - (0x3A1, 'M', 'ρ'), - (0x3A2, 'X'), - (0x3A3, 'M', 'σ'), - (0x3A4, 'M', 'τ'), - (0x3A5, 'M', 'υ'), - (0x3A6, 'M', 'φ'), - (0x3A7, 'M', 'χ'), - (0x3A8, 'M', 'ψ'), - (0x3A9, 'M', 'ω'), - (0x3AA, 'M', 'ϊ'), - (0x3AB, 'M', 'ϋ'), - (0x3AC, 'V'), - (0x3C2, 'D', 'σ'), - (0x3C3, 'V'), - (0x3CF, 'M', 'ϗ'), - (0x3D0, 'M', 'β'), - (0x3D1, 'M', 'θ'), - (0x3D2, 'M', 'υ'), - (0x3D3, 'M', 'ύ'), - (0x3D4, 'M', 'ϋ'), - (0x3D5, 'M', 'φ'), - (0x3D6, 'M', 'π'), - (0x3D7, 'V'), - (0x3D8, 'M', 'ϙ'), - (0x3D9, 'V'), - (0x3DA, 'M', 'ϛ'), - (0x3DB, 'V'), - (0x3DC, 'M', 'ϝ'), - (0x3DD, 'V'), - (0x3DE, 'M', 'ϟ'), - (0x3DF, 'V'), - (0x3E0, 'M', 'ϡ'), - (0x3E1, 'V'), - (0x3E2, 'M', 'ϣ'), - (0x3E3, 'V'), - (0x3E4, 'M', 'ϥ'), - (0x3E5, 'V'), - (0x3E6, 'M', 'ϧ'), - (0x3E7, 'V'), - (0x3E8, 'M', 'ϩ'), - (0x3E9, 'V'), - (0x3EA, 'M', 'ϫ'), - (0x3EB, 'V'), - (0x3EC, 'M', 'ϭ'), - (0x3ED, 'V'), - (0x3EE, 'M', 'ϯ'), - (0x3EF, 'V'), - (0x3F0, 'M', 'κ'), - (0x3F1, 'M', 'ρ'), - (0x3F2, 'M', 'σ'), - (0x3F3, 'V'), - (0x3F4, 'M', 'θ'), - (0x3F5, 'M', 'ε'), - (0x3F6, 'V'), - (0x3F7, 'M', 'ϸ'), - (0x3F8, 'V'), - (0x3F9, 'M', 'σ'), - (0x3FA, 'M', 'ϻ'), - (0x3FB, 'V'), - (0x3FD, 'M', 'ͻ'), - (0x3FE, 'M', 'ͼ'), - (0x3FF, 'M', 'ͽ'), - (0x400, 'M', 'ѐ'), - (0x401, 'M', 'ё'), - (0x402, 'M', 'ђ'), - ] - -def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x403, 'M', 'ѓ'), - (0x404, 'M', 'є'), - (0x405, 'M', 'ѕ'), - (0x406, 'M', 'і'), - (0x407, 'M', 'ї'), - (0x408, 'M', 'ј'), - (0x409, 'M', 'љ'), - (0x40A, 'M', 'њ'), - (0x40B, 'M', 'ћ'), - (0x40C, 'M', 'ќ'), - (0x40D, 'M', 'ѝ'), - (0x40E, 'M', 'ў'), - (0x40F, 'M', 'џ'), - (0x410, 'M', 'а'), - (0x411, 'M', 'б'), - (0x412, 'M', 'в'), - (0x413, 'M', 'г'), - (0x414, 'M', 'д'), - (0x415, 'M', 'е'), - (0x416, 'M', 'ж'), - (0x417, 'M', 'з'), - (0x418, 'M', 'и'), - (0x419, 'M', 'й'), - (0x41A, 'M', 'к'), - (0x41B, 'M', 'л'), - (0x41C, 'M', 'м'), - (0x41D, 'M', 'н'), - (0x41E, 'M', 'о'), - (0x41F, 'M', 'п'), - (0x420, 'M', 'р'), - (0x421, 'M', 'с'), - (0x422, 'M', 'т'), - (0x423, 'M', 'у'), - (0x424, 'M', 'ф'), - (0x425, 'M', 'х'), - (0x426, 'M', 'ц'), - (0x427, 'M', 'ч'), - (0x428, 'M', 'ш'), - (0x429, 'M', 'щ'), - (0x42A, 'M', 'ъ'), - (0x42B, 'M', 'ы'), - (0x42C, 'M', 'ь'), - (0x42D, 'M', 'э'), - (0x42E, 'M', 'ю'), - (0x42F, 'M', 'я'), - (0x430, 'V'), - (0x460, 'M', 'ѡ'), - (0x461, 'V'), - (0x462, 'M', 'ѣ'), - (0x463, 'V'), - (0x464, 'M', 'ѥ'), - (0x465, 'V'), - (0x466, 'M', 'ѧ'), - (0x467, 'V'), - (0x468, 'M', 'ѩ'), - (0x469, 'V'), - (0x46A, 'M', 'ѫ'), - (0x46B, 'V'), - (0x46C, 'M', 'ѭ'), - (0x46D, 'V'), - (0x46E, 'M', 'ѯ'), - (0x46F, 'V'), - (0x470, 'M', 'ѱ'), - (0x471, 'V'), - (0x472, 'M', 'ѳ'), - (0x473, 'V'), - (0x474, 'M', 'ѵ'), - (0x475, 'V'), - (0x476, 'M', 'ѷ'), - (0x477, 'V'), - (0x478, 'M', 'ѹ'), - (0x479, 'V'), - (0x47A, 'M', 'ѻ'), - (0x47B, 'V'), - (0x47C, 'M', 'ѽ'), - (0x47D, 'V'), - (0x47E, 'M', 'ѿ'), - (0x47F, 'V'), - (0x480, 'M', 'ҁ'), - (0x481, 'V'), - (0x48A, 'M', 'ҋ'), - (0x48B, 'V'), - (0x48C, 'M', 'ҍ'), - (0x48D, 'V'), - (0x48E, 'M', 'ҏ'), - (0x48F, 'V'), - (0x490, 'M', 'ґ'), - (0x491, 'V'), - (0x492, 'M', 'ғ'), - (0x493, 'V'), - (0x494, 'M', 'ҕ'), - (0x495, 'V'), - (0x496, 'M', 'җ'), - (0x497, 'V'), - (0x498, 'M', 'ҙ'), - (0x499, 'V'), - (0x49A, 'M', 'қ'), - (0x49B, 'V'), - (0x49C, 'M', 'ҝ'), - (0x49D, 'V'), - ] - -def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x49E, 'M', 'ҟ'), - (0x49F, 'V'), - (0x4A0, 'M', 'ҡ'), - (0x4A1, 'V'), - (0x4A2, 'M', 'ң'), - (0x4A3, 'V'), - (0x4A4, 'M', 'ҥ'), - (0x4A5, 'V'), - (0x4A6, 'M', 'ҧ'), - (0x4A7, 'V'), - (0x4A8, 'M', 'ҩ'), - (0x4A9, 'V'), - (0x4AA, 'M', 'ҫ'), - (0x4AB, 'V'), - (0x4AC, 'M', 'ҭ'), - (0x4AD, 'V'), - (0x4AE, 'M', 'ү'), - (0x4AF, 'V'), - (0x4B0, 'M', 'ұ'), - (0x4B1, 'V'), - (0x4B2, 'M', 'ҳ'), - (0x4B3, 'V'), - (0x4B4, 'M', 'ҵ'), - (0x4B5, 'V'), - (0x4B6, 'M', 'ҷ'), - (0x4B7, 'V'), - (0x4B8, 'M', 'ҹ'), - (0x4B9, 'V'), - (0x4BA, 'M', 'һ'), - (0x4BB, 'V'), - (0x4BC, 'M', 'ҽ'), - (0x4BD, 'V'), - (0x4BE, 'M', 'ҿ'), - (0x4BF, 'V'), - (0x4C0, 'X'), - (0x4C1, 'M', 'ӂ'), - (0x4C2, 'V'), - (0x4C3, 'M', 'ӄ'), - (0x4C4, 'V'), - (0x4C5, 'M', 'ӆ'), - (0x4C6, 'V'), - (0x4C7, 'M', 'ӈ'), - (0x4C8, 'V'), - (0x4C9, 'M', 'ӊ'), - (0x4CA, 'V'), - (0x4CB, 'M', 'ӌ'), - (0x4CC, 'V'), - (0x4CD, 'M', 'ӎ'), - (0x4CE, 'V'), - (0x4D0, 'M', 'ӑ'), - (0x4D1, 'V'), - (0x4D2, 'M', 'ӓ'), - (0x4D3, 'V'), - (0x4D4, 'M', 'ӕ'), - (0x4D5, 'V'), - (0x4D6, 'M', 'ӗ'), - (0x4D7, 'V'), - (0x4D8, 'M', 'ә'), - (0x4D9, 'V'), - (0x4DA, 'M', 'ӛ'), - (0x4DB, 'V'), - (0x4DC, 'M', 'ӝ'), - (0x4DD, 'V'), - (0x4DE, 'M', 'ӟ'), - (0x4DF, 'V'), - (0x4E0, 'M', 'ӡ'), - (0x4E1, 'V'), - (0x4E2, 'M', 'ӣ'), - (0x4E3, 'V'), - (0x4E4, 'M', 'ӥ'), - (0x4E5, 'V'), - (0x4E6, 'M', 'ӧ'), - (0x4E7, 'V'), - (0x4E8, 'M', 'ө'), - (0x4E9, 'V'), - (0x4EA, 'M', 'ӫ'), - (0x4EB, 'V'), - (0x4EC, 'M', 'ӭ'), - (0x4ED, 'V'), - (0x4EE, 'M', 'ӯ'), - (0x4EF, 'V'), - (0x4F0, 'M', 'ӱ'), - (0x4F1, 'V'), - (0x4F2, 'M', 'ӳ'), - (0x4F3, 'V'), - (0x4F4, 'M', 'ӵ'), - (0x4F5, 'V'), - (0x4F6, 'M', 'ӷ'), - (0x4F7, 'V'), - (0x4F8, 'M', 'ӹ'), - (0x4F9, 'V'), - (0x4FA, 'M', 'ӻ'), - (0x4FB, 'V'), - (0x4FC, 'M', 'ӽ'), - (0x4FD, 'V'), - (0x4FE, 'M', 'ӿ'), - (0x4FF, 'V'), - (0x500, 'M', 'ԁ'), - (0x501, 'V'), - (0x502, 'M', 'ԃ'), - ] - -def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x503, 'V'), - (0x504, 'M', 'ԅ'), - (0x505, 'V'), - (0x506, 'M', 'ԇ'), - (0x507, 'V'), - (0x508, 'M', 'ԉ'), - (0x509, 'V'), - (0x50A, 'M', 'ԋ'), - (0x50B, 'V'), - (0x50C, 'M', 'ԍ'), - (0x50D, 'V'), - (0x50E, 'M', 'ԏ'), - (0x50F, 'V'), - (0x510, 'M', 'ԑ'), - (0x511, 'V'), - (0x512, 'M', 'ԓ'), - (0x513, 'V'), - (0x514, 'M', 'ԕ'), - (0x515, 'V'), - (0x516, 'M', 'ԗ'), - (0x517, 'V'), - (0x518, 'M', 'ԙ'), - (0x519, 'V'), - (0x51A, 'M', 'ԛ'), - (0x51B, 'V'), - (0x51C, 'M', 'ԝ'), - (0x51D, 'V'), - (0x51E, 'M', 'ԟ'), - (0x51F, 'V'), - (0x520, 'M', 'ԡ'), - (0x521, 'V'), - (0x522, 'M', 'ԣ'), - (0x523, 'V'), - (0x524, 'M', 'ԥ'), - (0x525, 'V'), - (0x526, 'M', 'ԧ'), - (0x527, 'V'), - (0x528, 'M', 'ԩ'), - (0x529, 'V'), - (0x52A, 'M', 'ԫ'), - (0x52B, 'V'), - (0x52C, 'M', 'ԭ'), - (0x52D, 'V'), - (0x52E, 'M', 'ԯ'), - (0x52F, 'V'), - (0x530, 'X'), - (0x531, 'M', 'ա'), - (0x532, 'M', 'բ'), - (0x533, 'M', 'գ'), - (0x534, 'M', 'դ'), - (0x535, 'M', 'ե'), - (0x536, 'M', 'զ'), - (0x537, 'M', 'է'), - (0x538, 'M', 'ը'), - (0x539, 'M', 'թ'), - (0x53A, 'M', 'ժ'), - (0x53B, 'M', 'ի'), - (0x53C, 'M', 'լ'), - (0x53D, 'M', 'խ'), - (0x53E, 'M', 'ծ'), - (0x53F, 'M', 'կ'), - (0x540, 'M', 'հ'), - (0x541, 'M', 'ձ'), - (0x542, 'M', 'ղ'), - (0x543, 'M', 'ճ'), - (0x544, 'M', 'մ'), - (0x545, 'M', 'յ'), - (0x546, 'M', 'ն'), - (0x547, 'M', 'շ'), - (0x548, 'M', 'ո'), - (0x549, 'M', 'չ'), - (0x54A, 'M', 'պ'), - (0x54B, 'M', 'ջ'), - (0x54C, 'M', 'ռ'), - (0x54D, 'M', 'ս'), - (0x54E, 'M', 'վ'), - (0x54F, 'M', 'տ'), - (0x550, 'M', 'ր'), - (0x551, 'M', 'ց'), - (0x552, 'M', 'ւ'), - (0x553, 'M', 'փ'), - (0x554, 'M', 'ք'), - (0x555, 'M', 'օ'), - (0x556, 'M', 'ֆ'), - (0x557, 'X'), - (0x559, 'V'), - (0x587, 'M', 'եւ'), - (0x588, 'V'), - (0x58B, 'X'), - (0x58D, 'V'), - (0x590, 'X'), - (0x591, 'V'), - (0x5C8, 'X'), - (0x5D0, 'V'), - (0x5EB, 'X'), - (0x5EF, 'V'), - (0x5F5, 'X'), - (0x606, 'V'), - (0x61C, 'X'), - (0x61D, 'V'), - ] - -def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x675, 'M', 'اٴ'), - (0x676, 'M', 'وٴ'), - (0x677, 'M', 'ۇٴ'), - (0x678, 'M', 'يٴ'), - (0x679, 'V'), - (0x6DD, 'X'), - (0x6DE, 'V'), - (0x70E, 'X'), - (0x710, 'V'), - (0x74B, 'X'), - (0x74D, 'V'), - (0x7B2, 'X'), - (0x7C0, 'V'), - (0x7FB, 'X'), - (0x7FD, 'V'), - (0x82E, 'X'), - (0x830, 'V'), - (0x83F, 'X'), - (0x840, 'V'), - (0x85C, 'X'), - (0x85E, 'V'), - (0x85F, 'X'), - (0x860, 'V'), - (0x86B, 'X'), - (0x870, 'V'), - (0x88F, 'X'), - (0x898, 'V'), - (0x8E2, 'X'), - (0x8E3, 'V'), - (0x958, 'M', 'क़'), - (0x959, 'M', 'ख़'), - (0x95A, 'M', 'ग़'), - (0x95B, 'M', 'ज़'), - (0x95C, 'M', 'ड़'), - (0x95D, 'M', 'ढ़'), - (0x95E, 'M', 'फ़'), - (0x95F, 'M', 'य़'), - (0x960, 'V'), - (0x984, 'X'), - (0x985, 'V'), - (0x98D, 'X'), - (0x98F, 'V'), - (0x991, 'X'), - (0x993, 'V'), - (0x9A9, 'X'), - (0x9AA, 'V'), - (0x9B1, 'X'), - (0x9B2, 'V'), - (0x9B3, 'X'), - (0x9B6, 'V'), - (0x9BA, 'X'), - (0x9BC, 'V'), - (0x9C5, 'X'), - (0x9C7, 'V'), - (0x9C9, 'X'), - (0x9CB, 'V'), - (0x9CF, 'X'), - (0x9D7, 'V'), - (0x9D8, 'X'), - (0x9DC, 'M', 'ড়'), - (0x9DD, 'M', 'ঢ়'), - (0x9DE, 'X'), - (0x9DF, 'M', 'য়'), - (0x9E0, 'V'), - (0x9E4, 'X'), - (0x9E6, 'V'), - (0x9FF, 'X'), - (0xA01, 'V'), - (0xA04, 'X'), - (0xA05, 'V'), - (0xA0B, 'X'), - (0xA0F, 'V'), - (0xA11, 'X'), - (0xA13, 'V'), - (0xA29, 'X'), - (0xA2A, 'V'), - (0xA31, 'X'), - (0xA32, 'V'), - (0xA33, 'M', 'ਲ਼'), - (0xA34, 'X'), - (0xA35, 'V'), - (0xA36, 'M', 'ਸ਼'), - (0xA37, 'X'), - (0xA38, 'V'), - (0xA3A, 'X'), - (0xA3C, 'V'), - (0xA3D, 'X'), - (0xA3E, 'V'), - (0xA43, 'X'), - (0xA47, 'V'), - (0xA49, 'X'), - (0xA4B, 'V'), - (0xA4E, 'X'), - (0xA51, 'V'), - (0xA52, 'X'), - (0xA59, 'M', 'ਖ਼'), - (0xA5A, 'M', 'ਗ਼'), - (0xA5B, 'M', 'ਜ਼'), - (0xA5C, 'V'), - (0xA5D, 'X'), - ] - -def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA5E, 'M', 'ਫ਼'), - (0xA5F, 'X'), - (0xA66, 'V'), - (0xA77, 'X'), - (0xA81, 'V'), - (0xA84, 'X'), - (0xA85, 'V'), - (0xA8E, 'X'), - (0xA8F, 'V'), - (0xA92, 'X'), - (0xA93, 'V'), - (0xAA9, 'X'), - (0xAAA, 'V'), - (0xAB1, 'X'), - (0xAB2, 'V'), - (0xAB4, 'X'), - (0xAB5, 'V'), - (0xABA, 'X'), - (0xABC, 'V'), - (0xAC6, 'X'), - (0xAC7, 'V'), - (0xACA, 'X'), - (0xACB, 'V'), - (0xACE, 'X'), - (0xAD0, 'V'), - (0xAD1, 'X'), - (0xAE0, 'V'), - (0xAE4, 'X'), - (0xAE6, 'V'), - (0xAF2, 'X'), - (0xAF9, 'V'), - (0xB00, 'X'), - (0xB01, 'V'), - (0xB04, 'X'), - (0xB05, 'V'), - (0xB0D, 'X'), - (0xB0F, 'V'), - (0xB11, 'X'), - (0xB13, 'V'), - (0xB29, 'X'), - (0xB2A, 'V'), - (0xB31, 'X'), - (0xB32, 'V'), - (0xB34, 'X'), - (0xB35, 'V'), - (0xB3A, 'X'), - (0xB3C, 'V'), - (0xB45, 'X'), - (0xB47, 'V'), - (0xB49, 'X'), - (0xB4B, 'V'), - (0xB4E, 'X'), - (0xB55, 'V'), - (0xB58, 'X'), - (0xB5C, 'M', 'ଡ଼'), - (0xB5D, 'M', 'ଢ଼'), - (0xB5E, 'X'), - (0xB5F, 'V'), - (0xB64, 'X'), - (0xB66, 'V'), - (0xB78, 'X'), - (0xB82, 'V'), - (0xB84, 'X'), - (0xB85, 'V'), - (0xB8B, 'X'), - (0xB8E, 'V'), - (0xB91, 'X'), - (0xB92, 'V'), - (0xB96, 'X'), - (0xB99, 'V'), - (0xB9B, 'X'), - (0xB9C, 'V'), - (0xB9D, 'X'), - (0xB9E, 'V'), - (0xBA0, 'X'), - (0xBA3, 'V'), - (0xBA5, 'X'), - (0xBA8, 'V'), - (0xBAB, 'X'), - (0xBAE, 'V'), - (0xBBA, 'X'), - (0xBBE, 'V'), - (0xBC3, 'X'), - (0xBC6, 'V'), - (0xBC9, 'X'), - (0xBCA, 'V'), - (0xBCE, 'X'), - (0xBD0, 'V'), - (0xBD1, 'X'), - (0xBD7, 'V'), - (0xBD8, 'X'), - (0xBE6, 'V'), - (0xBFB, 'X'), - (0xC00, 'V'), - (0xC0D, 'X'), - (0xC0E, 'V'), - (0xC11, 'X'), - (0xC12, 'V'), - (0xC29, 'X'), - (0xC2A, 'V'), - ] - -def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC3A, 'X'), - (0xC3C, 'V'), - (0xC45, 'X'), - (0xC46, 'V'), - (0xC49, 'X'), - (0xC4A, 'V'), - (0xC4E, 'X'), - (0xC55, 'V'), - (0xC57, 'X'), - (0xC58, 'V'), - (0xC5B, 'X'), - (0xC5D, 'V'), - (0xC5E, 'X'), - (0xC60, 'V'), - (0xC64, 'X'), - (0xC66, 'V'), - (0xC70, 'X'), - (0xC77, 'V'), - (0xC8D, 'X'), - (0xC8E, 'V'), - (0xC91, 'X'), - (0xC92, 'V'), - (0xCA9, 'X'), - (0xCAA, 'V'), - (0xCB4, 'X'), - (0xCB5, 'V'), - (0xCBA, 'X'), - (0xCBC, 'V'), - (0xCC5, 'X'), - (0xCC6, 'V'), - (0xCC9, 'X'), - (0xCCA, 'V'), - (0xCCE, 'X'), - (0xCD5, 'V'), - (0xCD7, 'X'), - (0xCDD, 'V'), - (0xCDF, 'X'), - (0xCE0, 'V'), - (0xCE4, 'X'), - (0xCE6, 'V'), - (0xCF0, 'X'), - (0xCF1, 'V'), - (0xCF4, 'X'), - (0xD00, 'V'), - (0xD0D, 'X'), - (0xD0E, 'V'), - (0xD11, 'X'), - (0xD12, 'V'), - (0xD45, 'X'), - (0xD46, 'V'), - (0xD49, 'X'), - (0xD4A, 'V'), - (0xD50, 'X'), - (0xD54, 'V'), - (0xD64, 'X'), - (0xD66, 'V'), - (0xD80, 'X'), - (0xD81, 'V'), - (0xD84, 'X'), - (0xD85, 'V'), - (0xD97, 'X'), - (0xD9A, 'V'), - (0xDB2, 'X'), - (0xDB3, 'V'), - (0xDBC, 'X'), - (0xDBD, 'V'), - (0xDBE, 'X'), - (0xDC0, 'V'), - (0xDC7, 'X'), - (0xDCA, 'V'), - (0xDCB, 'X'), - (0xDCF, 'V'), - (0xDD5, 'X'), - (0xDD6, 'V'), - (0xDD7, 'X'), - (0xDD8, 'V'), - (0xDE0, 'X'), - (0xDE6, 'V'), - (0xDF0, 'X'), - (0xDF2, 'V'), - (0xDF5, 'X'), - (0xE01, 'V'), - (0xE33, 'M', 'ํา'), - (0xE34, 'V'), - (0xE3B, 'X'), - (0xE3F, 'V'), - (0xE5C, 'X'), - (0xE81, 'V'), - (0xE83, 'X'), - (0xE84, 'V'), - (0xE85, 'X'), - (0xE86, 'V'), - (0xE8B, 'X'), - (0xE8C, 'V'), - (0xEA4, 'X'), - (0xEA5, 'V'), - (0xEA6, 'X'), - (0xEA7, 'V'), - (0xEB3, 'M', 'ໍາ'), - (0xEB4, 'V'), - ] - -def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xEBE, 'X'), - (0xEC0, 'V'), - (0xEC5, 'X'), - (0xEC6, 'V'), - (0xEC7, 'X'), - (0xEC8, 'V'), - (0xECF, 'X'), - (0xED0, 'V'), - (0xEDA, 'X'), - (0xEDC, 'M', 'ຫນ'), - (0xEDD, 'M', 'ຫມ'), - (0xEDE, 'V'), - (0xEE0, 'X'), - (0xF00, 'V'), - (0xF0C, 'M', '་'), - (0xF0D, 'V'), - (0xF43, 'M', 'གྷ'), - (0xF44, 'V'), - (0xF48, 'X'), - (0xF49, 'V'), - (0xF4D, 'M', 'ཌྷ'), - (0xF4E, 'V'), - (0xF52, 'M', 'དྷ'), - (0xF53, 'V'), - (0xF57, 'M', 'བྷ'), - (0xF58, 'V'), - (0xF5C, 'M', 'ཛྷ'), - (0xF5D, 'V'), - (0xF69, 'M', 'ཀྵ'), - (0xF6A, 'V'), - (0xF6D, 'X'), - (0xF71, 'V'), - (0xF73, 'M', 'ཱི'), - (0xF74, 'V'), - (0xF75, 'M', 'ཱུ'), - (0xF76, 'M', 'ྲྀ'), - (0xF77, 'M', 'ྲཱྀ'), - (0xF78, 'M', 'ླྀ'), - (0xF79, 'M', 'ླཱྀ'), - (0xF7A, 'V'), - (0xF81, 'M', 'ཱྀ'), - (0xF82, 'V'), - (0xF93, 'M', 'ྒྷ'), - (0xF94, 'V'), - (0xF98, 'X'), - (0xF99, 'V'), - (0xF9D, 'M', 'ྜྷ'), - (0xF9E, 'V'), - (0xFA2, 'M', 'ྡྷ'), - (0xFA3, 'V'), - (0xFA7, 'M', 'ྦྷ'), - (0xFA8, 'V'), - (0xFAC, 'M', 'ྫྷ'), - (0xFAD, 'V'), - (0xFB9, 'M', 'ྐྵ'), - (0xFBA, 'V'), - (0xFBD, 'X'), - (0xFBE, 'V'), - (0xFCD, 'X'), - (0xFCE, 'V'), - (0xFDB, 'X'), - (0x1000, 'V'), - (0x10A0, 'X'), - (0x10C7, 'M', 'ⴧ'), - (0x10C8, 'X'), - (0x10CD, 'M', 'ⴭ'), - (0x10CE, 'X'), - (0x10D0, 'V'), - (0x10FC, 'M', 'ნ'), - (0x10FD, 'V'), - (0x115F, 'X'), - (0x1161, 'V'), - (0x1249, 'X'), - (0x124A, 'V'), - (0x124E, 'X'), - (0x1250, 'V'), - (0x1257, 'X'), - (0x1258, 'V'), - (0x1259, 'X'), - (0x125A, 'V'), - (0x125E, 'X'), - (0x1260, 'V'), - (0x1289, 'X'), - (0x128A, 'V'), - (0x128E, 'X'), - (0x1290, 'V'), - (0x12B1, 'X'), - (0x12B2, 'V'), - (0x12B6, 'X'), - (0x12B8, 'V'), - (0x12BF, 'X'), - (0x12C0, 'V'), - (0x12C1, 'X'), - (0x12C2, 'V'), - (0x12C6, 'X'), - (0x12C8, 'V'), - (0x12D7, 'X'), - (0x12D8, 'V'), - (0x1311, 'X'), - (0x1312, 'V'), - ] - -def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1316, 'X'), - (0x1318, 'V'), - (0x135B, 'X'), - (0x135D, 'V'), - (0x137D, 'X'), - (0x1380, 'V'), - (0x139A, 'X'), - (0x13A0, 'V'), - (0x13F6, 'X'), - (0x13F8, 'M', 'Ᏸ'), - (0x13F9, 'M', 'Ᏹ'), - (0x13FA, 'M', 'Ᏺ'), - (0x13FB, 'M', 'Ᏻ'), - (0x13FC, 'M', 'Ᏼ'), - (0x13FD, 'M', 'Ᏽ'), - (0x13FE, 'X'), - (0x1400, 'V'), - (0x1680, 'X'), - (0x1681, 'V'), - (0x169D, 'X'), - (0x16A0, 'V'), - (0x16F9, 'X'), - (0x1700, 'V'), - (0x1716, 'X'), - (0x171F, 'V'), - (0x1737, 'X'), - (0x1740, 'V'), - (0x1754, 'X'), - (0x1760, 'V'), - (0x176D, 'X'), - (0x176E, 'V'), - (0x1771, 'X'), - (0x1772, 'V'), - (0x1774, 'X'), - (0x1780, 'V'), - (0x17B4, 'X'), - (0x17B6, 'V'), - (0x17DE, 'X'), - (0x17E0, 'V'), - (0x17EA, 'X'), - (0x17F0, 'V'), - (0x17FA, 'X'), - (0x1800, 'V'), - (0x1806, 'X'), - (0x1807, 'V'), - (0x180B, 'I'), - (0x180E, 'X'), - (0x180F, 'I'), - (0x1810, 'V'), - (0x181A, 'X'), - (0x1820, 'V'), - (0x1879, 'X'), - (0x1880, 'V'), - (0x18AB, 'X'), - (0x18B0, 'V'), - (0x18F6, 'X'), - (0x1900, 'V'), - (0x191F, 'X'), - (0x1920, 'V'), - (0x192C, 'X'), - (0x1930, 'V'), - (0x193C, 'X'), - (0x1940, 'V'), - (0x1941, 'X'), - (0x1944, 'V'), - (0x196E, 'X'), - (0x1970, 'V'), - (0x1975, 'X'), - (0x1980, 'V'), - (0x19AC, 'X'), - (0x19B0, 'V'), - (0x19CA, 'X'), - (0x19D0, 'V'), - (0x19DB, 'X'), - (0x19DE, 'V'), - (0x1A1C, 'X'), - (0x1A1E, 'V'), - (0x1A5F, 'X'), - (0x1A60, 'V'), - (0x1A7D, 'X'), - (0x1A7F, 'V'), - (0x1A8A, 'X'), - (0x1A90, 'V'), - (0x1A9A, 'X'), - (0x1AA0, 'V'), - (0x1AAE, 'X'), - (0x1AB0, 'V'), - (0x1ACF, 'X'), - (0x1B00, 'V'), - (0x1B4D, 'X'), - (0x1B50, 'V'), - (0x1B7F, 'X'), - (0x1B80, 'V'), - (0x1BF4, 'X'), - (0x1BFC, 'V'), - (0x1C38, 'X'), - (0x1C3B, 'V'), - (0x1C4A, 'X'), - (0x1C4D, 'V'), - (0x1C80, 'M', 'в'), - ] - -def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1C81, 'M', 'д'), - (0x1C82, 'M', 'о'), - (0x1C83, 'M', 'с'), - (0x1C84, 'M', 'т'), - (0x1C86, 'M', 'ъ'), - (0x1C87, 'M', 'ѣ'), - (0x1C88, 'M', 'ꙋ'), - (0x1C89, 'X'), - (0x1C90, 'M', 'ა'), - (0x1C91, 'M', 'ბ'), - (0x1C92, 'M', 'გ'), - (0x1C93, 'M', 'დ'), - (0x1C94, 'M', 'ე'), - (0x1C95, 'M', 'ვ'), - (0x1C96, 'M', 'ზ'), - (0x1C97, 'M', 'თ'), - (0x1C98, 'M', 'ი'), - (0x1C99, 'M', 'კ'), - (0x1C9A, 'M', 'ლ'), - (0x1C9B, 'M', 'მ'), - (0x1C9C, 'M', 'ნ'), - (0x1C9D, 'M', 'ო'), - (0x1C9E, 'M', 'პ'), - (0x1C9F, 'M', 'ჟ'), - (0x1CA0, 'M', 'რ'), - (0x1CA1, 'M', 'ს'), - (0x1CA2, 'M', 'ტ'), - (0x1CA3, 'M', 'უ'), - (0x1CA4, 'M', 'ფ'), - (0x1CA5, 'M', 'ქ'), - (0x1CA6, 'M', 'ღ'), - (0x1CA7, 'M', 'ყ'), - (0x1CA8, 'M', 'შ'), - (0x1CA9, 'M', 'ჩ'), - (0x1CAA, 'M', 'ც'), - (0x1CAB, 'M', 'ძ'), - (0x1CAC, 'M', 'წ'), - (0x1CAD, 'M', 'ჭ'), - (0x1CAE, 'M', 'ხ'), - (0x1CAF, 'M', 'ჯ'), - (0x1CB0, 'M', 'ჰ'), - (0x1CB1, 'M', 'ჱ'), - (0x1CB2, 'M', 'ჲ'), - (0x1CB3, 'M', 'ჳ'), - (0x1CB4, 'M', 'ჴ'), - (0x1CB5, 'M', 'ჵ'), - (0x1CB6, 'M', 'ჶ'), - (0x1CB7, 'M', 'ჷ'), - (0x1CB8, 'M', 'ჸ'), - (0x1CB9, 'M', 'ჹ'), - (0x1CBA, 'M', 'ჺ'), - (0x1CBB, 'X'), - (0x1CBD, 'M', 'ჽ'), - (0x1CBE, 'M', 'ჾ'), - (0x1CBF, 'M', 'ჿ'), - (0x1CC0, 'V'), - (0x1CC8, 'X'), - (0x1CD0, 'V'), - (0x1CFB, 'X'), - (0x1D00, 'V'), - (0x1D2C, 'M', 'a'), - (0x1D2D, 'M', 'æ'), - (0x1D2E, 'M', 'b'), - (0x1D2F, 'V'), - (0x1D30, 'M', 'd'), - (0x1D31, 'M', 'e'), - (0x1D32, 'M', 'ǝ'), - (0x1D33, 'M', 'g'), - (0x1D34, 'M', 'h'), - (0x1D35, 'M', 'i'), - (0x1D36, 'M', 'j'), - (0x1D37, 'M', 'k'), - (0x1D38, 'M', 'l'), - (0x1D39, 'M', 'm'), - (0x1D3A, 'M', 'n'), - (0x1D3B, 'V'), - (0x1D3C, 'M', 'o'), - (0x1D3D, 'M', 'ȣ'), - (0x1D3E, 'M', 'p'), - (0x1D3F, 'M', 'r'), - (0x1D40, 'M', 't'), - (0x1D41, 'M', 'u'), - (0x1D42, 'M', 'w'), - (0x1D43, 'M', 'a'), - (0x1D44, 'M', 'ɐ'), - (0x1D45, 'M', 'ɑ'), - (0x1D46, 'M', 'ᴂ'), - (0x1D47, 'M', 'b'), - (0x1D48, 'M', 'd'), - (0x1D49, 'M', 'e'), - (0x1D4A, 'M', 'ə'), - (0x1D4B, 'M', 'ɛ'), - (0x1D4C, 'M', 'ɜ'), - (0x1D4D, 'M', 'g'), - (0x1D4E, 'V'), - (0x1D4F, 'M', 'k'), - (0x1D50, 'M', 'm'), - (0x1D51, 'M', 'ŋ'), - (0x1D52, 'M', 'o'), - (0x1D53, 'M', 'ɔ'), - ] - -def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D54, 'M', 'ᴖ'), - (0x1D55, 'M', 'ᴗ'), - (0x1D56, 'M', 'p'), - (0x1D57, 'M', 't'), - (0x1D58, 'M', 'u'), - (0x1D59, 'M', 'ᴝ'), - (0x1D5A, 'M', 'ɯ'), - (0x1D5B, 'M', 'v'), - (0x1D5C, 'M', 'ᴥ'), - (0x1D5D, 'M', 'β'), - (0x1D5E, 'M', 'γ'), - (0x1D5F, 'M', 'δ'), - (0x1D60, 'M', 'φ'), - (0x1D61, 'M', 'χ'), - (0x1D62, 'M', 'i'), - (0x1D63, 'M', 'r'), - (0x1D64, 'M', 'u'), - (0x1D65, 'M', 'v'), - (0x1D66, 'M', 'β'), - (0x1D67, 'M', 'γ'), - (0x1D68, 'M', 'ρ'), - (0x1D69, 'M', 'φ'), - (0x1D6A, 'M', 'χ'), - (0x1D6B, 'V'), - (0x1D78, 'M', 'н'), - (0x1D79, 'V'), - (0x1D9B, 'M', 'ɒ'), - (0x1D9C, 'M', 'c'), - (0x1D9D, 'M', 'ɕ'), - (0x1D9E, 'M', 'ð'), - (0x1D9F, 'M', 'ɜ'), - (0x1DA0, 'M', 'f'), - (0x1DA1, 'M', 'ɟ'), - (0x1DA2, 'M', 'ɡ'), - (0x1DA3, 'M', 'ɥ'), - (0x1DA4, 'M', 'ɨ'), - (0x1DA5, 'M', 'ɩ'), - (0x1DA6, 'M', 'ɪ'), - (0x1DA7, 'M', 'ᵻ'), - (0x1DA8, 'M', 'ʝ'), - (0x1DA9, 'M', 'ɭ'), - (0x1DAA, 'M', 'ᶅ'), - (0x1DAB, 'M', 'ʟ'), - (0x1DAC, 'M', 'ɱ'), - (0x1DAD, 'M', 'ɰ'), - (0x1DAE, 'M', 'ɲ'), - (0x1DAF, 'M', 'ɳ'), - (0x1DB0, 'M', 'ɴ'), - (0x1DB1, 'M', 'ɵ'), - (0x1DB2, 'M', 'ɸ'), - (0x1DB3, 'M', 'ʂ'), - (0x1DB4, 'M', 'ʃ'), - (0x1DB5, 'M', 'ƫ'), - (0x1DB6, 'M', 'ʉ'), - (0x1DB7, 'M', 'ʊ'), - (0x1DB8, 'M', 'ᴜ'), - (0x1DB9, 'M', 'ʋ'), - (0x1DBA, 'M', 'ʌ'), - (0x1DBB, 'M', 'z'), - (0x1DBC, 'M', 'ʐ'), - (0x1DBD, 'M', 'ʑ'), - (0x1DBE, 'M', 'ʒ'), - (0x1DBF, 'M', 'θ'), - (0x1DC0, 'V'), - (0x1E00, 'M', 'ḁ'), - (0x1E01, 'V'), - (0x1E02, 'M', 'ḃ'), - (0x1E03, 'V'), - (0x1E04, 'M', 'ḅ'), - (0x1E05, 'V'), - (0x1E06, 'M', 'ḇ'), - (0x1E07, 'V'), - (0x1E08, 'M', 'ḉ'), - (0x1E09, 'V'), - (0x1E0A, 'M', 'ḋ'), - (0x1E0B, 'V'), - (0x1E0C, 'M', 'ḍ'), - (0x1E0D, 'V'), - (0x1E0E, 'M', 'ḏ'), - (0x1E0F, 'V'), - (0x1E10, 'M', 'ḑ'), - (0x1E11, 'V'), - (0x1E12, 'M', 'ḓ'), - (0x1E13, 'V'), - (0x1E14, 'M', 'ḕ'), - (0x1E15, 'V'), - (0x1E16, 'M', 'ḗ'), - (0x1E17, 'V'), - (0x1E18, 'M', 'ḙ'), - (0x1E19, 'V'), - (0x1E1A, 'M', 'ḛ'), - (0x1E1B, 'V'), - (0x1E1C, 'M', 'ḝ'), - (0x1E1D, 'V'), - (0x1E1E, 'M', 'ḟ'), - (0x1E1F, 'V'), - (0x1E20, 'M', 'ḡ'), - (0x1E21, 'V'), - (0x1E22, 'M', 'ḣ'), - (0x1E23, 'V'), - ] - -def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E24, 'M', 'ḥ'), - (0x1E25, 'V'), - (0x1E26, 'M', 'ḧ'), - (0x1E27, 'V'), - (0x1E28, 'M', 'ḩ'), - (0x1E29, 'V'), - (0x1E2A, 'M', 'ḫ'), - (0x1E2B, 'V'), - (0x1E2C, 'M', 'ḭ'), - (0x1E2D, 'V'), - (0x1E2E, 'M', 'ḯ'), - (0x1E2F, 'V'), - (0x1E30, 'M', 'ḱ'), - (0x1E31, 'V'), - (0x1E32, 'M', 'ḳ'), - (0x1E33, 'V'), - (0x1E34, 'M', 'ḵ'), - (0x1E35, 'V'), - (0x1E36, 'M', 'ḷ'), - (0x1E37, 'V'), - (0x1E38, 'M', 'ḹ'), - (0x1E39, 'V'), - (0x1E3A, 'M', 'ḻ'), - (0x1E3B, 'V'), - (0x1E3C, 'M', 'ḽ'), - (0x1E3D, 'V'), - (0x1E3E, 'M', 'ḿ'), - (0x1E3F, 'V'), - (0x1E40, 'M', 'ṁ'), - (0x1E41, 'V'), - (0x1E42, 'M', 'ṃ'), - (0x1E43, 'V'), - (0x1E44, 'M', 'ṅ'), - (0x1E45, 'V'), - (0x1E46, 'M', 'ṇ'), - (0x1E47, 'V'), - (0x1E48, 'M', 'ṉ'), - (0x1E49, 'V'), - (0x1E4A, 'M', 'ṋ'), - (0x1E4B, 'V'), - (0x1E4C, 'M', 'ṍ'), - (0x1E4D, 'V'), - (0x1E4E, 'M', 'ṏ'), - (0x1E4F, 'V'), - (0x1E50, 'M', 'ṑ'), - (0x1E51, 'V'), - (0x1E52, 'M', 'ṓ'), - (0x1E53, 'V'), - (0x1E54, 'M', 'ṕ'), - (0x1E55, 'V'), - (0x1E56, 'M', 'ṗ'), - (0x1E57, 'V'), - (0x1E58, 'M', 'ṙ'), - (0x1E59, 'V'), - (0x1E5A, 'M', 'ṛ'), - (0x1E5B, 'V'), - (0x1E5C, 'M', 'ṝ'), - (0x1E5D, 'V'), - (0x1E5E, 'M', 'ṟ'), - (0x1E5F, 'V'), - (0x1E60, 'M', 'ṡ'), - (0x1E61, 'V'), - (0x1E62, 'M', 'ṣ'), - (0x1E63, 'V'), - (0x1E64, 'M', 'ṥ'), - (0x1E65, 'V'), - (0x1E66, 'M', 'ṧ'), - (0x1E67, 'V'), - (0x1E68, 'M', 'ṩ'), - (0x1E69, 'V'), - (0x1E6A, 'M', 'ṫ'), - (0x1E6B, 'V'), - (0x1E6C, 'M', 'ṭ'), - (0x1E6D, 'V'), - (0x1E6E, 'M', 'ṯ'), - (0x1E6F, 'V'), - (0x1E70, 'M', 'ṱ'), - (0x1E71, 'V'), - (0x1E72, 'M', 'ṳ'), - (0x1E73, 'V'), - (0x1E74, 'M', 'ṵ'), - (0x1E75, 'V'), - (0x1E76, 'M', 'ṷ'), - (0x1E77, 'V'), - (0x1E78, 'M', 'ṹ'), - (0x1E79, 'V'), - (0x1E7A, 'M', 'ṻ'), - (0x1E7B, 'V'), - (0x1E7C, 'M', 'ṽ'), - (0x1E7D, 'V'), - (0x1E7E, 'M', 'ṿ'), - (0x1E7F, 'V'), - (0x1E80, 'M', 'ẁ'), - (0x1E81, 'V'), - (0x1E82, 'M', 'ẃ'), - (0x1E83, 'V'), - (0x1E84, 'M', 'ẅ'), - (0x1E85, 'V'), - (0x1E86, 'M', 'ẇ'), - (0x1E87, 'V'), - ] - -def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E88, 'M', 'ẉ'), - (0x1E89, 'V'), - (0x1E8A, 'M', 'ẋ'), - (0x1E8B, 'V'), - (0x1E8C, 'M', 'ẍ'), - (0x1E8D, 'V'), - (0x1E8E, 'M', 'ẏ'), - (0x1E8F, 'V'), - (0x1E90, 'M', 'ẑ'), - (0x1E91, 'V'), - (0x1E92, 'M', 'ẓ'), - (0x1E93, 'V'), - (0x1E94, 'M', 'ẕ'), - (0x1E95, 'V'), - (0x1E9A, 'M', 'aʾ'), - (0x1E9B, 'M', 'ṡ'), - (0x1E9C, 'V'), - (0x1E9E, 'M', 'ss'), - (0x1E9F, 'V'), - (0x1EA0, 'M', 'ạ'), - (0x1EA1, 'V'), - (0x1EA2, 'M', 'ả'), - (0x1EA3, 'V'), - (0x1EA4, 'M', 'ấ'), - (0x1EA5, 'V'), - (0x1EA6, 'M', 'ầ'), - (0x1EA7, 'V'), - (0x1EA8, 'M', 'ẩ'), - (0x1EA9, 'V'), - (0x1EAA, 'M', 'ẫ'), - (0x1EAB, 'V'), - (0x1EAC, 'M', 'ậ'), - (0x1EAD, 'V'), - (0x1EAE, 'M', 'ắ'), - (0x1EAF, 'V'), - (0x1EB0, 'M', 'ằ'), - (0x1EB1, 'V'), - (0x1EB2, 'M', 'ẳ'), - (0x1EB3, 'V'), - (0x1EB4, 'M', 'ẵ'), - (0x1EB5, 'V'), - (0x1EB6, 'M', 'ặ'), - (0x1EB7, 'V'), - (0x1EB8, 'M', 'ẹ'), - (0x1EB9, 'V'), - (0x1EBA, 'M', 'ẻ'), - (0x1EBB, 'V'), - (0x1EBC, 'M', 'ẽ'), - (0x1EBD, 'V'), - (0x1EBE, 'M', 'ế'), - (0x1EBF, 'V'), - (0x1EC0, 'M', 'ề'), - (0x1EC1, 'V'), - (0x1EC2, 'M', 'ể'), - (0x1EC3, 'V'), - (0x1EC4, 'M', 'ễ'), - (0x1EC5, 'V'), - (0x1EC6, 'M', 'ệ'), - (0x1EC7, 'V'), - (0x1EC8, 'M', 'ỉ'), - (0x1EC9, 'V'), - (0x1ECA, 'M', 'ị'), - (0x1ECB, 'V'), - (0x1ECC, 'M', 'ọ'), - (0x1ECD, 'V'), - (0x1ECE, 'M', 'ỏ'), - (0x1ECF, 'V'), - (0x1ED0, 'M', 'ố'), - (0x1ED1, 'V'), - (0x1ED2, 'M', 'ồ'), - (0x1ED3, 'V'), - (0x1ED4, 'M', 'ổ'), - (0x1ED5, 'V'), - (0x1ED6, 'M', 'ỗ'), - (0x1ED7, 'V'), - (0x1ED8, 'M', 'ộ'), - (0x1ED9, 'V'), - (0x1EDA, 'M', 'ớ'), - (0x1EDB, 'V'), - (0x1EDC, 'M', 'ờ'), - (0x1EDD, 'V'), - (0x1EDE, 'M', 'ở'), - (0x1EDF, 'V'), - (0x1EE0, 'M', 'ỡ'), - (0x1EE1, 'V'), - (0x1EE2, 'M', 'ợ'), - (0x1EE3, 'V'), - (0x1EE4, 'M', 'ụ'), - (0x1EE5, 'V'), - (0x1EE6, 'M', 'ủ'), - (0x1EE7, 'V'), - (0x1EE8, 'M', 'ứ'), - (0x1EE9, 'V'), - (0x1EEA, 'M', 'ừ'), - (0x1EEB, 'V'), - (0x1EEC, 'M', 'ử'), - (0x1EED, 'V'), - (0x1EEE, 'M', 'ữ'), - (0x1EEF, 'V'), - (0x1EF0, 'M', 'ự'), - ] - -def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EF1, 'V'), - (0x1EF2, 'M', 'ỳ'), - (0x1EF3, 'V'), - (0x1EF4, 'M', 'ỵ'), - (0x1EF5, 'V'), - (0x1EF6, 'M', 'ỷ'), - (0x1EF7, 'V'), - (0x1EF8, 'M', 'ỹ'), - (0x1EF9, 'V'), - (0x1EFA, 'M', 'ỻ'), - (0x1EFB, 'V'), - (0x1EFC, 'M', 'ỽ'), - (0x1EFD, 'V'), - (0x1EFE, 'M', 'ỿ'), - (0x1EFF, 'V'), - (0x1F08, 'M', 'ἀ'), - (0x1F09, 'M', 'ἁ'), - (0x1F0A, 'M', 'ἂ'), - (0x1F0B, 'M', 'ἃ'), - (0x1F0C, 'M', 'ἄ'), - (0x1F0D, 'M', 'ἅ'), - (0x1F0E, 'M', 'ἆ'), - (0x1F0F, 'M', 'ἇ'), - (0x1F10, 'V'), - (0x1F16, 'X'), - (0x1F18, 'M', 'ἐ'), - (0x1F19, 'M', 'ἑ'), - (0x1F1A, 'M', 'ἒ'), - (0x1F1B, 'M', 'ἓ'), - (0x1F1C, 'M', 'ἔ'), - (0x1F1D, 'M', 'ἕ'), - (0x1F1E, 'X'), - (0x1F20, 'V'), - (0x1F28, 'M', 'ἠ'), - (0x1F29, 'M', 'ἡ'), - (0x1F2A, 'M', 'ἢ'), - (0x1F2B, 'M', 'ἣ'), - (0x1F2C, 'M', 'ἤ'), - (0x1F2D, 'M', 'ἥ'), - (0x1F2E, 'M', 'ἦ'), - (0x1F2F, 'M', 'ἧ'), - (0x1F30, 'V'), - (0x1F38, 'M', 'ἰ'), - (0x1F39, 'M', 'ἱ'), - (0x1F3A, 'M', 'ἲ'), - (0x1F3B, 'M', 'ἳ'), - (0x1F3C, 'M', 'ἴ'), - (0x1F3D, 'M', 'ἵ'), - (0x1F3E, 'M', 'ἶ'), - (0x1F3F, 'M', 'ἷ'), - (0x1F40, 'V'), - (0x1F46, 'X'), - (0x1F48, 'M', 'ὀ'), - (0x1F49, 'M', 'ὁ'), - (0x1F4A, 'M', 'ὂ'), - (0x1F4B, 'M', 'ὃ'), - (0x1F4C, 'M', 'ὄ'), - (0x1F4D, 'M', 'ὅ'), - (0x1F4E, 'X'), - (0x1F50, 'V'), - (0x1F58, 'X'), - (0x1F59, 'M', 'ὑ'), - (0x1F5A, 'X'), - (0x1F5B, 'M', 'ὓ'), - (0x1F5C, 'X'), - (0x1F5D, 'M', 'ὕ'), - (0x1F5E, 'X'), - (0x1F5F, 'M', 'ὗ'), - (0x1F60, 'V'), - (0x1F68, 'M', 'ὠ'), - (0x1F69, 'M', 'ὡ'), - (0x1F6A, 'M', 'ὢ'), - (0x1F6B, 'M', 'ὣ'), - (0x1F6C, 'M', 'ὤ'), - (0x1F6D, 'M', 'ὥ'), - (0x1F6E, 'M', 'ὦ'), - (0x1F6F, 'M', 'ὧ'), - (0x1F70, 'V'), - (0x1F71, 'M', 'ά'), - (0x1F72, 'V'), - (0x1F73, 'M', 'έ'), - (0x1F74, 'V'), - (0x1F75, 'M', 'ή'), - (0x1F76, 'V'), - (0x1F77, 'M', 'ί'), - (0x1F78, 'V'), - (0x1F79, 'M', 'ό'), - (0x1F7A, 'V'), - (0x1F7B, 'M', 'ύ'), - (0x1F7C, 'V'), - (0x1F7D, 'M', 'ώ'), - (0x1F7E, 'X'), - (0x1F80, 'M', 'ἀι'), - (0x1F81, 'M', 'ἁι'), - (0x1F82, 'M', 'ἂι'), - (0x1F83, 'M', 'ἃι'), - (0x1F84, 'M', 'ἄι'), - (0x1F85, 'M', 'ἅι'), - (0x1F86, 'M', 'ἆι'), - (0x1F87, 'M', 'ἇι'), - ] - -def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F88, 'M', 'ἀι'), - (0x1F89, 'M', 'ἁι'), - (0x1F8A, 'M', 'ἂι'), - (0x1F8B, 'M', 'ἃι'), - (0x1F8C, 'M', 'ἄι'), - (0x1F8D, 'M', 'ἅι'), - (0x1F8E, 'M', 'ἆι'), - (0x1F8F, 'M', 'ἇι'), - (0x1F90, 'M', 'ἠι'), - (0x1F91, 'M', 'ἡι'), - (0x1F92, 'M', 'ἢι'), - (0x1F93, 'M', 'ἣι'), - (0x1F94, 'M', 'ἤι'), - (0x1F95, 'M', 'ἥι'), - (0x1F96, 'M', 'ἦι'), - (0x1F97, 'M', 'ἧι'), - (0x1F98, 'M', 'ἠι'), - (0x1F99, 'M', 'ἡι'), - (0x1F9A, 'M', 'ἢι'), - (0x1F9B, 'M', 'ἣι'), - (0x1F9C, 'M', 'ἤι'), - (0x1F9D, 'M', 'ἥι'), - (0x1F9E, 'M', 'ἦι'), - (0x1F9F, 'M', 'ἧι'), - (0x1FA0, 'M', 'ὠι'), - (0x1FA1, 'M', 'ὡι'), - (0x1FA2, 'M', 'ὢι'), - (0x1FA3, 'M', 'ὣι'), - (0x1FA4, 'M', 'ὤι'), - (0x1FA5, 'M', 'ὥι'), - (0x1FA6, 'M', 'ὦι'), - (0x1FA7, 'M', 'ὧι'), - (0x1FA8, 'M', 'ὠι'), - (0x1FA9, 'M', 'ὡι'), - (0x1FAA, 'M', 'ὢι'), - (0x1FAB, 'M', 'ὣι'), - (0x1FAC, 'M', 'ὤι'), - (0x1FAD, 'M', 'ὥι'), - (0x1FAE, 'M', 'ὦι'), - (0x1FAF, 'M', 'ὧι'), - (0x1FB0, 'V'), - (0x1FB2, 'M', 'ὰι'), - (0x1FB3, 'M', 'αι'), - (0x1FB4, 'M', 'άι'), - (0x1FB5, 'X'), - (0x1FB6, 'V'), - (0x1FB7, 'M', 'ᾶι'), - (0x1FB8, 'M', 'ᾰ'), - (0x1FB9, 'M', 'ᾱ'), - (0x1FBA, 'M', 'ὰ'), - (0x1FBB, 'M', 'ά'), - (0x1FBC, 'M', 'αι'), - (0x1FBD, '3', ' ̓'), - (0x1FBE, 'M', 'ι'), - (0x1FBF, '3', ' ̓'), - (0x1FC0, '3', ' ͂'), - (0x1FC1, '3', ' ̈͂'), - (0x1FC2, 'M', 'ὴι'), - (0x1FC3, 'M', 'ηι'), - (0x1FC4, 'M', 'ήι'), - (0x1FC5, 'X'), - (0x1FC6, 'V'), - (0x1FC7, 'M', 'ῆι'), - (0x1FC8, 'M', 'ὲ'), - (0x1FC9, 'M', 'έ'), - (0x1FCA, 'M', 'ὴ'), - (0x1FCB, 'M', 'ή'), - (0x1FCC, 'M', 'ηι'), - (0x1FCD, '3', ' ̓̀'), - (0x1FCE, '3', ' ̓́'), - (0x1FCF, '3', ' ̓͂'), - (0x1FD0, 'V'), - (0x1FD3, 'M', 'ΐ'), - (0x1FD4, 'X'), - (0x1FD6, 'V'), - (0x1FD8, 'M', 'ῐ'), - (0x1FD9, 'M', 'ῑ'), - (0x1FDA, 'M', 'ὶ'), - (0x1FDB, 'M', 'ί'), - (0x1FDC, 'X'), - (0x1FDD, '3', ' ̔̀'), - (0x1FDE, '3', ' ̔́'), - (0x1FDF, '3', ' ̔͂'), - (0x1FE0, 'V'), - (0x1FE3, 'M', 'ΰ'), - (0x1FE4, 'V'), - (0x1FE8, 'M', 'ῠ'), - (0x1FE9, 'M', 'ῡ'), - (0x1FEA, 'M', 'ὺ'), - (0x1FEB, 'M', 'ύ'), - (0x1FEC, 'M', 'ῥ'), - (0x1FED, '3', ' ̈̀'), - (0x1FEE, '3', ' ̈́'), - (0x1FEF, '3', '`'), - (0x1FF0, 'X'), - (0x1FF2, 'M', 'ὼι'), - (0x1FF3, 'M', 'ωι'), - (0x1FF4, 'M', 'ώι'), - (0x1FF5, 'X'), - (0x1FF6, 'V'), - ] - -def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FF7, 'M', 'ῶι'), - (0x1FF8, 'M', 'ὸ'), - (0x1FF9, 'M', 'ό'), - (0x1FFA, 'M', 'ὼ'), - (0x1FFB, 'M', 'ώ'), - (0x1FFC, 'M', 'ωι'), - (0x1FFD, '3', ' ́'), - (0x1FFE, '3', ' ̔'), - (0x1FFF, 'X'), - (0x2000, '3', ' '), - (0x200B, 'I'), - (0x200C, 'D', ''), - (0x200E, 'X'), - (0x2010, 'V'), - (0x2011, 'M', '‐'), - (0x2012, 'V'), - (0x2017, '3', ' ̳'), - (0x2018, 'V'), - (0x2024, 'X'), - (0x2027, 'V'), - (0x2028, 'X'), - (0x202F, '3', ' '), - (0x2030, 'V'), - (0x2033, 'M', '′′'), - (0x2034, 'M', '′′′'), - (0x2035, 'V'), - (0x2036, 'M', '‵‵'), - (0x2037, 'M', '‵‵‵'), - (0x2038, 'V'), - (0x203C, '3', '!!'), - (0x203D, 'V'), - (0x203E, '3', ' ̅'), - (0x203F, 'V'), - (0x2047, '3', '??'), - (0x2048, '3', '?!'), - (0x2049, '3', '!?'), - (0x204A, 'V'), - (0x2057, 'M', '′′′′'), - (0x2058, 'V'), - (0x205F, '3', ' '), - (0x2060, 'I'), - (0x2061, 'X'), - (0x2064, 'I'), - (0x2065, 'X'), - (0x2070, 'M', '0'), - (0x2071, 'M', 'i'), - (0x2072, 'X'), - (0x2074, 'M', '4'), - (0x2075, 'M', '5'), - (0x2076, 'M', '6'), - (0x2077, 'M', '7'), - (0x2078, 'M', '8'), - (0x2079, 'M', '9'), - (0x207A, '3', '+'), - (0x207B, 'M', '−'), - (0x207C, '3', '='), - (0x207D, '3', '('), - (0x207E, '3', ')'), - (0x207F, 'M', 'n'), - (0x2080, 'M', '0'), - (0x2081, 'M', '1'), - (0x2082, 'M', '2'), - (0x2083, 'M', '3'), - (0x2084, 'M', '4'), - (0x2085, 'M', '5'), - (0x2086, 'M', '6'), - (0x2087, 'M', '7'), - (0x2088, 'M', '8'), - (0x2089, 'M', '9'), - (0x208A, '3', '+'), - (0x208B, 'M', '−'), - (0x208C, '3', '='), - (0x208D, '3', '('), - (0x208E, '3', ')'), - (0x208F, 'X'), - (0x2090, 'M', 'a'), - (0x2091, 'M', 'e'), - (0x2092, 'M', 'o'), - (0x2093, 'M', 'x'), - (0x2094, 'M', 'ə'), - (0x2095, 'M', 'h'), - (0x2096, 'M', 'k'), - (0x2097, 'M', 'l'), - (0x2098, 'M', 'm'), - (0x2099, 'M', 'n'), - (0x209A, 'M', 'p'), - (0x209B, 'M', 's'), - (0x209C, 'M', 't'), - (0x209D, 'X'), - (0x20A0, 'V'), - (0x20A8, 'M', 'rs'), - (0x20A9, 'V'), - (0x20C1, 'X'), - (0x20D0, 'V'), - (0x20F1, 'X'), - (0x2100, '3', 'a/c'), - (0x2101, '3', 'a/s'), - (0x2102, 'M', 'c'), - (0x2103, 'M', '°c'), - (0x2104, 'V'), - ] - -def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2105, '3', 'c/o'), - (0x2106, '3', 'c/u'), - (0x2107, 'M', 'ɛ'), - (0x2108, 'V'), - (0x2109, 'M', '°f'), - (0x210A, 'M', 'g'), - (0x210B, 'M', 'h'), - (0x210F, 'M', 'ħ'), - (0x2110, 'M', 'i'), - (0x2112, 'M', 'l'), - (0x2114, 'V'), - (0x2115, 'M', 'n'), - (0x2116, 'M', 'no'), - (0x2117, 'V'), - (0x2119, 'M', 'p'), - (0x211A, 'M', 'q'), - (0x211B, 'M', 'r'), - (0x211E, 'V'), - (0x2120, 'M', 'sm'), - (0x2121, 'M', 'tel'), - (0x2122, 'M', 'tm'), - (0x2123, 'V'), - (0x2124, 'M', 'z'), - (0x2125, 'V'), - (0x2126, 'M', 'ω'), - (0x2127, 'V'), - (0x2128, 'M', 'z'), - (0x2129, 'V'), - (0x212A, 'M', 'k'), - (0x212B, 'M', 'å'), - (0x212C, 'M', 'b'), - (0x212D, 'M', 'c'), - (0x212E, 'V'), - (0x212F, 'M', 'e'), - (0x2131, 'M', 'f'), - (0x2132, 'X'), - (0x2133, 'M', 'm'), - (0x2134, 'M', 'o'), - (0x2135, 'M', 'א'), - (0x2136, 'M', 'ב'), - (0x2137, 'M', 'ג'), - (0x2138, 'M', 'ד'), - (0x2139, 'M', 'i'), - (0x213A, 'V'), - (0x213B, 'M', 'fax'), - (0x213C, 'M', 'π'), - (0x213D, 'M', 'γ'), - (0x213F, 'M', 'π'), - (0x2140, 'M', '∑'), - (0x2141, 'V'), - (0x2145, 'M', 'd'), - (0x2147, 'M', 'e'), - (0x2148, 'M', 'i'), - (0x2149, 'M', 'j'), - (0x214A, 'V'), - (0x2150, 'M', '1⁄7'), - (0x2151, 'M', '1⁄9'), - (0x2152, 'M', '1⁄10'), - (0x2153, 'M', '1⁄3'), - (0x2154, 'M', '2⁄3'), - (0x2155, 'M', '1⁄5'), - (0x2156, 'M', '2⁄5'), - (0x2157, 'M', '3⁄5'), - (0x2158, 'M', '4⁄5'), - (0x2159, 'M', '1⁄6'), - (0x215A, 'M', '5⁄6'), - (0x215B, 'M', '1⁄8'), - (0x215C, 'M', '3⁄8'), - (0x215D, 'M', '5⁄8'), - (0x215E, 'M', '7⁄8'), - (0x215F, 'M', '1⁄'), - (0x2160, 'M', 'i'), - (0x2161, 'M', 'ii'), - (0x2162, 'M', 'iii'), - (0x2163, 'M', 'iv'), - (0x2164, 'M', 'v'), - (0x2165, 'M', 'vi'), - (0x2166, 'M', 'vii'), - (0x2167, 'M', 'viii'), - (0x2168, 'M', 'ix'), - (0x2169, 'M', 'x'), - (0x216A, 'M', 'xi'), - (0x216B, 'M', 'xii'), - (0x216C, 'M', 'l'), - (0x216D, 'M', 'c'), - (0x216E, 'M', 'd'), - (0x216F, 'M', 'm'), - (0x2170, 'M', 'i'), - (0x2171, 'M', 'ii'), - (0x2172, 'M', 'iii'), - (0x2173, 'M', 'iv'), - (0x2174, 'M', 'v'), - (0x2175, 'M', 'vi'), - (0x2176, 'M', 'vii'), - (0x2177, 'M', 'viii'), - (0x2178, 'M', 'ix'), - (0x2179, 'M', 'x'), - (0x217A, 'M', 'xi'), - (0x217B, 'M', 'xii'), - (0x217C, 'M', 'l'), - ] - -def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x217D, 'M', 'c'), - (0x217E, 'M', 'd'), - (0x217F, 'M', 'm'), - (0x2180, 'V'), - (0x2183, 'X'), - (0x2184, 'V'), - (0x2189, 'M', '0⁄3'), - (0x218A, 'V'), - (0x218C, 'X'), - (0x2190, 'V'), - (0x222C, 'M', '∫∫'), - (0x222D, 'M', '∫∫∫'), - (0x222E, 'V'), - (0x222F, 'M', '∮∮'), - (0x2230, 'M', '∮∮∮'), - (0x2231, 'V'), - (0x2260, '3'), - (0x2261, 'V'), - (0x226E, '3'), - (0x2270, 'V'), - (0x2329, 'M', '〈'), - (0x232A, 'M', '〉'), - (0x232B, 'V'), - (0x2427, 'X'), - (0x2440, 'V'), - (0x244B, 'X'), - (0x2460, 'M', '1'), - (0x2461, 'M', '2'), - (0x2462, 'M', '3'), - (0x2463, 'M', '4'), - (0x2464, 'M', '5'), - (0x2465, 'M', '6'), - (0x2466, 'M', '7'), - (0x2467, 'M', '8'), - (0x2468, 'M', '9'), - (0x2469, 'M', '10'), - (0x246A, 'M', '11'), - (0x246B, 'M', '12'), - (0x246C, 'M', '13'), - (0x246D, 'M', '14'), - (0x246E, 'M', '15'), - (0x246F, 'M', '16'), - (0x2470, 'M', '17'), - (0x2471, 'M', '18'), - (0x2472, 'M', '19'), - (0x2473, 'M', '20'), - (0x2474, '3', '(1)'), - (0x2475, '3', '(2)'), - (0x2476, '3', '(3)'), - (0x2477, '3', '(4)'), - (0x2478, '3', '(5)'), - (0x2479, '3', '(6)'), - (0x247A, '3', '(7)'), - (0x247B, '3', '(8)'), - (0x247C, '3', '(9)'), - (0x247D, '3', '(10)'), - (0x247E, '3', '(11)'), - (0x247F, '3', '(12)'), - (0x2480, '3', '(13)'), - (0x2481, '3', '(14)'), - (0x2482, '3', '(15)'), - (0x2483, '3', '(16)'), - (0x2484, '3', '(17)'), - (0x2485, '3', '(18)'), - (0x2486, '3', '(19)'), - (0x2487, '3', '(20)'), - (0x2488, 'X'), - (0x249C, '3', '(a)'), - (0x249D, '3', '(b)'), - (0x249E, '3', '(c)'), - (0x249F, '3', '(d)'), - (0x24A0, '3', '(e)'), - (0x24A1, '3', '(f)'), - (0x24A2, '3', '(g)'), - (0x24A3, '3', '(h)'), - (0x24A4, '3', '(i)'), - (0x24A5, '3', '(j)'), - (0x24A6, '3', '(k)'), - (0x24A7, '3', '(l)'), - (0x24A8, '3', '(m)'), - (0x24A9, '3', '(n)'), - (0x24AA, '3', '(o)'), - (0x24AB, '3', '(p)'), - (0x24AC, '3', '(q)'), - (0x24AD, '3', '(r)'), - (0x24AE, '3', '(s)'), - (0x24AF, '3', '(t)'), - (0x24B0, '3', '(u)'), - (0x24B1, '3', '(v)'), - (0x24B2, '3', '(w)'), - (0x24B3, '3', '(x)'), - (0x24B4, '3', '(y)'), - (0x24B5, '3', '(z)'), - (0x24B6, 'M', 'a'), - (0x24B7, 'M', 'b'), - (0x24B8, 'M', 'c'), - (0x24B9, 'M', 'd'), - (0x24BA, 'M', 'e'), - (0x24BB, 'M', 'f'), - (0x24BC, 'M', 'g'), - ] - -def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x24BD, 'M', 'h'), - (0x24BE, 'M', 'i'), - (0x24BF, 'M', 'j'), - (0x24C0, 'M', 'k'), - (0x24C1, 'M', 'l'), - (0x24C2, 'M', 'm'), - (0x24C3, 'M', 'n'), - (0x24C4, 'M', 'o'), - (0x24C5, 'M', 'p'), - (0x24C6, 'M', 'q'), - (0x24C7, 'M', 'r'), - (0x24C8, 'M', 's'), - (0x24C9, 'M', 't'), - (0x24CA, 'M', 'u'), - (0x24CB, 'M', 'v'), - (0x24CC, 'M', 'w'), - (0x24CD, 'M', 'x'), - (0x24CE, 'M', 'y'), - (0x24CF, 'M', 'z'), - (0x24D0, 'M', 'a'), - (0x24D1, 'M', 'b'), - (0x24D2, 'M', 'c'), - (0x24D3, 'M', 'd'), - (0x24D4, 'M', 'e'), - (0x24D5, 'M', 'f'), - (0x24D6, 'M', 'g'), - (0x24D7, 'M', 'h'), - (0x24D8, 'M', 'i'), - (0x24D9, 'M', 'j'), - (0x24DA, 'M', 'k'), - (0x24DB, 'M', 'l'), - (0x24DC, 'M', 'm'), - (0x24DD, 'M', 'n'), - (0x24DE, 'M', 'o'), - (0x24DF, 'M', 'p'), - (0x24E0, 'M', 'q'), - (0x24E1, 'M', 'r'), - (0x24E2, 'M', 's'), - (0x24E3, 'M', 't'), - (0x24E4, 'M', 'u'), - (0x24E5, 'M', 'v'), - (0x24E6, 'M', 'w'), - (0x24E7, 'M', 'x'), - (0x24E8, 'M', 'y'), - (0x24E9, 'M', 'z'), - (0x24EA, 'M', '0'), - (0x24EB, 'V'), - (0x2A0C, 'M', '∫∫∫∫'), - (0x2A0D, 'V'), - (0x2A74, '3', '::='), - (0x2A75, '3', '=='), - (0x2A76, '3', '==='), - (0x2A77, 'V'), - (0x2ADC, 'M', '⫝̸'), - (0x2ADD, 'V'), - (0x2B74, 'X'), - (0x2B76, 'V'), - (0x2B96, 'X'), - (0x2B97, 'V'), - (0x2C00, 'M', 'ⰰ'), - (0x2C01, 'M', 'ⰱ'), - (0x2C02, 'M', 'ⰲ'), - (0x2C03, 'M', 'ⰳ'), - (0x2C04, 'M', 'ⰴ'), - (0x2C05, 'M', 'ⰵ'), - (0x2C06, 'M', 'ⰶ'), - (0x2C07, 'M', 'ⰷ'), - (0x2C08, 'M', 'ⰸ'), - (0x2C09, 'M', 'ⰹ'), - (0x2C0A, 'M', 'ⰺ'), - (0x2C0B, 'M', 'ⰻ'), - (0x2C0C, 'M', 'ⰼ'), - (0x2C0D, 'M', 'ⰽ'), - (0x2C0E, 'M', 'ⰾ'), - (0x2C0F, 'M', 'ⰿ'), - (0x2C10, 'M', 'ⱀ'), - (0x2C11, 'M', 'ⱁ'), - (0x2C12, 'M', 'ⱂ'), - (0x2C13, 'M', 'ⱃ'), - (0x2C14, 'M', 'ⱄ'), - (0x2C15, 'M', 'ⱅ'), - (0x2C16, 'M', 'ⱆ'), - (0x2C17, 'M', 'ⱇ'), - (0x2C18, 'M', 'ⱈ'), - (0x2C19, 'M', 'ⱉ'), - (0x2C1A, 'M', 'ⱊ'), - (0x2C1B, 'M', 'ⱋ'), - (0x2C1C, 'M', 'ⱌ'), - (0x2C1D, 'M', 'ⱍ'), - (0x2C1E, 'M', 'ⱎ'), - (0x2C1F, 'M', 'ⱏ'), - (0x2C20, 'M', 'ⱐ'), - (0x2C21, 'M', 'ⱑ'), - (0x2C22, 'M', 'ⱒ'), - (0x2C23, 'M', 'ⱓ'), - (0x2C24, 'M', 'ⱔ'), - (0x2C25, 'M', 'ⱕ'), - (0x2C26, 'M', 'ⱖ'), - (0x2C27, 'M', 'ⱗ'), - (0x2C28, 'M', 'ⱘ'), - ] - -def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2C29, 'M', 'ⱙ'), - (0x2C2A, 'M', 'ⱚ'), - (0x2C2B, 'M', 'ⱛ'), - (0x2C2C, 'M', 'ⱜ'), - (0x2C2D, 'M', 'ⱝ'), - (0x2C2E, 'M', 'ⱞ'), - (0x2C2F, 'M', 'ⱟ'), - (0x2C30, 'V'), - (0x2C60, 'M', 'ⱡ'), - (0x2C61, 'V'), - (0x2C62, 'M', 'ɫ'), - (0x2C63, 'M', 'ᵽ'), - (0x2C64, 'M', 'ɽ'), - (0x2C65, 'V'), - (0x2C67, 'M', 'ⱨ'), - (0x2C68, 'V'), - (0x2C69, 'M', 'ⱪ'), - (0x2C6A, 'V'), - (0x2C6B, 'M', 'ⱬ'), - (0x2C6C, 'V'), - (0x2C6D, 'M', 'ɑ'), - (0x2C6E, 'M', 'ɱ'), - (0x2C6F, 'M', 'ɐ'), - (0x2C70, 'M', 'ɒ'), - (0x2C71, 'V'), - (0x2C72, 'M', 'ⱳ'), - (0x2C73, 'V'), - (0x2C75, 'M', 'ⱶ'), - (0x2C76, 'V'), - (0x2C7C, 'M', 'j'), - (0x2C7D, 'M', 'v'), - (0x2C7E, 'M', 'ȿ'), - (0x2C7F, 'M', 'ɀ'), - (0x2C80, 'M', 'ⲁ'), - (0x2C81, 'V'), - (0x2C82, 'M', 'ⲃ'), - (0x2C83, 'V'), - (0x2C84, 'M', 'ⲅ'), - (0x2C85, 'V'), - (0x2C86, 'M', 'ⲇ'), - (0x2C87, 'V'), - (0x2C88, 'M', 'ⲉ'), - (0x2C89, 'V'), - (0x2C8A, 'M', 'ⲋ'), - (0x2C8B, 'V'), - (0x2C8C, 'M', 'ⲍ'), - (0x2C8D, 'V'), - (0x2C8E, 'M', 'ⲏ'), - (0x2C8F, 'V'), - (0x2C90, 'M', 'ⲑ'), - (0x2C91, 'V'), - (0x2C92, 'M', 'ⲓ'), - (0x2C93, 'V'), - (0x2C94, 'M', 'ⲕ'), - (0x2C95, 'V'), - (0x2C96, 'M', 'ⲗ'), - (0x2C97, 'V'), - (0x2C98, 'M', 'ⲙ'), - (0x2C99, 'V'), - (0x2C9A, 'M', 'ⲛ'), - (0x2C9B, 'V'), - (0x2C9C, 'M', 'ⲝ'), - (0x2C9D, 'V'), - (0x2C9E, 'M', 'ⲟ'), - (0x2C9F, 'V'), - (0x2CA0, 'M', 'ⲡ'), - (0x2CA1, 'V'), - (0x2CA2, 'M', 'ⲣ'), - (0x2CA3, 'V'), - (0x2CA4, 'M', 'ⲥ'), - (0x2CA5, 'V'), - (0x2CA6, 'M', 'ⲧ'), - (0x2CA7, 'V'), - (0x2CA8, 'M', 'ⲩ'), - (0x2CA9, 'V'), - (0x2CAA, 'M', 'ⲫ'), - (0x2CAB, 'V'), - (0x2CAC, 'M', 'ⲭ'), - (0x2CAD, 'V'), - (0x2CAE, 'M', 'ⲯ'), - (0x2CAF, 'V'), - (0x2CB0, 'M', 'ⲱ'), - (0x2CB1, 'V'), - (0x2CB2, 'M', 'ⲳ'), - (0x2CB3, 'V'), - (0x2CB4, 'M', 'ⲵ'), - (0x2CB5, 'V'), - (0x2CB6, 'M', 'ⲷ'), - (0x2CB7, 'V'), - (0x2CB8, 'M', 'ⲹ'), - (0x2CB9, 'V'), - (0x2CBA, 'M', 'ⲻ'), - (0x2CBB, 'V'), - (0x2CBC, 'M', 'ⲽ'), - (0x2CBD, 'V'), - (0x2CBE, 'M', 'ⲿ'), - (0x2CBF, 'V'), - (0x2CC0, 'M', 'ⳁ'), - (0x2CC1, 'V'), - (0x2CC2, 'M', 'ⳃ'), - ] - -def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2CC3, 'V'), - (0x2CC4, 'M', 'ⳅ'), - (0x2CC5, 'V'), - (0x2CC6, 'M', 'ⳇ'), - (0x2CC7, 'V'), - (0x2CC8, 'M', 'ⳉ'), - (0x2CC9, 'V'), - (0x2CCA, 'M', 'ⳋ'), - (0x2CCB, 'V'), - (0x2CCC, 'M', 'ⳍ'), - (0x2CCD, 'V'), - (0x2CCE, 'M', 'ⳏ'), - (0x2CCF, 'V'), - (0x2CD0, 'M', 'ⳑ'), - (0x2CD1, 'V'), - (0x2CD2, 'M', 'ⳓ'), - (0x2CD3, 'V'), - (0x2CD4, 'M', 'ⳕ'), - (0x2CD5, 'V'), - (0x2CD6, 'M', 'ⳗ'), - (0x2CD7, 'V'), - (0x2CD8, 'M', 'ⳙ'), - (0x2CD9, 'V'), - (0x2CDA, 'M', 'ⳛ'), - (0x2CDB, 'V'), - (0x2CDC, 'M', 'ⳝ'), - (0x2CDD, 'V'), - (0x2CDE, 'M', 'ⳟ'), - (0x2CDF, 'V'), - (0x2CE0, 'M', 'ⳡ'), - (0x2CE1, 'V'), - (0x2CE2, 'M', 'ⳣ'), - (0x2CE3, 'V'), - (0x2CEB, 'M', 'ⳬ'), - (0x2CEC, 'V'), - (0x2CED, 'M', 'ⳮ'), - (0x2CEE, 'V'), - (0x2CF2, 'M', 'ⳳ'), - (0x2CF3, 'V'), - (0x2CF4, 'X'), - (0x2CF9, 'V'), - (0x2D26, 'X'), - (0x2D27, 'V'), - (0x2D28, 'X'), - (0x2D2D, 'V'), - (0x2D2E, 'X'), - (0x2D30, 'V'), - (0x2D68, 'X'), - (0x2D6F, 'M', 'ⵡ'), - (0x2D70, 'V'), - (0x2D71, 'X'), - (0x2D7F, 'V'), - (0x2D97, 'X'), - (0x2DA0, 'V'), - (0x2DA7, 'X'), - (0x2DA8, 'V'), - (0x2DAF, 'X'), - (0x2DB0, 'V'), - (0x2DB7, 'X'), - (0x2DB8, 'V'), - (0x2DBF, 'X'), - (0x2DC0, 'V'), - (0x2DC7, 'X'), - (0x2DC8, 'V'), - (0x2DCF, 'X'), - (0x2DD0, 'V'), - (0x2DD7, 'X'), - (0x2DD8, 'V'), - (0x2DDF, 'X'), - (0x2DE0, 'V'), - (0x2E5E, 'X'), - (0x2E80, 'V'), - (0x2E9A, 'X'), - (0x2E9B, 'V'), - (0x2E9F, 'M', '母'), - (0x2EA0, 'V'), - (0x2EF3, 'M', '龟'), - (0x2EF4, 'X'), - (0x2F00, 'M', '一'), - (0x2F01, 'M', '丨'), - (0x2F02, 'M', '丶'), - (0x2F03, 'M', '丿'), - (0x2F04, 'M', '乙'), - (0x2F05, 'M', '亅'), - (0x2F06, 'M', '二'), - (0x2F07, 'M', '亠'), - (0x2F08, 'M', '人'), - (0x2F09, 'M', '儿'), - (0x2F0A, 'M', '入'), - (0x2F0B, 'M', '八'), - (0x2F0C, 'M', '冂'), - (0x2F0D, 'M', '冖'), - (0x2F0E, 'M', '冫'), - (0x2F0F, 'M', '几'), - (0x2F10, 'M', '凵'), - (0x2F11, 'M', '刀'), - (0x2F12, 'M', '力'), - (0x2F13, 'M', '勹'), - (0x2F14, 'M', '匕'), - (0x2F15, 'M', '匚'), - ] - -def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F16, 'M', '匸'), - (0x2F17, 'M', '十'), - (0x2F18, 'M', '卜'), - (0x2F19, 'M', '卩'), - (0x2F1A, 'M', '厂'), - (0x2F1B, 'M', '厶'), - (0x2F1C, 'M', '又'), - (0x2F1D, 'M', '口'), - (0x2F1E, 'M', '囗'), - (0x2F1F, 'M', '土'), - (0x2F20, 'M', '士'), - (0x2F21, 'M', '夂'), - (0x2F22, 'M', '夊'), - (0x2F23, 'M', '夕'), - (0x2F24, 'M', '大'), - (0x2F25, 'M', '女'), - (0x2F26, 'M', '子'), - (0x2F27, 'M', '宀'), - (0x2F28, 'M', '寸'), - (0x2F29, 'M', '小'), - (0x2F2A, 'M', '尢'), - (0x2F2B, 'M', '尸'), - (0x2F2C, 'M', '屮'), - (0x2F2D, 'M', '山'), - (0x2F2E, 'M', '巛'), - (0x2F2F, 'M', '工'), - (0x2F30, 'M', '己'), - (0x2F31, 'M', '巾'), - (0x2F32, 'M', '干'), - (0x2F33, 'M', '幺'), - (0x2F34, 'M', '广'), - (0x2F35, 'M', '廴'), - (0x2F36, 'M', '廾'), - (0x2F37, 'M', '弋'), - (0x2F38, 'M', '弓'), - (0x2F39, 'M', '彐'), - (0x2F3A, 'M', '彡'), - (0x2F3B, 'M', '彳'), - (0x2F3C, 'M', '心'), - (0x2F3D, 'M', '戈'), - (0x2F3E, 'M', '戶'), - (0x2F3F, 'M', '手'), - (0x2F40, 'M', '支'), - (0x2F41, 'M', '攴'), - (0x2F42, 'M', '文'), - (0x2F43, 'M', '斗'), - (0x2F44, 'M', '斤'), - (0x2F45, 'M', '方'), - (0x2F46, 'M', '无'), - (0x2F47, 'M', '日'), - (0x2F48, 'M', '曰'), - (0x2F49, 'M', '月'), - (0x2F4A, 'M', '木'), - (0x2F4B, 'M', '欠'), - (0x2F4C, 'M', '止'), - (0x2F4D, 'M', '歹'), - (0x2F4E, 'M', '殳'), - (0x2F4F, 'M', '毋'), - (0x2F50, 'M', '比'), - (0x2F51, 'M', '毛'), - (0x2F52, 'M', '氏'), - (0x2F53, 'M', '气'), - (0x2F54, 'M', '水'), - (0x2F55, 'M', '火'), - (0x2F56, 'M', '爪'), - (0x2F57, 'M', '父'), - (0x2F58, 'M', '爻'), - (0x2F59, 'M', '爿'), - (0x2F5A, 'M', '片'), - (0x2F5B, 'M', '牙'), - (0x2F5C, 'M', '牛'), - (0x2F5D, 'M', '犬'), - (0x2F5E, 'M', '玄'), - (0x2F5F, 'M', '玉'), - (0x2F60, 'M', '瓜'), - (0x2F61, 'M', '瓦'), - (0x2F62, 'M', '甘'), - (0x2F63, 'M', '生'), - (0x2F64, 'M', '用'), - (0x2F65, 'M', '田'), - (0x2F66, 'M', '疋'), - (0x2F67, 'M', '疒'), - (0x2F68, 'M', '癶'), - (0x2F69, 'M', '白'), - (0x2F6A, 'M', '皮'), - (0x2F6B, 'M', '皿'), - (0x2F6C, 'M', '目'), - (0x2F6D, 'M', '矛'), - (0x2F6E, 'M', '矢'), - (0x2F6F, 'M', '石'), - (0x2F70, 'M', '示'), - (0x2F71, 'M', '禸'), - (0x2F72, 'M', '禾'), - (0x2F73, 'M', '穴'), - (0x2F74, 'M', '立'), - (0x2F75, 'M', '竹'), - (0x2F76, 'M', '米'), - (0x2F77, 'M', '糸'), - (0x2F78, 'M', '缶'), - (0x2F79, 'M', '网'), - ] - -def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F7A, 'M', '羊'), - (0x2F7B, 'M', '羽'), - (0x2F7C, 'M', '老'), - (0x2F7D, 'M', '而'), - (0x2F7E, 'M', '耒'), - (0x2F7F, 'M', '耳'), - (0x2F80, 'M', '聿'), - (0x2F81, 'M', '肉'), - (0x2F82, 'M', '臣'), - (0x2F83, 'M', '自'), - (0x2F84, 'M', '至'), - (0x2F85, 'M', '臼'), - (0x2F86, 'M', '舌'), - (0x2F87, 'M', '舛'), - (0x2F88, 'M', '舟'), - (0x2F89, 'M', '艮'), - (0x2F8A, 'M', '色'), - (0x2F8B, 'M', '艸'), - (0x2F8C, 'M', '虍'), - (0x2F8D, 'M', '虫'), - (0x2F8E, 'M', '血'), - (0x2F8F, 'M', '行'), - (0x2F90, 'M', '衣'), - (0x2F91, 'M', '襾'), - (0x2F92, 'M', '見'), - (0x2F93, 'M', '角'), - (0x2F94, 'M', '言'), - (0x2F95, 'M', '谷'), - (0x2F96, 'M', '豆'), - (0x2F97, 'M', '豕'), - (0x2F98, 'M', '豸'), - (0x2F99, 'M', '貝'), - (0x2F9A, 'M', '赤'), - (0x2F9B, 'M', '走'), - (0x2F9C, 'M', '足'), - (0x2F9D, 'M', '身'), - (0x2F9E, 'M', '車'), - (0x2F9F, 'M', '辛'), - (0x2FA0, 'M', '辰'), - (0x2FA1, 'M', '辵'), - (0x2FA2, 'M', '邑'), - (0x2FA3, 'M', '酉'), - (0x2FA4, 'M', '釆'), - (0x2FA5, 'M', '里'), - (0x2FA6, 'M', '金'), - (0x2FA7, 'M', '長'), - (0x2FA8, 'M', '門'), - (0x2FA9, 'M', '阜'), - (0x2FAA, 'M', '隶'), - (0x2FAB, 'M', '隹'), - (0x2FAC, 'M', '雨'), - (0x2FAD, 'M', '靑'), - (0x2FAE, 'M', '非'), - (0x2FAF, 'M', '面'), - (0x2FB0, 'M', '革'), - (0x2FB1, 'M', '韋'), - (0x2FB2, 'M', '韭'), - (0x2FB3, 'M', '音'), - (0x2FB4, 'M', '頁'), - (0x2FB5, 'M', '風'), - (0x2FB6, 'M', '飛'), - (0x2FB7, 'M', '食'), - (0x2FB8, 'M', '首'), - (0x2FB9, 'M', '香'), - (0x2FBA, 'M', '馬'), - (0x2FBB, 'M', '骨'), - (0x2FBC, 'M', '高'), - (0x2FBD, 'M', '髟'), - (0x2FBE, 'M', '鬥'), - (0x2FBF, 'M', '鬯'), - (0x2FC0, 'M', '鬲'), - (0x2FC1, 'M', '鬼'), - (0x2FC2, 'M', '魚'), - (0x2FC3, 'M', '鳥'), - (0x2FC4, 'M', '鹵'), - (0x2FC5, 'M', '鹿'), - (0x2FC6, 'M', '麥'), - (0x2FC7, 'M', '麻'), - (0x2FC8, 'M', '黃'), - (0x2FC9, 'M', '黍'), - (0x2FCA, 'M', '黑'), - (0x2FCB, 'M', '黹'), - (0x2FCC, 'M', '黽'), - (0x2FCD, 'M', '鼎'), - (0x2FCE, 'M', '鼓'), - (0x2FCF, 'M', '鼠'), - (0x2FD0, 'M', '鼻'), - (0x2FD1, 'M', '齊'), - (0x2FD2, 'M', '齒'), - (0x2FD3, 'M', '龍'), - (0x2FD4, 'M', '龜'), - (0x2FD5, 'M', '龠'), - (0x2FD6, 'X'), - (0x3000, '3', ' '), - (0x3001, 'V'), - (0x3002, 'M', '.'), - (0x3003, 'V'), - (0x3036, 'M', '〒'), - (0x3037, 'V'), - (0x3038, 'M', '十'), - ] - -def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3039, 'M', '卄'), - (0x303A, 'M', '卅'), - (0x303B, 'V'), - (0x3040, 'X'), - (0x3041, 'V'), - (0x3097, 'X'), - (0x3099, 'V'), - (0x309B, '3', ' ゙'), - (0x309C, '3', ' ゚'), - (0x309D, 'V'), - (0x309F, 'M', 'より'), - (0x30A0, 'V'), - (0x30FF, 'M', 'コト'), - (0x3100, 'X'), - (0x3105, 'V'), - (0x3130, 'X'), - (0x3131, 'M', 'ᄀ'), - (0x3132, 'M', 'ᄁ'), - (0x3133, 'M', 'ᆪ'), - (0x3134, 'M', 'ᄂ'), - (0x3135, 'M', 'ᆬ'), - (0x3136, 'M', 'ᆭ'), - (0x3137, 'M', 'ᄃ'), - (0x3138, 'M', 'ᄄ'), - (0x3139, 'M', 'ᄅ'), - (0x313A, 'M', 'ᆰ'), - (0x313B, 'M', 'ᆱ'), - (0x313C, 'M', 'ᆲ'), - (0x313D, 'M', 'ᆳ'), - (0x313E, 'M', 'ᆴ'), - (0x313F, 'M', 'ᆵ'), - (0x3140, 'M', 'ᄚ'), - (0x3141, 'M', 'ᄆ'), - (0x3142, 'M', 'ᄇ'), - (0x3143, 'M', 'ᄈ'), - (0x3144, 'M', 'ᄡ'), - (0x3145, 'M', 'ᄉ'), - (0x3146, 'M', 'ᄊ'), - (0x3147, 'M', 'ᄋ'), - (0x3148, 'M', 'ᄌ'), - (0x3149, 'M', 'ᄍ'), - (0x314A, 'M', 'ᄎ'), - (0x314B, 'M', 'ᄏ'), - (0x314C, 'M', 'ᄐ'), - (0x314D, 'M', 'ᄑ'), - (0x314E, 'M', 'ᄒ'), - (0x314F, 'M', 'ᅡ'), - (0x3150, 'M', 'ᅢ'), - (0x3151, 'M', 'ᅣ'), - (0x3152, 'M', 'ᅤ'), - (0x3153, 'M', 'ᅥ'), - (0x3154, 'M', 'ᅦ'), - (0x3155, 'M', 'ᅧ'), - (0x3156, 'M', 'ᅨ'), - (0x3157, 'M', 'ᅩ'), - (0x3158, 'M', 'ᅪ'), - (0x3159, 'M', 'ᅫ'), - (0x315A, 'M', 'ᅬ'), - (0x315B, 'M', 'ᅭ'), - (0x315C, 'M', 'ᅮ'), - (0x315D, 'M', 'ᅯ'), - (0x315E, 'M', 'ᅰ'), - (0x315F, 'M', 'ᅱ'), - (0x3160, 'M', 'ᅲ'), - (0x3161, 'M', 'ᅳ'), - (0x3162, 'M', 'ᅴ'), - (0x3163, 'M', 'ᅵ'), - (0x3164, 'X'), - (0x3165, 'M', 'ᄔ'), - (0x3166, 'M', 'ᄕ'), - (0x3167, 'M', 'ᇇ'), - (0x3168, 'M', 'ᇈ'), - (0x3169, 'M', 'ᇌ'), - (0x316A, 'M', 'ᇎ'), - (0x316B, 'M', 'ᇓ'), - (0x316C, 'M', 'ᇗ'), - (0x316D, 'M', 'ᇙ'), - (0x316E, 'M', 'ᄜ'), - (0x316F, 'M', 'ᇝ'), - (0x3170, 'M', 'ᇟ'), - (0x3171, 'M', 'ᄝ'), - (0x3172, 'M', 'ᄞ'), - (0x3173, 'M', 'ᄠ'), - (0x3174, 'M', 'ᄢ'), - (0x3175, 'M', 'ᄣ'), - (0x3176, 'M', 'ᄧ'), - (0x3177, 'M', 'ᄩ'), - (0x3178, 'M', 'ᄫ'), - (0x3179, 'M', 'ᄬ'), - (0x317A, 'M', 'ᄭ'), - (0x317B, 'M', 'ᄮ'), - (0x317C, 'M', 'ᄯ'), - (0x317D, 'M', 'ᄲ'), - (0x317E, 'M', 'ᄶ'), - (0x317F, 'M', 'ᅀ'), - (0x3180, 'M', 'ᅇ'), - (0x3181, 'M', 'ᅌ'), - (0x3182, 'M', 'ᇱ'), - (0x3183, 'M', 'ᇲ'), - (0x3184, 'M', 'ᅗ'), - ] - -def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3185, 'M', 'ᅘ'), - (0x3186, 'M', 'ᅙ'), - (0x3187, 'M', 'ᆄ'), - (0x3188, 'M', 'ᆅ'), - (0x3189, 'M', 'ᆈ'), - (0x318A, 'M', 'ᆑ'), - (0x318B, 'M', 'ᆒ'), - (0x318C, 'M', 'ᆔ'), - (0x318D, 'M', 'ᆞ'), - (0x318E, 'M', 'ᆡ'), - (0x318F, 'X'), - (0x3190, 'V'), - (0x3192, 'M', '一'), - (0x3193, 'M', '二'), - (0x3194, 'M', '三'), - (0x3195, 'M', '四'), - (0x3196, 'M', '上'), - (0x3197, 'M', '中'), - (0x3198, 'M', '下'), - (0x3199, 'M', '甲'), - (0x319A, 'M', '乙'), - (0x319B, 'M', '丙'), - (0x319C, 'M', '丁'), - (0x319D, 'M', '天'), - (0x319E, 'M', '地'), - (0x319F, 'M', '人'), - (0x31A0, 'V'), - (0x31E4, 'X'), - (0x31F0, 'V'), - (0x3200, '3', '(ᄀ)'), - (0x3201, '3', '(ᄂ)'), - (0x3202, '3', '(ᄃ)'), - (0x3203, '3', '(ᄅ)'), - (0x3204, '3', '(ᄆ)'), - (0x3205, '3', '(ᄇ)'), - (0x3206, '3', '(ᄉ)'), - (0x3207, '3', '(ᄋ)'), - (0x3208, '3', '(ᄌ)'), - (0x3209, '3', '(ᄎ)'), - (0x320A, '3', '(ᄏ)'), - (0x320B, '3', '(ᄐ)'), - (0x320C, '3', '(ᄑ)'), - (0x320D, '3', '(ᄒ)'), - (0x320E, '3', '(가)'), - (0x320F, '3', '(나)'), - (0x3210, '3', '(다)'), - (0x3211, '3', '(라)'), - (0x3212, '3', '(마)'), - (0x3213, '3', '(바)'), - (0x3214, '3', '(사)'), - (0x3215, '3', '(아)'), - (0x3216, '3', '(자)'), - (0x3217, '3', '(차)'), - (0x3218, '3', '(카)'), - (0x3219, '3', '(타)'), - (0x321A, '3', '(파)'), - (0x321B, '3', '(하)'), - (0x321C, '3', '(주)'), - (0x321D, '3', '(오전)'), - (0x321E, '3', '(오후)'), - (0x321F, 'X'), - (0x3220, '3', '(一)'), - (0x3221, '3', '(二)'), - (0x3222, '3', '(三)'), - (0x3223, '3', '(四)'), - (0x3224, '3', '(五)'), - (0x3225, '3', '(六)'), - (0x3226, '3', '(七)'), - (0x3227, '3', '(八)'), - (0x3228, '3', '(九)'), - (0x3229, '3', '(十)'), - (0x322A, '3', '(月)'), - (0x322B, '3', '(火)'), - (0x322C, '3', '(水)'), - (0x322D, '3', '(木)'), - (0x322E, '3', '(金)'), - (0x322F, '3', '(土)'), - (0x3230, '3', '(日)'), - (0x3231, '3', '(株)'), - (0x3232, '3', '(有)'), - (0x3233, '3', '(社)'), - (0x3234, '3', '(名)'), - (0x3235, '3', '(特)'), - (0x3236, '3', '(財)'), - (0x3237, '3', '(祝)'), - (0x3238, '3', '(労)'), - (0x3239, '3', '(代)'), - (0x323A, '3', '(呼)'), - (0x323B, '3', '(学)'), - (0x323C, '3', '(監)'), - (0x323D, '3', '(企)'), - (0x323E, '3', '(資)'), - (0x323F, '3', '(協)'), - (0x3240, '3', '(祭)'), - (0x3241, '3', '(休)'), - (0x3242, '3', '(自)'), - (0x3243, '3', '(至)'), - (0x3244, 'M', '問'), - (0x3245, 'M', '幼'), - (0x3246, 'M', '文'), - ] - -def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3247, 'M', '箏'), - (0x3248, 'V'), - (0x3250, 'M', 'pte'), - (0x3251, 'M', '21'), - (0x3252, 'M', '22'), - (0x3253, 'M', '23'), - (0x3254, 'M', '24'), - (0x3255, 'M', '25'), - (0x3256, 'M', '26'), - (0x3257, 'M', '27'), - (0x3258, 'M', '28'), - (0x3259, 'M', '29'), - (0x325A, 'M', '30'), - (0x325B, 'M', '31'), - (0x325C, 'M', '32'), - (0x325D, 'M', '33'), - (0x325E, 'M', '34'), - (0x325F, 'M', '35'), - (0x3260, 'M', 'ᄀ'), - (0x3261, 'M', 'ᄂ'), - (0x3262, 'M', 'ᄃ'), - (0x3263, 'M', 'ᄅ'), - (0x3264, 'M', 'ᄆ'), - (0x3265, 'M', 'ᄇ'), - (0x3266, 'M', 'ᄉ'), - (0x3267, 'M', 'ᄋ'), - (0x3268, 'M', 'ᄌ'), - (0x3269, 'M', 'ᄎ'), - (0x326A, 'M', 'ᄏ'), - (0x326B, 'M', 'ᄐ'), - (0x326C, 'M', 'ᄑ'), - (0x326D, 'M', 'ᄒ'), - (0x326E, 'M', '가'), - (0x326F, 'M', '나'), - (0x3270, 'M', '다'), - (0x3271, 'M', '라'), - (0x3272, 'M', '마'), - (0x3273, 'M', '바'), - (0x3274, 'M', '사'), - (0x3275, 'M', '아'), - (0x3276, 'M', '자'), - (0x3277, 'M', '차'), - (0x3278, 'M', '카'), - (0x3279, 'M', '타'), - (0x327A, 'M', '파'), - (0x327B, 'M', '하'), - (0x327C, 'M', '참고'), - (0x327D, 'M', '주의'), - (0x327E, 'M', '우'), - (0x327F, 'V'), - (0x3280, 'M', '一'), - (0x3281, 'M', '二'), - (0x3282, 'M', '三'), - (0x3283, 'M', '四'), - (0x3284, 'M', '五'), - (0x3285, 'M', '六'), - (0x3286, 'M', '七'), - (0x3287, 'M', '八'), - (0x3288, 'M', '九'), - (0x3289, 'M', '十'), - (0x328A, 'M', '月'), - (0x328B, 'M', '火'), - (0x328C, 'M', '水'), - (0x328D, 'M', '木'), - (0x328E, 'M', '金'), - (0x328F, 'M', '土'), - (0x3290, 'M', '日'), - (0x3291, 'M', '株'), - (0x3292, 'M', '有'), - (0x3293, 'M', '社'), - (0x3294, 'M', '名'), - (0x3295, 'M', '特'), - (0x3296, 'M', '財'), - (0x3297, 'M', '祝'), - (0x3298, 'M', '労'), - (0x3299, 'M', '秘'), - (0x329A, 'M', '男'), - (0x329B, 'M', '女'), - (0x329C, 'M', '適'), - (0x329D, 'M', '優'), - (0x329E, 'M', '印'), - (0x329F, 'M', '注'), - (0x32A0, 'M', '項'), - (0x32A1, 'M', '休'), - (0x32A2, 'M', '写'), - (0x32A3, 'M', '正'), - (0x32A4, 'M', '上'), - (0x32A5, 'M', '中'), - (0x32A6, 'M', '下'), - (0x32A7, 'M', '左'), - (0x32A8, 'M', '右'), - (0x32A9, 'M', '医'), - (0x32AA, 'M', '宗'), - (0x32AB, 'M', '学'), - (0x32AC, 'M', '監'), - (0x32AD, 'M', '企'), - (0x32AE, 'M', '資'), - (0x32AF, 'M', '協'), - (0x32B0, 'M', '夜'), - (0x32B1, 'M', '36'), - ] - -def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x32B2, 'M', '37'), - (0x32B3, 'M', '38'), - (0x32B4, 'M', '39'), - (0x32B5, 'M', '40'), - (0x32B6, 'M', '41'), - (0x32B7, 'M', '42'), - (0x32B8, 'M', '43'), - (0x32B9, 'M', '44'), - (0x32BA, 'M', '45'), - (0x32BB, 'M', '46'), - (0x32BC, 'M', '47'), - (0x32BD, 'M', '48'), - (0x32BE, 'M', '49'), - (0x32BF, 'M', '50'), - (0x32C0, 'M', '1月'), - (0x32C1, 'M', '2月'), - (0x32C2, 'M', '3月'), - (0x32C3, 'M', '4月'), - (0x32C4, 'M', '5月'), - (0x32C5, 'M', '6月'), - (0x32C6, 'M', '7月'), - (0x32C7, 'M', '8月'), - (0x32C8, 'M', '9月'), - (0x32C9, 'M', '10月'), - (0x32CA, 'M', '11月'), - (0x32CB, 'M', '12月'), - (0x32CC, 'M', 'hg'), - (0x32CD, 'M', 'erg'), - (0x32CE, 'M', 'ev'), - (0x32CF, 'M', 'ltd'), - (0x32D0, 'M', 'ア'), - (0x32D1, 'M', 'イ'), - (0x32D2, 'M', 'ウ'), - (0x32D3, 'M', 'エ'), - (0x32D4, 'M', 'オ'), - (0x32D5, 'M', 'カ'), - (0x32D6, 'M', 'キ'), - (0x32D7, 'M', 'ク'), - (0x32D8, 'M', 'ケ'), - (0x32D9, 'M', 'コ'), - (0x32DA, 'M', 'サ'), - (0x32DB, 'M', 'シ'), - (0x32DC, 'M', 'ス'), - (0x32DD, 'M', 'セ'), - (0x32DE, 'M', 'ソ'), - (0x32DF, 'M', 'タ'), - (0x32E0, 'M', 'チ'), - (0x32E1, 'M', 'ツ'), - (0x32E2, 'M', 'テ'), - (0x32E3, 'M', 'ト'), - (0x32E4, 'M', 'ナ'), - (0x32E5, 'M', 'ニ'), - (0x32E6, 'M', 'ヌ'), - (0x32E7, 'M', 'ネ'), - (0x32E8, 'M', 'ノ'), - (0x32E9, 'M', 'ハ'), - (0x32EA, 'M', 'ヒ'), - (0x32EB, 'M', 'フ'), - (0x32EC, 'M', 'ヘ'), - (0x32ED, 'M', 'ホ'), - (0x32EE, 'M', 'マ'), - (0x32EF, 'M', 'ミ'), - (0x32F0, 'M', 'ム'), - (0x32F1, 'M', 'メ'), - (0x32F2, 'M', 'モ'), - (0x32F3, 'M', 'ヤ'), - (0x32F4, 'M', 'ユ'), - (0x32F5, 'M', 'ヨ'), - (0x32F6, 'M', 'ラ'), - (0x32F7, 'M', 'リ'), - (0x32F8, 'M', 'ル'), - (0x32F9, 'M', 'レ'), - (0x32FA, 'M', 'ロ'), - (0x32FB, 'M', 'ワ'), - (0x32FC, 'M', 'ヰ'), - (0x32FD, 'M', 'ヱ'), - (0x32FE, 'M', 'ヲ'), - (0x32FF, 'M', '令和'), - (0x3300, 'M', 'アパート'), - (0x3301, 'M', 'アルファ'), - (0x3302, 'M', 'アンペア'), - (0x3303, 'M', 'アール'), - (0x3304, 'M', 'イニング'), - (0x3305, 'M', 'インチ'), - (0x3306, 'M', 'ウォン'), - (0x3307, 'M', 'エスクード'), - (0x3308, 'M', 'エーカー'), - (0x3309, 'M', 'オンス'), - (0x330A, 'M', 'オーム'), - (0x330B, 'M', 'カイリ'), - (0x330C, 'M', 'カラット'), - (0x330D, 'M', 'カロリー'), - (0x330E, 'M', 'ガロン'), - (0x330F, 'M', 'ガンマ'), - (0x3310, 'M', 'ギガ'), - (0x3311, 'M', 'ギニー'), - (0x3312, 'M', 'キュリー'), - (0x3313, 'M', 'ギルダー'), - (0x3314, 'M', 'キロ'), - (0x3315, 'M', 'キログラム'), - ] - -def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3316, 'M', 'キロメートル'), - (0x3317, 'M', 'キロワット'), - (0x3318, 'M', 'グラム'), - (0x3319, 'M', 'グラムトン'), - (0x331A, 'M', 'クルゼイロ'), - (0x331B, 'M', 'クローネ'), - (0x331C, 'M', 'ケース'), - (0x331D, 'M', 'コルナ'), - (0x331E, 'M', 'コーポ'), - (0x331F, 'M', 'サイクル'), - (0x3320, 'M', 'サンチーム'), - (0x3321, 'M', 'シリング'), - (0x3322, 'M', 'センチ'), - (0x3323, 'M', 'セント'), - (0x3324, 'M', 'ダース'), - (0x3325, 'M', 'デシ'), - (0x3326, 'M', 'ドル'), - (0x3327, 'M', 'トン'), - (0x3328, 'M', 'ナノ'), - (0x3329, 'M', 'ノット'), - (0x332A, 'M', 'ハイツ'), - (0x332B, 'M', 'パーセント'), - (0x332C, 'M', 'パーツ'), - (0x332D, 'M', 'バーレル'), - (0x332E, 'M', 'ピアストル'), - (0x332F, 'M', 'ピクル'), - (0x3330, 'M', 'ピコ'), - (0x3331, 'M', 'ビル'), - (0x3332, 'M', 'ファラッド'), - (0x3333, 'M', 'フィート'), - (0x3334, 'M', 'ブッシェル'), - (0x3335, 'M', 'フラン'), - (0x3336, 'M', 'ヘクタール'), - (0x3337, 'M', 'ペソ'), - (0x3338, 'M', 'ペニヒ'), - (0x3339, 'M', 'ヘルツ'), - (0x333A, 'M', 'ペンス'), - (0x333B, 'M', 'ページ'), - (0x333C, 'M', 'ベータ'), - (0x333D, 'M', 'ポイント'), - (0x333E, 'M', 'ボルト'), - (0x333F, 'M', 'ホン'), - (0x3340, 'M', 'ポンド'), - (0x3341, 'M', 'ホール'), - (0x3342, 'M', 'ホーン'), - (0x3343, 'M', 'マイクロ'), - (0x3344, 'M', 'マイル'), - (0x3345, 'M', 'マッハ'), - (0x3346, 'M', 'マルク'), - (0x3347, 'M', 'マンション'), - (0x3348, 'M', 'ミクロン'), - (0x3349, 'M', 'ミリ'), - (0x334A, 'M', 'ミリバール'), - (0x334B, 'M', 'メガ'), - (0x334C, 'M', 'メガトン'), - (0x334D, 'M', 'メートル'), - (0x334E, 'M', 'ヤード'), - (0x334F, 'M', 'ヤール'), - (0x3350, 'M', 'ユアン'), - (0x3351, 'M', 'リットル'), - (0x3352, 'M', 'リラ'), - (0x3353, 'M', 'ルピー'), - (0x3354, 'M', 'ルーブル'), - (0x3355, 'M', 'レム'), - (0x3356, 'M', 'レントゲン'), - (0x3357, 'M', 'ワット'), - (0x3358, 'M', '0点'), - (0x3359, 'M', '1点'), - (0x335A, 'M', '2点'), - (0x335B, 'M', '3点'), - (0x335C, 'M', '4点'), - (0x335D, 'M', '5点'), - (0x335E, 'M', '6点'), - (0x335F, 'M', '7点'), - (0x3360, 'M', '8点'), - (0x3361, 'M', '9点'), - (0x3362, 'M', '10点'), - (0x3363, 'M', '11点'), - (0x3364, 'M', '12点'), - (0x3365, 'M', '13点'), - (0x3366, 'M', '14点'), - (0x3367, 'M', '15点'), - (0x3368, 'M', '16点'), - (0x3369, 'M', '17点'), - (0x336A, 'M', '18点'), - (0x336B, 'M', '19点'), - (0x336C, 'M', '20点'), - (0x336D, 'M', '21点'), - (0x336E, 'M', '22点'), - (0x336F, 'M', '23点'), - (0x3370, 'M', '24点'), - (0x3371, 'M', 'hpa'), - (0x3372, 'M', 'da'), - (0x3373, 'M', 'au'), - (0x3374, 'M', 'bar'), - (0x3375, 'M', 'ov'), - (0x3376, 'M', 'pc'), - (0x3377, 'M', 'dm'), - (0x3378, 'M', 'dm2'), - (0x3379, 'M', 'dm3'), - ] - -def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x337A, 'M', 'iu'), - (0x337B, 'M', '平成'), - (0x337C, 'M', '昭和'), - (0x337D, 'M', '大正'), - (0x337E, 'M', '明治'), - (0x337F, 'M', '株式会社'), - (0x3380, 'M', 'pa'), - (0x3381, 'M', 'na'), - (0x3382, 'M', 'μa'), - (0x3383, 'M', 'ma'), - (0x3384, 'M', 'ka'), - (0x3385, 'M', 'kb'), - (0x3386, 'M', 'mb'), - (0x3387, 'M', 'gb'), - (0x3388, 'M', 'cal'), - (0x3389, 'M', 'kcal'), - (0x338A, 'M', 'pf'), - (0x338B, 'M', 'nf'), - (0x338C, 'M', 'μf'), - (0x338D, 'M', 'μg'), - (0x338E, 'M', 'mg'), - (0x338F, 'M', 'kg'), - (0x3390, 'M', 'hz'), - (0x3391, 'M', 'khz'), - (0x3392, 'M', 'mhz'), - (0x3393, 'M', 'ghz'), - (0x3394, 'M', 'thz'), - (0x3395, 'M', 'μl'), - (0x3396, 'M', 'ml'), - (0x3397, 'M', 'dl'), - (0x3398, 'M', 'kl'), - (0x3399, 'M', 'fm'), - (0x339A, 'M', 'nm'), - (0x339B, 'M', 'μm'), - (0x339C, 'M', 'mm'), - (0x339D, 'M', 'cm'), - (0x339E, 'M', 'km'), - (0x339F, 'M', 'mm2'), - (0x33A0, 'M', 'cm2'), - (0x33A1, 'M', 'm2'), - (0x33A2, 'M', 'km2'), - (0x33A3, 'M', 'mm3'), - (0x33A4, 'M', 'cm3'), - (0x33A5, 'M', 'm3'), - (0x33A6, 'M', 'km3'), - (0x33A7, 'M', 'm∕s'), - (0x33A8, 'M', 'm∕s2'), - (0x33A9, 'M', 'pa'), - (0x33AA, 'M', 'kpa'), - (0x33AB, 'M', 'mpa'), - (0x33AC, 'M', 'gpa'), - (0x33AD, 'M', 'rad'), - (0x33AE, 'M', 'rad∕s'), - (0x33AF, 'M', 'rad∕s2'), - (0x33B0, 'M', 'ps'), - (0x33B1, 'M', 'ns'), - (0x33B2, 'M', 'μs'), - (0x33B3, 'M', 'ms'), - (0x33B4, 'M', 'pv'), - (0x33B5, 'M', 'nv'), - (0x33B6, 'M', 'μv'), - (0x33B7, 'M', 'mv'), - (0x33B8, 'M', 'kv'), - (0x33B9, 'M', 'mv'), - (0x33BA, 'M', 'pw'), - (0x33BB, 'M', 'nw'), - (0x33BC, 'M', 'μw'), - (0x33BD, 'M', 'mw'), - (0x33BE, 'M', 'kw'), - (0x33BF, 'M', 'mw'), - (0x33C0, 'M', 'kω'), - (0x33C1, 'M', 'mω'), - (0x33C2, 'X'), - (0x33C3, 'M', 'bq'), - (0x33C4, 'M', 'cc'), - (0x33C5, 'M', 'cd'), - (0x33C6, 'M', 'c∕kg'), - (0x33C7, 'X'), - (0x33C8, 'M', 'db'), - (0x33C9, 'M', 'gy'), - (0x33CA, 'M', 'ha'), - (0x33CB, 'M', 'hp'), - (0x33CC, 'M', 'in'), - (0x33CD, 'M', 'kk'), - (0x33CE, 'M', 'km'), - (0x33CF, 'M', 'kt'), - (0x33D0, 'M', 'lm'), - (0x33D1, 'M', 'ln'), - (0x33D2, 'M', 'log'), - (0x33D3, 'M', 'lx'), - (0x33D4, 'M', 'mb'), - (0x33D5, 'M', 'mil'), - (0x33D6, 'M', 'mol'), - (0x33D7, 'M', 'ph'), - (0x33D8, 'X'), - (0x33D9, 'M', 'ppm'), - (0x33DA, 'M', 'pr'), - (0x33DB, 'M', 'sr'), - (0x33DC, 'M', 'sv'), - (0x33DD, 'M', 'wb'), - ] - -def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x33DE, 'M', 'v∕m'), - (0x33DF, 'M', 'a∕m'), - (0x33E0, 'M', '1日'), - (0x33E1, 'M', '2日'), - (0x33E2, 'M', '3日'), - (0x33E3, 'M', '4日'), - (0x33E4, 'M', '5日'), - (0x33E5, 'M', '6日'), - (0x33E6, 'M', '7日'), - (0x33E7, 'M', '8日'), - (0x33E8, 'M', '9日'), - (0x33E9, 'M', '10日'), - (0x33EA, 'M', '11日'), - (0x33EB, 'M', '12日'), - (0x33EC, 'M', '13日'), - (0x33ED, 'M', '14日'), - (0x33EE, 'M', '15日'), - (0x33EF, 'M', '16日'), - (0x33F0, 'M', '17日'), - (0x33F1, 'M', '18日'), - (0x33F2, 'M', '19日'), - (0x33F3, 'M', '20日'), - (0x33F4, 'M', '21日'), - (0x33F5, 'M', '22日'), - (0x33F6, 'M', '23日'), - (0x33F7, 'M', '24日'), - (0x33F8, 'M', '25日'), - (0x33F9, 'M', '26日'), - (0x33FA, 'M', '27日'), - (0x33FB, 'M', '28日'), - (0x33FC, 'M', '29日'), - (0x33FD, 'M', '30日'), - (0x33FE, 'M', '31日'), - (0x33FF, 'M', 'gal'), - (0x3400, 'V'), - (0xA48D, 'X'), - (0xA490, 'V'), - (0xA4C7, 'X'), - (0xA4D0, 'V'), - (0xA62C, 'X'), - (0xA640, 'M', 'ꙁ'), - (0xA641, 'V'), - (0xA642, 'M', 'ꙃ'), - (0xA643, 'V'), - (0xA644, 'M', 'ꙅ'), - (0xA645, 'V'), - (0xA646, 'M', 'ꙇ'), - (0xA647, 'V'), - (0xA648, 'M', 'ꙉ'), - (0xA649, 'V'), - (0xA64A, 'M', 'ꙋ'), - (0xA64B, 'V'), - (0xA64C, 'M', 'ꙍ'), - (0xA64D, 'V'), - (0xA64E, 'M', 'ꙏ'), - (0xA64F, 'V'), - (0xA650, 'M', 'ꙑ'), - (0xA651, 'V'), - (0xA652, 'M', 'ꙓ'), - (0xA653, 'V'), - (0xA654, 'M', 'ꙕ'), - (0xA655, 'V'), - (0xA656, 'M', 'ꙗ'), - (0xA657, 'V'), - (0xA658, 'M', 'ꙙ'), - (0xA659, 'V'), - (0xA65A, 'M', 'ꙛ'), - (0xA65B, 'V'), - (0xA65C, 'M', 'ꙝ'), - (0xA65D, 'V'), - (0xA65E, 'M', 'ꙟ'), - (0xA65F, 'V'), - (0xA660, 'M', 'ꙡ'), - (0xA661, 'V'), - (0xA662, 'M', 'ꙣ'), - (0xA663, 'V'), - (0xA664, 'M', 'ꙥ'), - (0xA665, 'V'), - (0xA666, 'M', 'ꙧ'), - (0xA667, 'V'), - (0xA668, 'M', 'ꙩ'), - (0xA669, 'V'), - (0xA66A, 'M', 'ꙫ'), - (0xA66B, 'V'), - (0xA66C, 'M', 'ꙭ'), - (0xA66D, 'V'), - (0xA680, 'M', 'ꚁ'), - (0xA681, 'V'), - (0xA682, 'M', 'ꚃ'), - (0xA683, 'V'), - (0xA684, 'M', 'ꚅ'), - (0xA685, 'V'), - (0xA686, 'M', 'ꚇ'), - (0xA687, 'V'), - (0xA688, 'M', 'ꚉ'), - (0xA689, 'V'), - (0xA68A, 'M', 'ꚋ'), - (0xA68B, 'V'), - (0xA68C, 'M', 'ꚍ'), - (0xA68D, 'V'), - ] - -def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA68E, 'M', 'ꚏ'), - (0xA68F, 'V'), - (0xA690, 'M', 'ꚑ'), - (0xA691, 'V'), - (0xA692, 'M', 'ꚓ'), - (0xA693, 'V'), - (0xA694, 'M', 'ꚕ'), - (0xA695, 'V'), - (0xA696, 'M', 'ꚗ'), - (0xA697, 'V'), - (0xA698, 'M', 'ꚙ'), - (0xA699, 'V'), - (0xA69A, 'M', 'ꚛ'), - (0xA69B, 'V'), - (0xA69C, 'M', 'ъ'), - (0xA69D, 'M', 'ь'), - (0xA69E, 'V'), - (0xA6F8, 'X'), - (0xA700, 'V'), - (0xA722, 'M', 'ꜣ'), - (0xA723, 'V'), - (0xA724, 'M', 'ꜥ'), - (0xA725, 'V'), - (0xA726, 'M', 'ꜧ'), - (0xA727, 'V'), - (0xA728, 'M', 'ꜩ'), - (0xA729, 'V'), - (0xA72A, 'M', 'ꜫ'), - (0xA72B, 'V'), - (0xA72C, 'M', 'ꜭ'), - (0xA72D, 'V'), - (0xA72E, 'M', 'ꜯ'), - (0xA72F, 'V'), - (0xA732, 'M', 'ꜳ'), - (0xA733, 'V'), - (0xA734, 'M', 'ꜵ'), - (0xA735, 'V'), - (0xA736, 'M', 'ꜷ'), - (0xA737, 'V'), - (0xA738, 'M', 'ꜹ'), - (0xA739, 'V'), - (0xA73A, 'M', 'ꜻ'), - (0xA73B, 'V'), - (0xA73C, 'M', 'ꜽ'), - (0xA73D, 'V'), - (0xA73E, 'M', 'ꜿ'), - (0xA73F, 'V'), - (0xA740, 'M', 'ꝁ'), - (0xA741, 'V'), - (0xA742, 'M', 'ꝃ'), - (0xA743, 'V'), - (0xA744, 'M', 'ꝅ'), - (0xA745, 'V'), - (0xA746, 'M', 'ꝇ'), - (0xA747, 'V'), - (0xA748, 'M', 'ꝉ'), - (0xA749, 'V'), - (0xA74A, 'M', 'ꝋ'), - (0xA74B, 'V'), - (0xA74C, 'M', 'ꝍ'), - (0xA74D, 'V'), - (0xA74E, 'M', 'ꝏ'), - (0xA74F, 'V'), - (0xA750, 'M', 'ꝑ'), - (0xA751, 'V'), - (0xA752, 'M', 'ꝓ'), - (0xA753, 'V'), - (0xA754, 'M', 'ꝕ'), - (0xA755, 'V'), - (0xA756, 'M', 'ꝗ'), - (0xA757, 'V'), - (0xA758, 'M', 'ꝙ'), - (0xA759, 'V'), - (0xA75A, 'M', 'ꝛ'), - (0xA75B, 'V'), - (0xA75C, 'M', 'ꝝ'), - (0xA75D, 'V'), - (0xA75E, 'M', 'ꝟ'), - (0xA75F, 'V'), - (0xA760, 'M', 'ꝡ'), - (0xA761, 'V'), - (0xA762, 'M', 'ꝣ'), - (0xA763, 'V'), - (0xA764, 'M', 'ꝥ'), - (0xA765, 'V'), - (0xA766, 'M', 'ꝧ'), - (0xA767, 'V'), - (0xA768, 'M', 'ꝩ'), - (0xA769, 'V'), - (0xA76A, 'M', 'ꝫ'), - (0xA76B, 'V'), - (0xA76C, 'M', 'ꝭ'), - (0xA76D, 'V'), - (0xA76E, 'M', 'ꝯ'), - (0xA76F, 'V'), - (0xA770, 'M', 'ꝯ'), - (0xA771, 'V'), - (0xA779, 'M', 'ꝺ'), - (0xA77A, 'V'), - (0xA77B, 'M', 'ꝼ'), - ] - -def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA77C, 'V'), - (0xA77D, 'M', 'ᵹ'), - (0xA77E, 'M', 'ꝿ'), - (0xA77F, 'V'), - (0xA780, 'M', 'ꞁ'), - (0xA781, 'V'), - (0xA782, 'M', 'ꞃ'), - (0xA783, 'V'), - (0xA784, 'M', 'ꞅ'), - (0xA785, 'V'), - (0xA786, 'M', 'ꞇ'), - (0xA787, 'V'), - (0xA78B, 'M', 'ꞌ'), - (0xA78C, 'V'), - (0xA78D, 'M', 'ɥ'), - (0xA78E, 'V'), - (0xA790, 'M', 'ꞑ'), - (0xA791, 'V'), - (0xA792, 'M', 'ꞓ'), - (0xA793, 'V'), - (0xA796, 'M', 'ꞗ'), - (0xA797, 'V'), - (0xA798, 'M', 'ꞙ'), - (0xA799, 'V'), - (0xA79A, 'M', 'ꞛ'), - (0xA79B, 'V'), - (0xA79C, 'M', 'ꞝ'), - (0xA79D, 'V'), - (0xA79E, 'M', 'ꞟ'), - (0xA79F, 'V'), - (0xA7A0, 'M', 'ꞡ'), - (0xA7A1, 'V'), - (0xA7A2, 'M', 'ꞣ'), - (0xA7A3, 'V'), - (0xA7A4, 'M', 'ꞥ'), - (0xA7A5, 'V'), - (0xA7A6, 'M', 'ꞧ'), - (0xA7A7, 'V'), - (0xA7A8, 'M', 'ꞩ'), - (0xA7A9, 'V'), - (0xA7AA, 'M', 'ɦ'), - (0xA7AB, 'M', 'ɜ'), - (0xA7AC, 'M', 'ɡ'), - (0xA7AD, 'M', 'ɬ'), - (0xA7AE, 'M', 'ɪ'), - (0xA7AF, 'V'), - (0xA7B0, 'M', 'ʞ'), - (0xA7B1, 'M', 'ʇ'), - (0xA7B2, 'M', 'ʝ'), - (0xA7B3, 'M', 'ꭓ'), - (0xA7B4, 'M', 'ꞵ'), - (0xA7B5, 'V'), - (0xA7B6, 'M', 'ꞷ'), - (0xA7B7, 'V'), - (0xA7B8, 'M', 'ꞹ'), - (0xA7B9, 'V'), - (0xA7BA, 'M', 'ꞻ'), - (0xA7BB, 'V'), - (0xA7BC, 'M', 'ꞽ'), - (0xA7BD, 'V'), - (0xA7BE, 'M', 'ꞿ'), - (0xA7BF, 'V'), - (0xA7C0, 'M', 'ꟁ'), - (0xA7C1, 'V'), - (0xA7C2, 'M', 'ꟃ'), - (0xA7C3, 'V'), - (0xA7C4, 'M', 'ꞔ'), - (0xA7C5, 'M', 'ʂ'), - (0xA7C6, 'M', 'ᶎ'), - (0xA7C7, 'M', 'ꟈ'), - (0xA7C8, 'V'), - (0xA7C9, 'M', 'ꟊ'), - (0xA7CA, 'V'), - (0xA7CB, 'X'), - (0xA7D0, 'M', 'ꟑ'), - (0xA7D1, 'V'), - (0xA7D2, 'X'), - (0xA7D3, 'V'), - (0xA7D4, 'X'), - (0xA7D5, 'V'), - (0xA7D6, 'M', 'ꟗ'), - (0xA7D7, 'V'), - (0xA7D8, 'M', 'ꟙ'), - (0xA7D9, 'V'), - (0xA7DA, 'X'), - (0xA7F2, 'M', 'c'), - (0xA7F3, 'M', 'f'), - (0xA7F4, 'M', 'q'), - (0xA7F5, 'M', 'ꟶ'), - (0xA7F6, 'V'), - (0xA7F8, 'M', 'ħ'), - (0xA7F9, 'M', 'œ'), - (0xA7FA, 'V'), - (0xA82D, 'X'), - (0xA830, 'V'), - (0xA83A, 'X'), - (0xA840, 'V'), - (0xA878, 'X'), - (0xA880, 'V'), - (0xA8C6, 'X'), - ] - -def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA8CE, 'V'), - (0xA8DA, 'X'), - (0xA8E0, 'V'), - (0xA954, 'X'), - (0xA95F, 'V'), - (0xA97D, 'X'), - (0xA980, 'V'), - (0xA9CE, 'X'), - (0xA9CF, 'V'), - (0xA9DA, 'X'), - (0xA9DE, 'V'), - (0xA9FF, 'X'), - (0xAA00, 'V'), - (0xAA37, 'X'), - (0xAA40, 'V'), - (0xAA4E, 'X'), - (0xAA50, 'V'), - (0xAA5A, 'X'), - (0xAA5C, 'V'), - (0xAAC3, 'X'), - (0xAADB, 'V'), - (0xAAF7, 'X'), - (0xAB01, 'V'), - (0xAB07, 'X'), - (0xAB09, 'V'), - (0xAB0F, 'X'), - (0xAB11, 'V'), - (0xAB17, 'X'), - (0xAB20, 'V'), - (0xAB27, 'X'), - (0xAB28, 'V'), - (0xAB2F, 'X'), - (0xAB30, 'V'), - (0xAB5C, 'M', 'ꜧ'), - (0xAB5D, 'M', 'ꬷ'), - (0xAB5E, 'M', 'ɫ'), - (0xAB5F, 'M', 'ꭒ'), - (0xAB60, 'V'), - (0xAB69, 'M', 'ʍ'), - (0xAB6A, 'V'), - (0xAB6C, 'X'), - (0xAB70, 'M', 'Ꭰ'), - (0xAB71, 'M', 'Ꭱ'), - (0xAB72, 'M', 'Ꭲ'), - (0xAB73, 'M', 'Ꭳ'), - (0xAB74, 'M', 'Ꭴ'), - (0xAB75, 'M', 'Ꭵ'), - (0xAB76, 'M', 'Ꭶ'), - (0xAB77, 'M', 'Ꭷ'), - (0xAB78, 'M', 'Ꭸ'), - (0xAB79, 'M', 'Ꭹ'), - (0xAB7A, 'M', 'Ꭺ'), - (0xAB7B, 'M', 'Ꭻ'), - (0xAB7C, 'M', 'Ꭼ'), - (0xAB7D, 'M', 'Ꭽ'), - (0xAB7E, 'M', 'Ꭾ'), - (0xAB7F, 'M', 'Ꭿ'), - (0xAB80, 'M', 'Ꮀ'), - (0xAB81, 'M', 'Ꮁ'), - (0xAB82, 'M', 'Ꮂ'), - (0xAB83, 'M', 'Ꮃ'), - (0xAB84, 'M', 'Ꮄ'), - (0xAB85, 'M', 'Ꮅ'), - (0xAB86, 'M', 'Ꮆ'), - (0xAB87, 'M', 'Ꮇ'), - (0xAB88, 'M', 'Ꮈ'), - (0xAB89, 'M', 'Ꮉ'), - (0xAB8A, 'M', 'Ꮊ'), - (0xAB8B, 'M', 'Ꮋ'), - (0xAB8C, 'M', 'Ꮌ'), - (0xAB8D, 'M', 'Ꮍ'), - (0xAB8E, 'M', 'Ꮎ'), - (0xAB8F, 'M', 'Ꮏ'), - (0xAB90, 'M', 'Ꮐ'), - (0xAB91, 'M', 'Ꮑ'), - (0xAB92, 'M', 'Ꮒ'), - (0xAB93, 'M', 'Ꮓ'), - (0xAB94, 'M', 'Ꮔ'), - (0xAB95, 'M', 'Ꮕ'), - (0xAB96, 'M', 'Ꮖ'), - (0xAB97, 'M', 'Ꮗ'), - (0xAB98, 'M', 'Ꮘ'), - (0xAB99, 'M', 'Ꮙ'), - (0xAB9A, 'M', 'Ꮚ'), - (0xAB9B, 'M', 'Ꮛ'), - (0xAB9C, 'M', 'Ꮜ'), - (0xAB9D, 'M', 'Ꮝ'), - (0xAB9E, 'M', 'Ꮞ'), - (0xAB9F, 'M', 'Ꮟ'), - (0xABA0, 'M', 'Ꮠ'), - (0xABA1, 'M', 'Ꮡ'), - (0xABA2, 'M', 'Ꮢ'), - (0xABA3, 'M', 'Ꮣ'), - (0xABA4, 'M', 'Ꮤ'), - (0xABA5, 'M', 'Ꮥ'), - (0xABA6, 'M', 'Ꮦ'), - (0xABA7, 'M', 'Ꮧ'), - (0xABA8, 'M', 'Ꮨ'), - (0xABA9, 'M', 'Ꮩ'), - (0xABAA, 'M', 'Ꮪ'), - ] - -def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xABAB, 'M', 'Ꮫ'), - (0xABAC, 'M', 'Ꮬ'), - (0xABAD, 'M', 'Ꮭ'), - (0xABAE, 'M', 'Ꮮ'), - (0xABAF, 'M', 'Ꮯ'), - (0xABB0, 'M', 'Ꮰ'), - (0xABB1, 'M', 'Ꮱ'), - (0xABB2, 'M', 'Ꮲ'), - (0xABB3, 'M', 'Ꮳ'), - (0xABB4, 'M', 'Ꮴ'), - (0xABB5, 'M', 'Ꮵ'), - (0xABB6, 'M', 'Ꮶ'), - (0xABB7, 'M', 'Ꮷ'), - (0xABB8, 'M', 'Ꮸ'), - (0xABB9, 'M', 'Ꮹ'), - (0xABBA, 'M', 'Ꮺ'), - (0xABBB, 'M', 'Ꮻ'), - (0xABBC, 'M', 'Ꮼ'), - (0xABBD, 'M', 'Ꮽ'), - (0xABBE, 'M', 'Ꮾ'), - (0xABBF, 'M', 'Ꮿ'), - (0xABC0, 'V'), - (0xABEE, 'X'), - (0xABF0, 'V'), - (0xABFA, 'X'), - (0xAC00, 'V'), - (0xD7A4, 'X'), - (0xD7B0, 'V'), - (0xD7C7, 'X'), - (0xD7CB, 'V'), - (0xD7FC, 'X'), - (0xF900, 'M', '豈'), - (0xF901, 'M', '更'), - (0xF902, 'M', '車'), - (0xF903, 'M', '賈'), - (0xF904, 'M', '滑'), - (0xF905, 'M', '串'), - (0xF906, 'M', '句'), - (0xF907, 'M', '龜'), - (0xF909, 'M', '契'), - (0xF90A, 'M', '金'), - (0xF90B, 'M', '喇'), - (0xF90C, 'M', '奈'), - (0xF90D, 'M', '懶'), - (0xF90E, 'M', '癩'), - (0xF90F, 'M', '羅'), - (0xF910, 'M', '蘿'), - (0xF911, 'M', '螺'), - (0xF912, 'M', '裸'), - (0xF913, 'M', '邏'), - (0xF914, 'M', '樂'), - (0xF915, 'M', '洛'), - (0xF916, 'M', '烙'), - (0xF917, 'M', '珞'), - (0xF918, 'M', '落'), - (0xF919, 'M', '酪'), - (0xF91A, 'M', '駱'), - (0xF91B, 'M', '亂'), - (0xF91C, 'M', '卵'), - (0xF91D, 'M', '欄'), - (0xF91E, 'M', '爛'), - (0xF91F, 'M', '蘭'), - (0xF920, 'M', '鸞'), - (0xF921, 'M', '嵐'), - (0xF922, 'M', '濫'), - (0xF923, 'M', '藍'), - (0xF924, 'M', '襤'), - (0xF925, 'M', '拉'), - (0xF926, 'M', '臘'), - (0xF927, 'M', '蠟'), - (0xF928, 'M', '廊'), - (0xF929, 'M', '朗'), - (0xF92A, 'M', '浪'), - (0xF92B, 'M', '狼'), - (0xF92C, 'M', '郎'), - (0xF92D, 'M', '來'), - (0xF92E, 'M', '冷'), - (0xF92F, 'M', '勞'), - (0xF930, 'M', '擄'), - (0xF931, 'M', '櫓'), - (0xF932, 'M', '爐'), - (0xF933, 'M', '盧'), - (0xF934, 'M', '老'), - (0xF935, 'M', '蘆'), - (0xF936, 'M', '虜'), - (0xF937, 'M', '路'), - (0xF938, 'M', '露'), - (0xF939, 'M', '魯'), - (0xF93A, 'M', '鷺'), - (0xF93B, 'M', '碌'), - (0xF93C, 'M', '祿'), - (0xF93D, 'M', '綠'), - (0xF93E, 'M', '菉'), - (0xF93F, 'M', '錄'), - (0xF940, 'M', '鹿'), - (0xF941, 'M', '論'), - (0xF942, 'M', '壟'), - (0xF943, 'M', '弄'), - (0xF944, 'M', '籠'), - (0xF945, 'M', '聾'), - ] - -def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF946, 'M', '牢'), - (0xF947, 'M', '磊'), - (0xF948, 'M', '賂'), - (0xF949, 'M', '雷'), - (0xF94A, 'M', '壘'), - (0xF94B, 'M', '屢'), - (0xF94C, 'M', '樓'), - (0xF94D, 'M', '淚'), - (0xF94E, 'M', '漏'), - (0xF94F, 'M', '累'), - (0xF950, 'M', '縷'), - (0xF951, 'M', '陋'), - (0xF952, 'M', '勒'), - (0xF953, 'M', '肋'), - (0xF954, 'M', '凜'), - (0xF955, 'M', '凌'), - (0xF956, 'M', '稜'), - (0xF957, 'M', '綾'), - (0xF958, 'M', '菱'), - (0xF959, 'M', '陵'), - (0xF95A, 'M', '讀'), - (0xF95B, 'M', '拏'), - (0xF95C, 'M', '樂'), - (0xF95D, 'M', '諾'), - (0xF95E, 'M', '丹'), - (0xF95F, 'M', '寧'), - (0xF960, 'M', '怒'), - (0xF961, 'M', '率'), - (0xF962, 'M', '異'), - (0xF963, 'M', '北'), - (0xF964, 'M', '磻'), - (0xF965, 'M', '便'), - (0xF966, 'M', '復'), - (0xF967, 'M', '不'), - (0xF968, 'M', '泌'), - (0xF969, 'M', '數'), - (0xF96A, 'M', '索'), - (0xF96B, 'M', '參'), - (0xF96C, 'M', '塞'), - (0xF96D, 'M', '省'), - (0xF96E, 'M', '葉'), - (0xF96F, 'M', '說'), - (0xF970, 'M', '殺'), - (0xF971, 'M', '辰'), - (0xF972, 'M', '沈'), - (0xF973, 'M', '拾'), - (0xF974, 'M', '若'), - (0xF975, 'M', '掠'), - (0xF976, 'M', '略'), - (0xF977, 'M', '亮'), - (0xF978, 'M', '兩'), - (0xF979, 'M', '凉'), - (0xF97A, 'M', '梁'), - (0xF97B, 'M', '糧'), - (0xF97C, 'M', '良'), - (0xF97D, 'M', '諒'), - (0xF97E, 'M', '量'), - (0xF97F, 'M', '勵'), - (0xF980, 'M', '呂'), - (0xF981, 'M', '女'), - (0xF982, 'M', '廬'), - (0xF983, 'M', '旅'), - (0xF984, 'M', '濾'), - (0xF985, 'M', '礪'), - (0xF986, 'M', '閭'), - (0xF987, 'M', '驪'), - (0xF988, 'M', '麗'), - (0xF989, 'M', '黎'), - (0xF98A, 'M', '力'), - (0xF98B, 'M', '曆'), - (0xF98C, 'M', '歷'), - (0xF98D, 'M', '轢'), - (0xF98E, 'M', '年'), - (0xF98F, 'M', '憐'), - (0xF990, 'M', '戀'), - (0xF991, 'M', '撚'), - (0xF992, 'M', '漣'), - (0xF993, 'M', '煉'), - (0xF994, 'M', '璉'), - (0xF995, 'M', '秊'), - (0xF996, 'M', '練'), - (0xF997, 'M', '聯'), - (0xF998, 'M', '輦'), - (0xF999, 'M', '蓮'), - (0xF99A, 'M', '連'), - (0xF99B, 'M', '鍊'), - (0xF99C, 'M', '列'), - (0xF99D, 'M', '劣'), - (0xF99E, 'M', '咽'), - (0xF99F, 'M', '烈'), - (0xF9A0, 'M', '裂'), - (0xF9A1, 'M', '說'), - (0xF9A2, 'M', '廉'), - (0xF9A3, 'M', '念'), - (0xF9A4, 'M', '捻'), - (0xF9A5, 'M', '殮'), - (0xF9A6, 'M', '簾'), - (0xF9A7, 'M', '獵'), - (0xF9A8, 'M', '令'), - (0xF9A9, 'M', '囹'), - ] - -def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF9AA, 'M', '寧'), - (0xF9AB, 'M', '嶺'), - (0xF9AC, 'M', '怜'), - (0xF9AD, 'M', '玲'), - (0xF9AE, 'M', '瑩'), - (0xF9AF, 'M', '羚'), - (0xF9B0, 'M', '聆'), - (0xF9B1, 'M', '鈴'), - (0xF9B2, 'M', '零'), - (0xF9B3, 'M', '靈'), - (0xF9B4, 'M', '領'), - (0xF9B5, 'M', '例'), - (0xF9B6, 'M', '禮'), - (0xF9B7, 'M', '醴'), - (0xF9B8, 'M', '隸'), - (0xF9B9, 'M', '惡'), - (0xF9BA, 'M', '了'), - (0xF9BB, 'M', '僚'), - (0xF9BC, 'M', '寮'), - (0xF9BD, 'M', '尿'), - (0xF9BE, 'M', '料'), - (0xF9BF, 'M', '樂'), - (0xF9C0, 'M', '燎'), - (0xF9C1, 'M', '療'), - (0xF9C2, 'M', '蓼'), - (0xF9C3, 'M', '遼'), - (0xF9C4, 'M', '龍'), - (0xF9C5, 'M', '暈'), - (0xF9C6, 'M', '阮'), - (0xF9C7, 'M', '劉'), - (0xF9C8, 'M', '杻'), - (0xF9C9, 'M', '柳'), - (0xF9CA, 'M', '流'), - (0xF9CB, 'M', '溜'), - (0xF9CC, 'M', '琉'), - (0xF9CD, 'M', '留'), - (0xF9CE, 'M', '硫'), - (0xF9CF, 'M', '紐'), - (0xF9D0, 'M', '類'), - (0xF9D1, 'M', '六'), - (0xF9D2, 'M', '戮'), - (0xF9D3, 'M', '陸'), - (0xF9D4, 'M', '倫'), - (0xF9D5, 'M', '崙'), - (0xF9D6, 'M', '淪'), - (0xF9D7, 'M', '輪'), - (0xF9D8, 'M', '律'), - (0xF9D9, 'M', '慄'), - (0xF9DA, 'M', '栗'), - (0xF9DB, 'M', '率'), - (0xF9DC, 'M', '隆'), - (0xF9DD, 'M', '利'), - (0xF9DE, 'M', '吏'), - (0xF9DF, 'M', '履'), - (0xF9E0, 'M', '易'), - (0xF9E1, 'M', '李'), - (0xF9E2, 'M', '梨'), - (0xF9E3, 'M', '泥'), - (0xF9E4, 'M', '理'), - (0xF9E5, 'M', '痢'), - (0xF9E6, 'M', '罹'), - (0xF9E7, 'M', '裏'), - (0xF9E8, 'M', '裡'), - (0xF9E9, 'M', '里'), - (0xF9EA, 'M', '離'), - (0xF9EB, 'M', '匿'), - (0xF9EC, 'M', '溺'), - (0xF9ED, 'M', '吝'), - (0xF9EE, 'M', '燐'), - (0xF9EF, 'M', '璘'), - (0xF9F0, 'M', '藺'), - (0xF9F1, 'M', '隣'), - (0xF9F2, 'M', '鱗'), - (0xF9F3, 'M', '麟'), - (0xF9F4, 'M', '林'), - (0xF9F5, 'M', '淋'), - (0xF9F6, 'M', '臨'), - (0xF9F7, 'M', '立'), - (0xF9F8, 'M', '笠'), - (0xF9F9, 'M', '粒'), - (0xF9FA, 'M', '狀'), - (0xF9FB, 'M', '炙'), - (0xF9FC, 'M', '識'), - (0xF9FD, 'M', '什'), - (0xF9FE, 'M', '茶'), - (0xF9FF, 'M', '刺'), - (0xFA00, 'M', '切'), - (0xFA01, 'M', '度'), - (0xFA02, 'M', '拓'), - (0xFA03, 'M', '糖'), - (0xFA04, 'M', '宅'), - (0xFA05, 'M', '洞'), - (0xFA06, 'M', '暴'), - (0xFA07, 'M', '輻'), - (0xFA08, 'M', '行'), - (0xFA09, 'M', '降'), - (0xFA0A, 'M', '見'), - (0xFA0B, 'M', '廓'), - (0xFA0C, 'M', '兀'), - (0xFA0D, 'M', '嗀'), - ] - -def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA0E, 'V'), - (0xFA10, 'M', '塚'), - (0xFA11, 'V'), - (0xFA12, 'M', '晴'), - (0xFA13, 'V'), - (0xFA15, 'M', '凞'), - (0xFA16, 'M', '猪'), - (0xFA17, 'M', '益'), - (0xFA18, 'M', '礼'), - (0xFA19, 'M', '神'), - (0xFA1A, 'M', '祥'), - (0xFA1B, 'M', '福'), - (0xFA1C, 'M', '靖'), - (0xFA1D, 'M', '精'), - (0xFA1E, 'M', '羽'), - (0xFA1F, 'V'), - (0xFA20, 'M', '蘒'), - (0xFA21, 'V'), - (0xFA22, 'M', '諸'), - (0xFA23, 'V'), - (0xFA25, 'M', '逸'), - (0xFA26, 'M', '都'), - (0xFA27, 'V'), - (0xFA2A, 'M', '飯'), - (0xFA2B, 'M', '飼'), - (0xFA2C, 'M', '館'), - (0xFA2D, 'M', '鶴'), - (0xFA2E, 'M', '郞'), - (0xFA2F, 'M', '隷'), - (0xFA30, 'M', '侮'), - (0xFA31, 'M', '僧'), - (0xFA32, 'M', '免'), - (0xFA33, 'M', '勉'), - (0xFA34, 'M', '勤'), - (0xFA35, 'M', '卑'), - (0xFA36, 'M', '喝'), - (0xFA37, 'M', '嘆'), - (0xFA38, 'M', '器'), - (0xFA39, 'M', '塀'), - (0xFA3A, 'M', '墨'), - (0xFA3B, 'M', '層'), - (0xFA3C, 'M', '屮'), - (0xFA3D, 'M', '悔'), - (0xFA3E, 'M', '慨'), - (0xFA3F, 'M', '憎'), - (0xFA40, 'M', '懲'), - (0xFA41, 'M', '敏'), - (0xFA42, 'M', '既'), - (0xFA43, 'M', '暑'), - (0xFA44, 'M', '梅'), - (0xFA45, 'M', '海'), - (0xFA46, 'M', '渚'), - (0xFA47, 'M', '漢'), - (0xFA48, 'M', '煮'), - (0xFA49, 'M', '爫'), - (0xFA4A, 'M', '琢'), - (0xFA4B, 'M', '碑'), - (0xFA4C, 'M', '社'), - (0xFA4D, 'M', '祉'), - (0xFA4E, 'M', '祈'), - (0xFA4F, 'M', '祐'), - (0xFA50, 'M', '祖'), - (0xFA51, 'M', '祝'), - (0xFA52, 'M', '禍'), - (0xFA53, 'M', '禎'), - (0xFA54, 'M', '穀'), - (0xFA55, 'M', '突'), - (0xFA56, 'M', '節'), - (0xFA57, 'M', '練'), - (0xFA58, 'M', '縉'), - (0xFA59, 'M', '繁'), - (0xFA5A, 'M', '署'), - (0xFA5B, 'M', '者'), - (0xFA5C, 'M', '臭'), - (0xFA5D, 'M', '艹'), - (0xFA5F, 'M', '著'), - (0xFA60, 'M', '褐'), - (0xFA61, 'M', '視'), - (0xFA62, 'M', '謁'), - (0xFA63, 'M', '謹'), - (0xFA64, 'M', '賓'), - (0xFA65, 'M', '贈'), - (0xFA66, 'M', '辶'), - (0xFA67, 'M', '逸'), - (0xFA68, 'M', '難'), - (0xFA69, 'M', '響'), - (0xFA6A, 'M', '頻'), - (0xFA6B, 'M', '恵'), - (0xFA6C, 'M', '𤋮'), - (0xFA6D, 'M', '舘'), - (0xFA6E, 'X'), - (0xFA70, 'M', '並'), - (0xFA71, 'M', '况'), - (0xFA72, 'M', '全'), - (0xFA73, 'M', '侀'), - (0xFA74, 'M', '充'), - (0xFA75, 'M', '冀'), - (0xFA76, 'M', '勇'), - (0xFA77, 'M', '勺'), - (0xFA78, 'M', '喝'), - ] - -def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA79, 'M', '啕'), - (0xFA7A, 'M', '喙'), - (0xFA7B, 'M', '嗢'), - (0xFA7C, 'M', '塚'), - (0xFA7D, 'M', '墳'), - (0xFA7E, 'M', '奄'), - (0xFA7F, 'M', '奔'), - (0xFA80, 'M', '婢'), - (0xFA81, 'M', '嬨'), - (0xFA82, 'M', '廒'), - (0xFA83, 'M', '廙'), - (0xFA84, 'M', '彩'), - (0xFA85, 'M', '徭'), - (0xFA86, 'M', '惘'), - (0xFA87, 'M', '慎'), - (0xFA88, 'M', '愈'), - (0xFA89, 'M', '憎'), - (0xFA8A, 'M', '慠'), - (0xFA8B, 'M', '懲'), - (0xFA8C, 'M', '戴'), - (0xFA8D, 'M', '揄'), - (0xFA8E, 'M', '搜'), - (0xFA8F, 'M', '摒'), - (0xFA90, 'M', '敖'), - (0xFA91, 'M', '晴'), - (0xFA92, 'M', '朗'), - (0xFA93, 'M', '望'), - (0xFA94, 'M', '杖'), - (0xFA95, 'M', '歹'), - (0xFA96, 'M', '殺'), - (0xFA97, 'M', '流'), - (0xFA98, 'M', '滛'), - (0xFA99, 'M', '滋'), - (0xFA9A, 'M', '漢'), - (0xFA9B, 'M', '瀞'), - (0xFA9C, 'M', '煮'), - (0xFA9D, 'M', '瞧'), - (0xFA9E, 'M', '爵'), - (0xFA9F, 'M', '犯'), - (0xFAA0, 'M', '猪'), - (0xFAA1, 'M', '瑱'), - (0xFAA2, 'M', '甆'), - (0xFAA3, 'M', '画'), - (0xFAA4, 'M', '瘝'), - (0xFAA5, 'M', '瘟'), - (0xFAA6, 'M', '益'), - (0xFAA7, 'M', '盛'), - (0xFAA8, 'M', '直'), - (0xFAA9, 'M', '睊'), - (0xFAAA, 'M', '着'), - (0xFAAB, 'M', '磌'), - (0xFAAC, 'M', '窱'), - (0xFAAD, 'M', '節'), - (0xFAAE, 'M', '类'), - (0xFAAF, 'M', '絛'), - (0xFAB0, 'M', '練'), - (0xFAB1, 'M', '缾'), - (0xFAB2, 'M', '者'), - (0xFAB3, 'M', '荒'), - (0xFAB4, 'M', '華'), - (0xFAB5, 'M', '蝹'), - (0xFAB6, 'M', '襁'), - (0xFAB7, 'M', '覆'), - (0xFAB8, 'M', '視'), - (0xFAB9, 'M', '調'), - (0xFABA, 'M', '諸'), - (0xFABB, 'M', '請'), - (0xFABC, 'M', '謁'), - (0xFABD, 'M', '諾'), - (0xFABE, 'M', '諭'), - (0xFABF, 'M', '謹'), - (0xFAC0, 'M', '變'), - (0xFAC1, 'M', '贈'), - (0xFAC2, 'M', '輸'), - (0xFAC3, 'M', '遲'), - (0xFAC4, 'M', '醙'), - (0xFAC5, 'M', '鉶'), - (0xFAC6, 'M', '陼'), - (0xFAC7, 'M', '難'), - (0xFAC8, 'M', '靖'), - (0xFAC9, 'M', '韛'), - (0xFACA, 'M', '響'), - (0xFACB, 'M', '頋'), - (0xFACC, 'M', '頻'), - (0xFACD, 'M', '鬒'), - (0xFACE, 'M', '龜'), - (0xFACF, 'M', '𢡊'), - (0xFAD0, 'M', '𢡄'), - (0xFAD1, 'M', '𣏕'), - (0xFAD2, 'M', '㮝'), - (0xFAD3, 'M', '䀘'), - (0xFAD4, 'M', '䀹'), - (0xFAD5, 'M', '𥉉'), - (0xFAD6, 'M', '𥳐'), - (0xFAD7, 'M', '𧻓'), - (0xFAD8, 'M', '齃'), - (0xFAD9, 'M', '龎'), - (0xFADA, 'X'), - (0xFB00, 'M', 'ff'), - (0xFB01, 'M', 'fi'), - ] - -def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFB02, 'M', 'fl'), - (0xFB03, 'M', 'ffi'), - (0xFB04, 'M', 'ffl'), - (0xFB05, 'M', 'st'), - (0xFB07, 'X'), - (0xFB13, 'M', 'մն'), - (0xFB14, 'M', 'մե'), - (0xFB15, 'M', 'մի'), - (0xFB16, 'M', 'վն'), - (0xFB17, 'M', 'մխ'), - (0xFB18, 'X'), - (0xFB1D, 'M', 'יִ'), - (0xFB1E, 'V'), - (0xFB1F, 'M', 'ײַ'), - (0xFB20, 'M', 'ע'), - (0xFB21, 'M', 'א'), - (0xFB22, 'M', 'ד'), - (0xFB23, 'M', 'ה'), - (0xFB24, 'M', 'כ'), - (0xFB25, 'M', 'ל'), - (0xFB26, 'M', 'ם'), - (0xFB27, 'M', 'ר'), - (0xFB28, 'M', 'ת'), - (0xFB29, '3', '+'), - (0xFB2A, 'M', 'שׁ'), - (0xFB2B, 'M', 'שׂ'), - (0xFB2C, 'M', 'שּׁ'), - (0xFB2D, 'M', 'שּׂ'), - (0xFB2E, 'M', 'אַ'), - (0xFB2F, 'M', 'אָ'), - (0xFB30, 'M', 'אּ'), - (0xFB31, 'M', 'בּ'), - (0xFB32, 'M', 'גּ'), - (0xFB33, 'M', 'דּ'), - (0xFB34, 'M', 'הּ'), - (0xFB35, 'M', 'וּ'), - (0xFB36, 'M', 'זּ'), - (0xFB37, 'X'), - (0xFB38, 'M', 'טּ'), - (0xFB39, 'M', 'יּ'), - (0xFB3A, 'M', 'ךּ'), - (0xFB3B, 'M', 'כּ'), - (0xFB3C, 'M', 'לּ'), - (0xFB3D, 'X'), - (0xFB3E, 'M', 'מּ'), - (0xFB3F, 'X'), - (0xFB40, 'M', 'נּ'), - (0xFB41, 'M', 'סּ'), - (0xFB42, 'X'), - (0xFB43, 'M', 'ףּ'), - (0xFB44, 'M', 'פּ'), - (0xFB45, 'X'), - (0xFB46, 'M', 'צּ'), - (0xFB47, 'M', 'קּ'), - (0xFB48, 'M', 'רּ'), - (0xFB49, 'M', 'שּ'), - (0xFB4A, 'M', 'תּ'), - (0xFB4B, 'M', 'וֹ'), - (0xFB4C, 'M', 'בֿ'), - (0xFB4D, 'M', 'כֿ'), - (0xFB4E, 'M', 'פֿ'), - (0xFB4F, 'M', 'אל'), - (0xFB50, 'M', 'ٱ'), - (0xFB52, 'M', 'ٻ'), - (0xFB56, 'M', 'پ'), - (0xFB5A, 'M', 'ڀ'), - (0xFB5E, 'M', 'ٺ'), - (0xFB62, 'M', 'ٿ'), - (0xFB66, 'M', 'ٹ'), - (0xFB6A, 'M', 'ڤ'), - (0xFB6E, 'M', 'ڦ'), - (0xFB72, 'M', 'ڄ'), - (0xFB76, 'M', 'ڃ'), - (0xFB7A, 'M', 'چ'), - (0xFB7E, 'M', 'ڇ'), - (0xFB82, 'M', 'ڍ'), - (0xFB84, 'M', 'ڌ'), - (0xFB86, 'M', 'ڎ'), - (0xFB88, 'M', 'ڈ'), - (0xFB8A, 'M', 'ژ'), - (0xFB8C, 'M', 'ڑ'), - (0xFB8E, 'M', 'ک'), - (0xFB92, 'M', 'گ'), - (0xFB96, 'M', 'ڳ'), - (0xFB9A, 'M', 'ڱ'), - (0xFB9E, 'M', 'ں'), - (0xFBA0, 'M', 'ڻ'), - (0xFBA4, 'M', 'ۀ'), - (0xFBA6, 'M', 'ہ'), - (0xFBAA, 'M', 'ھ'), - (0xFBAE, 'M', 'ے'), - (0xFBB0, 'M', 'ۓ'), - (0xFBB2, 'V'), - (0xFBC3, 'X'), - (0xFBD3, 'M', 'ڭ'), - (0xFBD7, 'M', 'ۇ'), - (0xFBD9, 'M', 'ۆ'), - (0xFBDB, 'M', 'ۈ'), - (0xFBDD, 'M', 'ۇٴ'), - (0xFBDE, 'M', 'ۋ'), - ] - -def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFBE0, 'M', 'ۅ'), - (0xFBE2, 'M', 'ۉ'), - (0xFBE4, 'M', 'ې'), - (0xFBE8, 'M', 'ى'), - (0xFBEA, 'M', 'ئا'), - (0xFBEC, 'M', 'ئە'), - (0xFBEE, 'M', 'ئو'), - (0xFBF0, 'M', 'ئۇ'), - (0xFBF2, 'M', 'ئۆ'), - (0xFBF4, 'M', 'ئۈ'), - (0xFBF6, 'M', 'ئې'), - (0xFBF9, 'M', 'ئى'), - (0xFBFC, 'M', 'ی'), - (0xFC00, 'M', 'ئج'), - (0xFC01, 'M', 'ئح'), - (0xFC02, 'M', 'ئم'), - (0xFC03, 'M', 'ئى'), - (0xFC04, 'M', 'ئي'), - (0xFC05, 'M', 'بج'), - (0xFC06, 'M', 'بح'), - (0xFC07, 'M', 'بخ'), - (0xFC08, 'M', 'بم'), - (0xFC09, 'M', 'بى'), - (0xFC0A, 'M', 'بي'), - (0xFC0B, 'M', 'تج'), - (0xFC0C, 'M', 'تح'), - (0xFC0D, 'M', 'تخ'), - (0xFC0E, 'M', 'تم'), - (0xFC0F, 'M', 'تى'), - (0xFC10, 'M', 'تي'), - (0xFC11, 'M', 'ثج'), - (0xFC12, 'M', 'ثم'), - (0xFC13, 'M', 'ثى'), - (0xFC14, 'M', 'ثي'), - (0xFC15, 'M', 'جح'), - (0xFC16, 'M', 'جم'), - (0xFC17, 'M', 'حج'), - (0xFC18, 'M', 'حم'), - (0xFC19, 'M', 'خج'), - (0xFC1A, 'M', 'خح'), - (0xFC1B, 'M', 'خم'), - (0xFC1C, 'M', 'سج'), - (0xFC1D, 'M', 'سح'), - (0xFC1E, 'M', 'سخ'), - (0xFC1F, 'M', 'سم'), - (0xFC20, 'M', 'صح'), - (0xFC21, 'M', 'صم'), - (0xFC22, 'M', 'ضج'), - (0xFC23, 'M', 'ضح'), - (0xFC24, 'M', 'ضخ'), - (0xFC25, 'M', 'ضم'), - (0xFC26, 'M', 'طح'), - (0xFC27, 'M', 'طم'), - (0xFC28, 'M', 'ظم'), - (0xFC29, 'M', 'عج'), - (0xFC2A, 'M', 'عم'), - (0xFC2B, 'M', 'غج'), - (0xFC2C, 'M', 'غم'), - (0xFC2D, 'M', 'فج'), - (0xFC2E, 'M', 'فح'), - (0xFC2F, 'M', 'فخ'), - (0xFC30, 'M', 'فم'), - (0xFC31, 'M', 'فى'), - (0xFC32, 'M', 'في'), - (0xFC33, 'M', 'قح'), - (0xFC34, 'M', 'قم'), - (0xFC35, 'M', 'قى'), - (0xFC36, 'M', 'قي'), - (0xFC37, 'M', 'كا'), - (0xFC38, 'M', 'كج'), - (0xFC39, 'M', 'كح'), - (0xFC3A, 'M', 'كخ'), - (0xFC3B, 'M', 'كل'), - (0xFC3C, 'M', 'كم'), - (0xFC3D, 'M', 'كى'), - (0xFC3E, 'M', 'كي'), - (0xFC3F, 'M', 'لج'), - (0xFC40, 'M', 'لح'), - (0xFC41, 'M', 'لخ'), - (0xFC42, 'M', 'لم'), - (0xFC43, 'M', 'لى'), - (0xFC44, 'M', 'لي'), - (0xFC45, 'M', 'مج'), - (0xFC46, 'M', 'مح'), - (0xFC47, 'M', 'مخ'), - (0xFC48, 'M', 'مم'), - (0xFC49, 'M', 'مى'), - (0xFC4A, 'M', 'مي'), - (0xFC4B, 'M', 'نج'), - (0xFC4C, 'M', 'نح'), - (0xFC4D, 'M', 'نخ'), - (0xFC4E, 'M', 'نم'), - (0xFC4F, 'M', 'نى'), - (0xFC50, 'M', 'ني'), - (0xFC51, 'M', 'هج'), - (0xFC52, 'M', 'هم'), - (0xFC53, 'M', 'هى'), - (0xFC54, 'M', 'هي'), - (0xFC55, 'M', 'يج'), - (0xFC56, 'M', 'يح'), - ] - -def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFC57, 'M', 'يخ'), - (0xFC58, 'M', 'يم'), - (0xFC59, 'M', 'يى'), - (0xFC5A, 'M', 'يي'), - (0xFC5B, 'M', 'ذٰ'), - (0xFC5C, 'M', 'رٰ'), - (0xFC5D, 'M', 'ىٰ'), - (0xFC5E, '3', ' ٌّ'), - (0xFC5F, '3', ' ٍّ'), - (0xFC60, '3', ' َّ'), - (0xFC61, '3', ' ُّ'), - (0xFC62, '3', ' ِّ'), - (0xFC63, '3', ' ّٰ'), - (0xFC64, 'M', 'ئر'), - (0xFC65, 'M', 'ئز'), - (0xFC66, 'M', 'ئم'), - (0xFC67, 'M', 'ئن'), - (0xFC68, 'M', 'ئى'), - (0xFC69, 'M', 'ئي'), - (0xFC6A, 'M', 'بر'), - (0xFC6B, 'M', 'بز'), - (0xFC6C, 'M', 'بم'), - (0xFC6D, 'M', 'بن'), - (0xFC6E, 'M', 'بى'), - (0xFC6F, 'M', 'بي'), - (0xFC70, 'M', 'تر'), - (0xFC71, 'M', 'تز'), - (0xFC72, 'M', 'تم'), - (0xFC73, 'M', 'تن'), - (0xFC74, 'M', 'تى'), - (0xFC75, 'M', 'تي'), - (0xFC76, 'M', 'ثر'), - (0xFC77, 'M', 'ثز'), - (0xFC78, 'M', 'ثم'), - (0xFC79, 'M', 'ثن'), - (0xFC7A, 'M', 'ثى'), - (0xFC7B, 'M', 'ثي'), - (0xFC7C, 'M', 'فى'), - (0xFC7D, 'M', 'في'), - (0xFC7E, 'M', 'قى'), - (0xFC7F, 'M', 'قي'), - (0xFC80, 'M', 'كا'), - (0xFC81, 'M', 'كل'), - (0xFC82, 'M', 'كم'), - (0xFC83, 'M', 'كى'), - (0xFC84, 'M', 'كي'), - (0xFC85, 'M', 'لم'), - (0xFC86, 'M', 'لى'), - (0xFC87, 'M', 'لي'), - (0xFC88, 'M', 'ما'), - (0xFC89, 'M', 'مم'), - (0xFC8A, 'M', 'نر'), - (0xFC8B, 'M', 'نز'), - (0xFC8C, 'M', 'نم'), - (0xFC8D, 'M', 'نن'), - (0xFC8E, 'M', 'نى'), - (0xFC8F, 'M', 'ني'), - (0xFC90, 'M', 'ىٰ'), - (0xFC91, 'M', 'ير'), - (0xFC92, 'M', 'يز'), - (0xFC93, 'M', 'يم'), - (0xFC94, 'M', 'ين'), - (0xFC95, 'M', 'يى'), - (0xFC96, 'M', 'يي'), - (0xFC97, 'M', 'ئج'), - (0xFC98, 'M', 'ئح'), - (0xFC99, 'M', 'ئخ'), - (0xFC9A, 'M', 'ئم'), - (0xFC9B, 'M', 'ئه'), - (0xFC9C, 'M', 'بج'), - (0xFC9D, 'M', 'بح'), - (0xFC9E, 'M', 'بخ'), - (0xFC9F, 'M', 'بم'), - (0xFCA0, 'M', 'به'), - (0xFCA1, 'M', 'تج'), - (0xFCA2, 'M', 'تح'), - (0xFCA3, 'M', 'تخ'), - (0xFCA4, 'M', 'تم'), - (0xFCA5, 'M', 'ته'), - (0xFCA6, 'M', 'ثم'), - (0xFCA7, 'M', 'جح'), - (0xFCA8, 'M', 'جم'), - (0xFCA9, 'M', 'حج'), - (0xFCAA, 'M', 'حم'), - (0xFCAB, 'M', 'خج'), - (0xFCAC, 'M', 'خم'), - (0xFCAD, 'M', 'سج'), - (0xFCAE, 'M', 'سح'), - (0xFCAF, 'M', 'سخ'), - (0xFCB0, 'M', 'سم'), - (0xFCB1, 'M', 'صح'), - (0xFCB2, 'M', 'صخ'), - (0xFCB3, 'M', 'صم'), - (0xFCB4, 'M', 'ضج'), - (0xFCB5, 'M', 'ضح'), - (0xFCB6, 'M', 'ضخ'), - (0xFCB7, 'M', 'ضم'), - (0xFCB8, 'M', 'طح'), - (0xFCB9, 'M', 'ظم'), - (0xFCBA, 'M', 'عج'), - ] - -def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFCBB, 'M', 'عم'), - (0xFCBC, 'M', 'غج'), - (0xFCBD, 'M', 'غم'), - (0xFCBE, 'M', 'فج'), - (0xFCBF, 'M', 'فح'), - (0xFCC0, 'M', 'فخ'), - (0xFCC1, 'M', 'فم'), - (0xFCC2, 'M', 'قح'), - (0xFCC3, 'M', 'قم'), - (0xFCC4, 'M', 'كج'), - (0xFCC5, 'M', 'كح'), - (0xFCC6, 'M', 'كخ'), - (0xFCC7, 'M', 'كل'), - (0xFCC8, 'M', 'كم'), - (0xFCC9, 'M', 'لج'), - (0xFCCA, 'M', 'لح'), - (0xFCCB, 'M', 'لخ'), - (0xFCCC, 'M', 'لم'), - (0xFCCD, 'M', 'له'), - (0xFCCE, 'M', 'مج'), - (0xFCCF, 'M', 'مح'), - (0xFCD0, 'M', 'مخ'), - (0xFCD1, 'M', 'مم'), - (0xFCD2, 'M', 'نج'), - (0xFCD3, 'M', 'نح'), - (0xFCD4, 'M', 'نخ'), - (0xFCD5, 'M', 'نم'), - (0xFCD6, 'M', 'نه'), - (0xFCD7, 'M', 'هج'), - (0xFCD8, 'M', 'هم'), - (0xFCD9, 'M', 'هٰ'), - (0xFCDA, 'M', 'يج'), - (0xFCDB, 'M', 'يح'), - (0xFCDC, 'M', 'يخ'), - (0xFCDD, 'M', 'يم'), - (0xFCDE, 'M', 'يه'), - (0xFCDF, 'M', 'ئم'), - (0xFCE0, 'M', 'ئه'), - (0xFCE1, 'M', 'بم'), - (0xFCE2, 'M', 'به'), - (0xFCE3, 'M', 'تم'), - (0xFCE4, 'M', 'ته'), - (0xFCE5, 'M', 'ثم'), - (0xFCE6, 'M', 'ثه'), - (0xFCE7, 'M', 'سم'), - (0xFCE8, 'M', 'سه'), - (0xFCE9, 'M', 'شم'), - (0xFCEA, 'M', 'شه'), - (0xFCEB, 'M', 'كل'), - (0xFCEC, 'M', 'كم'), - (0xFCED, 'M', 'لم'), - (0xFCEE, 'M', 'نم'), - (0xFCEF, 'M', 'نه'), - (0xFCF0, 'M', 'يم'), - (0xFCF1, 'M', 'يه'), - (0xFCF2, 'M', 'ـَّ'), - (0xFCF3, 'M', 'ـُّ'), - (0xFCF4, 'M', 'ـِّ'), - (0xFCF5, 'M', 'طى'), - (0xFCF6, 'M', 'طي'), - (0xFCF7, 'M', 'عى'), - (0xFCF8, 'M', 'عي'), - (0xFCF9, 'M', 'غى'), - (0xFCFA, 'M', 'غي'), - (0xFCFB, 'M', 'سى'), - (0xFCFC, 'M', 'سي'), - (0xFCFD, 'M', 'شى'), - (0xFCFE, 'M', 'شي'), - (0xFCFF, 'M', 'حى'), - (0xFD00, 'M', 'حي'), - (0xFD01, 'M', 'جى'), - (0xFD02, 'M', 'جي'), - (0xFD03, 'M', 'خى'), - (0xFD04, 'M', 'خي'), - (0xFD05, 'M', 'صى'), - (0xFD06, 'M', 'صي'), - (0xFD07, 'M', 'ضى'), - (0xFD08, 'M', 'ضي'), - (0xFD09, 'M', 'شج'), - (0xFD0A, 'M', 'شح'), - (0xFD0B, 'M', 'شخ'), - (0xFD0C, 'M', 'شم'), - (0xFD0D, 'M', 'شر'), - (0xFD0E, 'M', 'سر'), - (0xFD0F, 'M', 'صر'), - (0xFD10, 'M', 'ضر'), - (0xFD11, 'M', 'طى'), - (0xFD12, 'M', 'طي'), - (0xFD13, 'M', 'عى'), - (0xFD14, 'M', 'عي'), - (0xFD15, 'M', 'غى'), - (0xFD16, 'M', 'غي'), - (0xFD17, 'M', 'سى'), - (0xFD18, 'M', 'سي'), - (0xFD19, 'M', 'شى'), - (0xFD1A, 'M', 'شي'), - (0xFD1B, 'M', 'حى'), - (0xFD1C, 'M', 'حي'), - (0xFD1D, 'M', 'جى'), - (0xFD1E, 'M', 'جي'), - ] - -def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFD1F, 'M', 'خى'), - (0xFD20, 'M', 'خي'), - (0xFD21, 'M', 'صى'), - (0xFD22, 'M', 'صي'), - (0xFD23, 'M', 'ضى'), - (0xFD24, 'M', 'ضي'), - (0xFD25, 'M', 'شج'), - (0xFD26, 'M', 'شح'), - (0xFD27, 'M', 'شخ'), - (0xFD28, 'M', 'شم'), - (0xFD29, 'M', 'شر'), - (0xFD2A, 'M', 'سر'), - (0xFD2B, 'M', 'صر'), - (0xFD2C, 'M', 'ضر'), - (0xFD2D, 'M', 'شج'), - (0xFD2E, 'M', 'شح'), - (0xFD2F, 'M', 'شخ'), - (0xFD30, 'M', 'شم'), - (0xFD31, 'M', 'سه'), - (0xFD32, 'M', 'شه'), - (0xFD33, 'M', 'طم'), - (0xFD34, 'M', 'سج'), - (0xFD35, 'M', 'سح'), - (0xFD36, 'M', 'سخ'), - (0xFD37, 'M', 'شج'), - (0xFD38, 'M', 'شح'), - (0xFD39, 'M', 'شخ'), - (0xFD3A, 'M', 'طم'), - (0xFD3B, 'M', 'ظم'), - (0xFD3C, 'M', 'اً'), - (0xFD3E, 'V'), - (0xFD50, 'M', 'تجم'), - (0xFD51, 'M', 'تحج'), - (0xFD53, 'M', 'تحم'), - (0xFD54, 'M', 'تخم'), - (0xFD55, 'M', 'تمج'), - (0xFD56, 'M', 'تمح'), - (0xFD57, 'M', 'تمخ'), - (0xFD58, 'M', 'جمح'), - (0xFD5A, 'M', 'حمي'), - (0xFD5B, 'M', 'حمى'), - (0xFD5C, 'M', 'سحج'), - (0xFD5D, 'M', 'سجح'), - (0xFD5E, 'M', 'سجى'), - (0xFD5F, 'M', 'سمح'), - (0xFD61, 'M', 'سمج'), - (0xFD62, 'M', 'سمم'), - (0xFD64, 'M', 'صحح'), - (0xFD66, 'M', 'صمم'), - (0xFD67, 'M', 'شحم'), - (0xFD69, 'M', 'شجي'), - (0xFD6A, 'M', 'شمخ'), - (0xFD6C, 'M', 'شمم'), - (0xFD6E, 'M', 'ضحى'), - (0xFD6F, 'M', 'ضخم'), - (0xFD71, 'M', 'طمح'), - (0xFD73, 'M', 'طمم'), - (0xFD74, 'M', 'طمي'), - (0xFD75, 'M', 'عجم'), - (0xFD76, 'M', 'عمم'), - (0xFD78, 'M', 'عمى'), - (0xFD79, 'M', 'غمم'), - (0xFD7A, 'M', 'غمي'), - (0xFD7B, 'M', 'غمى'), - (0xFD7C, 'M', 'فخم'), - (0xFD7E, 'M', 'قمح'), - (0xFD7F, 'M', 'قمم'), - (0xFD80, 'M', 'لحم'), - (0xFD81, 'M', 'لحي'), - (0xFD82, 'M', 'لحى'), - (0xFD83, 'M', 'لجج'), - (0xFD85, 'M', 'لخم'), - (0xFD87, 'M', 'لمح'), - (0xFD89, 'M', 'محج'), - (0xFD8A, 'M', 'محم'), - (0xFD8B, 'M', 'محي'), - (0xFD8C, 'M', 'مجح'), - (0xFD8D, 'M', 'مجم'), - (0xFD8E, 'M', 'مخج'), - (0xFD8F, 'M', 'مخم'), - (0xFD90, 'X'), - (0xFD92, 'M', 'مجخ'), - (0xFD93, 'M', 'همج'), - (0xFD94, 'M', 'همم'), - (0xFD95, 'M', 'نحم'), - (0xFD96, 'M', 'نحى'), - (0xFD97, 'M', 'نجم'), - (0xFD99, 'M', 'نجى'), - (0xFD9A, 'M', 'نمي'), - (0xFD9B, 'M', 'نمى'), - (0xFD9C, 'M', 'يمم'), - (0xFD9E, 'M', 'بخي'), - (0xFD9F, 'M', 'تجي'), - (0xFDA0, 'M', 'تجى'), - (0xFDA1, 'M', 'تخي'), - (0xFDA2, 'M', 'تخى'), - (0xFDA3, 'M', 'تمي'), - (0xFDA4, 'M', 'تمى'), - (0xFDA5, 'M', 'جمي'), - (0xFDA6, 'M', 'جحى'), - ] - -def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFDA7, 'M', 'جمى'), - (0xFDA8, 'M', 'سخى'), - (0xFDA9, 'M', 'صحي'), - (0xFDAA, 'M', 'شحي'), - (0xFDAB, 'M', 'ضحي'), - (0xFDAC, 'M', 'لجي'), - (0xFDAD, 'M', 'لمي'), - (0xFDAE, 'M', 'يحي'), - (0xFDAF, 'M', 'يجي'), - (0xFDB0, 'M', 'يمي'), - (0xFDB1, 'M', 'ممي'), - (0xFDB2, 'M', 'قمي'), - (0xFDB3, 'M', 'نحي'), - (0xFDB4, 'M', 'قمح'), - (0xFDB5, 'M', 'لحم'), - (0xFDB6, 'M', 'عمي'), - (0xFDB7, 'M', 'كمي'), - (0xFDB8, 'M', 'نجح'), - (0xFDB9, 'M', 'مخي'), - (0xFDBA, 'M', 'لجم'), - (0xFDBB, 'M', 'كمم'), - (0xFDBC, 'M', 'لجم'), - (0xFDBD, 'M', 'نجح'), - (0xFDBE, 'M', 'جحي'), - (0xFDBF, 'M', 'حجي'), - (0xFDC0, 'M', 'مجي'), - (0xFDC1, 'M', 'فمي'), - (0xFDC2, 'M', 'بحي'), - (0xFDC3, 'M', 'كمم'), - (0xFDC4, 'M', 'عجم'), - (0xFDC5, 'M', 'صمم'), - (0xFDC6, 'M', 'سخي'), - (0xFDC7, 'M', 'نجي'), - (0xFDC8, 'X'), - (0xFDCF, 'V'), - (0xFDD0, 'X'), - (0xFDF0, 'M', 'صلے'), - (0xFDF1, 'M', 'قلے'), - (0xFDF2, 'M', 'الله'), - (0xFDF3, 'M', 'اكبر'), - (0xFDF4, 'M', 'محمد'), - (0xFDF5, 'M', 'صلعم'), - (0xFDF6, 'M', 'رسول'), - (0xFDF7, 'M', 'عليه'), - (0xFDF8, 'M', 'وسلم'), - (0xFDF9, 'M', 'صلى'), - (0xFDFA, '3', 'صلى الله عليه وسلم'), - (0xFDFB, '3', 'جل جلاله'), - (0xFDFC, 'M', 'ریال'), - (0xFDFD, 'V'), - (0xFE00, 'I'), - (0xFE10, '3', ','), - (0xFE11, 'M', '、'), - (0xFE12, 'X'), - (0xFE13, '3', ':'), - (0xFE14, '3', ';'), - (0xFE15, '3', '!'), - (0xFE16, '3', '?'), - (0xFE17, 'M', '〖'), - (0xFE18, 'M', '〗'), - (0xFE19, 'X'), - (0xFE20, 'V'), - (0xFE30, 'X'), - (0xFE31, 'M', '—'), - (0xFE32, 'M', '–'), - (0xFE33, '3', '_'), - (0xFE35, '3', '('), - (0xFE36, '3', ')'), - (0xFE37, '3', '{'), - (0xFE38, '3', '}'), - (0xFE39, 'M', '〔'), - (0xFE3A, 'M', '〕'), - (0xFE3B, 'M', '【'), - (0xFE3C, 'M', '】'), - (0xFE3D, 'M', '《'), - (0xFE3E, 'M', '》'), - (0xFE3F, 'M', '〈'), - (0xFE40, 'M', '〉'), - (0xFE41, 'M', '「'), - (0xFE42, 'M', '」'), - (0xFE43, 'M', '『'), - (0xFE44, 'M', '』'), - (0xFE45, 'V'), - (0xFE47, '3', '['), - (0xFE48, '3', ']'), - (0xFE49, '3', ' ̅'), - (0xFE4D, '3', '_'), - (0xFE50, '3', ','), - (0xFE51, 'M', '、'), - (0xFE52, 'X'), - (0xFE54, '3', ';'), - (0xFE55, '3', ':'), - (0xFE56, '3', '?'), - (0xFE57, '3', '!'), - (0xFE58, 'M', '—'), - (0xFE59, '3', '('), - (0xFE5A, '3', ')'), - (0xFE5B, '3', '{'), - (0xFE5C, '3', '}'), - (0xFE5D, 'M', '〔'), - ] - -def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFE5E, 'M', '〕'), - (0xFE5F, '3', '#'), - (0xFE60, '3', '&'), - (0xFE61, '3', '*'), - (0xFE62, '3', '+'), - (0xFE63, 'M', '-'), - (0xFE64, '3', '<'), - (0xFE65, '3', '>'), - (0xFE66, '3', '='), - (0xFE67, 'X'), - (0xFE68, '3', '\\'), - (0xFE69, '3', '$'), - (0xFE6A, '3', '%'), - (0xFE6B, '3', '@'), - (0xFE6C, 'X'), - (0xFE70, '3', ' ً'), - (0xFE71, 'M', 'ـً'), - (0xFE72, '3', ' ٌ'), - (0xFE73, 'V'), - (0xFE74, '3', ' ٍ'), - (0xFE75, 'X'), - (0xFE76, '3', ' َ'), - (0xFE77, 'M', 'ـَ'), - (0xFE78, '3', ' ُ'), - (0xFE79, 'M', 'ـُ'), - (0xFE7A, '3', ' ِ'), - (0xFE7B, 'M', 'ـِ'), - (0xFE7C, '3', ' ّ'), - (0xFE7D, 'M', 'ـّ'), - (0xFE7E, '3', ' ْ'), - (0xFE7F, 'M', 'ـْ'), - (0xFE80, 'M', 'ء'), - (0xFE81, 'M', 'آ'), - (0xFE83, 'M', 'أ'), - (0xFE85, 'M', 'ؤ'), - (0xFE87, 'M', 'إ'), - (0xFE89, 'M', 'ئ'), - (0xFE8D, 'M', 'ا'), - (0xFE8F, 'M', 'ب'), - (0xFE93, 'M', 'ة'), - (0xFE95, 'M', 'ت'), - (0xFE99, 'M', 'ث'), - (0xFE9D, 'M', 'ج'), - (0xFEA1, 'M', 'ح'), - (0xFEA5, 'M', 'خ'), - (0xFEA9, 'M', 'د'), - (0xFEAB, 'M', 'ذ'), - (0xFEAD, 'M', 'ر'), - (0xFEAF, 'M', 'ز'), - (0xFEB1, 'M', 'س'), - (0xFEB5, 'M', 'ش'), - (0xFEB9, 'M', 'ص'), - (0xFEBD, 'M', 'ض'), - (0xFEC1, 'M', 'ط'), - (0xFEC5, 'M', 'ظ'), - (0xFEC9, 'M', 'ع'), - (0xFECD, 'M', 'غ'), - (0xFED1, 'M', 'ف'), - (0xFED5, 'M', 'ق'), - (0xFED9, 'M', 'ك'), - (0xFEDD, 'M', 'ل'), - (0xFEE1, 'M', 'م'), - (0xFEE5, 'M', 'ن'), - (0xFEE9, 'M', 'ه'), - (0xFEED, 'M', 'و'), - (0xFEEF, 'M', 'ى'), - (0xFEF1, 'M', 'ي'), - (0xFEF5, 'M', 'لآ'), - (0xFEF7, 'M', 'لأ'), - (0xFEF9, 'M', 'لإ'), - (0xFEFB, 'M', 'لا'), - (0xFEFD, 'X'), - (0xFEFF, 'I'), - (0xFF00, 'X'), - (0xFF01, '3', '!'), - (0xFF02, '3', '"'), - (0xFF03, '3', '#'), - (0xFF04, '3', '$'), - (0xFF05, '3', '%'), - (0xFF06, '3', '&'), - (0xFF07, '3', '\''), - (0xFF08, '3', '('), - (0xFF09, '3', ')'), - (0xFF0A, '3', '*'), - (0xFF0B, '3', '+'), - (0xFF0C, '3', ','), - (0xFF0D, 'M', '-'), - (0xFF0E, 'M', '.'), - (0xFF0F, '3', '/'), - (0xFF10, 'M', '0'), - (0xFF11, 'M', '1'), - (0xFF12, 'M', '2'), - (0xFF13, 'M', '3'), - (0xFF14, 'M', '4'), - (0xFF15, 'M', '5'), - (0xFF16, 'M', '6'), - (0xFF17, 'M', '7'), - (0xFF18, 'M', '8'), - (0xFF19, 'M', '9'), - (0xFF1A, '3', ':'), - ] - -def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF1B, '3', ';'), - (0xFF1C, '3', '<'), - (0xFF1D, '3', '='), - (0xFF1E, '3', '>'), - (0xFF1F, '3', '?'), - (0xFF20, '3', '@'), - (0xFF21, 'M', 'a'), - (0xFF22, 'M', 'b'), - (0xFF23, 'M', 'c'), - (0xFF24, 'M', 'd'), - (0xFF25, 'M', 'e'), - (0xFF26, 'M', 'f'), - (0xFF27, 'M', 'g'), - (0xFF28, 'M', 'h'), - (0xFF29, 'M', 'i'), - (0xFF2A, 'M', 'j'), - (0xFF2B, 'M', 'k'), - (0xFF2C, 'M', 'l'), - (0xFF2D, 'M', 'm'), - (0xFF2E, 'M', 'n'), - (0xFF2F, 'M', 'o'), - (0xFF30, 'M', 'p'), - (0xFF31, 'M', 'q'), - (0xFF32, 'M', 'r'), - (0xFF33, 'M', 's'), - (0xFF34, 'M', 't'), - (0xFF35, 'M', 'u'), - (0xFF36, 'M', 'v'), - (0xFF37, 'M', 'w'), - (0xFF38, 'M', 'x'), - (0xFF39, 'M', 'y'), - (0xFF3A, 'M', 'z'), - (0xFF3B, '3', '['), - (0xFF3C, '3', '\\'), - (0xFF3D, '3', ']'), - (0xFF3E, '3', '^'), - (0xFF3F, '3', '_'), - (0xFF40, '3', '`'), - (0xFF41, 'M', 'a'), - (0xFF42, 'M', 'b'), - (0xFF43, 'M', 'c'), - (0xFF44, 'M', 'd'), - (0xFF45, 'M', 'e'), - (0xFF46, 'M', 'f'), - (0xFF47, 'M', 'g'), - (0xFF48, 'M', 'h'), - (0xFF49, 'M', 'i'), - (0xFF4A, 'M', 'j'), - (0xFF4B, 'M', 'k'), - (0xFF4C, 'M', 'l'), - (0xFF4D, 'M', 'm'), - (0xFF4E, 'M', 'n'), - (0xFF4F, 'M', 'o'), - (0xFF50, 'M', 'p'), - (0xFF51, 'M', 'q'), - (0xFF52, 'M', 'r'), - (0xFF53, 'M', 's'), - (0xFF54, 'M', 't'), - (0xFF55, 'M', 'u'), - (0xFF56, 'M', 'v'), - (0xFF57, 'M', 'w'), - (0xFF58, 'M', 'x'), - (0xFF59, 'M', 'y'), - (0xFF5A, 'M', 'z'), - (0xFF5B, '3', '{'), - (0xFF5C, '3', '|'), - (0xFF5D, '3', '}'), - (0xFF5E, '3', '~'), - (0xFF5F, 'M', '⦅'), - (0xFF60, 'M', '⦆'), - (0xFF61, 'M', '.'), - (0xFF62, 'M', '「'), - (0xFF63, 'M', '」'), - (0xFF64, 'M', '、'), - (0xFF65, 'M', '・'), - (0xFF66, 'M', 'ヲ'), - (0xFF67, 'M', 'ァ'), - (0xFF68, 'M', 'ィ'), - (0xFF69, 'M', 'ゥ'), - (0xFF6A, 'M', 'ェ'), - (0xFF6B, 'M', 'ォ'), - (0xFF6C, 'M', 'ャ'), - (0xFF6D, 'M', 'ュ'), - (0xFF6E, 'M', 'ョ'), - (0xFF6F, 'M', 'ッ'), - (0xFF70, 'M', 'ー'), - (0xFF71, 'M', 'ア'), - (0xFF72, 'M', 'イ'), - (0xFF73, 'M', 'ウ'), - (0xFF74, 'M', 'エ'), - (0xFF75, 'M', 'オ'), - (0xFF76, 'M', 'カ'), - (0xFF77, 'M', 'キ'), - (0xFF78, 'M', 'ク'), - (0xFF79, 'M', 'ケ'), - (0xFF7A, 'M', 'コ'), - (0xFF7B, 'M', 'サ'), - (0xFF7C, 'M', 'シ'), - (0xFF7D, 'M', 'ス'), - (0xFF7E, 'M', 'セ'), - ] - -def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF7F, 'M', 'ソ'), - (0xFF80, 'M', 'タ'), - (0xFF81, 'M', 'チ'), - (0xFF82, 'M', 'ツ'), - (0xFF83, 'M', 'テ'), - (0xFF84, 'M', 'ト'), - (0xFF85, 'M', 'ナ'), - (0xFF86, 'M', 'ニ'), - (0xFF87, 'M', 'ヌ'), - (0xFF88, 'M', 'ネ'), - (0xFF89, 'M', 'ノ'), - (0xFF8A, 'M', 'ハ'), - (0xFF8B, 'M', 'ヒ'), - (0xFF8C, 'M', 'フ'), - (0xFF8D, 'M', 'ヘ'), - (0xFF8E, 'M', 'ホ'), - (0xFF8F, 'M', 'マ'), - (0xFF90, 'M', 'ミ'), - (0xFF91, 'M', 'ム'), - (0xFF92, 'M', 'メ'), - (0xFF93, 'M', 'モ'), - (0xFF94, 'M', 'ヤ'), - (0xFF95, 'M', 'ユ'), - (0xFF96, 'M', 'ヨ'), - (0xFF97, 'M', 'ラ'), - (0xFF98, 'M', 'リ'), - (0xFF99, 'M', 'ル'), - (0xFF9A, 'M', 'レ'), - (0xFF9B, 'M', 'ロ'), - (0xFF9C, 'M', 'ワ'), - (0xFF9D, 'M', 'ン'), - (0xFF9E, 'M', '゙'), - (0xFF9F, 'M', '゚'), - (0xFFA0, 'X'), - (0xFFA1, 'M', 'ᄀ'), - (0xFFA2, 'M', 'ᄁ'), - (0xFFA3, 'M', 'ᆪ'), - (0xFFA4, 'M', 'ᄂ'), - (0xFFA5, 'M', 'ᆬ'), - (0xFFA6, 'M', 'ᆭ'), - (0xFFA7, 'M', 'ᄃ'), - (0xFFA8, 'M', 'ᄄ'), - (0xFFA9, 'M', 'ᄅ'), - (0xFFAA, 'M', 'ᆰ'), - (0xFFAB, 'M', 'ᆱ'), - (0xFFAC, 'M', 'ᆲ'), - (0xFFAD, 'M', 'ᆳ'), - (0xFFAE, 'M', 'ᆴ'), - (0xFFAF, 'M', 'ᆵ'), - (0xFFB0, 'M', 'ᄚ'), - (0xFFB1, 'M', 'ᄆ'), - (0xFFB2, 'M', 'ᄇ'), - (0xFFB3, 'M', 'ᄈ'), - (0xFFB4, 'M', 'ᄡ'), - (0xFFB5, 'M', 'ᄉ'), - (0xFFB6, 'M', 'ᄊ'), - (0xFFB7, 'M', 'ᄋ'), - (0xFFB8, 'M', 'ᄌ'), - (0xFFB9, 'M', 'ᄍ'), - (0xFFBA, 'M', 'ᄎ'), - (0xFFBB, 'M', 'ᄏ'), - (0xFFBC, 'M', 'ᄐ'), - (0xFFBD, 'M', 'ᄑ'), - (0xFFBE, 'M', 'ᄒ'), - (0xFFBF, 'X'), - (0xFFC2, 'M', 'ᅡ'), - (0xFFC3, 'M', 'ᅢ'), - (0xFFC4, 'M', 'ᅣ'), - (0xFFC5, 'M', 'ᅤ'), - (0xFFC6, 'M', 'ᅥ'), - (0xFFC7, 'M', 'ᅦ'), - (0xFFC8, 'X'), - (0xFFCA, 'M', 'ᅧ'), - (0xFFCB, 'M', 'ᅨ'), - (0xFFCC, 'M', 'ᅩ'), - (0xFFCD, 'M', 'ᅪ'), - (0xFFCE, 'M', 'ᅫ'), - (0xFFCF, 'M', 'ᅬ'), - (0xFFD0, 'X'), - (0xFFD2, 'M', 'ᅭ'), - (0xFFD3, 'M', 'ᅮ'), - (0xFFD4, 'M', 'ᅯ'), - (0xFFD5, 'M', 'ᅰ'), - (0xFFD6, 'M', 'ᅱ'), - (0xFFD7, 'M', 'ᅲ'), - (0xFFD8, 'X'), - (0xFFDA, 'M', 'ᅳ'), - (0xFFDB, 'M', 'ᅴ'), - (0xFFDC, 'M', 'ᅵ'), - (0xFFDD, 'X'), - (0xFFE0, 'M', '¢'), - (0xFFE1, 'M', '£'), - (0xFFE2, 'M', '¬'), - (0xFFE3, '3', ' ̄'), - (0xFFE4, 'M', '¦'), - (0xFFE5, 'M', '¥'), - (0xFFE6, 'M', '₩'), - (0xFFE7, 'X'), - (0xFFE8, 'M', '│'), - (0xFFE9, 'M', '←'), - ] - -def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFFEA, 'M', '↑'), - (0xFFEB, 'M', '→'), - (0xFFEC, 'M', '↓'), - (0xFFED, 'M', '■'), - (0xFFEE, 'M', '○'), - (0xFFEF, 'X'), - (0x10000, 'V'), - (0x1000C, 'X'), - (0x1000D, 'V'), - (0x10027, 'X'), - (0x10028, 'V'), - (0x1003B, 'X'), - (0x1003C, 'V'), - (0x1003E, 'X'), - (0x1003F, 'V'), - (0x1004E, 'X'), - (0x10050, 'V'), - (0x1005E, 'X'), - (0x10080, 'V'), - (0x100FB, 'X'), - (0x10100, 'V'), - (0x10103, 'X'), - (0x10107, 'V'), - (0x10134, 'X'), - (0x10137, 'V'), - (0x1018F, 'X'), - (0x10190, 'V'), - (0x1019D, 'X'), - (0x101A0, 'V'), - (0x101A1, 'X'), - (0x101D0, 'V'), - (0x101FE, 'X'), - (0x10280, 'V'), - (0x1029D, 'X'), - (0x102A0, 'V'), - (0x102D1, 'X'), - (0x102E0, 'V'), - (0x102FC, 'X'), - (0x10300, 'V'), - (0x10324, 'X'), - (0x1032D, 'V'), - (0x1034B, 'X'), - (0x10350, 'V'), - (0x1037B, 'X'), - (0x10380, 'V'), - (0x1039E, 'X'), - (0x1039F, 'V'), - (0x103C4, 'X'), - (0x103C8, 'V'), - (0x103D6, 'X'), - (0x10400, 'M', '𐐨'), - (0x10401, 'M', '𐐩'), - (0x10402, 'M', '𐐪'), - (0x10403, 'M', '𐐫'), - (0x10404, 'M', '𐐬'), - (0x10405, 'M', '𐐭'), - (0x10406, 'M', '𐐮'), - (0x10407, 'M', '𐐯'), - (0x10408, 'M', '𐐰'), - (0x10409, 'M', '𐐱'), - (0x1040A, 'M', '𐐲'), - (0x1040B, 'M', '𐐳'), - (0x1040C, 'M', '𐐴'), - (0x1040D, 'M', '𐐵'), - (0x1040E, 'M', '𐐶'), - (0x1040F, 'M', '𐐷'), - (0x10410, 'M', '𐐸'), - (0x10411, 'M', '𐐹'), - (0x10412, 'M', '𐐺'), - (0x10413, 'M', '𐐻'), - (0x10414, 'M', '𐐼'), - (0x10415, 'M', '𐐽'), - (0x10416, 'M', '𐐾'), - (0x10417, 'M', '𐐿'), - (0x10418, 'M', '𐑀'), - (0x10419, 'M', '𐑁'), - (0x1041A, 'M', '𐑂'), - (0x1041B, 'M', '𐑃'), - (0x1041C, 'M', '𐑄'), - (0x1041D, 'M', '𐑅'), - (0x1041E, 'M', '𐑆'), - (0x1041F, 'M', '𐑇'), - (0x10420, 'M', '𐑈'), - (0x10421, 'M', '𐑉'), - (0x10422, 'M', '𐑊'), - (0x10423, 'M', '𐑋'), - (0x10424, 'M', '𐑌'), - (0x10425, 'M', '𐑍'), - (0x10426, 'M', '𐑎'), - (0x10427, 'M', '𐑏'), - (0x10428, 'V'), - (0x1049E, 'X'), - (0x104A0, 'V'), - (0x104AA, 'X'), - (0x104B0, 'M', '𐓘'), - (0x104B1, 'M', '𐓙'), - (0x104B2, 'M', '𐓚'), - (0x104B3, 'M', '𐓛'), - (0x104B4, 'M', '𐓜'), - (0x104B5, 'M', '𐓝'), - ] - -def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x104B6, 'M', '𐓞'), - (0x104B7, 'M', '𐓟'), - (0x104B8, 'M', '𐓠'), - (0x104B9, 'M', '𐓡'), - (0x104BA, 'M', '𐓢'), - (0x104BB, 'M', '𐓣'), - (0x104BC, 'M', '𐓤'), - (0x104BD, 'M', '𐓥'), - (0x104BE, 'M', '𐓦'), - (0x104BF, 'M', '𐓧'), - (0x104C0, 'M', '𐓨'), - (0x104C1, 'M', '𐓩'), - (0x104C2, 'M', '𐓪'), - (0x104C3, 'M', '𐓫'), - (0x104C4, 'M', '𐓬'), - (0x104C5, 'M', '𐓭'), - (0x104C6, 'M', '𐓮'), - (0x104C7, 'M', '𐓯'), - (0x104C8, 'M', '𐓰'), - (0x104C9, 'M', '𐓱'), - (0x104CA, 'M', '𐓲'), - (0x104CB, 'M', '𐓳'), - (0x104CC, 'M', '𐓴'), - (0x104CD, 'M', '𐓵'), - (0x104CE, 'M', '𐓶'), - (0x104CF, 'M', '𐓷'), - (0x104D0, 'M', '𐓸'), - (0x104D1, 'M', '𐓹'), - (0x104D2, 'M', '𐓺'), - (0x104D3, 'M', '𐓻'), - (0x104D4, 'X'), - (0x104D8, 'V'), - (0x104FC, 'X'), - (0x10500, 'V'), - (0x10528, 'X'), - (0x10530, 'V'), - (0x10564, 'X'), - (0x1056F, 'V'), - (0x10570, 'M', '𐖗'), - (0x10571, 'M', '𐖘'), - (0x10572, 'M', '𐖙'), - (0x10573, 'M', '𐖚'), - (0x10574, 'M', '𐖛'), - (0x10575, 'M', '𐖜'), - (0x10576, 'M', '𐖝'), - (0x10577, 'M', '𐖞'), - (0x10578, 'M', '𐖟'), - (0x10579, 'M', '𐖠'), - (0x1057A, 'M', '𐖡'), - (0x1057B, 'X'), - (0x1057C, 'M', '𐖣'), - (0x1057D, 'M', '𐖤'), - (0x1057E, 'M', '𐖥'), - (0x1057F, 'M', '𐖦'), - (0x10580, 'M', '𐖧'), - (0x10581, 'M', '𐖨'), - (0x10582, 'M', '𐖩'), - (0x10583, 'M', '𐖪'), - (0x10584, 'M', '𐖫'), - (0x10585, 'M', '𐖬'), - (0x10586, 'M', '𐖭'), - (0x10587, 'M', '𐖮'), - (0x10588, 'M', '𐖯'), - (0x10589, 'M', '𐖰'), - (0x1058A, 'M', '𐖱'), - (0x1058B, 'X'), - (0x1058C, 'M', '𐖳'), - (0x1058D, 'M', '𐖴'), - (0x1058E, 'M', '𐖵'), - (0x1058F, 'M', '𐖶'), - (0x10590, 'M', '𐖷'), - (0x10591, 'M', '𐖸'), - (0x10592, 'M', '𐖹'), - (0x10593, 'X'), - (0x10594, 'M', '𐖻'), - (0x10595, 'M', '𐖼'), - (0x10596, 'X'), - (0x10597, 'V'), - (0x105A2, 'X'), - (0x105A3, 'V'), - (0x105B2, 'X'), - (0x105B3, 'V'), - (0x105BA, 'X'), - (0x105BB, 'V'), - (0x105BD, 'X'), - (0x10600, 'V'), - (0x10737, 'X'), - (0x10740, 'V'), - (0x10756, 'X'), - (0x10760, 'V'), - (0x10768, 'X'), - (0x10780, 'V'), - (0x10781, 'M', 'ː'), - (0x10782, 'M', 'ˑ'), - (0x10783, 'M', 'æ'), - (0x10784, 'M', 'ʙ'), - (0x10785, 'M', 'ɓ'), - (0x10786, 'X'), - (0x10787, 'M', 'ʣ'), - (0x10788, 'M', 'ꭦ'), - ] - -def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10789, 'M', 'ʥ'), - (0x1078A, 'M', 'ʤ'), - (0x1078B, 'M', 'ɖ'), - (0x1078C, 'M', 'ɗ'), - (0x1078D, 'M', 'ᶑ'), - (0x1078E, 'M', 'ɘ'), - (0x1078F, 'M', 'ɞ'), - (0x10790, 'M', 'ʩ'), - (0x10791, 'M', 'ɤ'), - (0x10792, 'M', 'ɢ'), - (0x10793, 'M', 'ɠ'), - (0x10794, 'M', 'ʛ'), - (0x10795, 'M', 'ħ'), - (0x10796, 'M', 'ʜ'), - (0x10797, 'M', 'ɧ'), - (0x10798, 'M', 'ʄ'), - (0x10799, 'M', 'ʪ'), - (0x1079A, 'M', 'ʫ'), - (0x1079B, 'M', 'ɬ'), - (0x1079C, 'M', '𝼄'), - (0x1079D, 'M', 'ꞎ'), - (0x1079E, 'M', 'ɮ'), - (0x1079F, 'M', '𝼅'), - (0x107A0, 'M', 'ʎ'), - (0x107A1, 'M', '𝼆'), - (0x107A2, 'M', 'ø'), - (0x107A3, 'M', 'ɶ'), - (0x107A4, 'M', 'ɷ'), - (0x107A5, 'M', 'q'), - (0x107A6, 'M', 'ɺ'), - (0x107A7, 'M', '𝼈'), - (0x107A8, 'M', 'ɽ'), - (0x107A9, 'M', 'ɾ'), - (0x107AA, 'M', 'ʀ'), - (0x107AB, 'M', 'ʨ'), - (0x107AC, 'M', 'ʦ'), - (0x107AD, 'M', 'ꭧ'), - (0x107AE, 'M', 'ʧ'), - (0x107AF, 'M', 'ʈ'), - (0x107B0, 'M', 'ⱱ'), - (0x107B1, 'X'), - (0x107B2, 'M', 'ʏ'), - (0x107B3, 'M', 'ʡ'), - (0x107B4, 'M', 'ʢ'), - (0x107B5, 'M', 'ʘ'), - (0x107B6, 'M', 'ǀ'), - (0x107B7, 'M', 'ǁ'), - (0x107B8, 'M', 'ǂ'), - (0x107B9, 'M', '𝼊'), - (0x107BA, 'M', '𝼞'), - (0x107BB, 'X'), - (0x10800, 'V'), - (0x10806, 'X'), - (0x10808, 'V'), - (0x10809, 'X'), - (0x1080A, 'V'), - (0x10836, 'X'), - (0x10837, 'V'), - (0x10839, 'X'), - (0x1083C, 'V'), - (0x1083D, 'X'), - (0x1083F, 'V'), - (0x10856, 'X'), - (0x10857, 'V'), - (0x1089F, 'X'), - (0x108A7, 'V'), - (0x108B0, 'X'), - (0x108E0, 'V'), - (0x108F3, 'X'), - (0x108F4, 'V'), - (0x108F6, 'X'), - (0x108FB, 'V'), - (0x1091C, 'X'), - (0x1091F, 'V'), - (0x1093A, 'X'), - (0x1093F, 'V'), - (0x10940, 'X'), - (0x10980, 'V'), - (0x109B8, 'X'), - (0x109BC, 'V'), - (0x109D0, 'X'), - (0x109D2, 'V'), - (0x10A04, 'X'), - (0x10A05, 'V'), - (0x10A07, 'X'), - (0x10A0C, 'V'), - (0x10A14, 'X'), - (0x10A15, 'V'), - (0x10A18, 'X'), - (0x10A19, 'V'), - (0x10A36, 'X'), - (0x10A38, 'V'), - (0x10A3B, 'X'), - (0x10A3F, 'V'), - (0x10A49, 'X'), - (0x10A50, 'V'), - (0x10A59, 'X'), - (0x10A60, 'V'), - (0x10AA0, 'X'), - (0x10AC0, 'V'), - ] - -def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10AE7, 'X'), - (0x10AEB, 'V'), - (0x10AF7, 'X'), - (0x10B00, 'V'), - (0x10B36, 'X'), - (0x10B39, 'V'), - (0x10B56, 'X'), - (0x10B58, 'V'), - (0x10B73, 'X'), - (0x10B78, 'V'), - (0x10B92, 'X'), - (0x10B99, 'V'), - (0x10B9D, 'X'), - (0x10BA9, 'V'), - (0x10BB0, 'X'), - (0x10C00, 'V'), - (0x10C49, 'X'), - (0x10C80, 'M', '𐳀'), - (0x10C81, 'M', '𐳁'), - (0x10C82, 'M', '𐳂'), - (0x10C83, 'M', '𐳃'), - (0x10C84, 'M', '𐳄'), - (0x10C85, 'M', '𐳅'), - (0x10C86, 'M', '𐳆'), - (0x10C87, 'M', '𐳇'), - (0x10C88, 'M', '𐳈'), - (0x10C89, 'M', '𐳉'), - (0x10C8A, 'M', '𐳊'), - (0x10C8B, 'M', '𐳋'), - (0x10C8C, 'M', '𐳌'), - (0x10C8D, 'M', '𐳍'), - (0x10C8E, 'M', '𐳎'), - (0x10C8F, 'M', '𐳏'), - (0x10C90, 'M', '𐳐'), - (0x10C91, 'M', '𐳑'), - (0x10C92, 'M', '𐳒'), - (0x10C93, 'M', '𐳓'), - (0x10C94, 'M', '𐳔'), - (0x10C95, 'M', '𐳕'), - (0x10C96, 'M', '𐳖'), - (0x10C97, 'M', '𐳗'), - (0x10C98, 'M', '𐳘'), - (0x10C99, 'M', '𐳙'), - (0x10C9A, 'M', '𐳚'), - (0x10C9B, 'M', '𐳛'), - (0x10C9C, 'M', '𐳜'), - (0x10C9D, 'M', '𐳝'), - (0x10C9E, 'M', '𐳞'), - (0x10C9F, 'M', '𐳟'), - (0x10CA0, 'M', '𐳠'), - (0x10CA1, 'M', '𐳡'), - (0x10CA2, 'M', '𐳢'), - (0x10CA3, 'M', '𐳣'), - (0x10CA4, 'M', '𐳤'), - (0x10CA5, 'M', '𐳥'), - (0x10CA6, 'M', '𐳦'), - (0x10CA7, 'M', '𐳧'), - (0x10CA8, 'M', '𐳨'), - (0x10CA9, 'M', '𐳩'), - (0x10CAA, 'M', '𐳪'), - (0x10CAB, 'M', '𐳫'), - (0x10CAC, 'M', '𐳬'), - (0x10CAD, 'M', '𐳭'), - (0x10CAE, 'M', '𐳮'), - (0x10CAF, 'M', '𐳯'), - (0x10CB0, 'M', '𐳰'), - (0x10CB1, 'M', '𐳱'), - (0x10CB2, 'M', '𐳲'), - (0x10CB3, 'X'), - (0x10CC0, 'V'), - (0x10CF3, 'X'), - (0x10CFA, 'V'), - (0x10D28, 'X'), - (0x10D30, 'V'), - (0x10D3A, 'X'), - (0x10E60, 'V'), - (0x10E7F, 'X'), - (0x10E80, 'V'), - (0x10EAA, 'X'), - (0x10EAB, 'V'), - (0x10EAE, 'X'), - (0x10EB0, 'V'), - (0x10EB2, 'X'), - (0x10EFD, 'V'), - (0x10F28, 'X'), - (0x10F30, 'V'), - (0x10F5A, 'X'), - (0x10F70, 'V'), - (0x10F8A, 'X'), - (0x10FB0, 'V'), - (0x10FCC, 'X'), - (0x10FE0, 'V'), - (0x10FF7, 'X'), - (0x11000, 'V'), - (0x1104E, 'X'), - (0x11052, 'V'), - (0x11076, 'X'), - (0x1107F, 'V'), - (0x110BD, 'X'), - (0x110BE, 'V'), - ] - -def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x110C3, 'X'), - (0x110D0, 'V'), - (0x110E9, 'X'), - (0x110F0, 'V'), - (0x110FA, 'X'), - (0x11100, 'V'), - (0x11135, 'X'), - (0x11136, 'V'), - (0x11148, 'X'), - (0x11150, 'V'), - (0x11177, 'X'), - (0x11180, 'V'), - (0x111E0, 'X'), - (0x111E1, 'V'), - (0x111F5, 'X'), - (0x11200, 'V'), - (0x11212, 'X'), - (0x11213, 'V'), - (0x11242, 'X'), - (0x11280, 'V'), - (0x11287, 'X'), - (0x11288, 'V'), - (0x11289, 'X'), - (0x1128A, 'V'), - (0x1128E, 'X'), - (0x1128F, 'V'), - (0x1129E, 'X'), - (0x1129F, 'V'), - (0x112AA, 'X'), - (0x112B0, 'V'), - (0x112EB, 'X'), - (0x112F0, 'V'), - (0x112FA, 'X'), - (0x11300, 'V'), - (0x11304, 'X'), - (0x11305, 'V'), - (0x1130D, 'X'), - (0x1130F, 'V'), - (0x11311, 'X'), - (0x11313, 'V'), - (0x11329, 'X'), - (0x1132A, 'V'), - (0x11331, 'X'), - (0x11332, 'V'), - (0x11334, 'X'), - (0x11335, 'V'), - (0x1133A, 'X'), - (0x1133B, 'V'), - (0x11345, 'X'), - (0x11347, 'V'), - (0x11349, 'X'), - (0x1134B, 'V'), - (0x1134E, 'X'), - (0x11350, 'V'), - (0x11351, 'X'), - (0x11357, 'V'), - (0x11358, 'X'), - (0x1135D, 'V'), - (0x11364, 'X'), - (0x11366, 'V'), - (0x1136D, 'X'), - (0x11370, 'V'), - (0x11375, 'X'), - (0x11400, 'V'), - (0x1145C, 'X'), - (0x1145D, 'V'), - (0x11462, 'X'), - (0x11480, 'V'), - (0x114C8, 'X'), - (0x114D0, 'V'), - (0x114DA, 'X'), - (0x11580, 'V'), - (0x115B6, 'X'), - (0x115B8, 'V'), - (0x115DE, 'X'), - (0x11600, 'V'), - (0x11645, 'X'), - (0x11650, 'V'), - (0x1165A, 'X'), - (0x11660, 'V'), - (0x1166D, 'X'), - (0x11680, 'V'), - (0x116BA, 'X'), - (0x116C0, 'V'), - (0x116CA, 'X'), - (0x11700, 'V'), - (0x1171B, 'X'), - (0x1171D, 'V'), - (0x1172C, 'X'), - (0x11730, 'V'), - (0x11747, 'X'), - (0x11800, 'V'), - (0x1183C, 'X'), - (0x118A0, 'M', '𑣀'), - (0x118A1, 'M', '𑣁'), - (0x118A2, 'M', '𑣂'), - (0x118A3, 'M', '𑣃'), - (0x118A4, 'M', '𑣄'), - (0x118A5, 'M', '𑣅'), - (0x118A6, 'M', '𑣆'), - ] - -def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x118A7, 'M', '𑣇'), - (0x118A8, 'M', '𑣈'), - (0x118A9, 'M', '𑣉'), - (0x118AA, 'M', '𑣊'), - (0x118AB, 'M', '𑣋'), - (0x118AC, 'M', '𑣌'), - (0x118AD, 'M', '𑣍'), - (0x118AE, 'M', '𑣎'), - (0x118AF, 'M', '𑣏'), - (0x118B0, 'M', '𑣐'), - (0x118B1, 'M', '𑣑'), - (0x118B2, 'M', '𑣒'), - (0x118B3, 'M', '𑣓'), - (0x118B4, 'M', '𑣔'), - (0x118B5, 'M', '𑣕'), - (0x118B6, 'M', '𑣖'), - (0x118B7, 'M', '𑣗'), - (0x118B8, 'M', '𑣘'), - (0x118B9, 'M', '𑣙'), - (0x118BA, 'M', '𑣚'), - (0x118BB, 'M', '𑣛'), - (0x118BC, 'M', '𑣜'), - (0x118BD, 'M', '𑣝'), - (0x118BE, 'M', '𑣞'), - (0x118BF, 'M', '𑣟'), - (0x118C0, 'V'), - (0x118F3, 'X'), - (0x118FF, 'V'), - (0x11907, 'X'), - (0x11909, 'V'), - (0x1190A, 'X'), - (0x1190C, 'V'), - (0x11914, 'X'), - (0x11915, 'V'), - (0x11917, 'X'), - (0x11918, 'V'), - (0x11936, 'X'), - (0x11937, 'V'), - (0x11939, 'X'), - (0x1193B, 'V'), - (0x11947, 'X'), - (0x11950, 'V'), - (0x1195A, 'X'), - (0x119A0, 'V'), - (0x119A8, 'X'), - (0x119AA, 'V'), - (0x119D8, 'X'), - (0x119DA, 'V'), - (0x119E5, 'X'), - (0x11A00, 'V'), - (0x11A48, 'X'), - (0x11A50, 'V'), - (0x11AA3, 'X'), - (0x11AB0, 'V'), - (0x11AF9, 'X'), - (0x11B00, 'V'), - (0x11B0A, 'X'), - (0x11C00, 'V'), - (0x11C09, 'X'), - (0x11C0A, 'V'), - (0x11C37, 'X'), - (0x11C38, 'V'), - (0x11C46, 'X'), - (0x11C50, 'V'), - (0x11C6D, 'X'), - (0x11C70, 'V'), - (0x11C90, 'X'), - (0x11C92, 'V'), - (0x11CA8, 'X'), - (0x11CA9, 'V'), - (0x11CB7, 'X'), - (0x11D00, 'V'), - (0x11D07, 'X'), - (0x11D08, 'V'), - (0x11D0A, 'X'), - (0x11D0B, 'V'), - (0x11D37, 'X'), - (0x11D3A, 'V'), - (0x11D3B, 'X'), - (0x11D3C, 'V'), - (0x11D3E, 'X'), - (0x11D3F, 'V'), - (0x11D48, 'X'), - (0x11D50, 'V'), - (0x11D5A, 'X'), - (0x11D60, 'V'), - (0x11D66, 'X'), - (0x11D67, 'V'), - (0x11D69, 'X'), - (0x11D6A, 'V'), - (0x11D8F, 'X'), - (0x11D90, 'V'), - (0x11D92, 'X'), - (0x11D93, 'V'), - (0x11D99, 'X'), - (0x11DA0, 'V'), - (0x11DAA, 'X'), - (0x11EE0, 'V'), - (0x11EF9, 'X'), - (0x11F00, 'V'), - ] - -def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x11F11, 'X'), - (0x11F12, 'V'), - (0x11F3B, 'X'), - (0x11F3E, 'V'), - (0x11F5A, 'X'), - (0x11FB0, 'V'), - (0x11FB1, 'X'), - (0x11FC0, 'V'), - (0x11FF2, 'X'), - (0x11FFF, 'V'), - (0x1239A, 'X'), - (0x12400, 'V'), - (0x1246F, 'X'), - (0x12470, 'V'), - (0x12475, 'X'), - (0x12480, 'V'), - (0x12544, 'X'), - (0x12F90, 'V'), - (0x12FF3, 'X'), - (0x13000, 'V'), - (0x13430, 'X'), - (0x13440, 'V'), - (0x13456, 'X'), - (0x14400, 'V'), - (0x14647, 'X'), - (0x16800, 'V'), - (0x16A39, 'X'), - (0x16A40, 'V'), - (0x16A5F, 'X'), - (0x16A60, 'V'), - (0x16A6A, 'X'), - (0x16A6E, 'V'), - (0x16ABF, 'X'), - (0x16AC0, 'V'), - (0x16ACA, 'X'), - (0x16AD0, 'V'), - (0x16AEE, 'X'), - (0x16AF0, 'V'), - (0x16AF6, 'X'), - (0x16B00, 'V'), - (0x16B46, 'X'), - (0x16B50, 'V'), - (0x16B5A, 'X'), - (0x16B5B, 'V'), - (0x16B62, 'X'), - (0x16B63, 'V'), - (0x16B78, 'X'), - (0x16B7D, 'V'), - (0x16B90, 'X'), - (0x16E40, 'M', '𖹠'), - (0x16E41, 'M', '𖹡'), - (0x16E42, 'M', '𖹢'), - (0x16E43, 'M', '𖹣'), - (0x16E44, 'M', '𖹤'), - (0x16E45, 'M', '𖹥'), - (0x16E46, 'M', '𖹦'), - (0x16E47, 'M', '𖹧'), - (0x16E48, 'M', '𖹨'), - (0x16E49, 'M', '𖹩'), - (0x16E4A, 'M', '𖹪'), - (0x16E4B, 'M', '𖹫'), - (0x16E4C, 'M', '𖹬'), - (0x16E4D, 'M', '𖹭'), - (0x16E4E, 'M', '𖹮'), - (0x16E4F, 'M', '𖹯'), - (0x16E50, 'M', '𖹰'), - (0x16E51, 'M', '𖹱'), - (0x16E52, 'M', '𖹲'), - (0x16E53, 'M', '𖹳'), - (0x16E54, 'M', '𖹴'), - (0x16E55, 'M', '𖹵'), - (0x16E56, 'M', '𖹶'), - (0x16E57, 'M', '𖹷'), - (0x16E58, 'M', '𖹸'), - (0x16E59, 'M', '𖹹'), - (0x16E5A, 'M', '𖹺'), - (0x16E5B, 'M', '𖹻'), - (0x16E5C, 'M', '𖹼'), - (0x16E5D, 'M', '𖹽'), - (0x16E5E, 'M', '𖹾'), - (0x16E5F, 'M', '𖹿'), - (0x16E60, 'V'), - (0x16E9B, 'X'), - (0x16F00, 'V'), - (0x16F4B, 'X'), - (0x16F4F, 'V'), - (0x16F88, 'X'), - (0x16F8F, 'V'), - (0x16FA0, 'X'), - (0x16FE0, 'V'), - (0x16FE5, 'X'), - (0x16FF0, 'V'), - (0x16FF2, 'X'), - (0x17000, 'V'), - (0x187F8, 'X'), - (0x18800, 'V'), - (0x18CD6, 'X'), - (0x18D00, 'V'), - (0x18D09, 'X'), - (0x1AFF0, 'V'), - ] - -def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1AFF4, 'X'), - (0x1AFF5, 'V'), - (0x1AFFC, 'X'), - (0x1AFFD, 'V'), - (0x1AFFF, 'X'), - (0x1B000, 'V'), - (0x1B123, 'X'), - (0x1B132, 'V'), - (0x1B133, 'X'), - (0x1B150, 'V'), - (0x1B153, 'X'), - (0x1B155, 'V'), - (0x1B156, 'X'), - (0x1B164, 'V'), - (0x1B168, 'X'), - (0x1B170, 'V'), - (0x1B2FC, 'X'), - (0x1BC00, 'V'), - (0x1BC6B, 'X'), - (0x1BC70, 'V'), - (0x1BC7D, 'X'), - (0x1BC80, 'V'), - (0x1BC89, 'X'), - (0x1BC90, 'V'), - (0x1BC9A, 'X'), - (0x1BC9C, 'V'), - (0x1BCA0, 'I'), - (0x1BCA4, 'X'), - (0x1CF00, 'V'), - (0x1CF2E, 'X'), - (0x1CF30, 'V'), - (0x1CF47, 'X'), - (0x1CF50, 'V'), - (0x1CFC4, 'X'), - (0x1D000, 'V'), - (0x1D0F6, 'X'), - (0x1D100, 'V'), - (0x1D127, 'X'), - (0x1D129, 'V'), - (0x1D15E, 'M', '𝅗𝅥'), - (0x1D15F, 'M', '𝅘𝅥'), - (0x1D160, 'M', '𝅘𝅥𝅮'), - (0x1D161, 'M', '𝅘𝅥𝅯'), - (0x1D162, 'M', '𝅘𝅥𝅰'), - (0x1D163, 'M', '𝅘𝅥𝅱'), - (0x1D164, 'M', '𝅘𝅥𝅲'), - (0x1D165, 'V'), - (0x1D173, 'X'), - (0x1D17B, 'V'), - (0x1D1BB, 'M', '𝆹𝅥'), - (0x1D1BC, 'M', '𝆺𝅥'), - (0x1D1BD, 'M', '𝆹𝅥𝅮'), - (0x1D1BE, 'M', '𝆺𝅥𝅮'), - (0x1D1BF, 'M', '𝆹𝅥𝅯'), - (0x1D1C0, 'M', '𝆺𝅥𝅯'), - (0x1D1C1, 'V'), - (0x1D1EB, 'X'), - (0x1D200, 'V'), - (0x1D246, 'X'), - (0x1D2C0, 'V'), - (0x1D2D4, 'X'), - (0x1D2E0, 'V'), - (0x1D2F4, 'X'), - (0x1D300, 'V'), - (0x1D357, 'X'), - (0x1D360, 'V'), - (0x1D379, 'X'), - (0x1D400, 'M', 'a'), - (0x1D401, 'M', 'b'), - (0x1D402, 'M', 'c'), - (0x1D403, 'M', 'd'), - (0x1D404, 'M', 'e'), - (0x1D405, 'M', 'f'), - (0x1D406, 'M', 'g'), - (0x1D407, 'M', 'h'), - (0x1D408, 'M', 'i'), - (0x1D409, 'M', 'j'), - (0x1D40A, 'M', 'k'), - (0x1D40B, 'M', 'l'), - (0x1D40C, 'M', 'm'), - (0x1D40D, 'M', 'n'), - (0x1D40E, 'M', 'o'), - (0x1D40F, 'M', 'p'), - (0x1D410, 'M', 'q'), - (0x1D411, 'M', 'r'), - (0x1D412, 'M', 's'), - (0x1D413, 'M', 't'), - (0x1D414, 'M', 'u'), - (0x1D415, 'M', 'v'), - (0x1D416, 'M', 'w'), - (0x1D417, 'M', 'x'), - (0x1D418, 'M', 'y'), - (0x1D419, 'M', 'z'), - (0x1D41A, 'M', 'a'), - (0x1D41B, 'M', 'b'), - (0x1D41C, 'M', 'c'), - (0x1D41D, 'M', 'd'), - (0x1D41E, 'M', 'e'), - (0x1D41F, 'M', 'f'), - (0x1D420, 'M', 'g'), - ] - -def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D421, 'M', 'h'), - (0x1D422, 'M', 'i'), - (0x1D423, 'M', 'j'), - (0x1D424, 'M', 'k'), - (0x1D425, 'M', 'l'), - (0x1D426, 'M', 'm'), - (0x1D427, 'M', 'n'), - (0x1D428, 'M', 'o'), - (0x1D429, 'M', 'p'), - (0x1D42A, 'M', 'q'), - (0x1D42B, 'M', 'r'), - (0x1D42C, 'M', 's'), - (0x1D42D, 'M', 't'), - (0x1D42E, 'M', 'u'), - (0x1D42F, 'M', 'v'), - (0x1D430, 'M', 'w'), - (0x1D431, 'M', 'x'), - (0x1D432, 'M', 'y'), - (0x1D433, 'M', 'z'), - (0x1D434, 'M', 'a'), - (0x1D435, 'M', 'b'), - (0x1D436, 'M', 'c'), - (0x1D437, 'M', 'd'), - (0x1D438, 'M', 'e'), - (0x1D439, 'M', 'f'), - (0x1D43A, 'M', 'g'), - (0x1D43B, 'M', 'h'), - (0x1D43C, 'M', 'i'), - (0x1D43D, 'M', 'j'), - (0x1D43E, 'M', 'k'), - (0x1D43F, 'M', 'l'), - (0x1D440, 'M', 'm'), - (0x1D441, 'M', 'n'), - (0x1D442, 'M', 'o'), - (0x1D443, 'M', 'p'), - (0x1D444, 'M', 'q'), - (0x1D445, 'M', 'r'), - (0x1D446, 'M', 's'), - (0x1D447, 'M', 't'), - (0x1D448, 'M', 'u'), - (0x1D449, 'M', 'v'), - (0x1D44A, 'M', 'w'), - (0x1D44B, 'M', 'x'), - (0x1D44C, 'M', 'y'), - (0x1D44D, 'M', 'z'), - (0x1D44E, 'M', 'a'), - (0x1D44F, 'M', 'b'), - (0x1D450, 'M', 'c'), - (0x1D451, 'M', 'd'), - (0x1D452, 'M', 'e'), - (0x1D453, 'M', 'f'), - (0x1D454, 'M', 'g'), - (0x1D455, 'X'), - (0x1D456, 'M', 'i'), - (0x1D457, 'M', 'j'), - (0x1D458, 'M', 'k'), - (0x1D459, 'M', 'l'), - (0x1D45A, 'M', 'm'), - (0x1D45B, 'M', 'n'), - (0x1D45C, 'M', 'o'), - (0x1D45D, 'M', 'p'), - (0x1D45E, 'M', 'q'), - (0x1D45F, 'M', 'r'), - (0x1D460, 'M', 's'), - (0x1D461, 'M', 't'), - (0x1D462, 'M', 'u'), - (0x1D463, 'M', 'v'), - (0x1D464, 'M', 'w'), - (0x1D465, 'M', 'x'), - (0x1D466, 'M', 'y'), - (0x1D467, 'M', 'z'), - (0x1D468, 'M', 'a'), - (0x1D469, 'M', 'b'), - (0x1D46A, 'M', 'c'), - (0x1D46B, 'M', 'd'), - (0x1D46C, 'M', 'e'), - (0x1D46D, 'M', 'f'), - (0x1D46E, 'M', 'g'), - (0x1D46F, 'M', 'h'), - (0x1D470, 'M', 'i'), - (0x1D471, 'M', 'j'), - (0x1D472, 'M', 'k'), - (0x1D473, 'M', 'l'), - (0x1D474, 'M', 'm'), - (0x1D475, 'M', 'n'), - (0x1D476, 'M', 'o'), - (0x1D477, 'M', 'p'), - (0x1D478, 'M', 'q'), - (0x1D479, 'M', 'r'), - (0x1D47A, 'M', 's'), - (0x1D47B, 'M', 't'), - (0x1D47C, 'M', 'u'), - (0x1D47D, 'M', 'v'), - (0x1D47E, 'M', 'w'), - (0x1D47F, 'M', 'x'), - (0x1D480, 'M', 'y'), - (0x1D481, 'M', 'z'), - (0x1D482, 'M', 'a'), - (0x1D483, 'M', 'b'), - (0x1D484, 'M', 'c'), - ] - -def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D485, 'M', 'd'), - (0x1D486, 'M', 'e'), - (0x1D487, 'M', 'f'), - (0x1D488, 'M', 'g'), - (0x1D489, 'M', 'h'), - (0x1D48A, 'M', 'i'), - (0x1D48B, 'M', 'j'), - (0x1D48C, 'M', 'k'), - (0x1D48D, 'M', 'l'), - (0x1D48E, 'M', 'm'), - (0x1D48F, 'M', 'n'), - (0x1D490, 'M', 'o'), - (0x1D491, 'M', 'p'), - (0x1D492, 'M', 'q'), - (0x1D493, 'M', 'r'), - (0x1D494, 'M', 's'), - (0x1D495, 'M', 't'), - (0x1D496, 'M', 'u'), - (0x1D497, 'M', 'v'), - (0x1D498, 'M', 'w'), - (0x1D499, 'M', 'x'), - (0x1D49A, 'M', 'y'), - (0x1D49B, 'M', 'z'), - (0x1D49C, 'M', 'a'), - (0x1D49D, 'X'), - (0x1D49E, 'M', 'c'), - (0x1D49F, 'M', 'd'), - (0x1D4A0, 'X'), - (0x1D4A2, 'M', 'g'), - (0x1D4A3, 'X'), - (0x1D4A5, 'M', 'j'), - (0x1D4A6, 'M', 'k'), - (0x1D4A7, 'X'), - (0x1D4A9, 'M', 'n'), - (0x1D4AA, 'M', 'o'), - (0x1D4AB, 'M', 'p'), - (0x1D4AC, 'M', 'q'), - (0x1D4AD, 'X'), - (0x1D4AE, 'M', 's'), - (0x1D4AF, 'M', 't'), - (0x1D4B0, 'M', 'u'), - (0x1D4B1, 'M', 'v'), - (0x1D4B2, 'M', 'w'), - (0x1D4B3, 'M', 'x'), - (0x1D4B4, 'M', 'y'), - (0x1D4B5, 'M', 'z'), - (0x1D4B6, 'M', 'a'), - (0x1D4B7, 'M', 'b'), - (0x1D4B8, 'M', 'c'), - (0x1D4B9, 'M', 'd'), - (0x1D4BA, 'X'), - (0x1D4BB, 'M', 'f'), - (0x1D4BC, 'X'), - (0x1D4BD, 'M', 'h'), - (0x1D4BE, 'M', 'i'), - (0x1D4BF, 'M', 'j'), - (0x1D4C0, 'M', 'k'), - (0x1D4C1, 'M', 'l'), - (0x1D4C2, 'M', 'm'), - (0x1D4C3, 'M', 'n'), - (0x1D4C4, 'X'), - (0x1D4C5, 'M', 'p'), - (0x1D4C6, 'M', 'q'), - (0x1D4C7, 'M', 'r'), - (0x1D4C8, 'M', 's'), - (0x1D4C9, 'M', 't'), - (0x1D4CA, 'M', 'u'), - (0x1D4CB, 'M', 'v'), - (0x1D4CC, 'M', 'w'), - (0x1D4CD, 'M', 'x'), - (0x1D4CE, 'M', 'y'), - (0x1D4CF, 'M', 'z'), - (0x1D4D0, 'M', 'a'), - (0x1D4D1, 'M', 'b'), - (0x1D4D2, 'M', 'c'), - (0x1D4D3, 'M', 'd'), - (0x1D4D4, 'M', 'e'), - (0x1D4D5, 'M', 'f'), - (0x1D4D6, 'M', 'g'), - (0x1D4D7, 'M', 'h'), - (0x1D4D8, 'M', 'i'), - (0x1D4D9, 'M', 'j'), - (0x1D4DA, 'M', 'k'), - (0x1D4DB, 'M', 'l'), - (0x1D4DC, 'M', 'm'), - (0x1D4DD, 'M', 'n'), - (0x1D4DE, 'M', 'o'), - (0x1D4DF, 'M', 'p'), - (0x1D4E0, 'M', 'q'), - (0x1D4E1, 'M', 'r'), - (0x1D4E2, 'M', 's'), - (0x1D4E3, 'M', 't'), - (0x1D4E4, 'M', 'u'), - (0x1D4E5, 'M', 'v'), - (0x1D4E6, 'M', 'w'), - (0x1D4E7, 'M', 'x'), - (0x1D4E8, 'M', 'y'), - (0x1D4E9, 'M', 'z'), - (0x1D4EA, 'M', 'a'), - (0x1D4EB, 'M', 'b'), - ] - -def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D4EC, 'M', 'c'), - (0x1D4ED, 'M', 'd'), - (0x1D4EE, 'M', 'e'), - (0x1D4EF, 'M', 'f'), - (0x1D4F0, 'M', 'g'), - (0x1D4F1, 'M', 'h'), - (0x1D4F2, 'M', 'i'), - (0x1D4F3, 'M', 'j'), - (0x1D4F4, 'M', 'k'), - (0x1D4F5, 'M', 'l'), - (0x1D4F6, 'M', 'm'), - (0x1D4F7, 'M', 'n'), - (0x1D4F8, 'M', 'o'), - (0x1D4F9, 'M', 'p'), - (0x1D4FA, 'M', 'q'), - (0x1D4FB, 'M', 'r'), - (0x1D4FC, 'M', 's'), - (0x1D4FD, 'M', 't'), - (0x1D4FE, 'M', 'u'), - (0x1D4FF, 'M', 'v'), - (0x1D500, 'M', 'w'), - (0x1D501, 'M', 'x'), - (0x1D502, 'M', 'y'), - (0x1D503, 'M', 'z'), - (0x1D504, 'M', 'a'), - (0x1D505, 'M', 'b'), - (0x1D506, 'X'), - (0x1D507, 'M', 'd'), - (0x1D508, 'M', 'e'), - (0x1D509, 'M', 'f'), - (0x1D50A, 'M', 'g'), - (0x1D50B, 'X'), - (0x1D50D, 'M', 'j'), - (0x1D50E, 'M', 'k'), - (0x1D50F, 'M', 'l'), - (0x1D510, 'M', 'm'), - (0x1D511, 'M', 'n'), - (0x1D512, 'M', 'o'), - (0x1D513, 'M', 'p'), - (0x1D514, 'M', 'q'), - (0x1D515, 'X'), - (0x1D516, 'M', 's'), - (0x1D517, 'M', 't'), - (0x1D518, 'M', 'u'), - (0x1D519, 'M', 'v'), - (0x1D51A, 'M', 'w'), - (0x1D51B, 'M', 'x'), - (0x1D51C, 'M', 'y'), - (0x1D51D, 'X'), - (0x1D51E, 'M', 'a'), - (0x1D51F, 'M', 'b'), - (0x1D520, 'M', 'c'), - (0x1D521, 'M', 'd'), - (0x1D522, 'M', 'e'), - (0x1D523, 'M', 'f'), - (0x1D524, 'M', 'g'), - (0x1D525, 'M', 'h'), - (0x1D526, 'M', 'i'), - (0x1D527, 'M', 'j'), - (0x1D528, 'M', 'k'), - (0x1D529, 'M', 'l'), - (0x1D52A, 'M', 'm'), - (0x1D52B, 'M', 'n'), - (0x1D52C, 'M', 'o'), - (0x1D52D, 'M', 'p'), - (0x1D52E, 'M', 'q'), - (0x1D52F, 'M', 'r'), - (0x1D530, 'M', 's'), - (0x1D531, 'M', 't'), - (0x1D532, 'M', 'u'), - (0x1D533, 'M', 'v'), - (0x1D534, 'M', 'w'), - (0x1D535, 'M', 'x'), - (0x1D536, 'M', 'y'), - (0x1D537, 'M', 'z'), - (0x1D538, 'M', 'a'), - (0x1D539, 'M', 'b'), - (0x1D53A, 'X'), - (0x1D53B, 'M', 'd'), - (0x1D53C, 'M', 'e'), - (0x1D53D, 'M', 'f'), - (0x1D53E, 'M', 'g'), - (0x1D53F, 'X'), - (0x1D540, 'M', 'i'), - (0x1D541, 'M', 'j'), - (0x1D542, 'M', 'k'), - (0x1D543, 'M', 'l'), - (0x1D544, 'M', 'm'), - (0x1D545, 'X'), - (0x1D546, 'M', 'o'), - (0x1D547, 'X'), - (0x1D54A, 'M', 's'), - (0x1D54B, 'M', 't'), - (0x1D54C, 'M', 'u'), - (0x1D54D, 'M', 'v'), - (0x1D54E, 'M', 'w'), - (0x1D54F, 'M', 'x'), - (0x1D550, 'M', 'y'), - (0x1D551, 'X'), - (0x1D552, 'M', 'a'), - ] - -def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D553, 'M', 'b'), - (0x1D554, 'M', 'c'), - (0x1D555, 'M', 'd'), - (0x1D556, 'M', 'e'), - (0x1D557, 'M', 'f'), - (0x1D558, 'M', 'g'), - (0x1D559, 'M', 'h'), - (0x1D55A, 'M', 'i'), - (0x1D55B, 'M', 'j'), - (0x1D55C, 'M', 'k'), - (0x1D55D, 'M', 'l'), - (0x1D55E, 'M', 'm'), - (0x1D55F, 'M', 'n'), - (0x1D560, 'M', 'o'), - (0x1D561, 'M', 'p'), - (0x1D562, 'M', 'q'), - (0x1D563, 'M', 'r'), - (0x1D564, 'M', 's'), - (0x1D565, 'M', 't'), - (0x1D566, 'M', 'u'), - (0x1D567, 'M', 'v'), - (0x1D568, 'M', 'w'), - (0x1D569, 'M', 'x'), - (0x1D56A, 'M', 'y'), - (0x1D56B, 'M', 'z'), - (0x1D56C, 'M', 'a'), - (0x1D56D, 'M', 'b'), - (0x1D56E, 'M', 'c'), - (0x1D56F, 'M', 'd'), - (0x1D570, 'M', 'e'), - (0x1D571, 'M', 'f'), - (0x1D572, 'M', 'g'), - (0x1D573, 'M', 'h'), - (0x1D574, 'M', 'i'), - (0x1D575, 'M', 'j'), - (0x1D576, 'M', 'k'), - (0x1D577, 'M', 'l'), - (0x1D578, 'M', 'm'), - (0x1D579, 'M', 'n'), - (0x1D57A, 'M', 'o'), - (0x1D57B, 'M', 'p'), - (0x1D57C, 'M', 'q'), - (0x1D57D, 'M', 'r'), - (0x1D57E, 'M', 's'), - (0x1D57F, 'M', 't'), - (0x1D580, 'M', 'u'), - (0x1D581, 'M', 'v'), - (0x1D582, 'M', 'w'), - (0x1D583, 'M', 'x'), - (0x1D584, 'M', 'y'), - (0x1D585, 'M', 'z'), - (0x1D586, 'M', 'a'), - (0x1D587, 'M', 'b'), - (0x1D588, 'M', 'c'), - (0x1D589, 'M', 'd'), - (0x1D58A, 'M', 'e'), - (0x1D58B, 'M', 'f'), - (0x1D58C, 'M', 'g'), - (0x1D58D, 'M', 'h'), - (0x1D58E, 'M', 'i'), - (0x1D58F, 'M', 'j'), - (0x1D590, 'M', 'k'), - (0x1D591, 'M', 'l'), - (0x1D592, 'M', 'm'), - (0x1D593, 'M', 'n'), - (0x1D594, 'M', 'o'), - (0x1D595, 'M', 'p'), - (0x1D596, 'M', 'q'), - (0x1D597, 'M', 'r'), - (0x1D598, 'M', 's'), - (0x1D599, 'M', 't'), - (0x1D59A, 'M', 'u'), - (0x1D59B, 'M', 'v'), - (0x1D59C, 'M', 'w'), - (0x1D59D, 'M', 'x'), - (0x1D59E, 'M', 'y'), - (0x1D59F, 'M', 'z'), - (0x1D5A0, 'M', 'a'), - (0x1D5A1, 'M', 'b'), - (0x1D5A2, 'M', 'c'), - (0x1D5A3, 'M', 'd'), - (0x1D5A4, 'M', 'e'), - (0x1D5A5, 'M', 'f'), - (0x1D5A6, 'M', 'g'), - (0x1D5A7, 'M', 'h'), - (0x1D5A8, 'M', 'i'), - (0x1D5A9, 'M', 'j'), - (0x1D5AA, 'M', 'k'), - (0x1D5AB, 'M', 'l'), - (0x1D5AC, 'M', 'm'), - (0x1D5AD, 'M', 'n'), - (0x1D5AE, 'M', 'o'), - (0x1D5AF, 'M', 'p'), - (0x1D5B0, 'M', 'q'), - (0x1D5B1, 'M', 'r'), - (0x1D5B2, 'M', 's'), - (0x1D5B3, 'M', 't'), - (0x1D5B4, 'M', 'u'), - (0x1D5B5, 'M', 'v'), - (0x1D5B6, 'M', 'w'), - ] - -def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D5B7, 'M', 'x'), - (0x1D5B8, 'M', 'y'), - (0x1D5B9, 'M', 'z'), - (0x1D5BA, 'M', 'a'), - (0x1D5BB, 'M', 'b'), - (0x1D5BC, 'M', 'c'), - (0x1D5BD, 'M', 'd'), - (0x1D5BE, 'M', 'e'), - (0x1D5BF, 'M', 'f'), - (0x1D5C0, 'M', 'g'), - (0x1D5C1, 'M', 'h'), - (0x1D5C2, 'M', 'i'), - (0x1D5C3, 'M', 'j'), - (0x1D5C4, 'M', 'k'), - (0x1D5C5, 'M', 'l'), - (0x1D5C6, 'M', 'm'), - (0x1D5C7, 'M', 'n'), - (0x1D5C8, 'M', 'o'), - (0x1D5C9, 'M', 'p'), - (0x1D5CA, 'M', 'q'), - (0x1D5CB, 'M', 'r'), - (0x1D5CC, 'M', 's'), - (0x1D5CD, 'M', 't'), - (0x1D5CE, 'M', 'u'), - (0x1D5CF, 'M', 'v'), - (0x1D5D0, 'M', 'w'), - (0x1D5D1, 'M', 'x'), - (0x1D5D2, 'M', 'y'), - (0x1D5D3, 'M', 'z'), - (0x1D5D4, 'M', 'a'), - (0x1D5D5, 'M', 'b'), - (0x1D5D6, 'M', 'c'), - (0x1D5D7, 'M', 'd'), - (0x1D5D8, 'M', 'e'), - (0x1D5D9, 'M', 'f'), - (0x1D5DA, 'M', 'g'), - (0x1D5DB, 'M', 'h'), - (0x1D5DC, 'M', 'i'), - (0x1D5DD, 'M', 'j'), - (0x1D5DE, 'M', 'k'), - (0x1D5DF, 'M', 'l'), - (0x1D5E0, 'M', 'm'), - (0x1D5E1, 'M', 'n'), - (0x1D5E2, 'M', 'o'), - (0x1D5E3, 'M', 'p'), - (0x1D5E4, 'M', 'q'), - (0x1D5E5, 'M', 'r'), - (0x1D5E6, 'M', 's'), - (0x1D5E7, 'M', 't'), - (0x1D5E8, 'M', 'u'), - (0x1D5E9, 'M', 'v'), - (0x1D5EA, 'M', 'w'), - (0x1D5EB, 'M', 'x'), - (0x1D5EC, 'M', 'y'), - (0x1D5ED, 'M', 'z'), - (0x1D5EE, 'M', 'a'), - (0x1D5EF, 'M', 'b'), - (0x1D5F0, 'M', 'c'), - (0x1D5F1, 'M', 'd'), - (0x1D5F2, 'M', 'e'), - (0x1D5F3, 'M', 'f'), - (0x1D5F4, 'M', 'g'), - (0x1D5F5, 'M', 'h'), - (0x1D5F6, 'M', 'i'), - (0x1D5F7, 'M', 'j'), - (0x1D5F8, 'M', 'k'), - (0x1D5F9, 'M', 'l'), - (0x1D5FA, 'M', 'm'), - (0x1D5FB, 'M', 'n'), - (0x1D5FC, 'M', 'o'), - (0x1D5FD, 'M', 'p'), - (0x1D5FE, 'M', 'q'), - (0x1D5FF, 'M', 'r'), - (0x1D600, 'M', 's'), - (0x1D601, 'M', 't'), - (0x1D602, 'M', 'u'), - (0x1D603, 'M', 'v'), - (0x1D604, 'M', 'w'), - (0x1D605, 'M', 'x'), - (0x1D606, 'M', 'y'), - (0x1D607, 'M', 'z'), - (0x1D608, 'M', 'a'), - (0x1D609, 'M', 'b'), - (0x1D60A, 'M', 'c'), - (0x1D60B, 'M', 'd'), - (0x1D60C, 'M', 'e'), - (0x1D60D, 'M', 'f'), - (0x1D60E, 'M', 'g'), - (0x1D60F, 'M', 'h'), - (0x1D610, 'M', 'i'), - (0x1D611, 'M', 'j'), - (0x1D612, 'M', 'k'), - (0x1D613, 'M', 'l'), - (0x1D614, 'M', 'm'), - (0x1D615, 'M', 'n'), - (0x1D616, 'M', 'o'), - (0x1D617, 'M', 'p'), - (0x1D618, 'M', 'q'), - (0x1D619, 'M', 'r'), - (0x1D61A, 'M', 's'), - ] - -def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D61B, 'M', 't'), - (0x1D61C, 'M', 'u'), - (0x1D61D, 'M', 'v'), - (0x1D61E, 'M', 'w'), - (0x1D61F, 'M', 'x'), - (0x1D620, 'M', 'y'), - (0x1D621, 'M', 'z'), - (0x1D622, 'M', 'a'), - (0x1D623, 'M', 'b'), - (0x1D624, 'M', 'c'), - (0x1D625, 'M', 'd'), - (0x1D626, 'M', 'e'), - (0x1D627, 'M', 'f'), - (0x1D628, 'M', 'g'), - (0x1D629, 'M', 'h'), - (0x1D62A, 'M', 'i'), - (0x1D62B, 'M', 'j'), - (0x1D62C, 'M', 'k'), - (0x1D62D, 'M', 'l'), - (0x1D62E, 'M', 'm'), - (0x1D62F, 'M', 'n'), - (0x1D630, 'M', 'o'), - (0x1D631, 'M', 'p'), - (0x1D632, 'M', 'q'), - (0x1D633, 'M', 'r'), - (0x1D634, 'M', 's'), - (0x1D635, 'M', 't'), - (0x1D636, 'M', 'u'), - (0x1D637, 'M', 'v'), - (0x1D638, 'M', 'w'), - (0x1D639, 'M', 'x'), - (0x1D63A, 'M', 'y'), - (0x1D63B, 'M', 'z'), - (0x1D63C, 'M', 'a'), - (0x1D63D, 'M', 'b'), - (0x1D63E, 'M', 'c'), - (0x1D63F, 'M', 'd'), - (0x1D640, 'M', 'e'), - (0x1D641, 'M', 'f'), - (0x1D642, 'M', 'g'), - (0x1D643, 'M', 'h'), - (0x1D644, 'M', 'i'), - (0x1D645, 'M', 'j'), - (0x1D646, 'M', 'k'), - (0x1D647, 'M', 'l'), - (0x1D648, 'M', 'm'), - (0x1D649, 'M', 'n'), - (0x1D64A, 'M', 'o'), - (0x1D64B, 'M', 'p'), - (0x1D64C, 'M', 'q'), - (0x1D64D, 'M', 'r'), - (0x1D64E, 'M', 's'), - (0x1D64F, 'M', 't'), - (0x1D650, 'M', 'u'), - (0x1D651, 'M', 'v'), - (0x1D652, 'M', 'w'), - (0x1D653, 'M', 'x'), - (0x1D654, 'M', 'y'), - (0x1D655, 'M', 'z'), - (0x1D656, 'M', 'a'), - (0x1D657, 'M', 'b'), - (0x1D658, 'M', 'c'), - (0x1D659, 'M', 'd'), - (0x1D65A, 'M', 'e'), - (0x1D65B, 'M', 'f'), - (0x1D65C, 'M', 'g'), - (0x1D65D, 'M', 'h'), - (0x1D65E, 'M', 'i'), - (0x1D65F, 'M', 'j'), - (0x1D660, 'M', 'k'), - (0x1D661, 'M', 'l'), - (0x1D662, 'M', 'm'), - (0x1D663, 'M', 'n'), - (0x1D664, 'M', 'o'), - (0x1D665, 'M', 'p'), - (0x1D666, 'M', 'q'), - (0x1D667, 'M', 'r'), - (0x1D668, 'M', 's'), - (0x1D669, 'M', 't'), - (0x1D66A, 'M', 'u'), - (0x1D66B, 'M', 'v'), - (0x1D66C, 'M', 'w'), - (0x1D66D, 'M', 'x'), - (0x1D66E, 'M', 'y'), - (0x1D66F, 'M', 'z'), - (0x1D670, 'M', 'a'), - (0x1D671, 'M', 'b'), - (0x1D672, 'M', 'c'), - (0x1D673, 'M', 'd'), - (0x1D674, 'M', 'e'), - (0x1D675, 'M', 'f'), - (0x1D676, 'M', 'g'), - (0x1D677, 'M', 'h'), - (0x1D678, 'M', 'i'), - (0x1D679, 'M', 'j'), - (0x1D67A, 'M', 'k'), - (0x1D67B, 'M', 'l'), - (0x1D67C, 'M', 'm'), - (0x1D67D, 'M', 'n'), - (0x1D67E, 'M', 'o'), - ] - -def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D67F, 'M', 'p'), - (0x1D680, 'M', 'q'), - (0x1D681, 'M', 'r'), - (0x1D682, 'M', 's'), - (0x1D683, 'M', 't'), - (0x1D684, 'M', 'u'), - (0x1D685, 'M', 'v'), - (0x1D686, 'M', 'w'), - (0x1D687, 'M', 'x'), - (0x1D688, 'M', 'y'), - (0x1D689, 'M', 'z'), - (0x1D68A, 'M', 'a'), - (0x1D68B, 'M', 'b'), - (0x1D68C, 'M', 'c'), - (0x1D68D, 'M', 'd'), - (0x1D68E, 'M', 'e'), - (0x1D68F, 'M', 'f'), - (0x1D690, 'M', 'g'), - (0x1D691, 'M', 'h'), - (0x1D692, 'M', 'i'), - (0x1D693, 'M', 'j'), - (0x1D694, 'M', 'k'), - (0x1D695, 'M', 'l'), - (0x1D696, 'M', 'm'), - (0x1D697, 'M', 'n'), - (0x1D698, 'M', 'o'), - (0x1D699, 'M', 'p'), - (0x1D69A, 'M', 'q'), - (0x1D69B, 'M', 'r'), - (0x1D69C, 'M', 's'), - (0x1D69D, 'M', 't'), - (0x1D69E, 'M', 'u'), - (0x1D69F, 'M', 'v'), - (0x1D6A0, 'M', 'w'), - (0x1D6A1, 'M', 'x'), - (0x1D6A2, 'M', 'y'), - (0x1D6A3, 'M', 'z'), - (0x1D6A4, 'M', 'ı'), - (0x1D6A5, 'M', 'ȷ'), - (0x1D6A6, 'X'), - (0x1D6A8, 'M', 'α'), - (0x1D6A9, 'M', 'β'), - (0x1D6AA, 'M', 'γ'), - (0x1D6AB, 'M', 'δ'), - (0x1D6AC, 'M', 'ε'), - (0x1D6AD, 'M', 'ζ'), - (0x1D6AE, 'M', 'η'), - (0x1D6AF, 'M', 'θ'), - (0x1D6B0, 'M', 'ι'), - (0x1D6B1, 'M', 'κ'), - (0x1D6B2, 'M', 'λ'), - (0x1D6B3, 'M', 'μ'), - (0x1D6B4, 'M', 'ν'), - (0x1D6B5, 'M', 'ξ'), - (0x1D6B6, 'M', 'ο'), - (0x1D6B7, 'M', 'π'), - (0x1D6B8, 'M', 'ρ'), - (0x1D6B9, 'M', 'θ'), - (0x1D6BA, 'M', 'σ'), - (0x1D6BB, 'M', 'τ'), - (0x1D6BC, 'M', 'υ'), - (0x1D6BD, 'M', 'φ'), - (0x1D6BE, 'M', 'χ'), - (0x1D6BF, 'M', 'ψ'), - (0x1D6C0, 'M', 'ω'), - (0x1D6C1, 'M', '∇'), - (0x1D6C2, 'M', 'α'), - (0x1D6C3, 'M', 'β'), - (0x1D6C4, 'M', 'γ'), - (0x1D6C5, 'M', 'δ'), - (0x1D6C6, 'M', 'ε'), - (0x1D6C7, 'M', 'ζ'), - (0x1D6C8, 'M', 'η'), - (0x1D6C9, 'M', 'θ'), - (0x1D6CA, 'M', 'ι'), - (0x1D6CB, 'M', 'κ'), - (0x1D6CC, 'M', 'λ'), - (0x1D6CD, 'M', 'μ'), - (0x1D6CE, 'M', 'ν'), - (0x1D6CF, 'M', 'ξ'), - (0x1D6D0, 'M', 'ο'), - (0x1D6D1, 'M', 'π'), - (0x1D6D2, 'M', 'ρ'), - (0x1D6D3, 'M', 'σ'), - (0x1D6D5, 'M', 'τ'), - (0x1D6D6, 'M', 'υ'), - (0x1D6D7, 'M', 'φ'), - (0x1D6D8, 'M', 'χ'), - (0x1D6D9, 'M', 'ψ'), - (0x1D6DA, 'M', 'ω'), - (0x1D6DB, 'M', '∂'), - (0x1D6DC, 'M', 'ε'), - (0x1D6DD, 'M', 'θ'), - (0x1D6DE, 'M', 'κ'), - (0x1D6DF, 'M', 'φ'), - (0x1D6E0, 'M', 'ρ'), - (0x1D6E1, 'M', 'π'), - (0x1D6E2, 'M', 'α'), - (0x1D6E3, 'M', 'β'), - (0x1D6E4, 'M', 'γ'), - ] - -def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D6E5, 'M', 'δ'), - (0x1D6E6, 'M', 'ε'), - (0x1D6E7, 'M', 'ζ'), - (0x1D6E8, 'M', 'η'), - (0x1D6E9, 'M', 'θ'), - (0x1D6EA, 'M', 'ι'), - (0x1D6EB, 'M', 'κ'), - (0x1D6EC, 'M', 'λ'), - (0x1D6ED, 'M', 'μ'), - (0x1D6EE, 'M', 'ν'), - (0x1D6EF, 'M', 'ξ'), - (0x1D6F0, 'M', 'ο'), - (0x1D6F1, 'M', 'π'), - (0x1D6F2, 'M', 'ρ'), - (0x1D6F3, 'M', 'θ'), - (0x1D6F4, 'M', 'σ'), - (0x1D6F5, 'M', 'τ'), - (0x1D6F6, 'M', 'υ'), - (0x1D6F7, 'M', 'φ'), - (0x1D6F8, 'M', 'χ'), - (0x1D6F9, 'M', 'ψ'), - (0x1D6FA, 'M', 'ω'), - (0x1D6FB, 'M', '∇'), - (0x1D6FC, 'M', 'α'), - (0x1D6FD, 'M', 'β'), - (0x1D6FE, 'M', 'γ'), - (0x1D6FF, 'M', 'δ'), - (0x1D700, 'M', 'ε'), - (0x1D701, 'M', 'ζ'), - (0x1D702, 'M', 'η'), - (0x1D703, 'M', 'θ'), - (0x1D704, 'M', 'ι'), - (0x1D705, 'M', 'κ'), - (0x1D706, 'M', 'λ'), - (0x1D707, 'M', 'μ'), - (0x1D708, 'M', 'ν'), - (0x1D709, 'M', 'ξ'), - (0x1D70A, 'M', 'ο'), - (0x1D70B, 'M', 'π'), - (0x1D70C, 'M', 'ρ'), - (0x1D70D, 'M', 'σ'), - (0x1D70F, 'M', 'τ'), - (0x1D710, 'M', 'υ'), - (0x1D711, 'M', 'φ'), - (0x1D712, 'M', 'χ'), - (0x1D713, 'M', 'ψ'), - (0x1D714, 'M', 'ω'), - (0x1D715, 'M', '∂'), - (0x1D716, 'M', 'ε'), - (0x1D717, 'M', 'θ'), - (0x1D718, 'M', 'κ'), - (0x1D719, 'M', 'φ'), - (0x1D71A, 'M', 'ρ'), - (0x1D71B, 'M', 'π'), - (0x1D71C, 'M', 'α'), - (0x1D71D, 'M', 'β'), - (0x1D71E, 'M', 'γ'), - (0x1D71F, 'M', 'δ'), - (0x1D720, 'M', 'ε'), - (0x1D721, 'M', 'ζ'), - (0x1D722, 'M', 'η'), - (0x1D723, 'M', 'θ'), - (0x1D724, 'M', 'ι'), - (0x1D725, 'M', 'κ'), - (0x1D726, 'M', 'λ'), - (0x1D727, 'M', 'μ'), - (0x1D728, 'M', 'ν'), - (0x1D729, 'M', 'ξ'), - (0x1D72A, 'M', 'ο'), - (0x1D72B, 'M', 'π'), - (0x1D72C, 'M', 'ρ'), - (0x1D72D, 'M', 'θ'), - (0x1D72E, 'M', 'σ'), - (0x1D72F, 'M', 'τ'), - (0x1D730, 'M', 'υ'), - (0x1D731, 'M', 'φ'), - (0x1D732, 'M', 'χ'), - (0x1D733, 'M', 'ψ'), - (0x1D734, 'M', 'ω'), - (0x1D735, 'M', '∇'), - (0x1D736, 'M', 'α'), - (0x1D737, 'M', 'β'), - (0x1D738, 'M', 'γ'), - (0x1D739, 'M', 'δ'), - (0x1D73A, 'M', 'ε'), - (0x1D73B, 'M', 'ζ'), - (0x1D73C, 'M', 'η'), - (0x1D73D, 'M', 'θ'), - (0x1D73E, 'M', 'ι'), - (0x1D73F, 'M', 'κ'), - (0x1D740, 'M', 'λ'), - (0x1D741, 'M', 'μ'), - (0x1D742, 'M', 'ν'), - (0x1D743, 'M', 'ξ'), - (0x1D744, 'M', 'ο'), - (0x1D745, 'M', 'π'), - (0x1D746, 'M', 'ρ'), - (0x1D747, 'M', 'σ'), - (0x1D749, 'M', 'τ'), - (0x1D74A, 'M', 'υ'), - ] - -def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D74B, 'M', 'φ'), - (0x1D74C, 'M', 'χ'), - (0x1D74D, 'M', 'ψ'), - (0x1D74E, 'M', 'ω'), - (0x1D74F, 'M', '∂'), - (0x1D750, 'M', 'ε'), - (0x1D751, 'M', 'θ'), - (0x1D752, 'M', 'κ'), - (0x1D753, 'M', 'φ'), - (0x1D754, 'M', 'ρ'), - (0x1D755, 'M', 'π'), - (0x1D756, 'M', 'α'), - (0x1D757, 'M', 'β'), - (0x1D758, 'M', 'γ'), - (0x1D759, 'M', 'δ'), - (0x1D75A, 'M', 'ε'), - (0x1D75B, 'M', 'ζ'), - (0x1D75C, 'M', 'η'), - (0x1D75D, 'M', 'θ'), - (0x1D75E, 'M', 'ι'), - (0x1D75F, 'M', 'κ'), - (0x1D760, 'M', 'λ'), - (0x1D761, 'M', 'μ'), - (0x1D762, 'M', 'ν'), - (0x1D763, 'M', 'ξ'), - (0x1D764, 'M', 'ο'), - (0x1D765, 'M', 'π'), - (0x1D766, 'M', 'ρ'), - (0x1D767, 'M', 'θ'), - (0x1D768, 'M', 'σ'), - (0x1D769, 'M', 'τ'), - (0x1D76A, 'M', 'υ'), - (0x1D76B, 'M', 'φ'), - (0x1D76C, 'M', 'χ'), - (0x1D76D, 'M', 'ψ'), - (0x1D76E, 'M', 'ω'), - (0x1D76F, 'M', '∇'), - (0x1D770, 'M', 'α'), - (0x1D771, 'M', 'β'), - (0x1D772, 'M', 'γ'), - (0x1D773, 'M', 'δ'), - (0x1D774, 'M', 'ε'), - (0x1D775, 'M', 'ζ'), - (0x1D776, 'M', 'η'), - (0x1D777, 'M', 'θ'), - (0x1D778, 'M', 'ι'), - (0x1D779, 'M', 'κ'), - (0x1D77A, 'M', 'λ'), - (0x1D77B, 'M', 'μ'), - (0x1D77C, 'M', 'ν'), - (0x1D77D, 'M', 'ξ'), - (0x1D77E, 'M', 'ο'), - (0x1D77F, 'M', 'π'), - (0x1D780, 'M', 'ρ'), - (0x1D781, 'M', 'σ'), - (0x1D783, 'M', 'τ'), - (0x1D784, 'M', 'υ'), - (0x1D785, 'M', 'φ'), - (0x1D786, 'M', 'χ'), - (0x1D787, 'M', 'ψ'), - (0x1D788, 'M', 'ω'), - (0x1D789, 'M', '∂'), - (0x1D78A, 'M', 'ε'), - (0x1D78B, 'M', 'θ'), - (0x1D78C, 'M', 'κ'), - (0x1D78D, 'M', 'φ'), - (0x1D78E, 'M', 'ρ'), - (0x1D78F, 'M', 'π'), - (0x1D790, 'M', 'α'), - (0x1D791, 'M', 'β'), - (0x1D792, 'M', 'γ'), - (0x1D793, 'M', 'δ'), - (0x1D794, 'M', 'ε'), - (0x1D795, 'M', 'ζ'), - (0x1D796, 'M', 'η'), - (0x1D797, 'M', 'θ'), - (0x1D798, 'M', 'ι'), - (0x1D799, 'M', 'κ'), - (0x1D79A, 'M', 'λ'), - (0x1D79B, 'M', 'μ'), - (0x1D79C, 'M', 'ν'), - (0x1D79D, 'M', 'ξ'), - (0x1D79E, 'M', 'ο'), - (0x1D79F, 'M', 'π'), - (0x1D7A0, 'M', 'ρ'), - (0x1D7A1, 'M', 'θ'), - (0x1D7A2, 'M', 'σ'), - (0x1D7A3, 'M', 'τ'), - (0x1D7A4, 'M', 'υ'), - (0x1D7A5, 'M', 'φ'), - (0x1D7A6, 'M', 'χ'), - (0x1D7A7, 'M', 'ψ'), - (0x1D7A8, 'M', 'ω'), - (0x1D7A9, 'M', '∇'), - (0x1D7AA, 'M', 'α'), - (0x1D7AB, 'M', 'β'), - (0x1D7AC, 'M', 'γ'), - (0x1D7AD, 'M', 'δ'), - (0x1D7AE, 'M', 'ε'), - (0x1D7AF, 'M', 'ζ'), - ] - -def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D7B0, 'M', 'η'), - (0x1D7B1, 'M', 'θ'), - (0x1D7B2, 'M', 'ι'), - (0x1D7B3, 'M', 'κ'), - (0x1D7B4, 'M', 'λ'), - (0x1D7B5, 'M', 'μ'), - (0x1D7B6, 'M', 'ν'), - (0x1D7B7, 'M', 'ξ'), - (0x1D7B8, 'M', 'ο'), - (0x1D7B9, 'M', 'π'), - (0x1D7BA, 'M', 'ρ'), - (0x1D7BB, 'M', 'σ'), - (0x1D7BD, 'M', 'τ'), - (0x1D7BE, 'M', 'υ'), - (0x1D7BF, 'M', 'φ'), - (0x1D7C0, 'M', 'χ'), - (0x1D7C1, 'M', 'ψ'), - (0x1D7C2, 'M', 'ω'), - (0x1D7C3, 'M', '∂'), - (0x1D7C4, 'M', 'ε'), - (0x1D7C5, 'M', 'θ'), - (0x1D7C6, 'M', 'κ'), - (0x1D7C7, 'M', 'φ'), - (0x1D7C8, 'M', 'ρ'), - (0x1D7C9, 'M', 'π'), - (0x1D7CA, 'M', 'ϝ'), - (0x1D7CC, 'X'), - (0x1D7CE, 'M', '0'), - (0x1D7CF, 'M', '1'), - (0x1D7D0, 'M', '2'), - (0x1D7D1, 'M', '3'), - (0x1D7D2, 'M', '4'), - (0x1D7D3, 'M', '5'), - (0x1D7D4, 'M', '6'), - (0x1D7D5, 'M', '7'), - (0x1D7D6, 'M', '8'), - (0x1D7D7, 'M', '9'), - (0x1D7D8, 'M', '0'), - (0x1D7D9, 'M', '1'), - (0x1D7DA, 'M', '2'), - (0x1D7DB, 'M', '3'), - (0x1D7DC, 'M', '4'), - (0x1D7DD, 'M', '5'), - (0x1D7DE, 'M', '6'), - (0x1D7DF, 'M', '7'), - (0x1D7E0, 'M', '8'), - (0x1D7E1, 'M', '9'), - (0x1D7E2, 'M', '0'), - (0x1D7E3, 'M', '1'), - (0x1D7E4, 'M', '2'), - (0x1D7E5, 'M', '3'), - (0x1D7E6, 'M', '4'), - (0x1D7E7, 'M', '5'), - (0x1D7E8, 'M', '6'), - (0x1D7E9, 'M', '7'), - (0x1D7EA, 'M', '8'), - (0x1D7EB, 'M', '9'), - (0x1D7EC, 'M', '0'), - (0x1D7ED, 'M', '1'), - (0x1D7EE, 'M', '2'), - (0x1D7EF, 'M', '3'), - (0x1D7F0, 'M', '4'), - (0x1D7F1, 'M', '5'), - (0x1D7F2, 'M', '6'), - (0x1D7F3, 'M', '7'), - (0x1D7F4, 'M', '8'), - (0x1D7F5, 'M', '9'), - (0x1D7F6, 'M', '0'), - (0x1D7F7, 'M', '1'), - (0x1D7F8, 'M', '2'), - (0x1D7F9, 'M', '3'), - (0x1D7FA, 'M', '4'), - (0x1D7FB, 'M', '5'), - (0x1D7FC, 'M', '6'), - (0x1D7FD, 'M', '7'), - (0x1D7FE, 'M', '8'), - (0x1D7FF, 'M', '9'), - (0x1D800, 'V'), - (0x1DA8C, 'X'), - (0x1DA9B, 'V'), - (0x1DAA0, 'X'), - (0x1DAA1, 'V'), - (0x1DAB0, 'X'), - (0x1DF00, 'V'), - (0x1DF1F, 'X'), - (0x1DF25, 'V'), - (0x1DF2B, 'X'), - (0x1E000, 'V'), - (0x1E007, 'X'), - (0x1E008, 'V'), - (0x1E019, 'X'), - (0x1E01B, 'V'), - (0x1E022, 'X'), - (0x1E023, 'V'), - (0x1E025, 'X'), - (0x1E026, 'V'), - (0x1E02B, 'X'), - (0x1E030, 'M', 'а'), - (0x1E031, 'M', 'б'), - (0x1E032, 'M', 'в'), - ] - -def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E033, 'M', 'г'), - (0x1E034, 'M', 'д'), - (0x1E035, 'M', 'е'), - (0x1E036, 'M', 'ж'), - (0x1E037, 'M', 'з'), - (0x1E038, 'M', 'и'), - (0x1E039, 'M', 'к'), - (0x1E03A, 'M', 'л'), - (0x1E03B, 'M', 'м'), - (0x1E03C, 'M', 'о'), - (0x1E03D, 'M', 'п'), - (0x1E03E, 'M', 'р'), - (0x1E03F, 'M', 'с'), - (0x1E040, 'M', 'т'), - (0x1E041, 'M', 'у'), - (0x1E042, 'M', 'ф'), - (0x1E043, 'M', 'х'), - (0x1E044, 'M', 'ц'), - (0x1E045, 'M', 'ч'), - (0x1E046, 'M', 'ш'), - (0x1E047, 'M', 'ы'), - (0x1E048, 'M', 'э'), - (0x1E049, 'M', 'ю'), - (0x1E04A, 'M', 'ꚉ'), - (0x1E04B, 'M', 'ә'), - (0x1E04C, 'M', 'і'), - (0x1E04D, 'M', 'ј'), - (0x1E04E, 'M', 'ө'), - (0x1E04F, 'M', 'ү'), - (0x1E050, 'M', 'ӏ'), - (0x1E051, 'M', 'а'), - (0x1E052, 'M', 'б'), - (0x1E053, 'M', 'в'), - (0x1E054, 'M', 'г'), - (0x1E055, 'M', 'д'), - (0x1E056, 'M', 'е'), - (0x1E057, 'M', 'ж'), - (0x1E058, 'M', 'з'), - (0x1E059, 'M', 'и'), - (0x1E05A, 'M', 'к'), - (0x1E05B, 'M', 'л'), - (0x1E05C, 'M', 'о'), - (0x1E05D, 'M', 'п'), - (0x1E05E, 'M', 'с'), - (0x1E05F, 'M', 'у'), - (0x1E060, 'M', 'ф'), - (0x1E061, 'M', 'х'), - (0x1E062, 'M', 'ц'), - (0x1E063, 'M', 'ч'), - (0x1E064, 'M', 'ш'), - (0x1E065, 'M', 'ъ'), - (0x1E066, 'M', 'ы'), - (0x1E067, 'M', 'ґ'), - (0x1E068, 'M', 'і'), - (0x1E069, 'M', 'ѕ'), - (0x1E06A, 'M', 'џ'), - (0x1E06B, 'M', 'ҫ'), - (0x1E06C, 'M', 'ꙑ'), - (0x1E06D, 'M', 'ұ'), - (0x1E06E, 'X'), - (0x1E08F, 'V'), - (0x1E090, 'X'), - (0x1E100, 'V'), - (0x1E12D, 'X'), - (0x1E130, 'V'), - (0x1E13E, 'X'), - (0x1E140, 'V'), - (0x1E14A, 'X'), - (0x1E14E, 'V'), - (0x1E150, 'X'), - (0x1E290, 'V'), - (0x1E2AF, 'X'), - (0x1E2C0, 'V'), - (0x1E2FA, 'X'), - (0x1E2FF, 'V'), - (0x1E300, 'X'), - (0x1E4D0, 'V'), - (0x1E4FA, 'X'), - (0x1E7E0, 'V'), - (0x1E7E7, 'X'), - (0x1E7E8, 'V'), - (0x1E7EC, 'X'), - (0x1E7ED, 'V'), - (0x1E7EF, 'X'), - (0x1E7F0, 'V'), - (0x1E7FF, 'X'), - (0x1E800, 'V'), - (0x1E8C5, 'X'), - (0x1E8C7, 'V'), - (0x1E8D7, 'X'), - (0x1E900, 'M', '𞤢'), - (0x1E901, 'M', '𞤣'), - (0x1E902, 'M', '𞤤'), - (0x1E903, 'M', '𞤥'), - (0x1E904, 'M', '𞤦'), - (0x1E905, 'M', '𞤧'), - (0x1E906, 'M', '𞤨'), - (0x1E907, 'M', '𞤩'), - (0x1E908, 'M', '𞤪'), - (0x1E909, 'M', '𞤫'), - ] - -def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E90A, 'M', '𞤬'), - (0x1E90B, 'M', '𞤭'), - (0x1E90C, 'M', '𞤮'), - (0x1E90D, 'M', '𞤯'), - (0x1E90E, 'M', '𞤰'), - (0x1E90F, 'M', '𞤱'), - (0x1E910, 'M', '𞤲'), - (0x1E911, 'M', '𞤳'), - (0x1E912, 'M', '𞤴'), - (0x1E913, 'M', '𞤵'), - (0x1E914, 'M', '𞤶'), - (0x1E915, 'M', '𞤷'), - (0x1E916, 'M', '𞤸'), - (0x1E917, 'M', '𞤹'), - (0x1E918, 'M', '𞤺'), - (0x1E919, 'M', '𞤻'), - (0x1E91A, 'M', '𞤼'), - (0x1E91B, 'M', '𞤽'), - (0x1E91C, 'M', '𞤾'), - (0x1E91D, 'M', '𞤿'), - (0x1E91E, 'M', '𞥀'), - (0x1E91F, 'M', '𞥁'), - (0x1E920, 'M', '𞥂'), - (0x1E921, 'M', '𞥃'), - (0x1E922, 'V'), - (0x1E94C, 'X'), - (0x1E950, 'V'), - (0x1E95A, 'X'), - (0x1E95E, 'V'), - (0x1E960, 'X'), - (0x1EC71, 'V'), - (0x1ECB5, 'X'), - (0x1ED01, 'V'), - (0x1ED3E, 'X'), - (0x1EE00, 'M', 'ا'), - (0x1EE01, 'M', 'ب'), - (0x1EE02, 'M', 'ج'), - (0x1EE03, 'M', 'د'), - (0x1EE04, 'X'), - (0x1EE05, 'M', 'و'), - (0x1EE06, 'M', 'ز'), - (0x1EE07, 'M', 'ح'), - (0x1EE08, 'M', 'ط'), - (0x1EE09, 'M', 'ي'), - (0x1EE0A, 'M', 'ك'), - (0x1EE0B, 'M', 'ل'), - (0x1EE0C, 'M', 'م'), - (0x1EE0D, 'M', 'ن'), - (0x1EE0E, 'M', 'س'), - (0x1EE0F, 'M', 'ع'), - (0x1EE10, 'M', 'ف'), - (0x1EE11, 'M', 'ص'), - (0x1EE12, 'M', 'ق'), - (0x1EE13, 'M', 'ر'), - (0x1EE14, 'M', 'ش'), - (0x1EE15, 'M', 'ت'), - (0x1EE16, 'M', 'ث'), - (0x1EE17, 'M', 'خ'), - (0x1EE18, 'M', 'ذ'), - (0x1EE19, 'M', 'ض'), - (0x1EE1A, 'M', 'ظ'), - (0x1EE1B, 'M', 'غ'), - (0x1EE1C, 'M', 'ٮ'), - (0x1EE1D, 'M', 'ں'), - (0x1EE1E, 'M', 'ڡ'), - (0x1EE1F, 'M', 'ٯ'), - (0x1EE20, 'X'), - (0x1EE21, 'M', 'ب'), - (0x1EE22, 'M', 'ج'), - (0x1EE23, 'X'), - (0x1EE24, 'M', 'ه'), - (0x1EE25, 'X'), - (0x1EE27, 'M', 'ح'), - (0x1EE28, 'X'), - (0x1EE29, 'M', 'ي'), - (0x1EE2A, 'M', 'ك'), - (0x1EE2B, 'M', 'ل'), - (0x1EE2C, 'M', 'م'), - (0x1EE2D, 'M', 'ن'), - (0x1EE2E, 'M', 'س'), - (0x1EE2F, 'M', 'ع'), - (0x1EE30, 'M', 'ف'), - (0x1EE31, 'M', 'ص'), - (0x1EE32, 'M', 'ق'), - (0x1EE33, 'X'), - (0x1EE34, 'M', 'ش'), - (0x1EE35, 'M', 'ت'), - (0x1EE36, 'M', 'ث'), - (0x1EE37, 'M', 'خ'), - (0x1EE38, 'X'), - (0x1EE39, 'M', 'ض'), - (0x1EE3A, 'X'), - (0x1EE3B, 'M', 'غ'), - (0x1EE3C, 'X'), - (0x1EE42, 'M', 'ج'), - (0x1EE43, 'X'), - (0x1EE47, 'M', 'ح'), - (0x1EE48, 'X'), - (0x1EE49, 'M', 'ي'), - (0x1EE4A, 'X'), - ] - -def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EE4B, 'M', 'ل'), - (0x1EE4C, 'X'), - (0x1EE4D, 'M', 'ن'), - (0x1EE4E, 'M', 'س'), - (0x1EE4F, 'M', 'ع'), - (0x1EE50, 'X'), - (0x1EE51, 'M', 'ص'), - (0x1EE52, 'M', 'ق'), - (0x1EE53, 'X'), - (0x1EE54, 'M', 'ش'), - (0x1EE55, 'X'), - (0x1EE57, 'M', 'خ'), - (0x1EE58, 'X'), - (0x1EE59, 'M', 'ض'), - (0x1EE5A, 'X'), - (0x1EE5B, 'M', 'غ'), - (0x1EE5C, 'X'), - (0x1EE5D, 'M', 'ں'), - (0x1EE5E, 'X'), - (0x1EE5F, 'M', 'ٯ'), - (0x1EE60, 'X'), - (0x1EE61, 'M', 'ب'), - (0x1EE62, 'M', 'ج'), - (0x1EE63, 'X'), - (0x1EE64, 'M', 'ه'), - (0x1EE65, 'X'), - (0x1EE67, 'M', 'ح'), - (0x1EE68, 'M', 'ط'), - (0x1EE69, 'M', 'ي'), - (0x1EE6A, 'M', 'ك'), - (0x1EE6B, 'X'), - (0x1EE6C, 'M', 'م'), - (0x1EE6D, 'M', 'ن'), - (0x1EE6E, 'M', 'س'), - (0x1EE6F, 'M', 'ع'), - (0x1EE70, 'M', 'ف'), - (0x1EE71, 'M', 'ص'), - (0x1EE72, 'M', 'ق'), - (0x1EE73, 'X'), - (0x1EE74, 'M', 'ش'), - (0x1EE75, 'M', 'ت'), - (0x1EE76, 'M', 'ث'), - (0x1EE77, 'M', 'خ'), - (0x1EE78, 'X'), - (0x1EE79, 'M', 'ض'), - (0x1EE7A, 'M', 'ظ'), - (0x1EE7B, 'M', 'غ'), - (0x1EE7C, 'M', 'ٮ'), - (0x1EE7D, 'X'), - (0x1EE7E, 'M', 'ڡ'), - (0x1EE7F, 'X'), - (0x1EE80, 'M', 'ا'), - (0x1EE81, 'M', 'ب'), - (0x1EE82, 'M', 'ج'), - (0x1EE83, 'M', 'د'), - (0x1EE84, 'M', 'ه'), - (0x1EE85, 'M', 'و'), - (0x1EE86, 'M', 'ز'), - (0x1EE87, 'M', 'ح'), - (0x1EE88, 'M', 'ط'), - (0x1EE89, 'M', 'ي'), - (0x1EE8A, 'X'), - (0x1EE8B, 'M', 'ل'), - (0x1EE8C, 'M', 'م'), - (0x1EE8D, 'M', 'ن'), - (0x1EE8E, 'M', 'س'), - (0x1EE8F, 'M', 'ع'), - (0x1EE90, 'M', 'ف'), - (0x1EE91, 'M', 'ص'), - (0x1EE92, 'M', 'ق'), - (0x1EE93, 'M', 'ر'), - (0x1EE94, 'M', 'ش'), - (0x1EE95, 'M', 'ت'), - (0x1EE96, 'M', 'ث'), - (0x1EE97, 'M', 'خ'), - (0x1EE98, 'M', 'ذ'), - (0x1EE99, 'M', 'ض'), - (0x1EE9A, 'M', 'ظ'), - (0x1EE9B, 'M', 'غ'), - (0x1EE9C, 'X'), - (0x1EEA1, 'M', 'ب'), - (0x1EEA2, 'M', 'ج'), - (0x1EEA3, 'M', 'د'), - (0x1EEA4, 'X'), - (0x1EEA5, 'M', 'و'), - (0x1EEA6, 'M', 'ز'), - (0x1EEA7, 'M', 'ح'), - (0x1EEA8, 'M', 'ط'), - (0x1EEA9, 'M', 'ي'), - (0x1EEAA, 'X'), - (0x1EEAB, 'M', 'ل'), - (0x1EEAC, 'M', 'م'), - (0x1EEAD, 'M', 'ن'), - (0x1EEAE, 'M', 'س'), - (0x1EEAF, 'M', 'ع'), - (0x1EEB0, 'M', 'ف'), - (0x1EEB1, 'M', 'ص'), - (0x1EEB2, 'M', 'ق'), - (0x1EEB3, 'M', 'ر'), - (0x1EEB4, 'M', 'ش'), - ] - -def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EEB5, 'M', 'ت'), - (0x1EEB6, 'M', 'ث'), - (0x1EEB7, 'M', 'خ'), - (0x1EEB8, 'M', 'ذ'), - (0x1EEB9, 'M', 'ض'), - (0x1EEBA, 'M', 'ظ'), - (0x1EEBB, 'M', 'غ'), - (0x1EEBC, 'X'), - (0x1EEF0, 'V'), - (0x1EEF2, 'X'), - (0x1F000, 'V'), - (0x1F02C, 'X'), - (0x1F030, 'V'), - (0x1F094, 'X'), - (0x1F0A0, 'V'), - (0x1F0AF, 'X'), - (0x1F0B1, 'V'), - (0x1F0C0, 'X'), - (0x1F0C1, 'V'), - (0x1F0D0, 'X'), - (0x1F0D1, 'V'), - (0x1F0F6, 'X'), - (0x1F101, '3', '0,'), - (0x1F102, '3', '1,'), - (0x1F103, '3', '2,'), - (0x1F104, '3', '3,'), - (0x1F105, '3', '4,'), - (0x1F106, '3', '5,'), - (0x1F107, '3', '6,'), - (0x1F108, '3', '7,'), - (0x1F109, '3', '8,'), - (0x1F10A, '3', '9,'), - (0x1F10B, 'V'), - (0x1F110, '3', '(a)'), - (0x1F111, '3', '(b)'), - (0x1F112, '3', '(c)'), - (0x1F113, '3', '(d)'), - (0x1F114, '3', '(e)'), - (0x1F115, '3', '(f)'), - (0x1F116, '3', '(g)'), - (0x1F117, '3', '(h)'), - (0x1F118, '3', '(i)'), - (0x1F119, '3', '(j)'), - (0x1F11A, '3', '(k)'), - (0x1F11B, '3', '(l)'), - (0x1F11C, '3', '(m)'), - (0x1F11D, '3', '(n)'), - (0x1F11E, '3', '(o)'), - (0x1F11F, '3', '(p)'), - (0x1F120, '3', '(q)'), - (0x1F121, '3', '(r)'), - (0x1F122, '3', '(s)'), - (0x1F123, '3', '(t)'), - (0x1F124, '3', '(u)'), - (0x1F125, '3', '(v)'), - (0x1F126, '3', '(w)'), - (0x1F127, '3', '(x)'), - (0x1F128, '3', '(y)'), - (0x1F129, '3', '(z)'), - (0x1F12A, 'M', '〔s〕'), - (0x1F12B, 'M', 'c'), - (0x1F12C, 'M', 'r'), - (0x1F12D, 'M', 'cd'), - (0x1F12E, 'M', 'wz'), - (0x1F12F, 'V'), - (0x1F130, 'M', 'a'), - (0x1F131, 'M', 'b'), - (0x1F132, 'M', 'c'), - (0x1F133, 'M', 'd'), - (0x1F134, 'M', 'e'), - (0x1F135, 'M', 'f'), - (0x1F136, 'M', 'g'), - (0x1F137, 'M', 'h'), - (0x1F138, 'M', 'i'), - (0x1F139, 'M', 'j'), - (0x1F13A, 'M', 'k'), - (0x1F13B, 'M', 'l'), - (0x1F13C, 'M', 'm'), - (0x1F13D, 'M', 'n'), - (0x1F13E, 'M', 'o'), - (0x1F13F, 'M', 'p'), - (0x1F140, 'M', 'q'), - (0x1F141, 'M', 'r'), - (0x1F142, 'M', 's'), - (0x1F143, 'M', 't'), - (0x1F144, 'M', 'u'), - (0x1F145, 'M', 'v'), - (0x1F146, 'M', 'w'), - (0x1F147, 'M', 'x'), - (0x1F148, 'M', 'y'), - (0x1F149, 'M', 'z'), - (0x1F14A, 'M', 'hv'), - (0x1F14B, 'M', 'mv'), - (0x1F14C, 'M', 'sd'), - (0x1F14D, 'M', 'ss'), - (0x1F14E, 'M', 'ppv'), - (0x1F14F, 'M', 'wc'), - (0x1F150, 'V'), - (0x1F16A, 'M', 'mc'), - (0x1F16B, 'M', 'md'), - ] - -def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F16C, 'M', 'mr'), - (0x1F16D, 'V'), - (0x1F190, 'M', 'dj'), - (0x1F191, 'V'), - (0x1F1AE, 'X'), - (0x1F1E6, 'V'), - (0x1F200, 'M', 'ほか'), - (0x1F201, 'M', 'ココ'), - (0x1F202, 'M', 'サ'), - (0x1F203, 'X'), - (0x1F210, 'M', '手'), - (0x1F211, 'M', '字'), - (0x1F212, 'M', '双'), - (0x1F213, 'M', 'デ'), - (0x1F214, 'M', '二'), - (0x1F215, 'M', '多'), - (0x1F216, 'M', '解'), - (0x1F217, 'M', '天'), - (0x1F218, 'M', '交'), - (0x1F219, 'M', '映'), - (0x1F21A, 'M', '無'), - (0x1F21B, 'M', '料'), - (0x1F21C, 'M', '前'), - (0x1F21D, 'M', '後'), - (0x1F21E, 'M', '再'), - (0x1F21F, 'M', '新'), - (0x1F220, 'M', '初'), - (0x1F221, 'M', '終'), - (0x1F222, 'M', '生'), - (0x1F223, 'M', '販'), - (0x1F224, 'M', '声'), - (0x1F225, 'M', '吹'), - (0x1F226, 'M', '演'), - (0x1F227, 'M', '投'), - (0x1F228, 'M', '捕'), - (0x1F229, 'M', '一'), - (0x1F22A, 'M', '三'), - (0x1F22B, 'M', '遊'), - (0x1F22C, 'M', '左'), - (0x1F22D, 'M', '中'), - (0x1F22E, 'M', '右'), - (0x1F22F, 'M', '指'), - (0x1F230, 'M', '走'), - (0x1F231, 'M', '打'), - (0x1F232, 'M', '禁'), - (0x1F233, 'M', '空'), - (0x1F234, 'M', '合'), - (0x1F235, 'M', '満'), - (0x1F236, 'M', '有'), - (0x1F237, 'M', '月'), - (0x1F238, 'M', '申'), - (0x1F239, 'M', '割'), - (0x1F23A, 'M', '営'), - (0x1F23B, 'M', '配'), - (0x1F23C, 'X'), - (0x1F240, 'M', '〔本〕'), - (0x1F241, 'M', '〔三〕'), - (0x1F242, 'M', '〔二〕'), - (0x1F243, 'M', '〔安〕'), - (0x1F244, 'M', '〔点〕'), - (0x1F245, 'M', '〔打〕'), - (0x1F246, 'M', '〔盗〕'), - (0x1F247, 'M', '〔勝〕'), - (0x1F248, 'M', '〔敗〕'), - (0x1F249, 'X'), - (0x1F250, 'M', '得'), - (0x1F251, 'M', '可'), - (0x1F252, 'X'), - (0x1F260, 'V'), - (0x1F266, 'X'), - (0x1F300, 'V'), - (0x1F6D8, 'X'), - (0x1F6DC, 'V'), - (0x1F6ED, 'X'), - (0x1F6F0, 'V'), - (0x1F6FD, 'X'), - (0x1F700, 'V'), - (0x1F777, 'X'), - (0x1F77B, 'V'), - (0x1F7DA, 'X'), - (0x1F7E0, 'V'), - (0x1F7EC, 'X'), - (0x1F7F0, 'V'), - (0x1F7F1, 'X'), - (0x1F800, 'V'), - (0x1F80C, 'X'), - (0x1F810, 'V'), - (0x1F848, 'X'), - (0x1F850, 'V'), - (0x1F85A, 'X'), - (0x1F860, 'V'), - (0x1F888, 'X'), - (0x1F890, 'V'), - (0x1F8AE, 'X'), - (0x1F8B0, 'V'), - (0x1F8B2, 'X'), - (0x1F900, 'V'), - (0x1FA54, 'X'), - (0x1FA60, 'V'), - (0x1FA6E, 'X'), - ] - -def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FA70, 'V'), - (0x1FA7D, 'X'), - (0x1FA80, 'V'), - (0x1FA89, 'X'), - (0x1FA90, 'V'), - (0x1FABE, 'X'), - (0x1FABF, 'V'), - (0x1FAC6, 'X'), - (0x1FACE, 'V'), - (0x1FADC, 'X'), - (0x1FAE0, 'V'), - (0x1FAE9, 'X'), - (0x1FAF0, 'V'), - (0x1FAF9, 'X'), - (0x1FB00, 'V'), - (0x1FB93, 'X'), - (0x1FB94, 'V'), - (0x1FBCB, 'X'), - (0x1FBF0, 'M', '0'), - (0x1FBF1, 'M', '1'), - (0x1FBF2, 'M', '2'), - (0x1FBF3, 'M', '3'), - (0x1FBF4, 'M', '4'), - (0x1FBF5, 'M', '5'), - (0x1FBF6, 'M', '6'), - (0x1FBF7, 'M', '7'), - (0x1FBF8, 'M', '8'), - (0x1FBF9, 'M', '9'), - (0x1FBFA, 'X'), - (0x20000, 'V'), - (0x2A6E0, 'X'), - (0x2A700, 'V'), - (0x2B73A, 'X'), - (0x2B740, 'V'), - (0x2B81E, 'X'), - (0x2B820, 'V'), - (0x2CEA2, 'X'), - (0x2CEB0, 'V'), - (0x2EBE1, 'X'), - (0x2F800, 'M', '丽'), - (0x2F801, 'M', '丸'), - (0x2F802, 'M', '乁'), - (0x2F803, 'M', '𠄢'), - (0x2F804, 'M', '你'), - (0x2F805, 'M', '侮'), - (0x2F806, 'M', '侻'), - (0x2F807, 'M', '倂'), - (0x2F808, 'M', '偺'), - (0x2F809, 'M', '備'), - (0x2F80A, 'M', '僧'), - (0x2F80B, 'M', '像'), - (0x2F80C, 'M', '㒞'), - (0x2F80D, 'M', '𠘺'), - (0x2F80E, 'M', '免'), - (0x2F80F, 'M', '兔'), - (0x2F810, 'M', '兤'), - (0x2F811, 'M', '具'), - (0x2F812, 'M', '𠔜'), - (0x2F813, 'M', '㒹'), - (0x2F814, 'M', '內'), - (0x2F815, 'M', '再'), - (0x2F816, 'M', '𠕋'), - (0x2F817, 'M', '冗'), - (0x2F818, 'M', '冤'), - (0x2F819, 'M', '仌'), - (0x2F81A, 'M', '冬'), - (0x2F81B, 'M', '况'), - (0x2F81C, 'M', '𩇟'), - (0x2F81D, 'M', '凵'), - (0x2F81E, 'M', '刃'), - (0x2F81F, 'M', '㓟'), - (0x2F820, 'M', '刻'), - (0x2F821, 'M', '剆'), - (0x2F822, 'M', '割'), - (0x2F823, 'M', '剷'), - (0x2F824, 'M', '㔕'), - (0x2F825, 'M', '勇'), - (0x2F826, 'M', '勉'), - (0x2F827, 'M', '勤'), - (0x2F828, 'M', '勺'), - (0x2F829, 'M', '包'), - (0x2F82A, 'M', '匆'), - (0x2F82B, 'M', '北'), - (0x2F82C, 'M', '卉'), - (0x2F82D, 'M', '卑'), - (0x2F82E, 'M', '博'), - (0x2F82F, 'M', '即'), - (0x2F830, 'M', '卽'), - (0x2F831, 'M', '卿'), - (0x2F834, 'M', '𠨬'), - (0x2F835, 'M', '灰'), - (0x2F836, 'M', '及'), - (0x2F837, 'M', '叟'), - (0x2F838, 'M', '𠭣'), - (0x2F839, 'M', '叫'), - (0x2F83A, 'M', '叱'), - (0x2F83B, 'M', '吆'), - (0x2F83C, 'M', '咞'), - (0x2F83D, 'M', '吸'), - (0x2F83E, 'M', '呈'), - ] - -def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F83F, 'M', '周'), - (0x2F840, 'M', '咢'), - (0x2F841, 'M', '哶'), - (0x2F842, 'M', '唐'), - (0x2F843, 'M', '啓'), - (0x2F844, 'M', '啣'), - (0x2F845, 'M', '善'), - (0x2F847, 'M', '喙'), - (0x2F848, 'M', '喫'), - (0x2F849, 'M', '喳'), - (0x2F84A, 'M', '嗂'), - (0x2F84B, 'M', '圖'), - (0x2F84C, 'M', '嘆'), - (0x2F84D, 'M', '圗'), - (0x2F84E, 'M', '噑'), - (0x2F84F, 'M', '噴'), - (0x2F850, 'M', '切'), - (0x2F851, 'M', '壮'), - (0x2F852, 'M', '城'), - (0x2F853, 'M', '埴'), - (0x2F854, 'M', '堍'), - (0x2F855, 'M', '型'), - (0x2F856, 'M', '堲'), - (0x2F857, 'M', '報'), - (0x2F858, 'M', '墬'), - (0x2F859, 'M', '𡓤'), - (0x2F85A, 'M', '売'), - (0x2F85B, 'M', '壷'), - (0x2F85C, 'M', '夆'), - (0x2F85D, 'M', '多'), - (0x2F85E, 'M', '夢'), - (0x2F85F, 'M', '奢'), - (0x2F860, 'M', '𡚨'), - (0x2F861, 'M', '𡛪'), - (0x2F862, 'M', '姬'), - (0x2F863, 'M', '娛'), - (0x2F864, 'M', '娧'), - (0x2F865, 'M', '姘'), - (0x2F866, 'M', '婦'), - (0x2F867, 'M', '㛮'), - (0x2F868, 'X'), - (0x2F869, 'M', '嬈'), - (0x2F86A, 'M', '嬾'), - (0x2F86C, 'M', '𡧈'), - (0x2F86D, 'M', '寃'), - (0x2F86E, 'M', '寘'), - (0x2F86F, 'M', '寧'), - (0x2F870, 'M', '寳'), - (0x2F871, 'M', '𡬘'), - (0x2F872, 'M', '寿'), - (0x2F873, 'M', '将'), - (0x2F874, 'X'), - (0x2F875, 'M', '尢'), - (0x2F876, 'M', '㞁'), - (0x2F877, 'M', '屠'), - (0x2F878, 'M', '屮'), - (0x2F879, 'M', '峀'), - (0x2F87A, 'M', '岍'), - (0x2F87B, 'M', '𡷤'), - (0x2F87C, 'M', '嵃'), - (0x2F87D, 'M', '𡷦'), - (0x2F87E, 'M', '嵮'), - (0x2F87F, 'M', '嵫'), - (0x2F880, 'M', '嵼'), - (0x2F881, 'M', '巡'), - (0x2F882, 'M', '巢'), - (0x2F883, 'M', '㠯'), - (0x2F884, 'M', '巽'), - (0x2F885, 'M', '帨'), - (0x2F886, 'M', '帽'), - (0x2F887, 'M', '幩'), - (0x2F888, 'M', '㡢'), - (0x2F889, 'M', '𢆃'), - (0x2F88A, 'M', '㡼'), - (0x2F88B, 'M', '庰'), - (0x2F88C, 'M', '庳'), - (0x2F88D, 'M', '庶'), - (0x2F88E, 'M', '廊'), - (0x2F88F, 'M', '𪎒'), - (0x2F890, 'M', '廾'), - (0x2F891, 'M', '𢌱'), - (0x2F893, 'M', '舁'), - (0x2F894, 'M', '弢'), - (0x2F896, 'M', '㣇'), - (0x2F897, 'M', '𣊸'), - (0x2F898, 'M', '𦇚'), - (0x2F899, 'M', '形'), - (0x2F89A, 'M', '彫'), - (0x2F89B, 'M', '㣣'), - (0x2F89C, 'M', '徚'), - (0x2F89D, 'M', '忍'), - (0x2F89E, 'M', '志'), - (0x2F89F, 'M', '忹'), - (0x2F8A0, 'M', '悁'), - (0x2F8A1, 'M', '㤺'), - (0x2F8A2, 'M', '㤜'), - (0x2F8A3, 'M', '悔'), - (0x2F8A4, 'M', '𢛔'), - (0x2F8A5, 'M', '惇'), - (0x2F8A6, 'M', '慈'), - ] - -def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F8A7, 'M', '慌'), - (0x2F8A8, 'M', '慎'), - (0x2F8A9, 'M', '慌'), - (0x2F8AA, 'M', '慺'), - (0x2F8AB, 'M', '憎'), - (0x2F8AC, 'M', '憲'), - (0x2F8AD, 'M', '憤'), - (0x2F8AE, 'M', '憯'), - (0x2F8AF, 'M', '懞'), - (0x2F8B0, 'M', '懲'), - (0x2F8B1, 'M', '懶'), - (0x2F8B2, 'M', '成'), - (0x2F8B3, 'M', '戛'), - (0x2F8B4, 'M', '扝'), - (0x2F8B5, 'M', '抱'), - (0x2F8B6, 'M', '拔'), - (0x2F8B7, 'M', '捐'), - (0x2F8B8, 'M', '𢬌'), - (0x2F8B9, 'M', '挽'), - (0x2F8BA, 'M', '拼'), - (0x2F8BB, 'M', '捨'), - (0x2F8BC, 'M', '掃'), - (0x2F8BD, 'M', '揤'), - (0x2F8BE, 'M', '𢯱'), - (0x2F8BF, 'M', '搢'), - (0x2F8C0, 'M', '揅'), - (0x2F8C1, 'M', '掩'), - (0x2F8C2, 'M', '㨮'), - (0x2F8C3, 'M', '摩'), - (0x2F8C4, 'M', '摾'), - (0x2F8C5, 'M', '撝'), - (0x2F8C6, 'M', '摷'), - (0x2F8C7, 'M', '㩬'), - (0x2F8C8, 'M', '敏'), - (0x2F8C9, 'M', '敬'), - (0x2F8CA, 'M', '𣀊'), - (0x2F8CB, 'M', '旣'), - (0x2F8CC, 'M', '書'), - (0x2F8CD, 'M', '晉'), - (0x2F8CE, 'M', '㬙'), - (0x2F8CF, 'M', '暑'), - (0x2F8D0, 'M', '㬈'), - (0x2F8D1, 'M', '㫤'), - (0x2F8D2, 'M', '冒'), - (0x2F8D3, 'M', '冕'), - (0x2F8D4, 'M', '最'), - (0x2F8D5, 'M', '暜'), - (0x2F8D6, 'M', '肭'), - (0x2F8D7, 'M', '䏙'), - (0x2F8D8, 'M', '朗'), - (0x2F8D9, 'M', '望'), - (0x2F8DA, 'M', '朡'), - (0x2F8DB, 'M', '杞'), - (0x2F8DC, 'M', '杓'), - (0x2F8DD, 'M', '𣏃'), - (0x2F8DE, 'M', '㭉'), - (0x2F8DF, 'M', '柺'), - (0x2F8E0, 'M', '枅'), - (0x2F8E1, 'M', '桒'), - (0x2F8E2, 'M', '梅'), - (0x2F8E3, 'M', '𣑭'), - (0x2F8E4, 'M', '梎'), - (0x2F8E5, 'M', '栟'), - (0x2F8E6, 'M', '椔'), - (0x2F8E7, 'M', '㮝'), - (0x2F8E8, 'M', '楂'), - (0x2F8E9, 'M', '榣'), - (0x2F8EA, 'M', '槪'), - (0x2F8EB, 'M', '檨'), - (0x2F8EC, 'M', '𣚣'), - (0x2F8ED, 'M', '櫛'), - (0x2F8EE, 'M', '㰘'), - (0x2F8EF, 'M', '次'), - (0x2F8F0, 'M', '𣢧'), - (0x2F8F1, 'M', '歔'), - (0x2F8F2, 'M', '㱎'), - (0x2F8F3, 'M', '歲'), - (0x2F8F4, 'M', '殟'), - (0x2F8F5, 'M', '殺'), - (0x2F8F6, 'M', '殻'), - (0x2F8F7, 'M', '𣪍'), - (0x2F8F8, 'M', '𡴋'), - (0x2F8F9, 'M', '𣫺'), - (0x2F8FA, 'M', '汎'), - (0x2F8FB, 'M', '𣲼'), - (0x2F8FC, 'M', '沿'), - (0x2F8FD, 'M', '泍'), - (0x2F8FE, 'M', '汧'), - (0x2F8FF, 'M', '洖'), - (0x2F900, 'M', '派'), - (0x2F901, 'M', '海'), - (0x2F902, 'M', '流'), - (0x2F903, 'M', '浩'), - (0x2F904, 'M', '浸'), - (0x2F905, 'M', '涅'), - (0x2F906, 'M', '𣴞'), - (0x2F907, 'M', '洴'), - (0x2F908, 'M', '港'), - (0x2F909, 'M', '湮'), - (0x2F90A, 'M', '㴳'), - ] - -def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F90B, 'M', '滋'), - (0x2F90C, 'M', '滇'), - (0x2F90D, 'M', '𣻑'), - (0x2F90E, 'M', '淹'), - (0x2F90F, 'M', '潮'), - (0x2F910, 'M', '𣽞'), - (0x2F911, 'M', '𣾎'), - (0x2F912, 'M', '濆'), - (0x2F913, 'M', '瀹'), - (0x2F914, 'M', '瀞'), - (0x2F915, 'M', '瀛'), - (0x2F916, 'M', '㶖'), - (0x2F917, 'M', '灊'), - (0x2F918, 'M', '災'), - (0x2F919, 'M', '灷'), - (0x2F91A, 'M', '炭'), - (0x2F91B, 'M', '𠔥'), - (0x2F91C, 'M', '煅'), - (0x2F91D, 'M', '𤉣'), - (0x2F91E, 'M', '熜'), - (0x2F91F, 'X'), - (0x2F920, 'M', '爨'), - (0x2F921, 'M', '爵'), - (0x2F922, 'M', '牐'), - (0x2F923, 'M', '𤘈'), - (0x2F924, 'M', '犀'), - (0x2F925, 'M', '犕'), - (0x2F926, 'M', '𤜵'), - (0x2F927, 'M', '𤠔'), - (0x2F928, 'M', '獺'), - (0x2F929, 'M', '王'), - (0x2F92A, 'M', '㺬'), - (0x2F92B, 'M', '玥'), - (0x2F92C, 'M', '㺸'), - (0x2F92E, 'M', '瑇'), - (0x2F92F, 'M', '瑜'), - (0x2F930, 'M', '瑱'), - (0x2F931, 'M', '璅'), - (0x2F932, 'M', '瓊'), - (0x2F933, 'M', '㼛'), - (0x2F934, 'M', '甤'), - (0x2F935, 'M', '𤰶'), - (0x2F936, 'M', '甾'), - (0x2F937, 'M', '𤲒'), - (0x2F938, 'M', '異'), - (0x2F939, 'M', '𢆟'), - (0x2F93A, 'M', '瘐'), - (0x2F93B, 'M', '𤾡'), - (0x2F93C, 'M', '𤾸'), - (0x2F93D, 'M', '𥁄'), - (0x2F93E, 'M', '㿼'), - (0x2F93F, 'M', '䀈'), - (0x2F940, 'M', '直'), - (0x2F941, 'M', '𥃳'), - (0x2F942, 'M', '𥃲'), - (0x2F943, 'M', '𥄙'), - (0x2F944, 'M', '𥄳'), - (0x2F945, 'M', '眞'), - (0x2F946, 'M', '真'), - (0x2F948, 'M', '睊'), - (0x2F949, 'M', '䀹'), - (0x2F94A, 'M', '瞋'), - (0x2F94B, 'M', '䁆'), - (0x2F94C, 'M', '䂖'), - (0x2F94D, 'M', '𥐝'), - (0x2F94E, 'M', '硎'), - (0x2F94F, 'M', '碌'), - (0x2F950, 'M', '磌'), - (0x2F951, 'M', '䃣'), - (0x2F952, 'M', '𥘦'), - (0x2F953, 'M', '祖'), - (0x2F954, 'M', '𥚚'), - (0x2F955, 'M', '𥛅'), - (0x2F956, 'M', '福'), - (0x2F957, 'M', '秫'), - (0x2F958, 'M', '䄯'), - (0x2F959, 'M', '穀'), - (0x2F95A, 'M', '穊'), - (0x2F95B, 'M', '穏'), - (0x2F95C, 'M', '𥥼'), - (0x2F95D, 'M', '𥪧'), - (0x2F95F, 'X'), - (0x2F960, 'M', '䈂'), - (0x2F961, 'M', '𥮫'), - (0x2F962, 'M', '篆'), - (0x2F963, 'M', '築'), - (0x2F964, 'M', '䈧'), - (0x2F965, 'M', '𥲀'), - (0x2F966, 'M', '糒'), - (0x2F967, 'M', '䊠'), - (0x2F968, 'M', '糨'), - (0x2F969, 'M', '糣'), - (0x2F96A, 'M', '紀'), - (0x2F96B, 'M', '𥾆'), - (0x2F96C, 'M', '絣'), - (0x2F96D, 'M', '䌁'), - (0x2F96E, 'M', '緇'), - (0x2F96F, 'M', '縂'), - (0x2F970, 'M', '繅'), - (0x2F971, 'M', '䌴'), - ] - -def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F972, 'M', '𦈨'), - (0x2F973, 'M', '𦉇'), - (0x2F974, 'M', '䍙'), - (0x2F975, 'M', '𦋙'), - (0x2F976, 'M', '罺'), - (0x2F977, 'M', '𦌾'), - (0x2F978, 'M', '羕'), - (0x2F979, 'M', '翺'), - (0x2F97A, 'M', '者'), - (0x2F97B, 'M', '𦓚'), - (0x2F97C, 'M', '𦔣'), - (0x2F97D, 'M', '聠'), - (0x2F97E, 'M', '𦖨'), - (0x2F97F, 'M', '聰'), - (0x2F980, 'M', '𣍟'), - (0x2F981, 'M', '䏕'), - (0x2F982, 'M', '育'), - (0x2F983, 'M', '脃'), - (0x2F984, 'M', '䐋'), - (0x2F985, 'M', '脾'), - (0x2F986, 'M', '媵'), - (0x2F987, 'M', '𦞧'), - (0x2F988, 'M', '𦞵'), - (0x2F989, 'M', '𣎓'), - (0x2F98A, 'M', '𣎜'), - (0x2F98B, 'M', '舁'), - (0x2F98C, 'M', '舄'), - (0x2F98D, 'M', '辞'), - (0x2F98E, 'M', '䑫'), - (0x2F98F, 'M', '芑'), - (0x2F990, 'M', '芋'), - (0x2F991, 'M', '芝'), - (0x2F992, 'M', '劳'), - (0x2F993, 'M', '花'), - (0x2F994, 'M', '芳'), - (0x2F995, 'M', '芽'), - (0x2F996, 'M', '苦'), - (0x2F997, 'M', '𦬼'), - (0x2F998, 'M', '若'), - (0x2F999, 'M', '茝'), - (0x2F99A, 'M', '荣'), - (0x2F99B, 'M', '莭'), - (0x2F99C, 'M', '茣'), - (0x2F99D, 'M', '莽'), - (0x2F99E, 'M', '菧'), - (0x2F99F, 'M', '著'), - (0x2F9A0, 'M', '荓'), - (0x2F9A1, 'M', '菊'), - (0x2F9A2, 'M', '菌'), - (0x2F9A3, 'M', '菜'), - (0x2F9A4, 'M', '𦰶'), - (0x2F9A5, 'M', '𦵫'), - (0x2F9A6, 'M', '𦳕'), - (0x2F9A7, 'M', '䔫'), - (0x2F9A8, 'M', '蓱'), - (0x2F9A9, 'M', '蓳'), - (0x2F9AA, 'M', '蔖'), - (0x2F9AB, 'M', '𧏊'), - (0x2F9AC, 'M', '蕤'), - (0x2F9AD, 'M', '𦼬'), - (0x2F9AE, 'M', '䕝'), - (0x2F9AF, 'M', '䕡'), - (0x2F9B0, 'M', '𦾱'), - (0x2F9B1, 'M', '𧃒'), - (0x2F9B2, 'M', '䕫'), - (0x2F9B3, 'M', '虐'), - (0x2F9B4, 'M', '虜'), - (0x2F9B5, 'M', '虧'), - (0x2F9B6, 'M', '虩'), - (0x2F9B7, 'M', '蚩'), - (0x2F9B8, 'M', '蚈'), - (0x2F9B9, 'M', '蜎'), - (0x2F9BA, 'M', '蛢'), - (0x2F9BB, 'M', '蝹'), - (0x2F9BC, 'M', '蜨'), - (0x2F9BD, 'M', '蝫'), - (0x2F9BE, 'M', '螆'), - (0x2F9BF, 'X'), - (0x2F9C0, 'M', '蟡'), - (0x2F9C1, 'M', '蠁'), - (0x2F9C2, 'M', '䗹'), - (0x2F9C3, 'M', '衠'), - (0x2F9C4, 'M', '衣'), - (0x2F9C5, 'M', '𧙧'), - (0x2F9C6, 'M', '裗'), - (0x2F9C7, 'M', '裞'), - (0x2F9C8, 'M', '䘵'), - (0x2F9C9, 'M', '裺'), - (0x2F9CA, 'M', '㒻'), - (0x2F9CB, 'M', '𧢮'), - (0x2F9CC, 'M', '𧥦'), - (0x2F9CD, 'M', '䚾'), - (0x2F9CE, 'M', '䛇'), - (0x2F9CF, 'M', '誠'), - (0x2F9D0, 'M', '諭'), - (0x2F9D1, 'M', '變'), - (0x2F9D2, 'M', '豕'), - (0x2F9D3, 'M', '𧲨'), - (0x2F9D4, 'M', '貫'), - (0x2F9D5, 'M', '賁'), - ] - -def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F9D6, 'M', '贛'), - (0x2F9D7, 'M', '起'), - (0x2F9D8, 'M', '𧼯'), - (0x2F9D9, 'M', '𠠄'), - (0x2F9DA, 'M', '跋'), - (0x2F9DB, 'M', '趼'), - (0x2F9DC, 'M', '跰'), - (0x2F9DD, 'M', '𠣞'), - (0x2F9DE, 'M', '軔'), - (0x2F9DF, 'M', '輸'), - (0x2F9E0, 'M', '𨗒'), - (0x2F9E1, 'M', '𨗭'), - (0x2F9E2, 'M', '邔'), - (0x2F9E3, 'M', '郱'), - (0x2F9E4, 'M', '鄑'), - (0x2F9E5, 'M', '𨜮'), - (0x2F9E6, 'M', '鄛'), - (0x2F9E7, 'M', '鈸'), - (0x2F9E8, 'M', '鋗'), - (0x2F9E9, 'M', '鋘'), - (0x2F9EA, 'M', '鉼'), - (0x2F9EB, 'M', '鏹'), - (0x2F9EC, 'M', '鐕'), - (0x2F9ED, 'M', '𨯺'), - (0x2F9EE, 'M', '開'), - (0x2F9EF, 'M', '䦕'), - (0x2F9F0, 'M', '閷'), - (0x2F9F1, 'M', '𨵷'), - (0x2F9F2, 'M', '䧦'), - (0x2F9F3, 'M', '雃'), - (0x2F9F4, 'M', '嶲'), - (0x2F9F5, 'M', '霣'), - (0x2F9F6, 'M', '𩅅'), - (0x2F9F7, 'M', '𩈚'), - (0x2F9F8, 'M', '䩮'), - (0x2F9F9, 'M', '䩶'), - (0x2F9FA, 'M', '韠'), - (0x2F9FB, 'M', '𩐊'), - (0x2F9FC, 'M', '䪲'), - (0x2F9FD, 'M', '𩒖'), - (0x2F9FE, 'M', '頋'), - (0x2FA00, 'M', '頩'), - (0x2FA01, 'M', '𩖶'), - (0x2FA02, 'M', '飢'), - (0x2FA03, 'M', '䬳'), - (0x2FA04, 'M', '餩'), - (0x2FA05, 'M', '馧'), - (0x2FA06, 'M', '駂'), - (0x2FA07, 'M', '駾'), - (0x2FA08, 'M', '䯎'), - (0x2FA09, 'M', '𩬰'), - (0x2FA0A, 'M', '鬒'), - (0x2FA0B, 'M', '鱀'), - (0x2FA0C, 'M', '鳽'), - (0x2FA0D, 'M', '䳎'), - (0x2FA0E, 'M', '䳭'), - (0x2FA0F, 'M', '鵧'), - (0x2FA10, 'M', '𪃎'), - (0x2FA11, 'M', '䳸'), - (0x2FA12, 'M', '𪄅'), - (0x2FA13, 'M', '𪈎'), - (0x2FA14, 'M', '𪊑'), - (0x2FA15, 'M', '麻'), - (0x2FA16, 'M', '䵖'), - (0x2FA17, 'M', '黹'), - (0x2FA18, 'M', '黾'), - (0x2FA19, 'M', '鼅'), - (0x2FA1A, 'M', '鼏'), - (0x2FA1B, 'M', '鼖'), - (0x2FA1C, 'M', '鼻'), - (0x2FA1D, 'M', '𪘀'), - (0x2FA1E, 'X'), - (0x30000, 'V'), - (0x3134B, 'X'), - (0x31350, 'V'), - (0x323B0, 'X'), - (0xE0100, 'I'), - (0xE01F0, 'X'), - ] - -uts46data = tuple( - _seg_0() - + _seg_1() - + _seg_2() - + _seg_3() - + _seg_4() - + _seg_5() - + _seg_6() - + _seg_7() - + _seg_8() - + _seg_9() - + _seg_10() - + _seg_11() - + _seg_12() - + _seg_13() - + _seg_14() - + _seg_15() - + _seg_16() - + _seg_17() - + _seg_18() - + _seg_19() - + _seg_20() - + _seg_21() - + _seg_22() - + _seg_23() - + _seg_24() - + _seg_25() - + _seg_26() - + _seg_27() - + _seg_28() - + _seg_29() - + _seg_30() - + _seg_31() - + _seg_32() - + _seg_33() - + _seg_34() - + _seg_35() - + _seg_36() - + _seg_37() - + _seg_38() - + _seg_39() - + _seg_40() - + _seg_41() - + _seg_42() - + _seg_43() - + _seg_44() - + _seg_45() - + _seg_46() - + _seg_47() - + _seg_48() - + _seg_49() - + _seg_50() - + _seg_51() - + _seg_52() - + _seg_53() - + _seg_54() - + _seg_55() - + _seg_56() - + _seg_57() - + _seg_58() - + _seg_59() - + _seg_60() - + _seg_61() - + _seg_62() - + _seg_63() - + _seg_64() - + _seg_65() - + _seg_66() - + _seg_67() - + _seg_68() - + _seg_69() - + _seg_70() - + _seg_71() - + _seg_72() - + _seg_73() - + _seg_74() - + _seg_75() - + _seg_76() - + _seg_77() - + _seg_78() - + _seg_79() - + _seg_80() - + _seg_81() -) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/.venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py b/.venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py deleted file mode 100644 index 5071021..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/msgpack/__init__.py +++ /dev/null @@ -1,57 +0,0 @@ -# coding: utf-8 -from .exceptions import * -from .ext import ExtType, Timestamp - -import os -import sys - - -version = (1, 0, 4) -__version__ = "1.0.4" - - -if os.environ.get("MSGPACK_PUREPYTHON") or sys.version_info[0] == 2: - from .fallback import Packer, unpackb, Unpacker -else: - try: - from ._cmsgpack import Packer, unpackb, Unpacker - except ImportError: - from .fallback import Packer, unpackb, Unpacker - - -def pack(o, stream, **kwargs): - """ - Pack object `o` and write it to `stream` - - See :class:`Packer` for options. - """ - packer = Packer(**kwargs) - stream.write(packer.pack(o)) - - -def packb(o, **kwargs): - """ - Pack object `o` and return packed bytes - - See :class:`Packer` for options. - """ - return Packer(**kwargs).pack(o) - - -def unpack(stream, **kwargs): - """ - Unpack an object from `stream`. - - Raises `ExtraData` when `stream` contains extra bytes. - See :class:`Unpacker` for options. - """ - data = stream.read() - return unpackb(data, **kwargs) - - -# alias for compatibility to simplejson/marshal/pickle. -load = unpack -loads = unpackb - -dump = pack -dumps = packb diff --git a/.venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py b/.venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py deleted file mode 100644 index d6d2615..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/msgpack/exceptions.py +++ /dev/null @@ -1,48 +0,0 @@ -class UnpackException(Exception): - """Base class for some exceptions raised while unpacking. - - NOTE: unpack may raise exception other than subclass of - UnpackException. If you want to catch all error, catch - Exception instead. - """ - - -class BufferFull(UnpackException): - pass - - -class OutOfData(UnpackException): - pass - - -class FormatError(ValueError, UnpackException): - """Invalid msgpack format""" - - -class StackError(ValueError, UnpackException): - """Too nested""" - - -# Deprecated. Use ValueError instead -UnpackValueError = ValueError - - -class ExtraData(UnpackValueError): - """ExtraData is raised when there is trailing data. - - This exception is raised while only one-shot (not streaming) - unpack. - """ - - def __init__(self, unpacked, extra): - self.unpacked = unpacked - self.extra = extra - - def __str__(self): - return "unpack(b) received extra data." - - -# Deprecated. Use Exception instead to catch all exception during packing. -PackException = Exception -PackValueError = ValueError -PackOverflowError = OverflowError diff --git a/.venv/Lib/site-packages/pip/_vendor/msgpack/ext.py b/.venv/Lib/site-packages/pip/_vendor/msgpack/ext.py deleted file mode 100644 index 25544c5..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/msgpack/ext.py +++ /dev/null @@ -1,193 +0,0 @@ -# coding: utf-8 -from collections import namedtuple -import datetime -import sys -import struct - - -PY2 = sys.version_info[0] == 2 - -if PY2: - int_types = (int, long) - _utc = None -else: - int_types = int - try: - _utc = datetime.timezone.utc - except AttributeError: - _utc = datetime.timezone(datetime.timedelta(0)) - - -class ExtType(namedtuple("ExtType", "code data")): - """ExtType represents ext type in msgpack.""" - - def __new__(cls, code, data): - if not isinstance(code, int): - raise TypeError("code must be int") - if not isinstance(data, bytes): - raise TypeError("data must be bytes") - if not 0 <= code <= 127: - raise ValueError("code must be 0~127") - return super(ExtType, cls).__new__(cls, code, data) - - -class Timestamp(object): - """Timestamp represents the Timestamp extension type in msgpack. - - When built with Cython, msgpack uses C methods to pack and unpack `Timestamp`. When using pure-Python - msgpack, :func:`to_bytes` and :func:`from_bytes` are used to pack and unpack `Timestamp`. - - This class is immutable: Do not override seconds and nanoseconds. - """ - - __slots__ = ["seconds", "nanoseconds"] - - def __init__(self, seconds, nanoseconds=0): - """Initialize a Timestamp object. - - :param int seconds: - Number of seconds since the UNIX epoch (00:00:00 UTC Jan 1 1970, minus leap seconds). - May be negative. - - :param int nanoseconds: - Number of nanoseconds to add to `seconds` to get fractional time. - Maximum is 999_999_999. Default is 0. - - Note: Negative times (before the UNIX epoch) are represented as negative seconds + positive ns. - """ - if not isinstance(seconds, int_types): - raise TypeError("seconds must be an interger") - if not isinstance(nanoseconds, int_types): - raise TypeError("nanoseconds must be an integer") - if not (0 <= nanoseconds < 10**9): - raise ValueError( - "nanoseconds must be a non-negative integer less than 999999999." - ) - self.seconds = seconds - self.nanoseconds = nanoseconds - - def __repr__(self): - """String representation of Timestamp.""" - return "Timestamp(seconds={0}, nanoseconds={1})".format( - self.seconds, self.nanoseconds - ) - - def __eq__(self, other): - """Check for equality with another Timestamp object""" - if type(other) is self.__class__: - return ( - self.seconds == other.seconds and self.nanoseconds == other.nanoseconds - ) - return False - - def __ne__(self, other): - """not-equals method (see :func:`__eq__()`)""" - return not self.__eq__(other) - - def __hash__(self): - return hash((self.seconds, self.nanoseconds)) - - @staticmethod - def from_bytes(b): - """Unpack bytes into a `Timestamp` object. - - Used for pure-Python msgpack unpacking. - - :param b: Payload from msgpack ext message with code -1 - :type b: bytes - - :returns: Timestamp object unpacked from msgpack ext payload - :rtype: Timestamp - """ - if len(b) == 4: - seconds = struct.unpack("!L", b)[0] - nanoseconds = 0 - elif len(b) == 8: - data64 = struct.unpack("!Q", b)[0] - seconds = data64 & 0x00000003FFFFFFFF - nanoseconds = data64 >> 34 - elif len(b) == 12: - nanoseconds, seconds = struct.unpack("!Iq", b) - else: - raise ValueError( - "Timestamp type can only be created from 32, 64, or 96-bit byte objects" - ) - return Timestamp(seconds, nanoseconds) - - def to_bytes(self): - """Pack this Timestamp object into bytes. - - Used for pure-Python msgpack packing. - - :returns data: Payload for EXT message with code -1 (timestamp type) - :rtype: bytes - """ - if (self.seconds >> 34) == 0: # seconds is non-negative and fits in 34 bits - data64 = self.nanoseconds << 34 | self.seconds - if data64 & 0xFFFFFFFF00000000 == 0: - # nanoseconds is zero and seconds < 2**32, so timestamp 32 - data = struct.pack("!L", data64) - else: - # timestamp 64 - data = struct.pack("!Q", data64) - else: - # timestamp 96 - data = struct.pack("!Iq", self.nanoseconds, self.seconds) - return data - - @staticmethod - def from_unix(unix_sec): - """Create a Timestamp from posix timestamp in seconds. - - :param unix_float: Posix timestamp in seconds. - :type unix_float: int or float. - """ - seconds = int(unix_sec // 1) - nanoseconds = int((unix_sec % 1) * 10**9) - return Timestamp(seconds, nanoseconds) - - def to_unix(self): - """Get the timestamp as a floating-point value. - - :returns: posix timestamp - :rtype: float - """ - return self.seconds + self.nanoseconds / 1e9 - - @staticmethod - def from_unix_nano(unix_ns): - """Create a Timestamp from posix timestamp in nanoseconds. - - :param int unix_ns: Posix timestamp in nanoseconds. - :rtype: Timestamp - """ - return Timestamp(*divmod(unix_ns, 10**9)) - - def to_unix_nano(self): - """Get the timestamp as a unixtime in nanoseconds. - - :returns: posix timestamp in nanoseconds - :rtype: int - """ - return self.seconds * 10**9 + self.nanoseconds - - def to_datetime(self): - """Get the timestamp as a UTC datetime. - - Python 2 is not supported. - - :rtype: datetime. - """ - return datetime.datetime.fromtimestamp(0, _utc) + datetime.timedelta( - seconds=self.to_unix() - ) - - @staticmethod - def from_datetime(dt): - """Create a Timestamp from datetime with tzinfo. - - Python 2 is not supported. - - :rtype: Timestamp - """ - return Timestamp.from_unix(dt.timestamp()) diff --git a/.venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py b/.venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py deleted file mode 100644 index f560c7b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/msgpack/fallback.py +++ /dev/null @@ -1,1010 +0,0 @@ -"""Fallback pure Python implementation of msgpack""" -from datetime import datetime as _DateTime -import sys -import struct - - -PY2 = sys.version_info[0] == 2 -if PY2: - int_types = (int, long) - - def dict_iteritems(d): - return d.iteritems() - -else: - int_types = int - unicode = str - xrange = range - - def dict_iteritems(d): - return d.items() - - -if sys.version_info < (3, 5): - # Ugly hack... - RecursionError = RuntimeError - - def _is_recursionerror(e): - return ( - len(e.args) == 1 - and isinstance(e.args[0], str) - and e.args[0].startswith("maximum recursion depth exceeded") - ) - -else: - - def _is_recursionerror(e): - return True - - -if hasattr(sys, "pypy_version_info"): - # StringIO is slow on PyPy, StringIO is faster. However: PyPy's own - # StringBuilder is fastest. - from __pypy__ import newlist_hint - - try: - from __pypy__.builders import BytesBuilder as StringBuilder - except ImportError: - from __pypy__.builders import StringBuilder - USING_STRINGBUILDER = True - - class StringIO(object): - def __init__(self, s=b""): - if s: - self.builder = StringBuilder(len(s)) - self.builder.append(s) - else: - self.builder = StringBuilder() - - def write(self, s): - if isinstance(s, memoryview): - s = s.tobytes() - elif isinstance(s, bytearray): - s = bytes(s) - self.builder.append(s) - - def getvalue(self): - return self.builder.build() - -else: - USING_STRINGBUILDER = False - from io import BytesIO as StringIO - - newlist_hint = lambda size: [] - - -from .exceptions import BufferFull, OutOfData, ExtraData, FormatError, StackError - -from .ext import ExtType, Timestamp - - -EX_SKIP = 0 -EX_CONSTRUCT = 1 -EX_READ_ARRAY_HEADER = 2 -EX_READ_MAP_HEADER = 3 - -TYPE_IMMEDIATE = 0 -TYPE_ARRAY = 1 -TYPE_MAP = 2 -TYPE_RAW = 3 -TYPE_BIN = 4 -TYPE_EXT = 5 - -DEFAULT_RECURSE_LIMIT = 511 - - -def _check_type_strict(obj, t, type=type, tuple=tuple): - if type(t) is tuple: - return type(obj) in t - else: - return type(obj) is t - - -def _get_data_from_buffer(obj): - view = memoryview(obj) - if view.itemsize != 1: - raise ValueError("cannot unpack from multi-byte object") - return view - - -def unpackb(packed, **kwargs): - """ - Unpack an object from `packed`. - - Raises ``ExtraData`` when *packed* contains extra bytes. - Raises ``ValueError`` when *packed* is incomplete. - Raises ``FormatError`` when *packed* is not valid msgpack. - Raises ``StackError`` when *packed* contains too nested. - Other exceptions can be raised during unpacking. - - See :class:`Unpacker` for options. - """ - unpacker = Unpacker(None, max_buffer_size=len(packed), **kwargs) - unpacker.feed(packed) - try: - ret = unpacker._unpack() - except OutOfData: - raise ValueError("Unpack failed: incomplete input") - except RecursionError as e: - if _is_recursionerror(e): - raise StackError - raise - if unpacker._got_extradata(): - raise ExtraData(ret, unpacker._get_extradata()) - return ret - - -if sys.version_info < (2, 7, 6): - - def _unpack_from(f, b, o=0): - """Explicit type cast for legacy struct.unpack_from""" - return struct.unpack_from(f, bytes(b), o) - -else: - _unpack_from = struct.unpack_from - -_NO_FORMAT_USED = "" -_MSGPACK_HEADERS = { - 0xC4: (1, _NO_FORMAT_USED, TYPE_BIN), - 0xC5: (2, ">H", TYPE_BIN), - 0xC6: (4, ">I", TYPE_BIN), - 0xC7: (2, "Bb", TYPE_EXT), - 0xC8: (3, ">Hb", TYPE_EXT), - 0xC9: (5, ">Ib", TYPE_EXT), - 0xCA: (4, ">f"), - 0xCB: (8, ">d"), - 0xCC: (1, _NO_FORMAT_USED), - 0xCD: (2, ">H"), - 0xCE: (4, ">I"), - 0xCF: (8, ">Q"), - 0xD0: (1, "b"), - 0xD1: (2, ">h"), - 0xD2: (4, ">i"), - 0xD3: (8, ">q"), - 0xD4: (1, "b1s", TYPE_EXT), - 0xD5: (2, "b2s", TYPE_EXT), - 0xD6: (4, "b4s", TYPE_EXT), - 0xD7: (8, "b8s", TYPE_EXT), - 0xD8: (16, "b16s", TYPE_EXT), - 0xD9: (1, _NO_FORMAT_USED, TYPE_RAW), - 0xDA: (2, ">H", TYPE_RAW), - 0xDB: (4, ">I", TYPE_RAW), - 0xDC: (2, ">H", TYPE_ARRAY), - 0xDD: (4, ">I", TYPE_ARRAY), - 0xDE: (2, ">H", TYPE_MAP), - 0xDF: (4, ">I", TYPE_MAP), -} - - -class Unpacker(object): - """Streaming unpacker. - - Arguments: - - :param file_like: - File-like object having `.read(n)` method. - If specified, unpacker reads serialized data from it and :meth:`feed()` is not usable. - - :param int read_size: - Used as `file_like.read(read_size)`. (default: `min(16*1024, max_buffer_size)`) - - :param bool use_list: - If true, unpack msgpack array to Python list. - Otherwise, unpack to Python tuple. (default: True) - - :param bool raw: - If true, unpack msgpack raw to Python bytes. - Otherwise, unpack to Python str by decoding with UTF-8 encoding (default). - - :param int timestamp: - Control how timestamp type is unpacked: - - 0 - Timestamp - 1 - float (Seconds from the EPOCH) - 2 - int (Nanoseconds from the EPOCH) - 3 - datetime.datetime (UTC). Python 2 is not supported. - - :param bool strict_map_key: - If true (default), only str or bytes are accepted for map (dict) keys. - - :param callable object_hook: - When specified, it should be callable. - Unpacker calls it with a dict argument after unpacking msgpack map. - (See also simplejson) - - :param callable object_pairs_hook: - When specified, it should be callable. - Unpacker calls it with a list of key-value pairs after unpacking msgpack map. - (See also simplejson) - - :param str unicode_errors: - The error handler for decoding unicode. (default: 'strict') - This option should be used only when you have msgpack data which - contains invalid UTF-8 string. - - :param int max_buffer_size: - Limits size of data waiting unpacked. 0 means 2**32-1. - The default value is 100*1024*1024 (100MiB). - Raises `BufferFull` exception when it is insufficient. - You should set this parameter when unpacking data from untrusted source. - - :param int max_str_len: - Deprecated, use *max_buffer_size* instead. - Limits max length of str. (default: max_buffer_size) - - :param int max_bin_len: - Deprecated, use *max_buffer_size* instead. - Limits max length of bin. (default: max_buffer_size) - - :param int max_array_len: - Limits max length of array. - (default: max_buffer_size) - - :param int max_map_len: - Limits max length of map. - (default: max_buffer_size//2) - - :param int max_ext_len: - Deprecated, use *max_buffer_size* instead. - Limits max size of ext type. (default: max_buffer_size) - - Example of streaming deserialize from file-like object:: - - unpacker = Unpacker(file_like) - for o in unpacker: - process(o) - - Example of streaming deserialize from socket:: - - unpacker = Unpacker() - while True: - buf = sock.recv(1024**2) - if not buf: - break - unpacker.feed(buf) - for o in unpacker: - process(o) - - Raises ``ExtraData`` when *packed* contains extra bytes. - Raises ``OutOfData`` when *packed* is incomplete. - Raises ``FormatError`` when *packed* is not valid msgpack. - Raises ``StackError`` when *packed* contains too nested. - Other exceptions can be raised during unpacking. - """ - - def __init__( - self, - file_like=None, - read_size=0, - use_list=True, - raw=False, - timestamp=0, - strict_map_key=True, - object_hook=None, - object_pairs_hook=None, - list_hook=None, - unicode_errors=None, - max_buffer_size=100 * 1024 * 1024, - ext_hook=ExtType, - max_str_len=-1, - max_bin_len=-1, - max_array_len=-1, - max_map_len=-1, - max_ext_len=-1, - ): - if unicode_errors is None: - unicode_errors = "strict" - - if file_like is None: - self._feeding = True - else: - if not callable(file_like.read): - raise TypeError("`file_like.read` must be callable") - self.file_like = file_like - self._feeding = False - - #: array of bytes fed. - self._buffer = bytearray() - #: Which position we currently reads - self._buff_i = 0 - - # When Unpacker is used as an iterable, between the calls to next(), - # the buffer is not "consumed" completely, for efficiency sake. - # Instead, it is done sloppily. To make sure we raise BufferFull at - # the correct moments, we have to keep track of how sloppy we were. - # Furthermore, when the buffer is incomplete (that is: in the case - # we raise an OutOfData) we need to rollback the buffer to the correct - # state, which _buf_checkpoint records. - self._buf_checkpoint = 0 - - if not max_buffer_size: - max_buffer_size = 2**31 - 1 - if max_str_len == -1: - max_str_len = max_buffer_size - if max_bin_len == -1: - max_bin_len = max_buffer_size - if max_array_len == -1: - max_array_len = max_buffer_size - if max_map_len == -1: - max_map_len = max_buffer_size // 2 - if max_ext_len == -1: - max_ext_len = max_buffer_size - - self._max_buffer_size = max_buffer_size - if read_size > self._max_buffer_size: - raise ValueError("read_size must be smaller than max_buffer_size") - self._read_size = read_size or min(self._max_buffer_size, 16 * 1024) - self._raw = bool(raw) - self._strict_map_key = bool(strict_map_key) - self._unicode_errors = unicode_errors - self._use_list = use_list - if not (0 <= timestamp <= 3): - raise ValueError("timestamp must be 0..3") - self._timestamp = timestamp - self._list_hook = list_hook - self._object_hook = object_hook - self._object_pairs_hook = object_pairs_hook - self._ext_hook = ext_hook - self._max_str_len = max_str_len - self._max_bin_len = max_bin_len - self._max_array_len = max_array_len - self._max_map_len = max_map_len - self._max_ext_len = max_ext_len - self._stream_offset = 0 - - if list_hook is not None and not callable(list_hook): - raise TypeError("`list_hook` is not callable") - if object_hook is not None and not callable(object_hook): - raise TypeError("`object_hook` is not callable") - if object_pairs_hook is not None and not callable(object_pairs_hook): - raise TypeError("`object_pairs_hook` is not callable") - if object_hook is not None and object_pairs_hook is not None: - raise TypeError( - "object_pairs_hook and object_hook are mutually " "exclusive" - ) - if not callable(ext_hook): - raise TypeError("`ext_hook` is not callable") - - def feed(self, next_bytes): - assert self._feeding - view = _get_data_from_buffer(next_bytes) - if len(self._buffer) - self._buff_i + len(view) > self._max_buffer_size: - raise BufferFull - - # Strip buffer before checkpoint before reading file. - if self._buf_checkpoint > 0: - del self._buffer[: self._buf_checkpoint] - self._buff_i -= self._buf_checkpoint - self._buf_checkpoint = 0 - - # Use extend here: INPLACE_ADD += doesn't reliably typecast memoryview in jython - self._buffer.extend(view) - - def _consume(self): - """Gets rid of the used parts of the buffer.""" - self._stream_offset += self._buff_i - self._buf_checkpoint - self._buf_checkpoint = self._buff_i - - def _got_extradata(self): - return self._buff_i < len(self._buffer) - - def _get_extradata(self): - return self._buffer[self._buff_i :] - - def read_bytes(self, n): - ret = self._read(n, raise_outofdata=False) - self._consume() - return ret - - def _read(self, n, raise_outofdata=True): - # (int) -> bytearray - self._reserve(n, raise_outofdata=raise_outofdata) - i = self._buff_i - ret = self._buffer[i : i + n] - self._buff_i = i + len(ret) - return ret - - def _reserve(self, n, raise_outofdata=True): - remain_bytes = len(self._buffer) - self._buff_i - n - - # Fast path: buffer has n bytes already - if remain_bytes >= 0: - return - - if self._feeding: - self._buff_i = self._buf_checkpoint - raise OutOfData - - # Strip buffer before checkpoint before reading file. - if self._buf_checkpoint > 0: - del self._buffer[: self._buf_checkpoint] - self._buff_i -= self._buf_checkpoint - self._buf_checkpoint = 0 - - # Read from file - remain_bytes = -remain_bytes - if remain_bytes + len(self._buffer) > self._max_buffer_size: - raise BufferFull - while remain_bytes > 0: - to_read_bytes = max(self._read_size, remain_bytes) - read_data = self.file_like.read(to_read_bytes) - if not read_data: - break - assert isinstance(read_data, bytes) - self._buffer += read_data - remain_bytes -= len(read_data) - - if len(self._buffer) < n + self._buff_i and raise_outofdata: - self._buff_i = 0 # rollback - raise OutOfData - - def _read_header(self): - typ = TYPE_IMMEDIATE - n = 0 - obj = None - self._reserve(1) - b = self._buffer[self._buff_i] - self._buff_i += 1 - if b & 0b10000000 == 0: - obj = b - elif b & 0b11100000 == 0b11100000: - obj = -1 - (b ^ 0xFF) - elif b & 0b11100000 == 0b10100000: - n = b & 0b00011111 - typ = TYPE_RAW - if n > self._max_str_len: - raise ValueError("%s exceeds max_str_len(%s)" % (n, self._max_str_len)) - obj = self._read(n) - elif b & 0b11110000 == 0b10010000: - n = b & 0b00001111 - typ = TYPE_ARRAY - if n > self._max_array_len: - raise ValueError( - "%s exceeds max_array_len(%s)" % (n, self._max_array_len) - ) - elif b & 0b11110000 == 0b10000000: - n = b & 0b00001111 - typ = TYPE_MAP - if n > self._max_map_len: - raise ValueError("%s exceeds max_map_len(%s)" % (n, self._max_map_len)) - elif b == 0xC0: - obj = None - elif b == 0xC2: - obj = False - elif b == 0xC3: - obj = True - elif 0xC4 <= b <= 0xC6: - size, fmt, typ = _MSGPACK_HEADERS[b] - self._reserve(size) - if len(fmt) > 0: - n = _unpack_from(fmt, self._buffer, self._buff_i)[0] - else: - n = self._buffer[self._buff_i] - self._buff_i += size - if n > self._max_bin_len: - raise ValueError("%s exceeds max_bin_len(%s)" % (n, self._max_bin_len)) - obj = self._read(n) - elif 0xC7 <= b <= 0xC9: - size, fmt, typ = _MSGPACK_HEADERS[b] - self._reserve(size) - L, n = _unpack_from(fmt, self._buffer, self._buff_i) - self._buff_i += size - if L > self._max_ext_len: - raise ValueError("%s exceeds max_ext_len(%s)" % (L, self._max_ext_len)) - obj = self._read(L) - elif 0xCA <= b <= 0xD3: - size, fmt = _MSGPACK_HEADERS[b] - self._reserve(size) - if len(fmt) > 0: - obj = _unpack_from(fmt, self._buffer, self._buff_i)[0] - else: - obj = self._buffer[self._buff_i] - self._buff_i += size - elif 0xD4 <= b <= 0xD8: - size, fmt, typ = _MSGPACK_HEADERS[b] - if self._max_ext_len < size: - raise ValueError( - "%s exceeds max_ext_len(%s)" % (size, self._max_ext_len) - ) - self._reserve(size + 1) - n, obj = _unpack_from(fmt, self._buffer, self._buff_i) - self._buff_i += size + 1 - elif 0xD9 <= b <= 0xDB: - size, fmt, typ = _MSGPACK_HEADERS[b] - self._reserve(size) - if len(fmt) > 0: - (n,) = _unpack_from(fmt, self._buffer, self._buff_i) - else: - n = self._buffer[self._buff_i] - self._buff_i += size - if n > self._max_str_len: - raise ValueError("%s exceeds max_str_len(%s)" % (n, self._max_str_len)) - obj = self._read(n) - elif 0xDC <= b <= 0xDD: - size, fmt, typ = _MSGPACK_HEADERS[b] - self._reserve(size) - (n,) = _unpack_from(fmt, self._buffer, self._buff_i) - self._buff_i += size - if n > self._max_array_len: - raise ValueError( - "%s exceeds max_array_len(%s)" % (n, self._max_array_len) - ) - elif 0xDE <= b <= 0xDF: - size, fmt, typ = _MSGPACK_HEADERS[b] - self._reserve(size) - (n,) = _unpack_from(fmt, self._buffer, self._buff_i) - self._buff_i += size - if n > self._max_map_len: - raise ValueError("%s exceeds max_map_len(%s)" % (n, self._max_map_len)) - else: - raise FormatError("Unknown header: 0x%x" % b) - return typ, n, obj - - def _unpack(self, execute=EX_CONSTRUCT): - typ, n, obj = self._read_header() - - if execute == EX_READ_ARRAY_HEADER: - if typ != TYPE_ARRAY: - raise ValueError("Expected array") - return n - if execute == EX_READ_MAP_HEADER: - if typ != TYPE_MAP: - raise ValueError("Expected map") - return n - # TODO should we eliminate the recursion? - if typ == TYPE_ARRAY: - if execute == EX_SKIP: - for i in xrange(n): - # TODO check whether we need to call `list_hook` - self._unpack(EX_SKIP) - return - ret = newlist_hint(n) - for i in xrange(n): - ret.append(self._unpack(EX_CONSTRUCT)) - if self._list_hook is not None: - ret = self._list_hook(ret) - # TODO is the interaction between `list_hook` and `use_list` ok? - return ret if self._use_list else tuple(ret) - if typ == TYPE_MAP: - if execute == EX_SKIP: - for i in xrange(n): - # TODO check whether we need to call hooks - self._unpack(EX_SKIP) - self._unpack(EX_SKIP) - return - if self._object_pairs_hook is not None: - ret = self._object_pairs_hook( - (self._unpack(EX_CONSTRUCT), self._unpack(EX_CONSTRUCT)) - for _ in xrange(n) - ) - else: - ret = {} - for _ in xrange(n): - key = self._unpack(EX_CONSTRUCT) - if self._strict_map_key and type(key) not in (unicode, bytes): - raise ValueError( - "%s is not allowed for map key" % str(type(key)) - ) - if not PY2 and type(key) is str: - key = sys.intern(key) - ret[key] = self._unpack(EX_CONSTRUCT) - if self._object_hook is not None: - ret = self._object_hook(ret) - return ret - if execute == EX_SKIP: - return - if typ == TYPE_RAW: - if self._raw: - obj = bytes(obj) - else: - obj = obj.decode("utf_8", self._unicode_errors) - return obj - if typ == TYPE_BIN: - return bytes(obj) - if typ == TYPE_EXT: - if n == -1: # timestamp - ts = Timestamp.from_bytes(bytes(obj)) - if self._timestamp == 1: - return ts.to_unix() - elif self._timestamp == 2: - return ts.to_unix_nano() - elif self._timestamp == 3: - return ts.to_datetime() - else: - return ts - else: - return self._ext_hook(n, bytes(obj)) - assert typ == TYPE_IMMEDIATE - return obj - - def __iter__(self): - return self - - def __next__(self): - try: - ret = self._unpack(EX_CONSTRUCT) - self._consume() - return ret - except OutOfData: - self._consume() - raise StopIteration - except RecursionError: - raise StackError - - next = __next__ - - def skip(self): - self._unpack(EX_SKIP) - self._consume() - - def unpack(self): - try: - ret = self._unpack(EX_CONSTRUCT) - except RecursionError: - raise StackError - self._consume() - return ret - - def read_array_header(self): - ret = self._unpack(EX_READ_ARRAY_HEADER) - self._consume() - return ret - - def read_map_header(self): - ret = self._unpack(EX_READ_MAP_HEADER) - self._consume() - return ret - - def tell(self): - return self._stream_offset - - -class Packer(object): - """ - MessagePack Packer - - Usage:: - - packer = Packer() - astream.write(packer.pack(a)) - astream.write(packer.pack(b)) - - Packer's constructor has some keyword arguments: - - :param callable default: - Convert user type to builtin type that Packer supports. - See also simplejson's document. - - :param bool use_single_float: - Use single precision float type for float. (default: False) - - :param bool autoreset: - Reset buffer after each pack and return its content as `bytes`. (default: True). - If set this to false, use `bytes()` to get content and `.reset()` to clear buffer. - - :param bool use_bin_type: - Use bin type introduced in msgpack spec 2.0 for bytes. - It also enables str8 type for unicode. (default: True) - - :param bool strict_types: - If set to true, types will be checked to be exact. Derived classes - from serializable types will not be serialized and will be - treated as unsupported type and forwarded to default. - Additionally tuples will not be serialized as lists. - This is useful when trying to implement accurate serialization - for python types. - - :param bool datetime: - If set to true, datetime with tzinfo is packed into Timestamp type. - Note that the tzinfo is stripped in the timestamp. - You can get UTC datetime with `timestamp=3` option of the Unpacker. - (Python 2 is not supported). - - :param str unicode_errors: - The error handler for encoding unicode. (default: 'strict') - DO NOT USE THIS!! This option is kept for very specific usage. - - Example of streaming deserialize from file-like object:: - - unpacker = Unpacker(file_like) - for o in unpacker: - process(o) - - Example of streaming deserialize from socket:: - - unpacker = Unpacker() - while True: - buf = sock.recv(1024**2) - if not buf: - break - unpacker.feed(buf) - for o in unpacker: - process(o) - - Raises ``ExtraData`` when *packed* contains extra bytes. - Raises ``OutOfData`` when *packed* is incomplete. - Raises ``FormatError`` when *packed* is not valid msgpack. - Raises ``StackError`` when *packed* contains too nested. - Other exceptions can be raised during unpacking. - """ - - def __init__( - self, - default=None, - use_single_float=False, - autoreset=True, - use_bin_type=True, - strict_types=False, - datetime=False, - unicode_errors=None, - ): - self._strict_types = strict_types - self._use_float = use_single_float - self._autoreset = autoreset - self._use_bin_type = use_bin_type - self._buffer = StringIO() - if PY2 and datetime: - raise ValueError("datetime is not supported in Python 2") - self._datetime = bool(datetime) - self._unicode_errors = unicode_errors or "strict" - if default is not None: - if not callable(default): - raise TypeError("default must be callable") - self._default = default - - def _pack( - self, - obj, - nest_limit=DEFAULT_RECURSE_LIMIT, - check=isinstance, - check_type_strict=_check_type_strict, - ): - default_used = False - if self._strict_types: - check = check_type_strict - list_types = list - else: - list_types = (list, tuple) - while True: - if nest_limit < 0: - raise ValueError("recursion limit exceeded") - if obj is None: - return self._buffer.write(b"\xc0") - if check(obj, bool): - if obj: - return self._buffer.write(b"\xc3") - return self._buffer.write(b"\xc2") - if check(obj, int_types): - if 0 <= obj < 0x80: - return self._buffer.write(struct.pack("B", obj)) - if -0x20 <= obj < 0: - return self._buffer.write(struct.pack("b", obj)) - if 0x80 <= obj <= 0xFF: - return self._buffer.write(struct.pack("BB", 0xCC, obj)) - if -0x80 <= obj < 0: - return self._buffer.write(struct.pack(">Bb", 0xD0, obj)) - if 0xFF < obj <= 0xFFFF: - return self._buffer.write(struct.pack(">BH", 0xCD, obj)) - if -0x8000 <= obj < -0x80: - return self._buffer.write(struct.pack(">Bh", 0xD1, obj)) - if 0xFFFF < obj <= 0xFFFFFFFF: - return self._buffer.write(struct.pack(">BI", 0xCE, obj)) - if -0x80000000 <= obj < -0x8000: - return self._buffer.write(struct.pack(">Bi", 0xD2, obj)) - if 0xFFFFFFFF < obj <= 0xFFFFFFFFFFFFFFFF: - return self._buffer.write(struct.pack(">BQ", 0xCF, obj)) - if -0x8000000000000000 <= obj < -0x80000000: - return self._buffer.write(struct.pack(">Bq", 0xD3, obj)) - if not default_used and self._default is not None: - obj = self._default(obj) - default_used = True - continue - raise OverflowError("Integer value out of range") - if check(obj, (bytes, bytearray)): - n = len(obj) - if n >= 2**32: - raise ValueError("%s is too large" % type(obj).__name__) - self._pack_bin_header(n) - return self._buffer.write(obj) - if check(obj, unicode): - obj = obj.encode("utf-8", self._unicode_errors) - n = len(obj) - if n >= 2**32: - raise ValueError("String is too large") - self._pack_raw_header(n) - return self._buffer.write(obj) - if check(obj, memoryview): - n = len(obj) * obj.itemsize - if n >= 2**32: - raise ValueError("Memoryview is too large") - self._pack_bin_header(n) - return self._buffer.write(obj) - if check(obj, float): - if self._use_float: - return self._buffer.write(struct.pack(">Bf", 0xCA, obj)) - return self._buffer.write(struct.pack(">Bd", 0xCB, obj)) - if check(obj, (ExtType, Timestamp)): - if check(obj, Timestamp): - code = -1 - data = obj.to_bytes() - else: - code = obj.code - data = obj.data - assert isinstance(code, int) - assert isinstance(data, bytes) - L = len(data) - if L == 1: - self._buffer.write(b"\xd4") - elif L == 2: - self._buffer.write(b"\xd5") - elif L == 4: - self._buffer.write(b"\xd6") - elif L == 8: - self._buffer.write(b"\xd7") - elif L == 16: - self._buffer.write(b"\xd8") - elif L <= 0xFF: - self._buffer.write(struct.pack(">BB", 0xC7, L)) - elif L <= 0xFFFF: - self._buffer.write(struct.pack(">BH", 0xC8, L)) - else: - self._buffer.write(struct.pack(">BI", 0xC9, L)) - self._buffer.write(struct.pack("b", code)) - self._buffer.write(data) - return - if check(obj, list_types): - n = len(obj) - self._pack_array_header(n) - for i in xrange(n): - self._pack(obj[i], nest_limit - 1) - return - if check(obj, dict): - return self._pack_map_pairs( - len(obj), dict_iteritems(obj), nest_limit - 1 - ) - - if self._datetime and check(obj, _DateTime) and obj.tzinfo is not None: - obj = Timestamp.from_datetime(obj) - default_used = 1 - continue - - if not default_used and self._default is not None: - obj = self._default(obj) - default_used = 1 - continue - - if self._datetime and check(obj, _DateTime): - raise ValueError("Cannot serialize %r where tzinfo=None" % (obj,)) - - raise TypeError("Cannot serialize %r" % (obj,)) - - def pack(self, obj): - try: - self._pack(obj) - except: - self._buffer = StringIO() # force reset - raise - if self._autoreset: - ret = self._buffer.getvalue() - self._buffer = StringIO() - return ret - - def pack_map_pairs(self, pairs): - self._pack_map_pairs(len(pairs), pairs) - if self._autoreset: - ret = self._buffer.getvalue() - self._buffer = StringIO() - return ret - - def pack_array_header(self, n): - if n >= 2**32: - raise ValueError - self._pack_array_header(n) - if self._autoreset: - ret = self._buffer.getvalue() - self._buffer = StringIO() - return ret - - def pack_map_header(self, n): - if n >= 2**32: - raise ValueError - self._pack_map_header(n) - if self._autoreset: - ret = self._buffer.getvalue() - self._buffer = StringIO() - return ret - - def pack_ext_type(self, typecode, data): - if not isinstance(typecode, int): - raise TypeError("typecode must have int type.") - if not 0 <= typecode <= 127: - raise ValueError("typecode should be 0-127") - if not isinstance(data, bytes): - raise TypeError("data must have bytes type") - L = len(data) - if L > 0xFFFFFFFF: - raise ValueError("Too large data") - if L == 1: - self._buffer.write(b"\xd4") - elif L == 2: - self._buffer.write(b"\xd5") - elif L == 4: - self._buffer.write(b"\xd6") - elif L == 8: - self._buffer.write(b"\xd7") - elif L == 16: - self._buffer.write(b"\xd8") - elif L <= 0xFF: - self._buffer.write(b"\xc7" + struct.pack("B", L)) - elif L <= 0xFFFF: - self._buffer.write(b"\xc8" + struct.pack(">H", L)) - else: - self._buffer.write(b"\xc9" + struct.pack(">I", L)) - self._buffer.write(struct.pack("B", typecode)) - self._buffer.write(data) - - def _pack_array_header(self, n): - if n <= 0x0F: - return self._buffer.write(struct.pack("B", 0x90 + n)) - if n <= 0xFFFF: - return self._buffer.write(struct.pack(">BH", 0xDC, n)) - if n <= 0xFFFFFFFF: - return self._buffer.write(struct.pack(">BI", 0xDD, n)) - raise ValueError("Array is too large") - - def _pack_map_header(self, n): - if n <= 0x0F: - return self._buffer.write(struct.pack("B", 0x80 + n)) - if n <= 0xFFFF: - return self._buffer.write(struct.pack(">BH", 0xDE, n)) - if n <= 0xFFFFFFFF: - return self._buffer.write(struct.pack(">BI", 0xDF, n)) - raise ValueError("Dict is too large") - - def _pack_map_pairs(self, n, pairs, nest_limit=DEFAULT_RECURSE_LIMIT): - self._pack_map_header(n) - for (k, v) in pairs: - self._pack(k, nest_limit - 1) - self._pack(v, nest_limit - 1) - - def _pack_raw_header(self, n): - if n <= 0x1F: - self._buffer.write(struct.pack("B", 0xA0 + n)) - elif self._use_bin_type and n <= 0xFF: - self._buffer.write(struct.pack(">BB", 0xD9, n)) - elif n <= 0xFFFF: - self._buffer.write(struct.pack(">BH", 0xDA, n)) - elif n <= 0xFFFFFFFF: - self._buffer.write(struct.pack(">BI", 0xDB, n)) - else: - raise ValueError("Raw is too large") - - def _pack_bin_header(self, n): - if not self._use_bin_type: - return self._pack_raw_header(n) - elif n <= 0xFF: - return self._buffer.write(struct.pack(">BB", 0xC4, n)) - elif n <= 0xFFFF: - return self._buffer.write(struct.pack(">BH", 0xC5, n)) - elif n <= 0xFFFFFFFF: - return self._buffer.write(struct.pack(">BI", 0xC6, n)) - else: - raise ValueError("Bin is too large") - - def bytes(self): - """Return internal buffer contents as bytes object""" - return self._buffer.getvalue() - - def reset(self): - """Reset internal buffer. - - This method is useful only when autoreset=False. - """ - self._buffer = StringIO() - - def getbuffer(self): - """Return view of internal buffer.""" - if USING_STRINGBUILDER or PY2: - return memoryview(self.bytes()) - else: - return self._buffer.getbuffer() diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/__about__.py b/.venv/Lib/site-packages/pip/_vendor/packaging/__about__.py deleted file mode 100644 index 3551bc2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/__about__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "21.3" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014-2019 %s" % __author__ diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/__init__.py b/.venv/Lib/site-packages/pip/_vendor/packaging/__init__.py deleted file mode 100644 index 3c50c5d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py b/.venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py deleted file mode 100644 index 4c379aa..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/_manylinux.py +++ /dev/null @@ -1,301 +0,0 @@ -import collections -import functools -import os -import re -import struct -import sys -import warnings -from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: Tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> Optional[_ELFFileHeader]: - try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header - - -def _is_linux_armhf() -> bool: - # hard-float ABI can be detected from the ELF header of the running - # process - # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result - - -def _have_compatible_abi(arch: str) -> bool: - if arch == "armv7l": - return _is_linux_armhf() - if arch == "i686": - return _is_linux_i686() - return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} - - -# If glibc ever changes its major version, we need to know what the last -# minor version was, so we can build the complete list of all versions. -# For now, guess what the highest minor version might be, assume it will -# be 50 for testing. Once this actually happens, update the dictionary -# with the actual value. -_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) - - -class _GLibCVersion(NamedTuple): - major: int - minor: int - - -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/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 - try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") - assert version_string is not None - _, version = version_string.split() - except (AssertionError, 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. - # - # We must also handle the special case where the executable is not a - # dynamically linked executable. This can occur when using musl libc, - # for example. In this situation, dlopen() will error, leading to an - # OSError. Interestingly, at least in the case of musl, there is no - # errno set on the OSError. The single string argument used to construct - # OSError comes from libc itself and is therefore not portable to - # hard code here. In any case, failure to call dlopen() means we - # can proceed, so we bail on our attempt. - try: - process_namespace = ctypes.CDLL(None) - except OSError: - return 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: str = gnu_get_libc_version() - # py2 / py3 compatibility: - if not isinstance(version_str, str): - version_str = version_str.decode("ascii") - - return version_str - - -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 _parse_glibc_version(version_str: str) -> Tuple[int, int]: - """Parse glibc version. - - We use a regexp instead of str.split because we want to discard any - random junk that might come after the minor version -- this might happen - in patched/forked versions of glibc (e.g. Linaro's version of glibc - uses version strings like "2.20-2014.11"). See gh-3588. - """ - m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) - if not m: - warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, - RuntimeWarning, - ) - return -1, -1 - return int(m.group("major")), int(m.group("minor")) - - -@functools.lru_cache() -def _get_glibc_version() -> Tuple[int, int]: - version_str = _glibc_version_string() - if version_str is None: - return (-1, -1) - return _parse_glibc_version(version_str) - - -# From PEP 513, PEP 600 -def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: - sys_glibc = _get_glibc_version() - if sys_glibc < version: - return False - # Check for presence of _manylinux module. - try: - import _manylinux # noqa - except ImportError: - return True - if hasattr(_manylinux, "manylinux_compatible"): - result = _manylinux.manylinux_compatible(version[0], version[1], arch) - if result is not None: - return bool(result) - return True - if version == _GLibCVersion(2, 5): - if hasattr(_manylinux, "manylinux1_compatible"): - return bool(_manylinux.manylinux1_compatible) - if version == _GLibCVersion(2, 12): - if hasattr(_manylinux, "manylinux2010_compatible"): - return bool(_manylinux.manylinux2010_compatible) - if version == _GLibCVersion(2, 17): - if hasattr(_manylinux, "manylinux2014_compatible"): - return bool(_manylinux.manylinux2014_compatible) - return True - - -_LEGACY_MANYLINUX_MAP = { - # CentOS 7 w/ glibc 2.17 (PEP 599) - (2, 17): "manylinux2014", - # CentOS 6 w/ glibc 2.12 (PEP 571) - (2, 12): "manylinux2010", - # CentOS 5 w/ glibc 2.5 (PEP 513) - (2, 5): "manylinux1", -} - - -def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): - return - # Oldest glibc to be supported regardless of architecture is (2, 17). - too_old_glibc2 = _GLibCVersion(2, 16) - if arch in {"x86_64", "i686"}: - # On x86/i686 also oldest glibc to be supported is (2, 5). - too_old_glibc2 = _GLibCVersion(2, 4) - current_glibc = _GLibCVersion(*_get_glibc_version()) - glibc_max_list = [current_glibc] - # We can assume compatibility across glibc major versions. - # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 - # - # Build a list of maximum glibc versions so that we can - # output the canonical list of all glibc from current_glibc - # down to too_old_glibc2, including all intermediary versions. - for glibc_major in range(current_glibc.major - 1, 1, -1): - glibc_minor = _LAST_GLIBC_MINOR[glibc_major] - glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) - for glibc_max in glibc_max_list: - if glibc_max.major == too_old_glibc2.major: - min_minor = too_old_glibc2.minor - else: - # For other glibc major versions oldest supported is (x, 0). - min_minor = -1 - for glibc_minor in range(glibc_max.minor, min_minor, -1): - glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) - tag = "manylinux_{}_{}".format(*glibc_version) - if _is_compatible(tag, arch, glibc_version): - yield linux.replace("linux", tag) - # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. - if glibc_version in _LEGACY_MANYLINUX_MAP: - legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version] - if _is_compatible(legacy_tag, arch, glibc_version): - yield linux.replace("linux", legacy_tag) diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py b/.venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py deleted file mode 100644 index 8ac3059..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/_musllinux.py +++ /dev/null @@ -1,136 +0,0 @@ -"""PEP 656 support. - -This module implements logic to detect if the currently running Python is -linked against musl, and what musl version is used. -""" - -import contextlib -import functools -import operator -import os -import re -import struct -import subprocess -import sys -from typing import IO, Iterator, NamedTuple, Optional, Tuple - - -def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) - - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None - - -class _MuslVersion(NamedTuple): - major: int - minor: int - - -def _parse_musl_version(output: str) -> Optional[_MuslVersion]: - lines = [n for n in (n.strip() for n in output.splitlines()) if n] - if len(lines) < 2 or lines[0][:4] != "musl": - return None - m = re.match(r"Version (\d+)\.(\d+)", lines[1]) - if not m: - return None - return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) - - -@functools.lru_cache() -def _get_musl_version(executable: str) -> Optional[_MuslVersion]: - """Detect currently-running musl runtime version. - - This is done by checking the specified executable's dynamic linking - information, and invoking the loader to parse its output for a version - string. If the loader is musl, the output would be something like:: - - musl libc (x86_64) - Version 1.2.2 - Dynamic Program Loader - """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except OSError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: - return None - proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) - return _parse_musl_version(proc.stderr) - - -def platform_tags(arch: str) -> Iterator[str]: - """Generate musllinux tags compatible to the current platform. - - :param arch: Should be the part of platform tag after the ``linux_`` - prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a - prerequisite for the current platform to be musllinux-compatible. - - :returns: An iterator of compatible musllinux tags. - """ - sys_musl = _get_musl_version(sys.executable) - if sys_musl is None: # Python not dynamically linked against musl. - return - for minor in range(sys_musl.minor, -1, -1): - yield f"musllinux_{sys_musl.major}_{minor}_{arch}" - - -if __name__ == "__main__": # pragma: no cover - import sysconfig - - plat = sysconfig.get_platform() - assert plat.startswith("linux-"), "not linux" - - print("plat:", plat) - print("musl:", _get_musl_version(sys.executable)) - print("tags:", end=" ") - for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): - print(t, end="\n ") diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/_structures.py b/.venv/Lib/site-packages/pip/_vendor/packaging/_structures.py deleted file mode 100644 index 90a6465..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/_structures.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -class InfinityType: - def __repr__(self) -> str: - return "Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return False - - def __le__(self, other: object) -> bool: - return False - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return True - - def __ge__(self, other: object) -> bool: - return True - - def __neg__(self: object) -> "NegativeInfinityType": - return NegativeInfinity - - -Infinity = InfinityType() - - -class NegativeInfinityType: - def __repr__(self) -> str: - return "-Infinity" - - def __hash__(self) -> int: - return hash(repr(self)) - - def __lt__(self, other: object) -> bool: - return True - - def __le__(self, other: object) -> bool: - return True - - def __eq__(self, other: object) -> bool: - return isinstance(other, self.__class__) - - def __gt__(self, other: object) -> bool: - return False - - def __ge__(self, other: object) -> bool: - return False - - def __neg__(self: object) -> InfinityType: - return Infinity - - -NegativeInfinity = NegativeInfinityType() diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/markers.py b/.venv/Lib/site-packages/pip/_vendor/packaging/markers.py deleted file mode 100644 index 540e7a4..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/markers.py +++ /dev/null @@ -1,304 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import operator -import os -import platform -import sys -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -from pip._vendor.pyparsing import ( # noqa: N817 - Forward, - Group, - Literal as L, - ParseException, - ParseResults, - QuotedString, - ZeroOrMore, - stringEnd, - stringStart, -) - -from .specifiers import InvalidSpecifier, Specifier - -__all__ = [ - "InvalidMarker", - "UndefinedComparison", - "UndefinedEnvironmentName", - "Marker", - "default_environment", -] - -Operator = Callable[[str, str], bool] - - -class InvalidMarker(ValueError): - """ - An invalid marker was found, users should refer to PEP 508. - """ - - -class UndefinedComparison(ValueError): - """ - An invalid operation was attempted on a value that doesn't support it. - """ - - -class UndefinedEnvironmentName(ValueError): - """ - A name was attempted to be used that does not exist inside of the - environment. - """ - - -class Node: - def __init__(self, value: Any) -> None: - self.value = value - - def __str__(self) -> str: - return str(self.value) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - def serialize(self) -> str: - return str(self) - - -class Value(Node): - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - def serialize(self) -> str: - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results - - -def _format_marker( - marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True -) -> str: - - assert isinstance(marker, (list, tuple, str)) - - # Sometimes we have a structure like [[...]] which is a single item list - # where the single item is itself it's own list. In that case we want skip - # the rest of this function so that we don't get extraneous () on the - # outside. - if ( - isinstance(marker, list) - and len(marker) == 1 - and isinstance(marker[0], (list, tuple)) - ): - return _format_marker(marker[0]) - - if isinstance(marker, list): - inner = (_format_marker(m, first=False) for m in marker) - if first: - return " ".join(inner) - else: - return "(" + " ".join(inner) + ")" - elif isinstance(marker, tuple): - return " ".join([m.serialize() for m in marker]) - else: - return marker - - -_operators: Dict[str, Operator] = { - "in": lambda lhs, rhs: lhs in rhs, - "not in": lambda lhs, rhs: lhs not in rhs, - "<": operator.lt, - "<=": operator.le, - "==": operator.eq, - "!=": operator.ne, - ">=": operator.ge, - ">": operator.gt, -} - - -def _eval_op(lhs: str, op: Op, rhs: str) -> bool: - try: - spec = Specifier("".join([op.serialize(), rhs])) - except InvalidSpecifier: - pass - else: - return spec.contains(lhs) - - oper: Optional[Operator] = _operators.get(op.serialize()) - if oper is None: - raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") - - return oper(lhs, rhs) - - -class Undefined: - pass - - -_undefined = Undefined() - - -def _get_env(environment: Dict[str, str], name: str) -> str: - value: Union[str, Undefined] = environment.get(name, _undefined) - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - f"{name!r} does not exist in evaluation environment." - ) - - return value - - -def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: - groups: List[List[bool]] = [[]] - - for marker in markers: - assert isinstance(marker, (list, tuple, str)) - - if isinstance(marker, list): - groups[-1].append(_evaluate_markers(marker, environment)) - elif isinstance(marker, tuple): - lhs, op, rhs = marker - - if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) - rhs_value = rhs.value - else: - lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) - - groups[-1].append(_eval_op(lhs_value, op, rhs_value)) - else: - assert marker in ["and", "or"] - if marker == "or": - groups.append([]) - - return any(all(item) for item in groups) - - -def format_full_version(info: "sys._version_info") -> str: - version = "{0.major}.{0.minor}.{0.micro}".format(info) - kind = info.releaselevel - if kind != "final": - version += kind[0] + str(info.serial) - return version - - -def default_environment() -> Dict[str, str]: - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name - return { - "implementation_name": implementation_name, - "implementation_version": iver, - "os_name": os.name, - "platform_machine": platform.machine(), - "platform_release": platform.release(), - "platform_system": platform.system(), - "platform_version": platform.version(), - "python_full_version": platform.python_version(), - "platform_python_implementation": platform.python_implementation(), - "python_version": ".".join(platform.python_version_tuple()[:2]), - "sys_platform": sys.platform, - } - - -class Marker: - def __init__(self, marker: str) -> None: - try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - raise InvalidMarker( - f"Invalid marker: {marker!r}, parse error at " - f"{marker[e.loc : e.loc + 8]!r}" - ) - - def __str__(self) -> str: - return _format_marker(self._markers) - - def __repr__(self) -> str: - return f"" - - def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: - """Evaluate a marker. - - Return the boolean from evaluating the given marker against the - environment. environment is an optional argument to override all or - part of the determined environment. - - The environment is determined from the current Python process. - """ - current_environment = default_environment() - if environment is not None: - current_environment.update(environment) - - return _evaluate_markers(self._markers, current_environment) diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/requirements.py b/.venv/Lib/site-packages/pip/_vendor/packaging/requirements.py deleted file mode 100644 index 1eab7dd..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/requirements.py +++ /dev/null @@ -1,146 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import re -import string -import urllib.parse -from typing import List, Optional as TOptional, Set - -from pip._vendor.pyparsing import ( # noqa - Combine, - Literal as L, - Optional, - ParseException, - Regex, - Word, - ZeroOrMore, - originalTextFor, - stringEnd, - stringStart, -) - -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet - - -class InvalidRequirement(ValueError): - """ - An invalid requirement was found, users should refer to PEP 508. - """ - - -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - -class Requirement: - """Parse a requirement. - - Parse a given requirement string into its parts, such as name, specifier, - URL, and extras. Raises InvalidRequirement on a badly-formed requirement - string. - """ - - # TODO: Can we test whether something is contained within a requirement? - # If so how do we do that? Do we need to test against the _name_ of - # the thing as well as the version? What about the markers? - # TODO: Can we normalize the name and extra name? - - def __init__(self, requirement_string: str) -> None: - try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' - ) - - self.name: str = req.name - if req.url: - parsed_url = urllib.parse.urlparse(req.url) - if parsed_url.scheme == "file": - if urllib.parse.urlunparse(parsed_url) != req.url: - raise InvalidRequirement("Invalid URL given") - elif not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc - ): - raise InvalidRequirement(f"Invalid URL: {req.url}") - self.url: TOptional[str] = req.url - else: - self.url = None - self.extras: Set[str] = set(req.extras.asList() if req.extras else []) - self.specifier: SpecifierSet = SpecifierSet(req.specifier) - self.marker: TOptional[Marker] = req.marker if req.marker else None - - def __str__(self) -> str: - parts: List[str] = [self.name] - - if self.extras: - formatted_extras = ",".join(sorted(self.extras)) - parts.append(f"[{formatted_extras}]") - - if self.specifier: - parts.append(str(self.specifier)) - - if self.url: - parts.append(f"@ {self.url}") - if self.marker: - parts.append(" ") - - if self.marker: - parts.append(f"; {self.marker}") - - return "".join(parts) - - def __repr__(self) -> str: - return f"" diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py b/.venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py deleted file mode 100644 index 0e218a6..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/specifiers.py +++ /dev/null @@ -1,802 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import abc -import functools -import itertools -import re -import warnings -from typing import ( - Callable, - Dict, - Iterable, - Iterator, - List, - Optional, - Pattern, - Set, - Tuple, - TypeVar, - Union, -) - -from .utils import canonicalize_version -from .version import LegacyVersion, Version, parse - -ParsedVersion = Union[Version, LegacyVersion] -UnparsedVersion = Union[Version, LegacyVersion, str] -VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) -CallableOperator = Callable[[ParsedVersion, str], bool] - - -class InvalidSpecifier(ValueError): - """ - An invalid specifier was found, users should refer to PEP 440. - """ - - -class BaseSpecifier(metaclass=abc.ABCMeta): - @abc.abstractmethod - def __str__(self) -> str: - """ - Returns the str representation of this Specifier like object. This - should be representative of the Specifier itself. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Returns a hash value for this Specifier like object. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Returns a boolean representing whether or not the two Specifier like - objects are equal. - """ - - @abc.abstractproperty - def prereleases(self) -> Optional[bool]: - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @prereleases.setter - def prereleases(self, value: bool) -> None: - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. - """ - - @abc.abstractmethod - def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: - """ - Determines if the given item is contained within this specifier. - """ - - @abc.abstractmethod - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - """ - Takes an iterable of items and filters them so that only items which - are contained within this specifier are allowed in it. - """ - - -class _IndividualSpecifier(BaseSpecifier): - - _operators: Dict[str, str] = {} - _regex: Pattern[str] - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" - - def __str__(self) -> str: - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> Tuple[str, str]: - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version - - @property - def operator(self) -> str: - return self._spec[0] - - @property - def version(self) -> str: - return self._spec[1] - - @property - def prereleases(self) -> Optional[bool]: - return self._prereleases - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: str) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable: CallableOperator = self._get_operator(self.operator) - return operator_callable(normalized_item, self.version) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later in case nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - super().__init__(spec, prereleases) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal( - self, prospective: LegacyVersion, spec: str - ) -> bool: - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn: Callable[["Specifier", ParsedVersion, str], bool] -) -> Callable[["Specifier", ParsedVersion, str], bool]: - @functools.wraps(fn) - def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(~=|==|!=|<=|>=|<|>|===)) - (?P - (?: - # The identity operators allow for an escape hatch that will - # do an exact string match of the version you wish to install. - # This will not be parsed by PEP 440 and we cannot determine - # any semantic meaning from it. This operator is discouraged - # but included entirely as an escape hatch. - (?<====) # Only match for the identity operator - \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. - ) - | - (?: - # The (non)equality operators allow for wild card and local - # versions to be specified so we have to define these two - # operators separately to enable that. - (?<===|!=) # Only match for equals and not equals - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. - (?: - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* - )? - ) - | - (?: - # The compatible operator requires at least two digits in the - # release segment. - (?<=~=) # Only match for the compatible operator - - \s* - v? - (?:[0-9]+!)? # epoch - [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release - ) - | - (?: - # All other operators only allow a sub set of what the - # (non)equality operators do. Specifically they do not allow - # local versions to be specified nor do they allow the prefix - # matching wild cards. - (?=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - "===": "arbitrary", - } - - @_require_version_compare - def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: - - # Compatible releases have an equivalent combination of >= and ==. That - # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to - # implement this in terms of the other specifiers instead of - # implementing it ourselves. The only thing we need to do is construct - # the other specifiers. - - # We want everything but the last item in the version, but we want to - # ignore suffix segments. - prefix = ".".join( - list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] - ) - - # Add the prefix notation to the end of our string - prefix += ".*" - - return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( - prospective, prefix - ) - - @_require_version_compare - def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: - - # We need special logic to handle prefix matching - if spec.endswith(".*"): - # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) - # Split the spec out by dots, and pretend that there is an implicit - # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* - - # Split the prospective version out by dots, and pretend that there - # is an implicit dot in between a release segment and a pre-release - # segment. - split_prospective = _version_split(str(prospective)) - - # Shorten the prospective version to be the same length as the spec - # so that we can determine if the specifier is a prefix of the - # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] - - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec - else: - # Convert our spec string into a Version - spec_version = Version(spec) - - # If the specifier does not have a local segment, then we want to - # act as if the prospective version also does not have a local - # segment. - if not spec_version.local: - prospective = Version(prospective.public) - - return prospective == spec_version - - @_require_version_compare - def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: - return not self._compare_equal(prospective, spec) - - @_require_version_compare - def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) <= Version(spec) - - @_require_version_compare - def _compare_greater_than_equal( - self, prospective: ParsedVersion, spec: str - ) -> bool: - - # NB: Local version identifiers are NOT permitted in the version - # specifier, so local version labels can be universally removed from - # the prospective version. - return Version(prospective.public) >= Version(spec) - - @_require_version_compare - def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is less than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective < spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a pre-release version, that we do not accept pre-release - # versions for the version mentioned in the specifier (e.g. <3.1 should - # not match 3.1.dev0, but should match 3.0.dev0). - if not spec.is_prerelease and prospective.is_prerelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # less than the spec version *and* it's not a pre-release of the same - # version in the spec. - return True - - @_require_version_compare - def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: - - # Convert our spec to a Version instance, since we'll want to work with - # it as a version. - spec = Version(spec_str) - - # Check to see if the prospective version is greater than the spec - # version. If it's not we can short circuit and just return False now - # instead of doing extra unneeded work. - if not prospective > spec: - return False - - # This special case is here so that, unless the specifier itself - # includes is a post-release version, that we do not accept - # post-release versions for the version mentioned in the specifier - # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). - if not spec.is_postrelease and prospective.is_postrelease: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is technically greater than, to match. - if prospective.local is not None: - if Version(prospective.base_version) == Version(spec.base_version): - return False - - # If we've gotten to here, it means that prospective version is both - # greater than the spec version *and* it's not a pre-release of the - # same version in the spec. - return True - - def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: - return str(prospective).lower() == str(spec).lower() - - @property - def prereleases(self) -> bool: - - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases - - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] - - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True - - return False - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - -_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") - - -def _version_split(version: str) -> List[str]: - result: List[str] = [] - for item in version.split("."): - match = _prefix_regex.search(item) - if match: - result.extend(match.groups()) - else: - result.append(item) - return result - - -def _is_not_suffix(segment: str) -> bool: - return not any( - segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") - ) - - -def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: - left_split, right_split = [], [] - - # Get the release segment of our versions - left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) - right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) - - # Get the rest of our versions - left_split.append(left[len(left_split[0]) :]) - right_split.append(right[len(right_split[0]) :]) - - # Insert our padding - left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) - right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - - return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) - - -class SpecifierSet(BaseSpecifier): - def __init__( - self, specifiers: str = "", prereleases: Optional[bool] = None - ) -> None: - - # Split on , to break each individual specifier into it's own item, and - # strip each item to remove leading/trailing whitespace. - split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] - - # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed: Set[_IndividualSpecifier] = set() - for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) - - # Turn our parsed specifiers into a frozen set and save them for later. - self._specs = frozenset(parsed) - - # Store our prereleases value so we can use it later to determine if - # we accept prereleases or not. - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"" - - def __str__(self) -> str: - return ",".join(sorted(str(s) for s in self._specs)) - - def __hash__(self) -> int: - return hash(self._specs) - - def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": - if isinstance(other, str): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - - if self._prereleases is None and other._prereleases is not None: - specifier._prereleases = other._prereleases - elif self._prereleases is not None and other._prereleases is None: - specifier._prereleases = self._prereleases - elif self._prereleases == other._prereleases: - specifier._prereleases = self._prereleases - else: - raise ValueError( - "Cannot combine SpecifierSets with True and False prerelease " - "overrides." - ) - - return specifier - - def __eq__(self, other: object) -> bool: - if isinstance(other, (str, _IndividualSpecifier)): - other = SpecifierSet(str(other)) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - return self._specs == other._specs - - def __len__(self) -> int: - return len(self._specs) - - def __iter__(self) -> Iterator[_IndividualSpecifier]: - return iter(self._specs) - - @property - def prereleases(self) -> Optional[bool]: - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: UnparsedVersion) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # We can determine if we're going to allow pre-releases by looking to - # see if any of the underlying items supports them. If none of them do - # and this item is a pre-release then we do not allow it and we can - # short circuit that here. - # Note: This means that 1.0.dev1 would not be contained in something - # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 - if not prereleases and item.is_prerelease: - return False - - # We simply dispatch to the underlying specs here to make sure that the - # given version is contained within all of them. - # Note: This use of all() here means that an empty set of specifiers - # will always return True, this is an explicit design decision. - return all(s.contains(item, prereleases=prereleases) for s in self._specs) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - # Determine if we're forcing a prerelease or not, if we're not forcing - # one for this particular filter call, then we'll use whatever the - # SpecifierSet thinks for whether or not we should support prereleases. - if prereleases is None: - prereleases = self.prereleases - - # If we have any specifiers, then we want to wrap our iterable in the - # filter method for each one, this will act as a logical AND amongst - # each specifier. - if self._specs: - for spec in self._specs: - iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable - # If we do not have any specifiers, then we need to have a rough filter - # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. - else: - filtered: List[VersionTypeVar] = [] - found_prereleases: List[VersionTypeVar] = [] - - item: UnparsedVersion - parsed_version: Union[Version, LegacyVersion] - - for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue - - # Store any item which is a pre-release for later unless we've - # already found a final version or we are accepting prereleases - if parsed_version.is_prerelease and not prereleases: - if not filtered: - found_prereleases.append(item) - else: - filtered.append(item) - - # If we've found no items except for pre-releases, then we'll go - # ahead and use the pre-releases - if not filtered and found_prereleases and prereleases is None: - return found_prereleases - - return filtered diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/tags.py b/.venv/Lib/site-packages/pip/_vendor/packaging/tags.py deleted file mode 100644 index 9a3d25a..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/tags.py +++ /dev/null @@ -1,487 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import logging -import platform -import sys -import sysconfig -from importlib.machinery import EXTENSION_SUFFIXES -from typing import ( - Dict, - FrozenSet, - Iterable, - Iterator, - List, - Optional, - Sequence, - Tuple, - Union, - cast, -) - -from . import _manylinux, _musllinux - -logger = logging.getLogger(__name__) - -PythonVersion = Sequence[int] -MacVersion = Tuple[int, int] - -INTERPRETER_SHORT_NAMES: Dict[str, str] = { - "python": "py", # Generic. - "cpython": "cp", - "pypy": "pp", - "ironpython": "ip", - "jython": "jy", -} - - -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 - - -class Tag: - """ - A representation of the tag triple for a wheel. - - Instances are considered immutable and thus are hashable. Equality checking - is also supported. - """ - - __slots__ = ["_interpreter", "_abi", "_platform", "_hash"] - - def __init__(self, interpreter: str, abi: str, platform: str) -> None: - self._interpreter = interpreter.lower() - self._abi = abi.lower() - self._platform = platform.lower() - # The __hash__ of every single element in a Set[Tag] will be evaluated each time - # that a set calls its `.disjoint()` method, which may be called hundreds of - # times when scanning a page of links for packages with tags matching that - # Set[Tag]. Pre-computing the value here produces significant speedups for - # downstream consumers. - self._hash = hash((self._interpreter, self._abi, self._platform)) - - @property - def interpreter(self) -> str: - return self._interpreter - - @property - def abi(self) -> str: - return self._abi - - @property - def platform(self) -> str: - return self._platform - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Tag): - return NotImplemented - - return ( - (self._hash == other._hash) # Short-circuit ASAP for perf reasons. - and (self._platform == other._platform) - and (self._abi == other._abi) - and (self._interpreter == other._interpreter) - ) - - def __hash__(self) -> int: - return self._hash - - def __str__(self) -> str: - return f"{self._interpreter}-{self._abi}-{self._platform}" - - def __repr__(self) -> str: - return f"<{self} @ {id(self)}>" - - -def parse_tag(tag: str) -> FrozenSet[Tag]: - """ - Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. - - Returning a set is required due to the possibility that the tag is a - compressed tag set. - """ - tags = set() - interpreters, abis, platforms = tag.split("-") - for interpreter in interpreters.split("."): - for abi in abis.split("."): - for platform_ in platforms.split("."): - tags.add(Tag(interpreter, abi, platform_)) - return frozenset(tags) - - -def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: - value = sysconfig.get_config_var(name) - if value is None and warn: - logger.debug( - "Config variable '%s' is unset, Python ABI tag may be incorrect", name - ) - return value - - -def _normalize_string(string: str) -> str: - return string.replace(".", "_").replace("-", "_") - - -def _abi3_applies(python_version: PythonVersion) -> bool: - """ - Determine if the Python version supports abi3. - - PEP 384 was first implemented in Python 3.2. - """ - return len(python_version) > 1 and tuple(python_version) >= (3, 2) - - -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: - py_version = tuple(py_version) # To allow for version comparison. - abis = [] - version = _version_nodot(py_version[:2]) - debug = pymalloc = ucs4 = "" - with_debug = _get_config_var("Py_DEBUG", warn) - has_refcount = hasattr(sys, "gettotalrefcount") - # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled - # extension modules is the best option. - # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 - has_ext = "_d.pyd" in EXTENSION_SUFFIXES - if with_debug or (with_debug is None and (has_refcount or has_ext)): - debug = "d" - if py_version < (3, 8): - with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) - if with_pymalloc or with_pymalloc is None: - pymalloc = "m" - if py_version < (3, 3): - unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) - if unicode_size == 4 or ( - unicode_size is None and sys.maxunicode == 0x10FFFF - ): - ucs4 = "u" - elif debug: - # Debug builds can also load "normal" extension modules. - # We can also assume no UCS-4 or pymalloc requirement. - abis.append(f"cp{version}") - abis.insert( - 0, - "cp{version}{debug}{pymalloc}{ucs4}".format( - version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 - ), - ) - return abis - - -def cpython_tags( - python_version: Optional[PythonVersion] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a CPython interpreter. - - The tags consist of: - - cp-- - - cp-abi3- - - cp-none- - - cp-abi3- # Older Python versions down to 3.2. - - If python_version only specifies a major version then user-provided ABIs and - the 'none' ABItag will be used. - - If 'abi3' or 'none' are specified in 'abis' then they will be yielded at - their normal position and not at the beginning. - """ - if not python_version: - python_version = sys.version_info[:2] - - interpreter = f"cp{_version_nodot(python_version[:2])}" - - if abis is None: - if len(python_version) > 1: - abis = _cpython_abis(python_version, warn) - else: - abis = [] - abis = list(abis) - # 'abi3' and 'none' are explicitly handled later. - for explicit_abi in ("abi3", "none"): - try: - abis.remove(explicit_abi) - except ValueError: - pass - - platforms = list(platforms or platform_tags()) - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - if _abi3_applies(python_version): - yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) - yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) - - if _abi3_applies(python_version): - for minor_version in range(python_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{version}".format( - version=_version_nodot((python_version[0], minor_version)) - ) - yield Tag(interpreter, "abi3", platform_) - - -def _generic_abi() -> Iterator[str]: - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) - - -def generic_tags( - interpreter: Optional[str] = None, - abis: Optional[Iterable[str]] = None, - platforms: Optional[Iterable[str]] = None, - *, - warn: bool = False, -) -> Iterator[Tag]: - """ - Yields the tags for a generic interpreter. - - The tags consist of: - - -- - - The "none" ABI will be added if it was not explicitly provided. - """ - if not interpreter: - interp_name = interpreter_name() - interp_version = interpreter_version(warn=warn) - interpreter = "".join([interp_name, interp_version]) - if abis is None: - abis = _generic_abi() - platforms = list(platforms or platform_tags()) - abis = list(abis) - if "none" not in abis: - abis.append("none") - for abi in abis: - for platform_ in platforms: - yield Tag(interpreter, abi, platform_) - - -def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: - """ - Yields Python versions in descending order. - - After the latest version, the major-only version will be yielded, and then - all previous versions of that major version. - """ - if len(py_version) > 1: - yield f"py{_version_nodot(py_version[:2])}" - yield f"py{py_version[0]}" - if len(py_version) > 1: - for minor in range(py_version[1] - 1, -1, -1): - yield f"py{_version_nodot((py_version[0], minor))}" - - -def compatible_tags( - python_version: Optional[PythonVersion] = None, - interpreter: Optional[str] = None, - platforms: Optional[Iterable[str]] = None, -) -> Iterator[Tag]: - """ - Yields the sequence of tags that are compatible with a specific version of Python. - - The tags consist of: - - py*-none- - - -none-any # ... if `interpreter` is provided. - - py*-none-any - """ - if not python_version: - python_version = sys.version_info[:2] - platforms = list(platforms or platform_tags()) - for version in _py_interpreter_range(python_version): - for platform_ in platforms: - yield Tag(version, "none", platform_) - if interpreter: - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(python_version): - yield Tag(version, "none", "any") - - -def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: - if not is_32bit: - return arch - - if arch.startswith("ppc"): - return "ppc" - - return "i386" - - -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: - formats = [cpu_arch] - if cpu_arch == "x86_64": - if version < (10, 4): - return [] - formats.extend(["intel", "fat64", "fat32"]) - - elif cpu_arch == "i386": - if version < (10, 4): - return [] - formats.extend(["intel", "fat32", "fat"]) - - elif cpu_arch == "ppc64": - # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? - if version > (10, 5) or version < (10, 4): - return [] - formats.append("fat64") - - elif cpu_arch == "ppc": - if version > (10, 6): - return [] - formats.extend(["fat32", "fat"]) - - if cpu_arch in {"arm64", "x86_64"}: - formats.append("universal2") - - if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: - formats.append("universal") - - return formats - - -def mac_platforms( - version: Optional[MacVersion] = None, arch: Optional[str] = None -) -> Iterator[str]: - """ - Yields the platform tags for a macOS system. - - The `version` parameter is a two-item tuple specifying the macOS version to - generate platform tags for. The `arch` parameter is the CPU architecture to - generate platform tags for. Both parameters default to the appropriate value - for the current system. - """ - version_str, _, cpu_arch = platform.mac_ver() - if version is None: - version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) - else: - version = version - if arch is None: - arch = _mac_arch(cpu_arch) - else: - arch = arch - - if (10, 0) <= version and version < (11, 0): - # Prior to Mac OS 11, each yearly release of Mac OS bumped the - # "minor" version number. The major version was always 10. - for minor_version in range(version[1], -1, -1): - compat_version = 10, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=10, minor=minor_version, binary_format=binary_format - ) - - if version >= (11, 0): - # Starting with Mac OS 11, each yearly release bumps the major version - # number. The minor versions are now the midyear updates. - for major_version in range(version[0], 10, -1): - compat_version = major_version, 0 - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=major_version, minor=0, binary_format=binary_format - ) - - if version >= (11, 0): - # Mac OS 11 on x86_64 is compatible with binaries from previous releases. - # Arm64 support was introduced in 11.0, so no Arm binaries from previous - # releases exist. - # - # However, the "universal2" binary format can have a - # macOS version earlier than 11.0 when the x86_64 part of the binary supports - # that version of macOS. - if arch == "x86_64": - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_formats = _mac_binary_formats(compat_version, arch) - for binary_format in binary_formats: - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - else: - for minor_version in range(16, 3, -1): - compat_version = 10, minor_version - binary_format = "universal2" - yield "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) - - -def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: - linux = _normalize_string(sysconfig.get_platform()) - if is_32bit: - if linux == "linux_x86_64": - linux = "linux_i686" - elif linux == "linux_aarch64": - linux = "linux_armv7l" - _, arch = linux.split("_", 1) - yield from _manylinux.platform_tags(linux, arch) - yield from _musllinux.platform_tags(arch) - yield linux - - -def _generic_platforms() -> Iterator[str]: - yield _normalize_string(sysconfig.get_platform()) - - -def platform_tags() -> Iterator[str]: - """ - Provides the platform tags for this installation. - """ - if platform.system() == "Darwin": - return mac_platforms() - elif platform.system() == "Linux": - return _linux_platforms() - else: - return _generic_platforms() - - -def interpreter_name() -> str: - """ - Returns the name of the running interpreter. - """ - name = sys.implementation.name - return INTERPRETER_SHORT_NAMES.get(name) or name - - -def interpreter_version(*, warn: bool = False) -> str: - """ - Returns the version of the running interpreter. - """ - version = _get_config_var("py_version_nodot", warn=warn) - if version: - version = str(version) - else: - version = _version_nodot(sys.version_info[:2]) - return version - - -def _version_nodot(version: PythonVersion) -> str: - return "".join(map(str, version)) - - -def sys_tags(*, warn: bool = False) -> Iterator[Tag]: - """ - Returns the sequence of tag triples for the running interpreter. - - The order of the sequence corresponds to priority order for the - interpreter, from most to least important. - """ - - interp_name = interpreter_name() - if interp_name == "cp": - yield from cpython_tags(warn=warn) - else: - yield from generic_tags() - - if interp_name == "pp": - yield from compatible_tags(interpreter="pp3") - else: - yield from compatible_tags() diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/utils.py b/.venv/Lib/site-packages/pip/_vendor/packaging/utils.py deleted file mode 100644 index bab11b8..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/utils.py +++ /dev/null @@ -1,136 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import re -from typing import FrozenSet, NewType, Tuple, Union, cast - -from .tags import Tag, parse_tag -from .version import InvalidVersion, Version - -BuildTag = Union[Tuple[()], Tuple[int, str]] -NormalizedName = NewType("NormalizedName", str) - - -class InvalidWheelFilename(ValueError): - """ - An invalid wheel filename was found, users should refer to PEP 427. - """ - - -class InvalidSdistFilename(ValueError): - """ - An invalid sdist filename was found, users should refer to the packaging user guide. - """ - - -_canonicalize_regex = re.compile(r"[-_.]+") -# PEP 427: The build number must start with a digit. -_build_tag_regex = re.compile(r"(\d+)(.*)") - - -def canonicalize_name(name: str) -> NormalizedName: - # This is taken from PEP 503. - value = _canonicalize_regex.sub("-", name).lower() - return cast(NormalizedName, value) - - -def canonicalize_version(version: Union[Version, str]) -> str: - """ - This is very similar to Version.__str__, but has one subtle difference - with the way it handles the release segment. - """ - if isinstance(version, str): - try: - parsed = Version(version) - except InvalidVersion: - # Legacy versions cannot be normalized - return version - else: - parsed = version - - parts = [] - - # Epoch - if parsed.epoch != 0: - parts.append(f"{parsed.epoch}!") - - # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) - - # Pre-release - if parsed.pre is not None: - parts.append("".join(str(x) for x in parsed.pre)) - - # Post-release - if parsed.post is not None: - parts.append(f".post{parsed.post}") - - # Development release - if parsed.dev is not None: - parts.append(f".dev{parsed.dev}") - - # Local version segment - if parsed.local is not None: - parts.append(f"+{parsed.local}") - - return "".join(parts) - - -def parse_wheel_filename( - filename: str, -) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: - if not filename.endswith(".whl"): - raise InvalidWheelFilename( - f"Invalid wheel filename (extension must be '.whl'): {filename}" - ) - - filename = filename[:-4] - dashes = filename.count("-") - if dashes not in (4, 5): - raise InvalidWheelFilename( - f"Invalid wheel filename (wrong number of parts): {filename}" - ) - - parts = filename.split("-", dashes - 2) - name_part = parts[0] - # See PEP 427 for the rules on escaping the project name - if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: - raise InvalidWheelFilename(f"Invalid project name: {filename}") - name = canonicalize_name(name_part) - version = Version(parts[1]) - if dashes == 5: - build_part = parts[2] - build_match = _build_tag_regex.match(build_part) - if build_match is None: - raise InvalidWheelFilename( - f"Invalid build number: {build_part} in '{filename}'" - ) - build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) - else: - build = () - tags = parse_tag(parts[-1]) - return (name, version, build, tags) - - -def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: - if filename.endswith(".tar.gz"): - file_stem = filename[: -len(".tar.gz")] - elif filename.endswith(".zip"): - file_stem = filename[: -len(".zip")] - else: - raise InvalidSdistFilename( - f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" - f" {filename}" - ) - - # We are requiring a PEP 440 version, which cannot contain dashes, - # so we split on the last dash. - name_part, sep, version_part = file_stem.rpartition("-") - if not sep: - raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") - - name = canonicalize_name(name_part) - version = Version(version_part) - return (name, version) diff --git a/.venv/Lib/site-packages/pip/_vendor/packaging/version.py b/.venv/Lib/site-packages/pip/_vendor/packaging/version.py deleted file mode 100644 index de9a09a..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/packaging/version.py +++ /dev/null @@ -1,504 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import collections -import itertools -import re -import warnings -from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union - -from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType - -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] - -InfiniteTypes = Union[InfinityType, NegativeInfinityType] -PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] -SubLocalType = Union[InfiniteTypes, int, str] -LocalType = Union[ - NegativeInfinityType, - Tuple[ - Union[ - SubLocalType, - Tuple[SubLocalType, str], - Tuple[NegativeInfinityType, SubLocalType], - ], - ..., - ], -] -CmpKey = Tuple[ - int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType -] -LegacyCmpKey = Tuple[int, Tuple[str, ...]] -VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool -] - -_Version = collections.namedtuple( - "_Version", ["epoch", "release", "dev", "pre", "post", "local"] -) - - -def parse(version: str) -> Union["LegacyVersion", "Version"]: - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. - """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) - - -class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. - """ - - -class _BaseVersion: - _key: Union[CmpKey, LegacyCmpKey] - - def __hash__(self) -> int: - return hash(self._key) - - # Please keep the duplicated `isinstance` check - # in the six comparisons hereunder - # unless you find a way to avoid adding overhead function calls. - def __lt__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key < other._key - - def __le__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key <= other._key - - def __eq__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key == other._key - - def __ge__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key >= other._key - - def __gt__(self, other: "_BaseVersion") -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key > other._key - - def __ne__(self, other: object) -> bool: - if not isinstance(other, _BaseVersion): - return NotImplemented - - return self._key != other._key - - -class LegacyVersion(_BaseVersion): - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def __str__(self) -> str: - return self._version - - def __repr__(self) -> str: - return f"" - - @property - def public(self) -> str: - return self._version - - @property - def base_version(self) -> str: - return self._version - - @property - def epoch(self) -> int: - return -1 - - @property - def release(self) -> None: - return None - - @property - def pre(self) -> None: - return None - - @property - def post(self) -> None: - return None - - @property - def dev(self) -> None: - return None - - @property - def local(self) -> None: - return None - - @property - def is_prerelease(self) -> bool: - return False - - @property - def is_postrelease(self) -> bool: - return False - - @property - def is_devrelease(self) -> bool: - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s: str) -> Iterator[str]: - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version: str) -> LegacyCmpKey: - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts: List[str] = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - -# Deliberately not anchored to the start and end of the string, to make it -# easier for 3rd party code to reuse -VERSION_PATTERN = r""" - v? - (?: - (?:(?P[0-9]+)!)? # epoch - (?P[0-9]+(?:\.[0-9]+)*) # release segment - (?P

!I7S{BS4i1|@R7s@ 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 diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/t64.exe b/.venv/Lib/site-packages/pip/_vendor/distlib/t64.exe deleted file mode 100644 index e8bebdba6d8f242244bf397ab067965d47c5093e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108032 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%0AHbpwE%ZM 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 H#ex3;_fXA_ diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/util.py b/.venv/Lib/site-packages/pip/_vendor/distlib/util.py deleted file mode 100644 index dd01849..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/util.py +++ /dev/null @@ -1,1932 +0,0 @@ -# -# Copyright (C) 2012-2021 The Python Software Foundation. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -import codecs -from collections import deque -import contextlib -import csv -from glob import iglob as std_iglob -import io -import json -import logging -import os -import py_compile -import re -import socket -try: - import ssl -except ImportError: # pragma: no cover - ssl = None -import subprocess -import sys -import tarfile -import tempfile -import textwrap - -try: - import threading -except ImportError: # pragma: no cover - import dummy_threading as threading -import time - -from . import DistlibException -from .compat import (string_types, text_type, shutil, raw_input, StringIO, - cache_from_source, urlopen, urljoin, httplib, xmlrpclib, - splittype, HTTPHandler, BaseConfigurator, valid_ident, - Container, configparser, URLError, ZipFile, fsdecode, - unquote, urlparse) - -logger = logging.getLogger(__name__) - -# -# Requirement parsing code as per PEP 508 -# - -IDENTIFIER = re.compile(r'^([\w\.-]+)\s*') -VERSION_IDENTIFIER = re.compile(r'^([\w\.*+-]+)\s*') -COMPARE_OP = re.compile(r'^(<=?|>=?|={2,3}|[~!]=)\s*') -MARKER_OP = re.compile(r'^((<=?)|(>=?)|={2,3}|[~!]=|in|not\s+in)\s*') -OR = re.compile(r'^or\b\s*') -AND = re.compile(r'^and\b\s*') -NON_SPACE = re.compile(r'(\S+)\s*') -STRING_CHUNK = re.compile(r'([\s\w\.{}()*+#:;,/?!~`@$%^&=|<>\[\]-]+)') - - -def parse_marker(marker_string): - """ - Parse a marker string and return a dictionary containing a marker expression. - - The dictionary will contain keys "op", "lhs" and "rhs" for non-terminals in - the expression grammar, or strings. A string contained in quotes is to be - interpreted as a literal string, and a string not contained in quotes is a - variable (such as os_name). - """ - def marker_var(remaining): - # either identifier, or literal string - m = IDENTIFIER.match(remaining) - if m: - result = m.groups()[0] - remaining = remaining[m.end():] - elif not remaining: - raise SyntaxError('unexpected end of input') - else: - q = remaining[0] - if q not in '\'"': - raise SyntaxError('invalid expression: %s' % remaining) - oq = '\'"'.replace(q, '') - remaining = remaining[1:] - parts = [q] - while remaining: - # either a string chunk, or oq, or q to terminate - if remaining[0] == q: - break - elif remaining[0] == oq: - parts.append(oq) - remaining = remaining[1:] - else: - m = STRING_CHUNK.match(remaining) - if not m: - raise SyntaxError('error in string literal: %s' % remaining) - parts.append(m.groups()[0]) - remaining = remaining[m.end():] - else: - s = ''.join(parts) - raise SyntaxError('unterminated string: %s' % s) - parts.append(q) - result = ''.join(parts) - remaining = remaining[1:].lstrip() # skip past closing quote - return result, remaining - - def marker_expr(remaining): - if remaining and remaining[0] == '(': - result, remaining = marker(remaining[1:].lstrip()) - if remaining[0] != ')': - raise SyntaxError('unterminated parenthesis: %s' % remaining) - remaining = remaining[1:].lstrip() - else: - lhs, remaining = marker_var(remaining) - while remaining: - m = MARKER_OP.match(remaining) - if not m: - break - op = m.groups()[0] - remaining = remaining[m.end():] - rhs, remaining = marker_var(remaining) - lhs = {'op': op, 'lhs': lhs, 'rhs': rhs} - result = lhs - return result, remaining - - def marker_and(remaining): - lhs, remaining = marker_expr(remaining) - while remaining: - m = AND.match(remaining) - if not m: - break - remaining = remaining[m.end():] - rhs, remaining = marker_expr(remaining) - lhs = {'op': 'and', 'lhs': lhs, 'rhs': rhs} - return lhs, remaining - - def marker(remaining): - lhs, remaining = marker_and(remaining) - while remaining: - m = OR.match(remaining) - if not m: - break - remaining = remaining[m.end():] - rhs, remaining = marker_and(remaining) - lhs = {'op': 'or', 'lhs': lhs, 'rhs': rhs} - return lhs, remaining - - return marker(marker_string) - - -def parse_requirement(req): - """ - Parse a requirement passed in as a string. Return a Container - whose attributes contain the various parts of the requirement. - """ - remaining = req.strip() - if not remaining or remaining.startswith('#'): - return None - m = IDENTIFIER.match(remaining) - if not m: - raise SyntaxError('name expected: %s' % remaining) - distname = m.groups()[0] - remaining = remaining[m.end():] - extras = mark_expr = versions = uri = None - if remaining and remaining[0] == '[': - i = remaining.find(']', 1) - if i < 0: - raise SyntaxError('unterminated extra: %s' % remaining) - s = remaining[1:i] - remaining = remaining[i + 1:].lstrip() - extras = [] - while s: - m = IDENTIFIER.match(s) - if not m: - raise SyntaxError('malformed extra: %s' % s) - extras.append(m.groups()[0]) - s = s[m.end():] - if not s: - break - if s[0] != ',': - raise SyntaxError('comma expected in extras: %s' % s) - s = s[1:].lstrip() - if not extras: - extras = None - if remaining: - if remaining[0] == '@': - # it's a URI - remaining = remaining[1:].lstrip() - m = NON_SPACE.match(remaining) - if not m: - raise SyntaxError('invalid URI: %s' % remaining) - uri = m.groups()[0] - t = urlparse(uri) - # 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 (t.scheme and t.netloc): - raise SyntaxError('Invalid URL: %s' % uri) - remaining = remaining[m.end():].lstrip() - else: - - def get_versions(ver_remaining): - """ - Return a list of operator, version tuples if any are - specified, else None. - """ - m = COMPARE_OP.match(ver_remaining) - versions = None - if m: - versions = [] - while True: - op = m.groups()[0] - ver_remaining = ver_remaining[m.end():] - m = VERSION_IDENTIFIER.match(ver_remaining) - if not m: - raise SyntaxError('invalid version: %s' % ver_remaining) - v = m.groups()[0] - versions.append((op, v)) - ver_remaining = ver_remaining[m.end():] - if not ver_remaining or ver_remaining[0] != ',': - break - ver_remaining = ver_remaining[1:].lstrip() - # Some packages have a trailing comma which would break things - # See issue #148 - if not ver_remaining: - break - m = COMPARE_OP.match(ver_remaining) - if not m: - raise SyntaxError('invalid constraint: %s' % ver_remaining) - if not versions: - versions = None - return versions, ver_remaining - - if remaining[0] != '(': - versions, remaining = get_versions(remaining) - else: - i = remaining.find(')', 1) - if i < 0: - raise SyntaxError('unterminated parenthesis: %s' % remaining) - s = remaining[1:i] - remaining = remaining[i + 1:].lstrip() - # As a special diversion from PEP 508, allow a version number - # a.b.c in parentheses as a synonym for ~= a.b.c (because this - # is allowed in earlier PEPs) - if COMPARE_OP.match(s): - versions, _ = get_versions(s) - else: - m = VERSION_IDENTIFIER.match(s) - if not m: - raise SyntaxError('invalid constraint: %s' % s) - v = m.groups()[0] - s = s[m.end():].lstrip() - if s: - raise SyntaxError('invalid constraint: %s' % s) - versions = [('~=', v)] - - if remaining: - if remaining[0] != ';': - raise SyntaxError('invalid requirement: %s' % remaining) - remaining = remaining[1:].lstrip() - - mark_expr, remaining = parse_marker(remaining) - - if remaining and remaining[0] != '#': - raise SyntaxError('unexpected trailing data: %s' % remaining) - - if not versions: - rs = distname - else: - rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions])) - return Container(name=distname, extras=extras, constraints=versions, - marker=mark_expr, url=uri, requirement=rs) - - -def get_resources_dests(resources_root, rules): - """Find destinations for resources files""" - - def get_rel_path(root, path): - # normalizes and returns a lstripped-/-separated path - root = root.replace(os.path.sep, '/') - path = path.replace(os.path.sep, '/') - assert path.startswith(root) - return path[len(root):].lstrip('/') - - destinations = {} - for base, suffix, dest in rules: - prefix = os.path.join(resources_root, base) - for abs_base in iglob(prefix): - abs_glob = os.path.join(abs_base, suffix) - for abs_path in iglob(abs_glob): - resource_file = get_rel_path(resources_root, abs_path) - if dest is None: # remove the entry if it was here - destinations.pop(resource_file, None) - else: - rel_path = get_rel_path(abs_base, abs_path) - rel_dest = dest.replace(os.path.sep, '/').rstrip('/') - destinations[resource_file] = rel_dest + '/' + rel_path - return destinations - - -def in_venv(): - if hasattr(sys, 'real_prefix'): - # virtualenv venvs - result = True - else: - # PEP 405 venvs - result = sys.prefix != getattr(sys, 'base_prefix', sys.prefix) - return result - - -def get_executable(): -# The __PYVENV_LAUNCHER__ dance is apparently no longer needed, as -# changes to the stub launcher mean that sys.executable always points -# to the stub on OS X -# if sys.platform == 'darwin' and ('__PYVENV_LAUNCHER__' -# in os.environ): -# result = os.environ['__PYVENV_LAUNCHER__'] -# else: -# result = sys.executable -# return result - # Avoid normcasing: see issue #143 - # result = os.path.normcase(sys.executable) - result = sys.executable - if not isinstance(result, text_type): - result = fsdecode(result) - return result - - -def proceed(prompt, allowed_chars, error_prompt=None, default=None): - p = prompt - while True: - s = raw_input(p) - p = prompt - if not s and default: - s = default - if s: - c = s[0].lower() - if c in allowed_chars: - break - if error_prompt: - p = '%c: %s\n%s' % (c, error_prompt, prompt) - return c - - -def extract_by_key(d, keys): - if isinstance(keys, string_types): - keys = keys.split() - result = {} - for key in keys: - if key in d: - result[key] = d[key] - return result - -def read_exports(stream): - if sys.version_info[0] >= 3: - # needs to be a text stream - stream = codecs.getreader('utf-8')(stream) - # Try to load as JSON, falling back on legacy format - data = stream.read() - stream = StringIO(data) - try: - jdata = json.load(stream) - result = jdata['extensions']['python.exports']['exports'] - for group, entries in result.items(): - for k, v in entries.items(): - s = '%s = %s' % (k, v) - entry = get_export_entry(s) - assert entry is not None - entries[k] = entry - return result - except Exception: - stream.seek(0, 0) - - def read_stream(cp, stream): - if hasattr(cp, 'read_file'): - cp.read_file(stream) - else: - cp.readfp(stream) - - cp = configparser.ConfigParser() - try: - read_stream(cp, stream) - except configparser.MissingSectionHeaderError: - stream.close() - data = textwrap.dedent(data) - stream = StringIO(data) - read_stream(cp, stream) - - result = {} - for key in cp.sections(): - result[key] = entries = {} - for name, value in cp.items(key): - s = '%s = %s' % (name, value) - entry = get_export_entry(s) - assert entry is not None - #entry.dist = self - entries[name] = entry - return result - - -def write_exports(exports, stream): - if sys.version_info[0] >= 3: - # needs to be a text stream - stream = codecs.getwriter('utf-8')(stream) - cp = configparser.ConfigParser() - for k, v in exports.items(): - # TODO check k, v for valid values - cp.add_section(k) - for entry in v.values(): - if entry.suffix is None: - s = entry.prefix - else: - s = '%s:%s' % (entry.prefix, entry.suffix) - if entry.flags: - s = '%s [%s]' % (s, ', '.join(entry.flags)) - cp.set(k, entry.name, s) - cp.write(stream) - - -@contextlib.contextmanager -def tempdir(): - td = tempfile.mkdtemp() - try: - yield td - finally: - shutil.rmtree(td) - -@contextlib.contextmanager -def chdir(d): - cwd = os.getcwd() - try: - os.chdir(d) - yield - finally: - os.chdir(cwd) - - -@contextlib.contextmanager -def socket_timeout(seconds=15): - cto = socket.getdefaulttimeout() - try: - socket.setdefaulttimeout(seconds) - yield - finally: - socket.setdefaulttimeout(cto) - - -class cached_property(object): - def __init__(self, func): - self.func = func - #for attr in ('__name__', '__module__', '__doc__'): - # setattr(self, attr, getattr(func, attr, None)) - - def __get__(self, obj, cls=None): - if obj is None: - return self - value = self.func(obj) - object.__setattr__(obj, self.func.__name__, value) - #obj.__dict__[self.func.__name__] = value = self.func(obj) - return value - -def convert_path(pathname): - """Return 'pathname' as a name that will work on the native filesystem. - - The path is split on '/' and put back together again using the current - directory separator. Needed because filenames in the setup script are - always supplied in Unix style, and have to be converted to the local - convention before we can actually use them in the filesystem. Raises - ValueError on non-Unix-ish systems if 'pathname' either starts or - ends with a slash. - """ - if os.sep == '/': - return pathname - if not pathname: - return pathname - if pathname[0] == '/': - raise ValueError("path '%s' cannot be absolute" % pathname) - if pathname[-1] == '/': - raise ValueError("path '%s' cannot end with '/'" % pathname) - - paths = pathname.split('/') - while os.curdir in paths: - paths.remove(os.curdir) - if not paths: - return os.curdir - return os.path.join(*paths) - - -class FileOperator(object): - def __init__(self, dry_run=False): - self.dry_run = dry_run - self.ensured = set() - self._init_record() - - def _init_record(self): - self.record = False - self.files_written = set() - self.dirs_created = set() - - def record_as_written(self, path): - if self.record: - self.files_written.add(path) - - def newer(self, source, target): - """Tell if the target is newer than the source. - - Returns true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. - - Returns false if both exist and 'target' is the same age or younger - than 'source'. Raise PackagingFileError if 'source' does not exist. - - Note that this test is not very accurate: files created in the same - second will have the same "age". - """ - if not os.path.exists(source): - raise DistlibException("file '%r' does not exist" % - os.path.abspath(source)) - if not os.path.exists(target): - return True - - return os.stat(source).st_mtime > os.stat(target).st_mtime - - def copy_file(self, infile, outfile, check=True): - """Copy a file respecting dry-run and force flags. - """ - self.ensure_dir(os.path.dirname(outfile)) - logger.info('Copying %s to %s', infile, outfile) - if not self.dry_run: - msg = None - if check: - if os.path.islink(outfile): - msg = '%s is a symlink' % outfile - elif os.path.exists(outfile) and not os.path.isfile(outfile): - msg = '%s is a non-regular file' % outfile - if msg: - raise ValueError(msg + ' which would be overwritten') - shutil.copyfile(infile, outfile) - self.record_as_written(outfile) - - def copy_stream(self, instream, outfile, encoding=None): - assert not os.path.isdir(outfile) - self.ensure_dir(os.path.dirname(outfile)) - logger.info('Copying stream %s to %s', instream, outfile) - if not self.dry_run: - if encoding is None: - outstream = open(outfile, 'wb') - else: - outstream = codecs.open(outfile, 'w', encoding=encoding) - try: - shutil.copyfileobj(instream, outstream) - finally: - outstream.close() - self.record_as_written(outfile) - - def write_binary_file(self, path, data): - self.ensure_dir(os.path.dirname(path)) - if not self.dry_run: - if os.path.exists(path): - os.remove(path) - with open(path, 'wb') as f: - f.write(data) - self.record_as_written(path) - - def write_text_file(self, path, data, encoding): - self.write_binary_file(path, data.encode(encoding)) - - def set_mode(self, bits, mask, files): - if os.name == 'posix' or (os.name == 'java' and os._name == 'posix'): - # Set the executable bits (owner, group, and world) on - # all the files specified. - for f in files: - if self.dry_run: - logger.info("changing mode of %s", f) - else: - mode = (os.stat(f).st_mode | bits) & mask - logger.info("changing mode of %s to %o", f, mode) - os.chmod(f, mode) - - set_executable_mode = lambda s, f: s.set_mode(0o555, 0o7777, f) - - def ensure_dir(self, path): - path = os.path.abspath(path) - if path not in self.ensured and not os.path.exists(path): - self.ensured.add(path) - d, f = os.path.split(path) - self.ensure_dir(d) - logger.info('Creating %s' % path) - if not self.dry_run: - os.mkdir(path) - if self.record: - self.dirs_created.add(path) - - def byte_compile(self, path, optimize=False, force=False, prefix=None, hashed_invalidation=False): - dpath = cache_from_source(path, not optimize) - logger.info('Byte-compiling %s to %s', path, dpath) - if not self.dry_run: - if force or self.newer(path, dpath): - if not prefix: - diagpath = None - else: - assert path.startswith(prefix) - diagpath = path[len(prefix):] - compile_kwargs = {} - if hashed_invalidation and hasattr(py_compile, 'PycInvalidationMode'): - compile_kwargs['invalidation_mode'] = py_compile.PycInvalidationMode.CHECKED_HASH - py_compile.compile(path, dpath, diagpath, True, **compile_kwargs) # raise error - self.record_as_written(dpath) - return dpath - - def ensure_removed(self, path): - if os.path.exists(path): - if os.path.isdir(path) and not os.path.islink(path): - logger.debug('Removing directory tree at %s', path) - if not self.dry_run: - shutil.rmtree(path) - if self.record: - if path in self.dirs_created: - self.dirs_created.remove(path) - else: - if os.path.islink(path): - s = 'link' - else: - s = 'file' - logger.debug('Removing %s %s', s, path) - if not self.dry_run: - os.remove(path) - if self.record: - if path in self.files_written: - self.files_written.remove(path) - - def is_writable(self, path): - result = False - while not result: - if os.path.exists(path): - result = os.access(path, os.W_OK) - break - parent = os.path.dirname(path) - if parent == path: - break - path = parent - return result - - def commit(self): - """ - Commit recorded changes, turn off recording, return - changes. - """ - assert self.record - result = self.files_written, self.dirs_created - self._init_record() - return result - - def rollback(self): - if not self.dry_run: - for f in list(self.files_written): - if os.path.exists(f): - os.remove(f) - # dirs should all be empty now, except perhaps for - # __pycache__ subdirs - # reverse so that subdirs appear before their parents - dirs = sorted(self.dirs_created, reverse=True) - for d in dirs: - flist = os.listdir(d) - if flist: - assert flist == ['__pycache__'] - sd = os.path.join(d, flist[0]) - os.rmdir(sd) - os.rmdir(d) # should fail if non-empty - self._init_record() - -def resolve(module_name, dotted_path): - if module_name in sys.modules: - mod = sys.modules[module_name] - else: - mod = __import__(module_name) - if dotted_path is None: - result = mod - else: - parts = dotted_path.split('.') - result = getattr(mod, parts.pop(0)) - for p in parts: - result = getattr(result, p) - return result - - -class ExportEntry(object): - def __init__(self, name, prefix, suffix, flags): - self.name = name - self.prefix = prefix - self.suffix = suffix - self.flags = flags - - @cached_property - def value(self): - return resolve(self.prefix, self.suffix) - - def __repr__(self): # pragma: no cover - return '' % (self.name, self.prefix, - self.suffix, self.flags) - - def __eq__(self, other): - if not isinstance(other, ExportEntry): - result = False - else: - result = (self.name == other.name and - self.prefix == other.prefix and - self.suffix == other.suffix and - self.flags == other.flags) - return result - - __hash__ = object.__hash__ - - -ENTRY_RE = re.compile(r'''(?P(\w|[-.+])+) - \s*=\s*(?P(\w+)([:\.]\w+)*) - \s*(\[\s*(?P[\w-]+(=\w+)?(,\s*\w+(=\w+)?)*)\s*\])? - ''', re.VERBOSE) - -def get_export_entry(specification): - m = ENTRY_RE.search(specification) - if not m: - result = None - if '[' in specification or ']' in specification: - raise DistlibException("Invalid specification " - "'%s'" % specification) - else: - d = m.groupdict() - name = d['name'] - path = d['callable'] - colons = path.count(':') - if colons == 0: - prefix, suffix = path, None - else: - if colons != 1: - raise DistlibException("Invalid specification " - "'%s'" % specification) - prefix, suffix = path.split(':') - flags = d['flags'] - if flags is None: - if '[' in specification or ']' in specification: - raise DistlibException("Invalid specification " - "'%s'" % specification) - flags = [] - else: - flags = [f.strip() for f in flags.split(',')] - result = ExportEntry(name, prefix, suffix, flags) - return result - - -def get_cache_base(suffix=None): - """ - Return the default base location for distlib caches. If the directory does - not exist, it is created. Use the suffix provided for the base directory, - and default to '.distlib' if it isn't provided. - - On Windows, if LOCALAPPDATA is defined in the environment, then it is - assumed to be a directory, and will be the parent directory of the result. - On POSIX, and on Windows if LOCALAPPDATA is not defined, the user's home - directory - using os.expanduser('~') - will be the parent directory of - the result. - - The result is just the directory '.distlib' in the parent directory as - determined above, or with the name specified with ``suffix``. - """ - if suffix is None: - suffix = '.distlib' - if os.name == 'nt' and 'LOCALAPPDATA' in os.environ: - result = os.path.expandvars('$localappdata') - else: - # Assume posix, or old Windows - result = os.path.expanduser('~') - # we use 'isdir' instead of 'exists', because we want to - # fail if there's a file with that name - if os.path.isdir(result): - usable = os.access(result, os.W_OK) - if not usable: - logger.warning('Directory exists but is not writable: %s', result) - else: - try: - os.makedirs(result) - usable = True - except OSError: - logger.warning('Unable to create %s', result, exc_info=True) - usable = False - if not usable: - result = tempfile.mkdtemp() - logger.warning('Default location unusable, using %s', result) - return os.path.join(result, suffix) - - -def path_to_cache_dir(path): - """ - Convert an absolute path to a directory name for use in a cache. - - The algorithm used is: - - #. On Windows, any ``':'`` in the drive is replaced with ``'---'``. - #. Any occurrence of ``os.sep`` is replaced with ``'--'``. - #. ``'.cache'`` is appended. - """ - d, p = os.path.splitdrive(os.path.abspath(path)) - if d: - d = d.replace(':', '---') - p = p.replace(os.sep, '--') - return d + p + '.cache' - - -def ensure_slash(s): - if not s.endswith('/'): - return s + '/' - return s - - -def parse_credentials(netloc): - username = password = None - if '@' in netloc: - prefix, netloc = netloc.rsplit('@', 1) - if ':' not in prefix: - username = prefix - else: - username, password = prefix.split(':', 1) - if username: - username = unquote(username) - if password: - password = unquote(password) - return username, password, netloc - - -def get_process_umask(): - result = os.umask(0o22) - os.umask(result) - return result - -def is_string_sequence(seq): - result = True - i = None - for i, s in enumerate(seq): - if not isinstance(s, string_types): - result = False - break - assert i is not None - return result - -PROJECT_NAME_AND_VERSION = re.compile('([a-z0-9_]+([.-][a-z_][a-z0-9_]*)*)-' - '([a-z0-9_.+-]+)', re.I) -PYTHON_VERSION = re.compile(r'-py(\d\.?\d?)') - - -def split_filename(filename, project_name=None): - """ - Extract name, version, python version from a filename (no extension) - - Return name, version, pyver or None - """ - result = None - pyver = None - filename = unquote(filename).replace(' ', '-') - m = PYTHON_VERSION.search(filename) - if m: - pyver = m.group(1) - filename = filename[:m.start()] - if project_name and len(filename) > len(project_name) + 1: - m = re.match(re.escape(project_name) + r'\b', filename) - if m: - n = m.end() - result = filename[:n], filename[n + 1:], pyver - if result is None: - m = PROJECT_NAME_AND_VERSION.match(filename) - if m: - result = m.group(1), m.group(3), pyver - return result - -# Allow spaces in name because of legacy dists like "Twisted Core" -NAME_VERSION_RE = re.compile(r'(?P[\w .-]+)\s*' - r'\(\s*(?P[^\s)]+)\)$') - -def parse_name_and_version(p): - """ - A utility method used to get name and version from a string. - - From e.g. a Provides-Dist value. - - :param p: A value in a form 'foo (1.0)' - :return: The name and version as a tuple. - """ - m = NAME_VERSION_RE.match(p) - if not m: - raise DistlibException('Ill-formed name/version string: \'%s\'' % p) - d = m.groupdict() - return d['name'].strip().lower(), d['ver'] - -def get_extras(requested, available): - result = set() - requested = set(requested or []) - available = set(available or []) - if '*' in requested: - requested.remove('*') - result |= available - for r in requested: - if r == '-': - result.add(r) - elif r.startswith('-'): - unwanted = r[1:] - if unwanted not in available: - logger.warning('undeclared extra: %s' % unwanted) - if unwanted in result: - result.remove(unwanted) - else: - if r not in available: - logger.warning('undeclared extra: %s' % r) - result.add(r) - return result -# -# Extended metadata functionality -# - -def _get_external_data(url): - result = {} - try: - # urlopen might fail if it runs into redirections, - # because of Python issue #13696. Fixed in locators - # using a custom redirect handler. - resp = urlopen(url) - headers = resp.info() - ct = headers.get('Content-Type') - if not ct.startswith('application/json'): - logger.debug('Unexpected response for JSON request: %s', ct) - else: - reader = codecs.getreader('utf-8')(resp) - #data = reader.read().decode('utf-8') - #result = json.loads(data) - result = json.load(reader) - except Exception as e: - logger.exception('Failed to get external data for %s: %s', url, e) - return result - -_external_data_base_url = 'https://www.red-dove.com/pypi/projects/' - -def get_project_data(name): - url = '%s/%s/project.json' % (name[0].upper(), name) - url = urljoin(_external_data_base_url, url) - result = _get_external_data(url) - return result - -def get_package_data(name, version): - url = '%s/%s/package-%s.json' % (name[0].upper(), name, version) - url = urljoin(_external_data_base_url, url) - return _get_external_data(url) - - -class Cache(object): - """ - A class implementing a cache for resources that need to live in the file system - e.g. shared libraries. This class was moved from resources to here because it - could be used by other modules, e.g. the wheel module. - """ - - def __init__(self, base): - """ - Initialise an instance. - - :param base: The base directory where the cache should be located. - """ - # we use 'isdir' instead of 'exists', because we want to - # fail if there's a file with that name - if not os.path.isdir(base): # pragma: no cover - os.makedirs(base) - if (os.stat(base).st_mode & 0o77) != 0: - logger.warning('Directory \'%s\' is not private', base) - self.base = os.path.abspath(os.path.normpath(base)) - - def prefix_to_dir(self, prefix): - """ - Converts a resource prefix to a directory name in the cache. - """ - return path_to_cache_dir(prefix) - - def clear(self): - """ - Clear the cache. - """ - not_removed = [] - for fn in os.listdir(self.base): - fn = os.path.join(self.base, fn) - try: - if os.path.islink(fn) or os.path.isfile(fn): - os.remove(fn) - elif os.path.isdir(fn): - shutil.rmtree(fn) - except Exception: - not_removed.append(fn) - return not_removed - - -class EventMixin(object): - """ - A very simple publish/subscribe system. - """ - def __init__(self): - self._subscribers = {} - - def add(self, event, subscriber, append=True): - """ - Add a subscriber for an event. - - :param event: The name of an event. - :param subscriber: The subscriber to be added (and called when the - event is published). - :param append: Whether to append or prepend the subscriber to an - existing subscriber list for the event. - """ - subs = self._subscribers - if event not in subs: - subs[event] = deque([subscriber]) - else: - sq = subs[event] - if append: - sq.append(subscriber) - else: - sq.appendleft(subscriber) - - def remove(self, event, subscriber): - """ - Remove a subscriber for an event. - - :param event: The name of an event. - :param subscriber: The subscriber to be removed. - """ - subs = self._subscribers - if event not in subs: - raise ValueError('No subscribers: %r' % event) - subs[event].remove(subscriber) - - def get_subscribers(self, event): - """ - Return an iterator for the subscribers for an event. - :param event: The event to return subscribers for. - """ - return iter(self._subscribers.get(event, ())) - - def publish(self, event, *args, **kwargs): - """ - Publish a event and return a list of values returned by its - subscribers. - - :param event: The event to publish. - :param args: The positional arguments to pass to the event's - subscribers. - :param kwargs: The keyword arguments to pass to the event's - subscribers. - """ - result = [] - for subscriber in self.get_subscribers(event): - try: - value = subscriber(event, *args, **kwargs) - except Exception: - logger.exception('Exception during event publication') - value = None - result.append(value) - logger.debug('publish %s: args = %s, kwargs = %s, result = %s', - event, args, kwargs, result) - return result - -# -# Simple sequencing -# -class Sequencer(object): - def __init__(self): - self._preds = {} - self._succs = {} - self._nodes = set() # nodes with no preds/succs - - def add_node(self, node): - self._nodes.add(node) - - def remove_node(self, node, edges=False): - if node in self._nodes: - self._nodes.remove(node) - if edges: - for p in set(self._preds.get(node, ())): - self.remove(p, node) - for s in set(self._succs.get(node, ())): - self.remove(node, s) - # Remove empties - for k, v in list(self._preds.items()): - if not v: - del self._preds[k] - for k, v in list(self._succs.items()): - if not v: - del self._succs[k] - - def add(self, pred, succ): - assert pred != succ - self._preds.setdefault(succ, set()).add(pred) - self._succs.setdefault(pred, set()).add(succ) - - def remove(self, pred, succ): - assert pred != succ - try: - preds = self._preds[succ] - succs = self._succs[pred] - except KeyError: # pragma: no cover - raise ValueError('%r not a successor of anything' % succ) - try: - preds.remove(pred) - succs.remove(succ) - except KeyError: # pragma: no cover - raise ValueError('%r not a successor of %r' % (succ, pred)) - - def is_step(self, step): - return (step in self._preds or step in self._succs or - step in self._nodes) - - def get_steps(self, final): - if not self.is_step(final): - raise ValueError('Unknown: %r' % final) - result = [] - todo = [] - seen = set() - todo.append(final) - while todo: - step = todo.pop(0) - if step in seen: - # if a step was already seen, - # move it to the end (so it will appear earlier - # when reversed on return) ... but not for the - # final step, as that would be confusing for - # users - if step != final: - result.remove(step) - result.append(step) - else: - seen.add(step) - result.append(step) - preds = self._preds.get(step, ()) - todo.extend(preds) - return reversed(result) - - @property - def strong_connections(self): - #http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm - index_counter = [0] - stack = [] - lowlinks = {} - index = {} - result = [] - - graph = self._succs - - def strongconnect(node): - # set the depth index for this node to the smallest unused index - index[node] = index_counter[0] - lowlinks[node] = index_counter[0] - index_counter[0] += 1 - stack.append(node) - - # Consider successors - try: - successors = graph[node] - except Exception: - successors = [] - for successor in successors: - if successor not in lowlinks: - # Successor has not yet been visited - strongconnect(successor) - lowlinks[node] = min(lowlinks[node],lowlinks[successor]) - elif successor in stack: - # the successor is in the stack and hence in the current - # strongly connected component (SCC) - lowlinks[node] = min(lowlinks[node],index[successor]) - - # If `node` is a root node, pop the stack and generate an SCC - if lowlinks[node] == index[node]: - connected_component = [] - - while True: - successor = stack.pop() - connected_component.append(successor) - if successor == node: break - component = tuple(connected_component) - # storing the result - result.append(component) - - for node in graph: - if node not in lowlinks: - strongconnect(node) - - return result - - @property - def dot(self): - result = ['digraph G {'] - for succ in self._preds: - preds = self._preds[succ] - for pred in preds: - result.append(' %s -> %s;' % (pred, succ)) - for node in self._nodes: - result.append(' %s;' % node) - result.append('}') - return '\n'.join(result) - -# -# Unarchiving functionality for zip, tar, tgz, tbz, whl -# - -ARCHIVE_EXTENSIONS = ('.tar.gz', '.tar.bz2', '.tar', '.zip', - '.tgz', '.tbz', '.whl') - -def unarchive(archive_filename, dest_dir, format=None, check=True): - - def check_path(path): - if not isinstance(path, text_type): - path = path.decode('utf-8') - p = os.path.abspath(os.path.join(dest_dir, path)) - if not p.startswith(dest_dir) or p[plen] != os.sep: - raise ValueError('path outside destination: %r' % p) - - dest_dir = os.path.abspath(dest_dir) - plen = len(dest_dir) - archive = None - if format is None: - if archive_filename.endswith(('.zip', '.whl')): - format = 'zip' - elif archive_filename.endswith(('.tar.gz', '.tgz')): - format = 'tgz' - mode = 'r:gz' - elif archive_filename.endswith(('.tar.bz2', '.tbz')): - format = 'tbz' - mode = 'r:bz2' - elif archive_filename.endswith('.tar'): - format = 'tar' - mode = 'r' - else: # pragma: no cover - raise ValueError('Unknown format for %r' % archive_filename) - try: - if format == 'zip': - archive = ZipFile(archive_filename, 'r') - if check: - names = archive.namelist() - for name in names: - check_path(name) - else: - archive = tarfile.open(archive_filename, mode) - if check: - names = archive.getnames() - for name in names: - check_path(name) - if format != 'zip' and sys.version_info[0] < 3: - # See Python issue 17153. If the dest path contains Unicode, - # tarfile extraction fails on Python 2.x if a member path name - # contains non-ASCII characters - it leads to an implicit - # bytes -> unicode conversion using ASCII to decode. - for tarinfo in archive.getmembers(): - if not isinstance(tarinfo.name, text_type): - tarinfo.name = tarinfo.name.decode('utf-8') - archive.extractall(dest_dir) - - finally: - if archive: - archive.close() - - -def zip_dir(directory): - """zip a directory tree into a BytesIO object""" - result = io.BytesIO() - dlen = len(directory) - with ZipFile(result, "w") as zf: - for root, dirs, files in os.walk(directory): - for name in files: - full = os.path.join(root, name) - rel = root[dlen:] - dest = os.path.join(rel, name) - zf.write(full, dest) - return result - -# -# Simple progress bar -# - -UNITS = ('', 'K', 'M', 'G','T','P') - - -class Progress(object): - unknown = 'UNKNOWN' - - def __init__(self, minval=0, maxval=100): - assert maxval is None or maxval >= minval - self.min = self.cur = minval - self.max = maxval - self.started = None - self.elapsed = 0 - self.done = False - - def update(self, curval): - assert self.min <= curval - assert self.max is None or curval <= self.max - self.cur = curval - now = time.time() - if self.started is None: - self.started = now - else: - self.elapsed = now - self.started - - def increment(self, incr): - assert incr >= 0 - self.update(self.cur + incr) - - def start(self): - self.update(self.min) - return self - - def stop(self): - if self.max is not None: - self.update(self.max) - self.done = True - - @property - def maximum(self): - return self.unknown if self.max is None else self.max - - @property - def percentage(self): - if self.done: - result = '100 %' - elif self.max is None: - result = ' ?? %' - else: - v = 100.0 * (self.cur - self.min) / (self.max - self.min) - result = '%3d %%' % v - return result - - def format_duration(self, duration): - if (duration <= 0) and self.max is None or self.cur == self.min: - result = '??:??:??' - #elif duration < 1: - # result = '--:--:--' - else: - result = time.strftime('%H:%M:%S', time.gmtime(duration)) - return result - - @property - def ETA(self): - if self.done: - prefix = 'Done' - t = self.elapsed - #import pdb; pdb.set_trace() - else: - prefix = 'ETA ' - if self.max is None: - t = -1 - elif self.elapsed == 0 or (self.cur == self.min): - t = 0 - else: - #import pdb; pdb.set_trace() - t = float(self.max - self.min) - t /= self.cur - self.min - t = (t - 1) * self.elapsed - return '%s: %s' % (prefix, self.format_duration(t)) - - @property - def speed(self): - if self.elapsed == 0: - result = 0.0 - else: - result = (self.cur - self.min) / self.elapsed - for unit in UNITS: - if result < 1000: - break - result /= 1000.0 - return '%d %sB/s' % (result, unit) - -# -# Glob functionality -# - -RICH_GLOB = re.compile(r'\{([^}]*)\}') -_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') -_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') - - -def iglob(path_glob): - """Extended globbing function that supports ** and {opt1,opt2,opt3}.""" - if _CHECK_RECURSIVE_GLOB.search(path_glob): - msg = """invalid glob %r: recursive glob "**" must be used alone""" - raise ValueError(msg % path_glob) - if _CHECK_MISMATCH_SET.search(path_glob): - msg = """invalid glob %r: mismatching set marker '{' or '}'""" - raise ValueError(msg % path_glob) - return _iglob(path_glob) - - -def _iglob(path_glob): - rich_path_glob = RICH_GLOB.split(path_glob, 1) - if len(rich_path_glob) > 1: - assert len(rich_path_glob) == 3, rich_path_glob - prefix, set, suffix = rich_path_glob - for item in set.split(','): - for path in _iglob(''.join((prefix, item, suffix))): - yield path - else: - if '**' not in path_glob: - for item in std_iglob(path_glob): - yield item - else: - prefix, radical = path_glob.split('**', 1) - if prefix == '': - prefix = '.' - if radical == '': - radical = '*' - else: - # we support both - radical = radical.lstrip('/') - radical = radical.lstrip('\\') - for path, dir, files in os.walk(prefix): - path = os.path.normpath(path) - for fn in _iglob(os.path.join(path, radical)): - yield fn - -if ssl: - from .compat import (HTTPSHandler as BaseHTTPSHandler, match_hostname, - CertificateError) - - -# -# HTTPSConnection which verifies certificates/matches domains -# - - class HTTPSConnection(httplib.HTTPSConnection): - ca_certs = None # set this to the path to the certs file (.pem) - check_domain = True # only used if ca_certs is not None - - # noinspection PyPropertyAccess - def connect(self): - sock = socket.create_connection((self.host, self.port), self.timeout) - if getattr(self, '_tunnel_host', False): - self.sock = sock - self._tunnel() - - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - if hasattr(ssl, 'OP_NO_SSLv2'): - context.options |= ssl.OP_NO_SSLv2 - if self.cert_file: - context.load_cert_chain(self.cert_file, self.key_file) - kwargs = {} - if self.ca_certs: - context.verify_mode = ssl.CERT_REQUIRED - context.load_verify_locations(cafile=self.ca_certs) - if getattr(ssl, 'HAS_SNI', False): - kwargs['server_hostname'] = self.host - - self.sock = context.wrap_socket(sock, **kwargs) - if self.ca_certs and self.check_domain: - try: - match_hostname(self.sock.getpeercert(), self.host) - logger.debug('Host verified: %s', self.host) - except CertificateError: # pragma: no cover - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - class HTTPSHandler(BaseHTTPSHandler): - def __init__(self, ca_certs, check_domain=True): - BaseHTTPSHandler.__init__(self) - self.ca_certs = ca_certs - self.check_domain = check_domain - - def _conn_maker(self, *args, **kwargs): - """ - This is called to create a connection instance. Normally you'd - pass a connection class to do_open, but it doesn't actually check for - a class, and just expects a callable. As long as we behave just as a - constructor would have, we should be OK. If it ever changes so that - we *must* pass a class, we'll create an UnsafeHTTPSConnection class - which just sets check_domain to False in the class definition, and - choose which one to pass to do_open. - """ - result = HTTPSConnection(*args, **kwargs) - if self.ca_certs: - result.ca_certs = self.ca_certs - result.check_domain = self.check_domain - return result - - def https_open(self, req): - try: - return self.do_open(self._conn_maker, req) - except URLError as e: - if 'certificate verify failed' in str(e.reason): - raise CertificateError('Unable to verify server certificate ' - 'for %s' % req.host) - else: - raise - - # - # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The- - # Middle proxy using HTTP listens on port 443, or an index mistakenly serves - # HTML containing a http://xyz link when it should be https://xyz), - # you can use the following handler class, which does not allow HTTP traffic. - # - # It works by inheriting from HTTPHandler - so build_opener won't add a - # handler for HTTP itself. - # - class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler): - def http_open(self, req): - raise URLError('Unexpected HTTP request on what should be a secure ' - 'connection: %s' % req) - -# -# XML-RPC with timeouts -# -class Transport(xmlrpclib.Transport): - def __init__(self, timeout, use_datetime=0): - self.timeout = timeout - xmlrpclib.Transport.__init__(self, use_datetime) - - def make_connection(self, host): - h, eh, x509 = self.get_host_info(host) - if not self._connection or host != self._connection[0]: - self._extra_headers = eh - self._connection = host, httplib.HTTPConnection(h) - return self._connection[1] - -if ssl: - class SafeTransport(xmlrpclib.SafeTransport): - def __init__(self, timeout, use_datetime=0): - self.timeout = timeout - xmlrpclib.SafeTransport.__init__(self, use_datetime) - - def make_connection(self, host): - h, eh, kwargs = self.get_host_info(host) - if not kwargs: - kwargs = {} - kwargs['timeout'] = self.timeout - if not self._connection or host != self._connection[0]: - self._extra_headers = eh - self._connection = host, httplib.HTTPSConnection(h, None, - **kwargs) - return self._connection[1] - - -class ServerProxy(xmlrpclib.ServerProxy): - def __init__(self, uri, **kwargs): - self.timeout = timeout = kwargs.pop('timeout', None) - # The above classes only come into play if a timeout - # is specified - if timeout is not None: - # scheme = splittype(uri) # deprecated as of Python 3.8 - scheme = urlparse(uri)[0] - use_datetime = kwargs.get('use_datetime', 0) - if scheme == 'https': - tcls = SafeTransport - else: - tcls = Transport - kwargs['transport'] = t = tcls(timeout, use_datetime=use_datetime) - self.transport = t - xmlrpclib.ServerProxy.__init__(self, uri, **kwargs) - -# -# CSV functionality. This is provided because on 2.x, the csv module can't -# handle Unicode. However, we need to deal with Unicode in e.g. RECORD files. -# - -def _csv_open(fn, mode, **kwargs): - if sys.version_info[0] < 3: - mode += 'b' - else: - kwargs['newline'] = '' - # Python 3 determines encoding from locale. Force 'utf-8' - # file encoding to match other forced utf-8 encoding - kwargs['encoding'] = 'utf-8' - return open(fn, mode, **kwargs) - - -class CSVBase(object): - defaults = { - 'delimiter': str(','), # The strs are used because we need native - 'quotechar': str('"'), # str in the csv API (2.x won't take - 'lineterminator': str('\n') # Unicode) - } - - def __enter__(self): - return self - - def __exit__(self, *exc_info): - self.stream.close() - - -class CSVReader(CSVBase): - def __init__(self, **kwargs): - if 'stream' in kwargs: - stream = kwargs['stream'] - if sys.version_info[0] >= 3: - # needs to be a text stream - stream = codecs.getreader('utf-8')(stream) - self.stream = stream - else: - self.stream = _csv_open(kwargs['path'], 'r') - self.reader = csv.reader(self.stream, **self.defaults) - - def __iter__(self): - return self - - def next(self): - result = next(self.reader) - if sys.version_info[0] < 3: - for i, item in enumerate(result): - if not isinstance(item, text_type): - result[i] = item.decode('utf-8') - return result - - __next__ = next - -class CSVWriter(CSVBase): - def __init__(self, fn, **kwargs): - self.stream = _csv_open(fn, 'w') - self.writer = csv.writer(self.stream, **self.defaults) - - def writerow(self, row): - if sys.version_info[0] < 3: - r = [] - for item in row: - if isinstance(item, text_type): - item = item.encode('utf-8') - r.append(item) - row = r - self.writer.writerow(row) - -# -# Configurator functionality -# - -class Configurator(BaseConfigurator): - - value_converters = dict(BaseConfigurator.value_converters) - value_converters['inc'] = 'inc_convert' - - def __init__(self, config, base=None): - super(Configurator, self).__init__(config) - self.base = base or os.getcwd() - - def configure_custom(self, config): - def convert(o): - if isinstance(o, (list, tuple)): - result = type(o)([convert(i) for i in o]) - elif isinstance(o, dict): - if '()' in o: - result = self.configure_custom(o) - else: - result = {} - for k in o: - result[k] = convert(o[k]) - else: - result = self.convert(o) - return result - - c = config.pop('()') - if not callable(c): - c = self.resolve(c) - props = config.pop('.', None) - # Check for valid identifiers - args = config.pop('[]', ()) - if args: - args = tuple([convert(o) for o in args]) - items = [(k, convert(config[k])) for k in config if valid_ident(k)] - kwargs = dict(items) - result = c(*args, **kwargs) - if props: - for n, v in props.items(): - setattr(result, n, convert(v)) - return result - - def __getitem__(self, key): - result = self.config[key] - if isinstance(result, dict) and '()' in result: - self.config[key] = result = self.configure_custom(result) - return result - - def inc_convert(self, value): - """Default converter for the inc:// protocol.""" - if not os.path.isabs(value): - value = os.path.join(self.base, value) - with codecs.open(value, 'r', encoding='utf-8') as f: - result = json.load(f) - return result - - -class SubprocessMixin(object): - """ - Mixin for running subprocesses and capturing their output - """ - def __init__(self, verbose=False, progress=None): - self.verbose = verbose - self.progress = progress - - def reader(self, stream, context): - """ - Read lines from a subprocess' output stream and either pass to a progress - callable (if specified) or write progress information to sys.stderr. - """ - progress = self.progress - verbose = self.verbose - while True: - s = stream.readline() - if not s: - break - if progress is not None: - progress(s, context) - else: - if not verbose: - sys.stderr.write('.') - else: - sys.stderr.write(s.decode('utf-8')) - sys.stderr.flush() - stream.close() - - def run_command(self, cmd, **kwargs): - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, **kwargs) - t1 = threading.Thread(target=self.reader, args=(p.stdout, 'stdout')) - t1.start() - t2 = threading.Thread(target=self.reader, args=(p.stderr, 'stderr')) - t2.start() - p.wait() - t1.join() - t2.join() - if self.progress is not None: - self.progress('done.', 'main') - elif self.verbose: - sys.stderr.write('done.\n') - return p - - -def normalize_name(name): - """Normalize a python package name a la PEP 503""" - # https://www.python.org/dev/peps/pep-0503/#normalized-names - return re.sub('[-_.]+', '-', name).lower() - -# def _get_pypirc_command(): - # """ - # Get the distutils command for interacting with PyPI configurations. - # :return: the command. - # """ - # from distutils.core import Distribution - # from distutils.config import PyPIRCCommand - # d = Distribution() - # return PyPIRCCommand(d) - -class PyPIRCFile(object): - - DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' - DEFAULT_REALM = 'pypi' - - def __init__(self, fn=None, url=None): - if fn is None: - fn = os.path.join(os.path.expanduser('~'), '.pypirc') - self.filename = fn - self.url = url - - def read(self): - result = {} - - if os.path.exists(self.filename): - repository = self.url or self.DEFAULT_REPOSITORY - - config = configparser.RawConfigParser() - config.read(self.filename) - sections = config.sections() - if 'distutils' in sections: - # let's get the list of servers - index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] - if _servers == []: - # nothing set, let's try to get the default pypi - if 'pypi' in sections: - _servers = ['pypi'] - else: - for server in _servers: - result = {'server': server} - result['username'] = config.get(server, 'username') - - # optional params - for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), - ('password', None)): - if config.has_option(server, key): - result[key] = config.get(server, key) - else: - result[key] = default - - # work around people having "repository" for the "pypi" - # section of their config set to the HTTP (rather than - # HTTPS) URL - if (server == 'pypi' and - repository in (self.DEFAULT_REPOSITORY, 'pypi')): - result['repository'] = self.DEFAULT_REPOSITORY - elif (result['server'] != repository and - result['repository'] != repository): - result = {} - elif 'server-login' in sections: - # old format - server = 'server-login' - if config.has_option(server, 'repository'): - repository = config.get(server, 'repository') - else: - repository = self.DEFAULT_REPOSITORY - result = { - 'username': config.get(server, 'username'), - 'password': config.get(server, 'password'), - 'repository': repository, - 'server': server, - 'realm': self.DEFAULT_REALM - } - return result - - def update(self, username, password): - # import pdb; pdb.set_trace() - config = configparser.RawConfigParser() - fn = self.filename - config.read(fn) - if not config.has_section('pypi'): - config.add_section('pypi') - config.set('pypi', 'username', username) - config.set('pypi', 'password', password) - with open(fn, 'w') as f: - config.write(f) - -def _load_pypirc(index): - """ - Read the PyPI access configuration as supported by distutils. - """ - return PyPIRCFile(url=index.url).read() - -def _store_pypirc(index): - PyPIRCFile().update(index.username, index.password) - -# -# get_platform()/get_host_platform() copied from Python 3.10.a0 source, with some minor -# tweaks -# - -def get_host_platform(): - """Return a string that identifies the current platform. This is used mainly to - distinguish platform-specific build directories and platform-specific built - distributions. Typically includes the OS name and version and the - architecture (as supplied by 'os.uname()'), although the exact information - included depends on the OS; eg. on Linux, the kernel version isn't - particularly important. - - Examples of returned values: - linux-i586 - linux-alpha (?) - solaris-2.6-sun4u - - Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win32 (all others - specifically, sys.platform is returned) - - For other non-POSIX platforms, currently just returns 'sys.platform'. - - """ - if os.name == 'nt': - if 'amd64' in sys.version.lower(): - return 'win-amd64' - if '(arm)' in sys.version.lower(): - return 'win-arm32' - if '(arm64)' in sys.version.lower(): - return 'win-arm64' - return sys.platform - - # Set for cross builds explicitly - if "_PYTHON_HOST_PLATFORM" in os.environ: - return os.environ["_PYTHON_HOST_PLATFORM"] - - if os.name != 'posix' or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. - return sys.platform - - # Try to distinguish various flavours of Unix - - (osname, host, release, version, machine) = os.uname() - - # Convert the OS name to lowercase, remove '/' characters, and translate - # spaces (for "Power Macintosh") - osname = osname.lower().replace('/', '') - machine = machine.replace(' ', '_').replace('/', '-') - - if osname[:5] == 'linux': - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - - elif osname[:5] == 'sunos': - if release[0] >= '5': # SunOS 5 == Solaris 2 - osname = 'solaris' - release = '%d.%s' % (int(release[0]) - 3, release[2:]) - # We can't use 'platform.architecture()[0]' because a - # bootstrap problem. We use a dict to get an error - # if some suspicious happens. - bitness = {2147483647:'32bit', 9223372036854775807:'64bit'} - machine += '.%s' % bitness[sys.maxsize] - # fall through to standard osname-release-machine representation - elif osname[:3] == 'aix': - from _aix_support import aix_platform - return aix_platform() - elif osname[:6] == 'cygwin': - osname = 'cygwin' - rel_re = re.compile (r'[\d.]+', re.ASCII) - m = rel_re.match(release) - if m: - release = m.group() - elif osname[:6] == 'darwin': - import _osx_support, distutils.sysconfig - osname, release, machine = _osx_support.get_platform_osx( - distutils.sysconfig.get_config_vars(), - osname, release, machine) - - return '%s-%s-%s' % (osname, release, machine) - - -_TARGET_TO_PLAT = { - 'x86' : 'win32', - 'x64' : 'win-amd64', - 'arm' : 'win-arm32', -} - - -def get_platform(): - if os.name != 'nt': - return get_host_platform() - cross_compilation_target = os.environ.get('VSCMD_ARG_TGT_ARCH') - if cross_compilation_target not in _TARGET_TO_PLAT: - return get_host_platform() - return _TARGET_TO_PLAT[cross_compilation_target] diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/version.py b/.venv/Lib/site-packages/pip/_vendor/distlib/version.py deleted file mode 100644 index c7c8bb6..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/distlib/version.py +++ /dev/null @@ -1,739 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2012-2017 The Python Software Foundation. -# See LICENSE.txt and CONTRIBUTORS.txt. -# -""" -Implementation of a flexible versioning scheme providing support for PEP-440, -setuptools-compatible and semantic versioning. -""" - -import logging -import re - -from .compat import string_types -from .util import parse_requirement - -__all__ = ['NormalizedVersion', 'NormalizedMatcher', - 'LegacyVersion', 'LegacyMatcher', - 'SemanticVersion', 'SemanticMatcher', - 'UnsupportedVersionError', 'get_scheme'] - -logger = logging.getLogger(__name__) - - -class UnsupportedVersionError(ValueError): - """This is an unsupported version.""" - pass - - -class Version(object): - def __init__(self, s): - self._string = s = s.strip() - self._parts = parts = self.parse(s) - assert isinstance(parts, tuple) - assert len(parts) > 0 - - def parse(self, s): - raise NotImplementedError('please implement in a subclass') - - def _check_compatible(self, other): - if type(self) != type(other): - raise TypeError('cannot compare %r and %r' % (self, other)) - - def __eq__(self, other): - self._check_compatible(other) - return self._parts == other._parts - - def __ne__(self, other): - return not self.__eq__(other) - - def __lt__(self, other): - self._check_compatible(other) - return self._parts < other._parts - - def __gt__(self, other): - return not (self.__lt__(other) or self.__eq__(other)) - - def __le__(self, other): - return self.__lt__(other) or self.__eq__(other) - - def __ge__(self, other): - return self.__gt__(other) or self.__eq__(other) - - # See http://docs.python.org/reference/datamodel#object.__hash__ - def __hash__(self): - return hash(self._parts) - - def __repr__(self): - return "%s('%s')" % (self.__class__.__name__, self._string) - - def __str__(self): - return self._string - - @property - def is_prerelease(self): - raise NotImplementedError('Please implement in subclasses.') - - -class Matcher(object): - version_class = None - - # value is either a callable or the name of a method - _operators = { - '<': lambda v, c, p: v < c, - '>': lambda v, c, p: v > c, - '<=': lambda v, c, p: v == c or v < c, - '>=': lambda v, c, p: v == c or v > c, - '==': lambda v, c, p: v == c, - '===': lambda v, c, p: v == c, - # by default, compatible => >=. - '~=': lambda v, c, p: v == c or v > c, - '!=': lambda v, c, p: v != c, - } - - # this is a method only to support alternative implementations - # via overriding - def parse_requirement(self, s): - return parse_requirement(s) - - def __init__(self, s): - if self.version_class is None: - raise ValueError('Please specify a version class') - self._string = s = s.strip() - r = self.parse_requirement(s) - if not r: - raise ValueError('Not valid: %r' % s) - self.name = r.name - self.key = self.name.lower() # for case-insensitive comparisons - clist = [] - if r.constraints: - # import pdb; pdb.set_trace() - for op, s in r.constraints: - if s.endswith('.*'): - if op not in ('==', '!='): - raise ValueError('\'.*\' not allowed for ' - '%r constraints' % op) - # Could be a partial version (e.g. for '2.*') which - # won't parse as a version, so keep it as a string - vn, prefix = s[:-2], True - # Just to check that vn is a valid version - self.version_class(vn) - else: - # Should parse as a version, so we can create an - # instance for the comparison - vn, prefix = self.version_class(s), False - clist.append((op, vn, prefix)) - self._parts = tuple(clist) - - def match(self, version): - """ - Check if the provided version matches the constraints. - - :param version: The version to match against this instance. - :type version: String or :class:`Version` instance. - """ - if isinstance(version, string_types): - version = self.version_class(version) - for operator, constraint, prefix in self._parts: - f = self._operators.get(operator) - if isinstance(f, string_types): - f = getattr(self, f) - if not f: - msg = ('%r not implemented ' - 'for %s' % (operator, self.__class__.__name__)) - raise NotImplementedError(msg) - if not f(version, constraint, prefix): - return False - return True - - @property - def exact_version(self): - result = None - if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='): - result = self._parts[0][1] - return result - - def _check_compatible(self, other): - if type(self) != type(other) or self.name != other.name: - raise TypeError('cannot compare %s and %s' % (self, other)) - - def __eq__(self, other): - self._check_compatible(other) - return self.key == other.key and self._parts == other._parts - - def __ne__(self, other): - return not self.__eq__(other) - - # See http://docs.python.org/reference/datamodel#object.__hash__ - def __hash__(self): - return hash(self.key) + hash(self._parts) - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._string) - - def __str__(self): - return self._string - - -PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|b|c|rc)(\d+))?' - r'(\.(post)(\d+))?(\.(dev)(\d+))?' - r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$') - - -def _pep_440_key(s): - s = s.strip() - m = PEP440_VERSION_RE.match(s) - if not m: - raise UnsupportedVersionError('Not a valid version: %s' % s) - groups = m.groups() - nums = tuple(int(v) for v in groups[1].split('.')) - while len(nums) > 1 and nums[-1] == 0: - nums = nums[:-1] - - if not groups[0]: - epoch = 0 - else: - epoch = int(groups[0][:-1]) - pre = groups[4:6] - post = groups[7:9] - dev = groups[10:12] - local = groups[13] - if pre == (None, None): - pre = () - else: - pre = pre[0], int(pre[1]) - if post == (None, None): - post = () - else: - post = post[0], int(post[1]) - if dev == (None, None): - dev = () - else: - dev = dev[0], int(dev[1]) - if local is None: - local = () - else: - parts = [] - for part in local.split('.'): - # to ensure that numeric compares as > lexicographic, avoid - # comparing them directly, but encode a tuple which ensures - # correct sorting - if part.isdigit(): - part = (1, int(part)) - else: - part = (0, part) - parts.append(part) - local = tuple(parts) - if not pre: - # either before pre-release, or final release and after - if not post and dev: - # before pre-release - pre = ('a', -1) # to sort before a0 - else: - pre = ('z',) # to sort after all pre-releases - # now look at the state of post and dev. - if not post: - post = ('_',) # sort before 'a' - if not dev: - dev = ('final',) - - #print('%s -> %s' % (s, m.groups())) - return epoch, nums, pre, post, dev, local - - -_normalized_key = _pep_440_key - - -class NormalizedVersion(Version): - """A rational version. - - Good: - 1.2 # equivalent to "1.2.0" - 1.2.0 - 1.2a1 - 1.2.3a2 - 1.2.3b1 - 1.2.3c1 - 1.2.3.4 - TODO: fill this out - - Bad: - 1 # minimum two numbers - 1.2a # release level must have a release serial - 1.2.3b - """ - def parse(self, s): - result = _normalized_key(s) - # _normalized_key loses trailing zeroes in the release - # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0 - # However, PEP 440 prefix matching needs it: for example, - # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0). - m = PEP440_VERSION_RE.match(s) # must succeed - groups = m.groups() - self._release_clause = tuple(int(v) for v in groups[1].split('.')) - return result - - PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev']) - - @property - def is_prerelease(self): - return any(t[0] in self.PREREL_TAGS for t in self._parts if t) - - -def _match_prefix(x, y): - x = str(x) - y = str(y) - if x == y: - return True - if not x.startswith(y): - return False - n = len(y) - return x[n] == '.' - - -class NormalizedMatcher(Matcher): - version_class = NormalizedVersion - - # value is either a callable or the name of a method - _operators = { - '~=': '_match_compatible', - '<': '_match_lt', - '>': '_match_gt', - '<=': '_match_le', - '>=': '_match_ge', - '==': '_match_eq', - '===': '_match_arbitrary', - '!=': '_match_ne', - } - - def _adjust_local(self, version, constraint, prefix): - if prefix: - strip_local = '+' not in constraint and version._parts[-1] - else: - # both constraint and version are - # NormalizedVersion instances. - # If constraint does not have a local component, - # ensure the version doesn't, either. - strip_local = not constraint._parts[-1] and version._parts[-1] - if strip_local: - s = version._string.split('+', 1)[0] - version = self.version_class(s) - return version, constraint - - def _match_lt(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - if version >= constraint: - return False - release_clause = constraint._release_clause - pfx = '.'.join([str(i) for i in release_clause]) - return not _match_prefix(version, pfx) - - def _match_gt(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - if version <= constraint: - return False - release_clause = constraint._release_clause - pfx = '.'.join([str(i) for i in release_clause]) - return not _match_prefix(version, pfx) - - def _match_le(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - return version <= constraint - - def _match_ge(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - return version >= constraint - - def _match_eq(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - if not prefix: - result = (version == constraint) - else: - result = _match_prefix(version, constraint) - return result - - def _match_arbitrary(self, version, constraint, prefix): - return str(version) == str(constraint) - - def _match_ne(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - if not prefix: - result = (version != constraint) - else: - result = not _match_prefix(version, constraint) - return result - - def _match_compatible(self, version, constraint, prefix): - version, constraint = self._adjust_local(version, constraint, prefix) - if version == constraint: - return True - if version < constraint: - return False -# if not prefix: -# return True - release_clause = constraint._release_clause - if len(release_clause) > 1: - release_clause = release_clause[:-1] - pfx = '.'.join([str(i) for i in release_clause]) - return _match_prefix(version, pfx) - -_REPLACEMENTS = ( - (re.compile('[.+-]$'), ''), # remove trailing puncts - (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start - (re.compile('^[.-]'), ''), # remove leading puncts - (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses - (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion) - (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion) - (re.compile('[.]{2,}'), '.'), # multiple runs of '.' - (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha - (re.compile(r'\b(pre-alpha|prealpha)\b'), - 'pre.alpha'), # standardise - (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses -) - -_SUFFIX_REPLACEMENTS = ( - (re.compile('^[:~._+-]+'), ''), # remove leading puncts - (re.compile('[,*")([\\]]'), ''), # remove unwanted chars - (re.compile('[~:+_ -]'), '.'), # replace illegal chars - (re.compile('[.]{2,}'), '.'), # multiple runs of '.' - (re.compile(r'\.$'), ''), # trailing '.' -) - -_NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)') - - -def _suggest_semantic_version(s): - """ - Try to suggest a semantic form for a version for which - _suggest_normalized_version couldn't come up with anything. - """ - result = s.strip().lower() - for pat, repl in _REPLACEMENTS: - result = pat.sub(repl, result) - if not result: - result = '0.0.0' - - # Now look for numeric prefix, and separate it out from - # the rest. - #import pdb; pdb.set_trace() - m = _NUMERIC_PREFIX.match(result) - if not m: - prefix = '0.0.0' - suffix = result - else: - prefix = m.groups()[0].split('.') - prefix = [int(i) for i in prefix] - while len(prefix) < 3: - prefix.append(0) - if len(prefix) == 3: - suffix = result[m.end():] - else: - suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():] - prefix = prefix[:3] - prefix = '.'.join([str(i) for i in prefix]) - suffix = suffix.strip() - if suffix: - #import pdb; pdb.set_trace() - # massage the suffix. - for pat, repl in _SUFFIX_REPLACEMENTS: - suffix = pat.sub(repl, suffix) - - if not suffix: - result = prefix - else: - sep = '-' if 'dev' in suffix else '+' - result = prefix + sep + suffix - if not is_semver(result): - result = None - return result - - -def _suggest_normalized_version(s): - """Suggest a normalized version close to the given version string. - - If you have a version string that isn't rational (i.e. NormalizedVersion - doesn't like it) then you might be able to get an equivalent (or close) - rational version from this function. - - This does a number of simple normalizations to the given string, based - on observation of versions currently in use on PyPI. Given a dump of - those version during PyCon 2009, 4287 of them: - - 2312 (53.93%) match NormalizedVersion without change - with the automatic suggestion - - 3474 (81.04%) match when using this suggestion method - - @param s {str} An irrational version string. - @returns A rational version string, or None, if couldn't determine one. - """ - try: - _normalized_key(s) - return s # already rational - except UnsupportedVersionError: - pass - - rs = s.lower() - - # part of this could use maketrans - for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'), - ('beta', 'b'), ('rc', 'c'), ('-final', ''), - ('-pre', 'c'), - ('-release', ''), ('.release', ''), ('-stable', ''), - ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''), - ('final', '')): - rs = rs.replace(orig, repl) - - # if something ends with dev or pre, we add a 0 - rs = re.sub(r"pre$", r"pre0", rs) - rs = re.sub(r"dev$", r"dev0", rs) - - # if we have something like "b-2" or "a.2" at the end of the - # version, that is probably beta, alpha, etc - # let's remove the dash or dot - rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs) - - # 1.0-dev-r371 -> 1.0.dev371 - # 0.1-dev-r79 -> 0.1.dev79 - rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs) - - # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1 - rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs) - - # Clean: v0.3, v1.0 - if rs.startswith('v'): - rs = rs[1:] - - # Clean leading '0's on numbers. - #TODO: unintended side-effect on, e.g., "2003.05.09" - # PyPI stats: 77 (~2%) better - rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs) - - # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers - # zero. - # PyPI stats: 245 (7.56%) better - rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs) - - # the 'dev-rNNN' tag is a dev tag - rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs) - - # clean the - when used as a pre delimiter - rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs) - - # a terminal "dev" or "devel" can be changed into ".dev0" - rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs) - - # a terminal "dev" can be changed into ".dev0" - rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs) - - # a terminal "final" or "stable" can be removed - rs = re.sub(r"(final|stable)$", "", rs) - - # The 'r' and the '-' tags are post release tags - # 0.4a1.r10 -> 0.4a1.post10 - # 0.9.33-17222 -> 0.9.33.post17222 - # 0.9.33-r17222 -> 0.9.33.post17222 - rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs) - - # Clean 'r' instead of 'dev' usage: - # 0.9.33+r17222 -> 0.9.33.dev17222 - # 1.0dev123 -> 1.0.dev123 - # 1.0.git123 -> 1.0.dev123 - # 1.0.bzr123 -> 1.0.dev123 - # 0.1a0dev.123 -> 0.1a0.dev123 - # PyPI stats: ~150 (~4%) better - rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs) - - # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage: - # 0.2.pre1 -> 0.2c1 - # 0.2-c1 -> 0.2c1 - # 1.0preview123 -> 1.0c123 - # PyPI stats: ~21 (0.62%) better - rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) - - # Tcl/Tk uses "px" for their post release markers - rs = re.sub(r"p(\d+)$", r".post\1", rs) - - try: - _normalized_key(rs) - except UnsupportedVersionError: - rs = None - return rs - -# -# Legacy version processing (distribute-compatible) -# - -_VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I) -_VERSION_REPLACE = { - 'pre': 'c', - 'preview': 'c', - '-': 'final-', - 'rc': 'c', - 'dev': '@', - '': None, - '.': None, -} - - -def _legacy_key(s): - def get_parts(s): - result = [] - for p in _VERSION_PART.split(s.lower()): - p = _VERSION_REPLACE.get(p, p) - if p: - if '0' <= p[:1] <= '9': - p = p.zfill(8) - else: - p = '*' + p - result.append(p) - result.append('*final') - return result - - result = [] - for p in get_parts(s): - if p.startswith('*'): - if p < '*final': - while result and result[-1] == '*final-': - result.pop() - while result and result[-1] == '00000000': - result.pop() - result.append(p) - return tuple(result) - - -class LegacyVersion(Version): - def parse(self, s): - return _legacy_key(s) - - @property - def is_prerelease(self): - result = False - for x in self._parts: - if (isinstance(x, string_types) and x.startswith('*') and - x < '*final'): - result = True - break - return result - - -class LegacyMatcher(Matcher): - version_class = LegacyVersion - - _operators = dict(Matcher._operators) - _operators['~='] = '_match_compatible' - - numeric_re = re.compile(r'^(\d+(\.\d+)*)') - - def _match_compatible(self, version, constraint, prefix): - if version < constraint: - return False - m = self.numeric_re.match(str(constraint)) - if not m: - logger.warning('Cannot compute compatible match for version %s ' - ' and constraint %s', version, constraint) - return True - s = m.groups()[0] - if '.' in s: - s = s.rsplit('.', 1)[0] - return _match_prefix(version, s) - -# -# Semantic versioning -# - -_SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)' - r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?' - r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I) - - -def is_semver(s): - return _SEMVER_RE.match(s) - - -def _semantic_key(s): - def make_tuple(s, absent): - if s is None: - result = (absent,) - else: - parts = s[1:].split('.') - # We can't compare ints and strings on Python 3, so fudge it - # by zero-filling numeric values so simulate a numeric comparison - result = tuple([p.zfill(8) if p.isdigit() else p for p in parts]) - return result - - m = is_semver(s) - if not m: - raise UnsupportedVersionError(s) - groups = m.groups() - major, minor, patch = [int(i) for i in groups[:3]] - # choose the '|' and '*' so that versions sort correctly - pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*') - return (major, minor, patch), pre, build - - -class SemanticVersion(Version): - def parse(self, s): - return _semantic_key(s) - - @property - def is_prerelease(self): - return self._parts[1][0] != '|' - - -class SemanticMatcher(Matcher): - version_class = SemanticVersion - - -class VersionScheme(object): - def __init__(self, key, matcher, suggester=None): - self.key = key - self.matcher = matcher - self.suggester = suggester - - def is_valid_version(self, s): - try: - self.matcher.version_class(s) - result = True - except UnsupportedVersionError: - result = False - return result - - def is_valid_matcher(self, s): - try: - self.matcher(s) - result = True - except UnsupportedVersionError: - result = False - return result - - def is_valid_constraint_list(self, s): - """ - Used for processing some metadata fields - """ - # See issue #140. Be tolerant of a single trailing comma. - if s.endswith(','): - s = s[:-1] - return self.is_valid_matcher('dummy_name (%s)' % s) - - def suggest(self, s): - if self.suggester is None: - result = None - else: - result = self.suggester(s) - return result - -_SCHEMES = { - 'normalized': VersionScheme(_normalized_key, NormalizedMatcher, - _suggest_normalized_version), - 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s), - 'semantic': VersionScheme(_semantic_key, SemanticMatcher, - _suggest_semantic_version), -} - -_SCHEMES['default'] = _SCHEMES['normalized'] - - -def get_scheme(name): - if name not in _SCHEMES: - raise ValueError('unknown scheme name: %r' % name) - return _SCHEMES[name] diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/w32.exe b/.venv/Lib/site-packages/pip/_vendor/distlib/w32.exe deleted file mode 100644 index 4ee2d3a31b59e8b50f433ecdf0be9e496e8cc3b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91648 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 diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe b/.venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe deleted file mode 100644 index 951d5817c9e6d81c94a173a0d9fead7f1f143331..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168448 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 diff --git a/.venv/Lib/site-packages/pip/_vendor/distlib/w64.exe b/.venv/Lib/site-packages/pip/_vendor/distlib/w64.exe deleted file mode 100644 index 5763076d2878093971a0ef9870e1cde7f556b18b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101888 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

                                          # pre-release
-            [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-        (?P                                         # post release
-            (?:-(?P[0-9]+))
-            |
-            (?:
-                [-_\.]?
-                (?Ppost|rev|r)
-                [-_\.]?
-                (?P[0-9]+)?
-            )
-        )?
-        (?P                                          # dev release
-            [-_\.]?
-            (?Pdev)
-            [-_\.]?
-            (?P[0-9]+)?
-        )?
-    )
-    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version: str) -> None:
-
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion(f"Invalid version: '{version}'")
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self) -> str:
-        return f""
-
-    def __str__(self) -> str:
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append(f"{self.epoch}!")
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        # Pre-release
-        if self.pre is not None:
-            parts.append("".join(str(x) for x in self.pre))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(f".post{self.post}")
-
-        # Development release
-        if self.dev is not None:
-            parts.append(f".dev{self.dev}")
-
-        # Local version segment
-        if self.local is not None:
-            parts.append(f"+{self.local}")
-
-        return "".join(parts)
-
-    @property
-    def epoch(self) -> int:
-        _epoch: int = self._version.epoch
-        return _epoch
-
-    @property
-    def release(self) -> Tuple[int, ...]:
-        _release: Tuple[int, ...] = self._version.release
-        return _release
-
-    @property
-    def pre(self) -> Optional[Tuple[str, int]]:
-        _pre: Optional[Tuple[str, int]] = self._version.pre
-        return _pre
-
-    @property
-    def post(self) -> Optional[int]:
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self) -> Optional[int]:
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self) -> Optional[str]:
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self) -> str:
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self) -> str:
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append(f"{self.epoch}!")
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self) -> bool:
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self) -> bool:
-        return self.post is not None
-
-    @property
-    def is_devrelease(self) -> bool:
-        return self.dev is not None
-
-    @property
-    def major(self) -> int:
-        return self.release[0] if len(self.release) >= 1 else 0
-
-    @property
-    def minor(self) -> int:
-        return self.release[1] if len(self.release) >= 2 else 0
-
-    @property
-    def micro(self) -> int:
-        return self.release[2] if len(self.release) >= 3 else 0
-
-
-def _parse_letter_version(
-    letter: str, number: Union[str, bytes, SupportsInt]
-) -> Optional[Tuple[str, int]]:
-
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-    return None
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local: str) -> Optional[LocalType]:
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-    return None
-
-
-def _cmpkey(
-    epoch: int,
-    release: Tuple[int, ...],
-    pre: Optional[Tuple[str, int]],
-    post: Optional[Tuple[str, int]],
-    dev: Optional[Tuple[str, int]],
-    local: Optional[Tuple[SubLocalType]],
-) -> CmpKey:
-
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    _release = tuple(
-        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        _pre: PrePostDevType = NegativeInfinity
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        _pre = Infinity
-    else:
-        _pre = pre
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        _post: PrePostDevType = NegativeInfinity
-
-    else:
-        _post = post
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        _dev: PrePostDevType = Infinity
-
-    else:
-        _dev = dev
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        _local: LocalType = NegativeInfinity
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        _local = tuple(
-            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
-        )
-
-    return epoch, _release, _pre, _post, _dev, _local
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pep517/__init__.py
deleted file mode 100644
index 38ea0f5..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""Wrappers to build Python packages using PEP 517 hooks
-"""
-
-__version__ = '0.13.0'
-
-from .wrappers import *  # noqa: F401, F403
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/_compat.py b/.venv/Lib/site-packages/pip/_vendor/pep517/_compat.py
deleted file mode 100644
index 95e509c..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/_compat.py
+++ /dev/null
@@ -1,8 +0,0 @@
-__all__ = ("tomllib",)
-
-import sys
-
-if sys.version_info >= (3, 11):
-    import tomllib
-else:
-    from pip._vendor import tomli as tomllib
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/build.py b/.venv/Lib/site-packages/pip/_vendor/pep517/build.py
deleted file mode 100644
index b30909c..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/build.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""Build a project using PEP 517 hooks.
-"""
-import argparse
-import logging
-import os
-import shutil
-import tempfile
-
-from ._compat import tomllib
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def validate_system(system):
-    """
-    Ensure build system has the requisite fields.
-    """
-    required = {'requires', 'build-backend'}
-    if not (required <= set(system)):
-        message = "Missing required fields: {missing}".format(
-            missing=required-set(system),
-        )
-        raise ValueError(message)
-
-
-def load_system(source_dir):
-    """
-    Load the build system from a source dir (pyproject.toml).
-    """
-    pyproject = os.path.join(source_dir, 'pyproject.toml')
-    with open(pyproject, 'rb') as f:
-        pyproject_data = tomllib.load(f)
-    return pyproject_data['build-system']
-
-
-def compat_system(source_dir):
-    """
-    Given a source dir, attempt to get a build system backend
-    and requirements from pyproject.toml. Fallback to
-    setuptools but only if the file was not found or a build
-    system was not indicated.
-    """
-    try:
-        system = load_system(source_dir)
-    except (FileNotFoundError, KeyError):
-        system = {}
-    system.setdefault(
-        'build-backend',
-        'setuptools.build_meta:__legacy__',
-    )
-    system.setdefault('requires', ['setuptools', 'wheel'])
-    return system
-
-
-def _do_build(hooks, env, dist, dest):
-    get_requires_name = 'get_requires_for_build_{dist}'.format(**locals())
-    get_requires = getattr(hooks, get_requires_name)
-    reqs = get_requires({})
-    log.info('Got build requires: %s', reqs)
-
-    env.pip_install(reqs)
-    log.info('Installed dynamic build dependencies')
-
-    with tempfile.TemporaryDirectory() as td:
-        log.info('Trying to build %s in %s', dist, td)
-        build_name = 'build_{dist}'.format(**locals())
-        build = getattr(hooks, build_name)
-        filename = build(td, {})
-        source = os.path.join(td, filename)
-        shutil.move(source, os.path.join(dest, os.path.basename(filename)))
-
-
-def build(source_dir, dist, dest=None, system=None):
-    system = system or load_system(source_dir)
-    dest = os.path.join(source_dir, dest or 'dist')
-    os.makedirs(dest, exist_ok=True)
-
-    validate_system(system)
-    hooks = Pep517HookCaller(
-        source_dir, system['build-backend'], system.get('backend-path')
-    )
-
-    with BuildEnvironment() as env:
-        env.pip_install(system['requires'])
-        _do_build(hooks, env, dist, dest)
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    'source_dir',
-    help="A directory containing pyproject.toml",
-)
-parser.add_argument(
-    '--binary', '-b',
-    action='store_true',
-    default=False,
-)
-parser.add_argument(
-    '--source', '-s',
-    action='store_true',
-    default=False,
-)
-parser.add_argument(
-    '--out-dir', '-o',
-    help="Destination in which to save the builds relative to source dir",
-)
-
-
-def main(args):
-    log.warning('pep517.build is deprecated. '
-                'Consider switching to https://pypi.org/project/build/')
-
-    # determine which dists to build
-    dists = list(filter(None, (
-        'sdist' if args.source or not args.binary else None,
-        'wheel' if args.binary or not args.source else None,
-    )))
-
-    for dist in dists:
-        build(args.source_dir, dist, args.out_dir)
-
-
-if __name__ == '__main__':
-    main(parser.parse_args())
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/check.py b/.venv/Lib/site-packages/pip/_vendor/pep517/check.py
deleted file mode 100644
index b79f627..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/check.py
+++ /dev/null
@@ -1,207 +0,0 @@
-"""Check a project and backend by attempting to build using PEP 517 hooks.
-"""
-import argparse
-import logging
-import os
-import shutil
-import sys
-import tarfile
-import zipfile
-from os.path import isfile
-from os.path import join as pjoin
-from subprocess import CalledProcessError
-from tempfile import mkdtemp
-
-from ._compat import tomllib
-from .colorlog import enable_colourful_output
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def check_build_sdist(hooks, build_sys_requires):
-    with BuildEnvironment() as env:
-        try:
-            env.pip_install(build_sys_requires)
-            log.info('Installed static build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install static build dependencies')
-            return False
-
-        try:
-            reqs = hooks.get_requires_for_build_sdist({})
-            log.info('Got build requires: %s', reqs)
-        except Exception:
-            log.error('Failure in get_requires_for_build_sdist', exc_info=True)
-            return False
-
-        try:
-            env.pip_install(reqs)
-            log.info('Installed dynamic build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install dynamic build dependencies')
-            return False
-
-        td = mkdtemp()
-        log.info('Trying to build sdist in %s', td)
-        try:
-            try:
-                filename = hooks.build_sdist(td, {})
-                log.info('build_sdist returned %r', filename)
-            except Exception:
-                log.info('Failure in build_sdist', exc_info=True)
-                return False
-
-            if not filename.endswith('.tar.gz'):
-                log.error(
-                    "Filename %s doesn't have .tar.gz extension", filename)
-                return False
-
-            path = pjoin(td, filename)
-            if isfile(path):
-                log.info("Output file %s exists", path)
-            else:
-                log.error("Output file %s does not exist", path)
-                return False
-
-            if tarfile.is_tarfile(path):
-                log.info("Output file is a tar file")
-            else:
-                log.error("Output file is not a tar file")
-                return False
-
-        finally:
-            shutil.rmtree(td)
-
-        return True
-
-
-def check_build_wheel(hooks, build_sys_requires):
-    with BuildEnvironment() as env:
-        try:
-            env.pip_install(build_sys_requires)
-            log.info('Installed static build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install static build dependencies')
-            return False
-
-        try:
-            reqs = hooks.get_requires_for_build_wheel({})
-            log.info('Got build requires: %s', reqs)
-        except Exception:
-            log.error('Failure in get_requires_for_build_sdist', exc_info=True)
-            return False
-
-        try:
-            env.pip_install(reqs)
-            log.info('Installed dynamic build dependencies')
-        except CalledProcessError:
-            log.error('Failed to install dynamic build dependencies')
-            return False
-
-        td = mkdtemp()
-        log.info('Trying to build wheel in %s', td)
-        try:
-            try:
-                filename = hooks.build_wheel(td, {})
-                log.info('build_wheel returned %r', filename)
-            except Exception:
-                log.info('Failure in build_wheel', exc_info=True)
-                return False
-
-            if not filename.endswith('.whl'):
-                log.error("Filename %s doesn't have .whl extension", filename)
-                return False
-
-            path = pjoin(td, filename)
-            if isfile(path):
-                log.info("Output file %s exists", path)
-            else:
-                log.error("Output file %s does not exist", path)
-                return False
-
-            if zipfile.is_zipfile(path):
-                log.info("Output file is a zip file")
-            else:
-                log.error("Output file is not a zip file")
-                return False
-
-        finally:
-            shutil.rmtree(td)
-
-        return True
-
-
-def check(source_dir):
-    pyproject = pjoin(source_dir, 'pyproject.toml')
-    if isfile(pyproject):
-        log.info('Found pyproject.toml')
-    else:
-        log.error('Missing pyproject.toml')
-        return False
-
-    try:
-        with open(pyproject, 'rb') as f:
-            pyproject_data = tomllib.load(f)
-        # Ensure the mandatory data can be loaded
-        buildsys = pyproject_data['build-system']
-        requires = buildsys['requires']
-        backend = buildsys['build-backend']
-        backend_path = buildsys.get('backend-path')
-        log.info('Loaded pyproject.toml')
-    except (tomllib.TOMLDecodeError, KeyError):
-        log.error("Invalid pyproject.toml", exc_info=True)
-        return False
-
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    sdist_ok = check_build_sdist(hooks, requires)
-    wheel_ok = check_build_wheel(hooks, requires)
-
-    if not sdist_ok:
-        log.warning('Sdist checks failed; scroll up to see')
-    if not wheel_ok:
-        log.warning('Wheel checks failed')
-
-    return sdist_ok
-
-
-def main(argv=None):
-    log.warning('pep517.check is deprecated. '
-                'Consider switching to https://pypi.org/project/build/')
-
-    ap = argparse.ArgumentParser()
-    ap.add_argument(
-        'source_dir',
-        help="A directory containing pyproject.toml")
-    args = ap.parse_args(argv)
-
-    enable_colourful_output()
-
-    ok = check(args.source_dir)
-
-    if ok:
-        print(ansi('Checks passed', 'green'))
-    else:
-        print(ansi('Checks failed', 'red'))
-        sys.exit(1)
-
-
-ansi_codes = {
-    'reset': '\x1b[0m',
-    'bold': '\x1b[1m',
-    'red': '\x1b[31m',
-    'green': '\x1b[32m',
-}
-
-
-def ansi(s, attr):
-    if os.name != 'nt' and sys.stdout.isatty():
-        return ansi_codes[attr] + str(s) + ansi_codes['reset']
-    else:
-        return str(s)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py b/.venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py
deleted file mode 100644
index 66310a7..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/colorlog.py
+++ /dev/null
@@ -1,113 +0,0 @@
-"""Nicer log formatting with colours.
-
-Code copied from Tornado, Apache licensed.
-"""
-# Copyright 2012 Facebook
-#
-# 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.
-
-import logging
-import sys
-
-try:
-    import curses
-except ImportError:
-    curses = None
-
-
-def _stderr_supports_color():
-    color = False
-    if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
-        try:
-            curses.setupterm()
-            if curses.tigetnum("colors") > 0:
-                color = True
-        except Exception:
-            pass
-    return color
-
-
-class LogFormatter(logging.Formatter):
-    """Log formatter with colour support
-    """
-    DEFAULT_COLORS = {
-        logging.INFO: 2,  # Green
-        logging.WARNING: 3,  # Yellow
-        logging.ERROR: 1,  # Red
-        logging.CRITICAL: 1,
-    }
-
-    def __init__(self, color=True, datefmt=None):
-        r"""
-        :arg bool color: Enables color support.
-        :arg string fmt: Log message format.
-        It will be applied to the attributes dict of log records. The
-        text between ``%(color)s`` and ``%(end_color)s`` will be colored
-        depending on the level if color support is on.
-        :arg dict colors: color mappings from logging level to terminal color
-        code
-        :arg string datefmt: Datetime format.
-        Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
-        .. versionchanged:: 3.2
-        Added ``fmt`` and ``datefmt`` arguments.
-        """
-        logging.Formatter.__init__(self, datefmt=datefmt)
-        self._colors = {}
-        if color and _stderr_supports_color():
-            # The curses module has some str/bytes confusion in
-            # python3. Until version 3.2.3, most methods return
-            # bytes, but only accept strings. In addition, we want to
-            # output these strings with the logging module, which
-            # works with unicode strings. The explicit calls to
-            # unicode() below are harmless in python2 but will do the
-            # right conversion in python 3.
-            fg_color = (curses.tigetstr("setaf") or
-                        curses.tigetstr("setf") or "")
-
-            for levelno, code in self.DEFAULT_COLORS.items():
-                self._colors[levelno] = str(
-                    curses.tparm(fg_color, code), "ascii")
-            self._normal = str(curses.tigetstr("sgr0"), "ascii")
-
-            scr = curses.initscr()
-            self.termwidth = scr.getmaxyx()[1]
-            curses.endwin()
-        else:
-            self._normal = ''
-            # Default width is usually 80, but too wide is
-            # worse than too narrow
-            self.termwidth = 70
-
-    def formatMessage(self, record):
-        mlen = len(record.message)
-        right_text = '{initial}-{name}'.format(initial=record.levelname[0],
-                                               name=record.name)
-        if mlen + len(right_text) < self.termwidth:
-            space = ' ' * (self.termwidth - (mlen + len(right_text)))
-        else:
-            space = '  '
-
-        if record.levelno in self._colors:
-            start_color = self._colors[record.levelno]
-            end_color = self._normal
-        else:
-            start_color = end_color = ''
-
-        return record.message + space + start_color + right_text + end_color
-
-
-def enable_colourful_output(level=logging.INFO):
-    handler = logging.StreamHandler()
-    handler.setFormatter(LogFormatter())
-    logging.root.addHandler(handler)
-    logging.root.setLevel(level)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/dirtools.py b/.venv/Lib/site-packages/pip/_vendor/pep517/dirtools.py
deleted file mode 100644
index 3eff4d8..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/dirtools.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import io
-import os
-import zipfile
-
-
-def dir_to_zipfile(root):
-    """Construct an in-memory zip file for a directory."""
-    buffer = io.BytesIO()
-    zip_file = zipfile.ZipFile(buffer, 'w')
-    for root, dirs, files in os.walk(root):
-        for path in dirs:
-            fs_path = os.path.join(root, path)
-            rel_path = os.path.relpath(fs_path, root)
-            zip_file.writestr(rel_path + '/', '')
-        for path in files:
-            fs_path = os.path.join(root, path)
-            rel_path = os.path.relpath(fs_path, root)
-            zip_file.write(fs_path, rel_path)
-    return zip_file
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py b/.venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py
deleted file mode 100644
index c0415c4..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/envbuild.py
+++ /dev/null
@@ -1,170 +0,0 @@
-"""Build wheels/sdists by installing build deps to a temporary environment.
-"""
-
-import logging
-import os
-import shutil
-import sys
-from subprocess import check_call
-from sysconfig import get_paths
-from tempfile import mkdtemp
-
-from ._compat import tomllib
-from .wrappers import LoggerWrapper, Pep517HookCaller
-
-log = logging.getLogger(__name__)
-
-
-def _load_pyproject(source_dir):
-    with open(
-            os.path.join(source_dir, 'pyproject.toml'),
-            'rb',
-            ) as f:
-        pyproject_data = tomllib.load(f)
-    buildsys = pyproject_data['build-system']
-    return (
-        buildsys['requires'],
-        buildsys['build-backend'],
-        buildsys.get('backend-path'),
-    )
-
-
-class BuildEnvironment:
-    """Context manager to install build deps in a simple temporary environment
-
-    Based on code I wrote for pip, which is MIT licensed.
-    """
-    # Copyright (c) 2008-2016 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.
-
-    path = None
-
-    def __init__(self, cleanup=True):
-        self._cleanup = cleanup
-
-    def __enter__(self):
-        self.path = mkdtemp(prefix='pep517-build-env-')
-        log.info('Temporary build environment: %s', self.path)
-
-        self.save_path = os.environ.get('PATH', None)
-        self.save_pythonpath = os.environ.get('PYTHONPATH', None)
-
-        install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix'
-        install_dirs = get_paths(install_scheme, vars={
-            'base': self.path,
-            'platbase': self.path,
-        })
-
-        scripts = install_dirs['scripts']
-        if self.save_path:
-            os.environ['PATH'] = scripts + os.pathsep + self.save_path
-        else:
-            os.environ['PATH'] = scripts + os.pathsep + os.defpath
-
-        if install_dirs['purelib'] == install_dirs['platlib']:
-            lib_dirs = install_dirs['purelib']
-        else:
-            lib_dirs = install_dirs['purelib'] + os.pathsep + \
-                install_dirs['platlib']
-        if self.save_pythonpath:
-            os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \
-                self.save_pythonpath
-        else:
-            os.environ['PYTHONPATH'] = lib_dirs
-
-        return self
-
-    def pip_install(self, reqs):
-        """Install dependencies into this env by calling pip in a subprocess"""
-        if not reqs:
-            return
-        log.info('Calling pip to install %s', reqs)
-        cmd = [
-            sys.executable, '-m', 'pip', 'install', '--ignore-installed',
-            '--prefix', self.path] + list(reqs)
-        check_call(
-            cmd,
-            stdout=LoggerWrapper(log, logging.INFO),
-            stderr=LoggerWrapper(log, logging.ERROR),
-        )
-
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        needs_cleanup = (
-            self._cleanup and
-            self.path is not None and
-            os.path.isdir(self.path)
-        )
-        if needs_cleanup:
-            shutil.rmtree(self.path)
-
-        if self.save_path is None:
-            os.environ.pop('PATH', None)
-        else:
-            os.environ['PATH'] = self.save_path
-
-        if self.save_pythonpath is None:
-            os.environ.pop('PYTHONPATH', None)
-        else:
-            os.environ['PYTHONPATH'] = self.save_pythonpath
-
-
-def build_wheel(source_dir, wheel_dir, config_settings=None):
-    """Build a wheel from a source directory using PEP 517 hooks.
-
-    :param str source_dir: Source directory containing pyproject.toml
-    :param str wheel_dir: Target directory to create wheel in
-    :param dict config_settings: Options to pass to build backend
-
-    This is a blocking function which will run pip in a subprocess to install
-    build requirements.
-    """
-    if config_settings is None:
-        config_settings = {}
-    requires, backend, backend_path = _load_pyproject(source_dir)
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    with BuildEnvironment() as env:
-        env.pip_install(requires)
-        reqs = hooks.get_requires_for_build_wheel(config_settings)
-        env.pip_install(reqs)
-        return hooks.build_wheel(wheel_dir, config_settings)
-
-
-def build_sdist(source_dir, sdist_dir, config_settings=None):
-    """Build an sdist from a source directory using PEP 517 hooks.
-
-    :param str source_dir: Source directory containing pyproject.toml
-    :param str sdist_dir: Target directory to place sdist in
-    :param dict config_settings: Options to pass to build backend
-
-    This is a blocking function which will run pip in a subprocess to install
-    build requirements.
-    """
-    if config_settings is None:
-        config_settings = {}
-    requires, backend, backend_path = _load_pyproject(source_dir)
-    hooks = Pep517HookCaller(source_dir, backend, backend_path)
-
-    with BuildEnvironment() as env:
-        env.pip_install(requires)
-        reqs = hooks.get_requires_for_build_sdist(config_settings)
-        env.pip_install(reqs)
-        return hooks.build_sdist(sdist_dir, config_settings)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py
deleted file mode 100644
index 281a356..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""This is a subpackage because the directory is on sys.path for _in_process.py
-
-The subpackage should stay as empty as possible to avoid shadowing modules that
-the backend might import.
-"""
-from contextlib import contextmanager
-from os.path import abspath, dirname
-from os.path import join as pjoin
-
-try:
-    import importlib.resources as resources
-    try:
-        resources.files
-    except AttributeError:
-        # Python 3.8 compatibility
-        def _in_proc_script_path():
-            return resources.path(__package__, '_in_process.py')
-    else:
-        def _in_proc_script_path():
-            return resources.as_file(
-                resources.files(__package__).joinpath('_in_process.py'))
-except ImportError:
-    # Python 3.6 compatibility
-    @contextmanager
-    def _in_proc_script_path():
-        yield pjoin(dirname(abspath(__file__)), '_in_process.py')
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py b/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py
deleted file mode 100644
index ae4cf9e..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py
+++ /dev/null
@@ -1,351 +0,0 @@
-"""This is invoked in a subprocess to call the build backend hooks.
-
-It expects:
-- Command line args: hook_name, control_dir
-- Environment variables:
-      PEP517_BUILD_BACKEND=entry.point:spec
-      PEP517_BACKEND_PATH=paths (separated with os.pathsep)
-- control_dir/input.json:
-  - {"kwargs": {...}}
-
-Results:
-- control_dir/output.json
-  - {"return_val": ...}
-"""
-import json
-import os
-import os.path
-import re
-import shutil
-import sys
-import traceback
-from glob import glob
-from importlib import import_module
-from os.path import join as pjoin
-
-# This file is run as a script, and `import wrappers` is not zip-safe, so we
-# include write_json() and read_json() from wrappers.py.
-
-
-def write_json(obj, path, **kwargs):
-    with open(path, 'w', encoding='utf-8') as f:
-        json.dump(obj, f, **kwargs)
-
-
-def read_json(path):
-    with open(path, encoding='utf-8') as f:
-        return json.load(f)
-
-
-class BackendUnavailable(Exception):
-    """Raised if we cannot import the backend"""
-    def __init__(self, traceback):
-        self.traceback = traceback
-
-
-class BackendInvalid(Exception):
-    """Raised if the backend is invalid"""
-    def __init__(self, message):
-        self.message = message
-
-
-class HookMissing(Exception):
-    """Raised if a hook is missing and we are not executing the fallback"""
-    def __init__(self, hook_name=None):
-        super().__init__(hook_name)
-        self.hook_name = hook_name
-
-
-def contained_in(filename, directory):
-    """Test if a file is located within the given directory."""
-    filename = os.path.normcase(os.path.abspath(filename))
-    directory = os.path.normcase(os.path.abspath(directory))
-    return os.path.commonprefix([filename, directory]) == directory
-
-
-def _build_backend():
-    """Find and load the build backend"""
-    # Add in-tree backend directories to the front of sys.path.
-    backend_path = os.environ.get('PEP517_BACKEND_PATH')
-    if backend_path:
-        extra_pathitems = backend_path.split(os.pathsep)
-        sys.path[:0] = extra_pathitems
-
-    ep = os.environ['PEP517_BUILD_BACKEND']
-    mod_path, _, obj_path = ep.partition(':')
-    try:
-        obj = import_module(mod_path)
-    except ImportError:
-        raise BackendUnavailable(traceback.format_exc())
-
-    if backend_path:
-        if not any(
-            contained_in(obj.__file__, path)
-            for path in extra_pathitems
-        ):
-            raise BackendInvalid("Backend was not loaded from backend-path")
-
-    if obj_path:
-        for path_part in obj_path.split('.'):
-            obj = getattr(obj, path_part)
-    return obj
-
-
-def _supported_features():
-    """Return the list of options features supported by the backend.
-
-    Returns a list of strings.
-    The only possible value is 'build_editable'.
-    """
-    backend = _build_backend()
-    features = []
-    if hasattr(backend, "build_editable"):
-        features.append("build_editable")
-    return features
-
-
-def get_requires_for_build_wheel(config_settings):
-    """Invoke the optional get_requires_for_build_wheel hook
-
-    Returns [] if the hook is not defined.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.get_requires_for_build_wheel
-    except AttributeError:
-        return []
-    else:
-        return hook(config_settings)
-
-
-def get_requires_for_build_editable(config_settings):
-    """Invoke the optional get_requires_for_build_editable hook
-
-    Returns [] if the hook is not defined.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.get_requires_for_build_editable
-    except AttributeError:
-        return []
-    else:
-        return hook(config_settings)
-
-
-def prepare_metadata_for_build_wheel(
-        metadata_directory, config_settings, _allow_fallback):
-    """Invoke optional prepare_metadata_for_build_wheel
-
-    Implements a fallback by building a wheel if the hook isn't defined,
-    unless _allow_fallback is False in which case HookMissing is raised.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.prepare_metadata_for_build_wheel
-    except AttributeError:
-        if not _allow_fallback:
-            raise HookMissing()
-        whl_basename = backend.build_wheel(metadata_directory, config_settings)
-        return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory,
-                                              config_settings)
-    else:
-        return hook(metadata_directory, config_settings)
-
-
-def prepare_metadata_for_build_editable(
-        metadata_directory, config_settings, _allow_fallback):
-    """Invoke optional prepare_metadata_for_build_editable
-
-    Implements a fallback by building an editable wheel if the hook isn't
-    defined, unless _allow_fallback is False in which case HookMissing is
-    raised.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.prepare_metadata_for_build_editable
-    except AttributeError:
-        if not _allow_fallback:
-            raise HookMissing()
-        try:
-            build_hook = backend.build_editable
-        except AttributeError:
-            raise HookMissing(hook_name='build_editable')
-        else:
-            whl_basename = build_hook(metadata_directory, config_settings)
-            return _get_wheel_metadata_from_wheel(whl_basename,
-                                                  metadata_directory,
-                                                  config_settings)
-    else:
-        return hook(metadata_directory, config_settings)
-
-
-WHEEL_BUILT_MARKER = 'PEP517_ALREADY_BUILT_WHEEL'
-
-
-def _dist_info_files(whl_zip):
-    """Identify the .dist-info folder inside a wheel ZipFile."""
-    res = []
-    for path in whl_zip.namelist():
-        m = re.match(r'[^/\\]+-[^/\\]+\.dist-info/', path)
-        if m:
-            res.append(path)
-    if res:
-        return res
-    raise Exception("No .dist-info folder found in wheel")
-
-
-def _get_wheel_metadata_from_wheel(
-        whl_basename, metadata_directory, config_settings):
-    """Extract the metadata from a wheel.
-
-    Fallback for when the build backend does not
-    define the 'get_wheel_metadata' hook.
-    """
-    from zipfile import ZipFile
-    with open(os.path.join(metadata_directory, WHEEL_BUILT_MARKER), 'wb'):
-        pass  # Touch marker file
-
-    whl_file = os.path.join(metadata_directory, whl_basename)
-    with ZipFile(whl_file) as zipf:
-        dist_info = _dist_info_files(zipf)
-        zipf.extractall(path=metadata_directory, members=dist_info)
-    return dist_info[0].split('/')[0]
-
-
-def _find_already_built_wheel(metadata_directory):
-    """Check for a wheel already built during the get_wheel_metadata hook.
-    """
-    if not metadata_directory:
-        return None
-    metadata_parent = os.path.dirname(metadata_directory)
-    if not os.path.isfile(pjoin(metadata_parent, WHEEL_BUILT_MARKER)):
-        return None
-
-    whl_files = glob(os.path.join(metadata_parent, '*.whl'))
-    if not whl_files:
-        print('Found wheel built marker, but no .whl files')
-        return None
-    if len(whl_files) > 1:
-        print('Found multiple .whl files; unspecified behaviour. '
-              'Will call build_wheel.')
-        return None
-
-    # Exactly one .whl file
-    return whl_files[0]
-
-
-def build_wheel(wheel_directory, config_settings, metadata_directory=None):
-    """Invoke the mandatory build_wheel hook.
-
-    If a wheel was already built in the
-    prepare_metadata_for_build_wheel fallback, this
-    will copy it rather than rebuilding the wheel.
-    """
-    prebuilt_whl = _find_already_built_wheel(metadata_directory)
-    if prebuilt_whl:
-        shutil.copy2(prebuilt_whl, wheel_directory)
-        return os.path.basename(prebuilt_whl)
-
-    return _build_backend().build_wheel(wheel_directory, config_settings,
-                                        metadata_directory)
-
-
-def build_editable(wheel_directory, config_settings, metadata_directory=None):
-    """Invoke the optional build_editable hook.
-
-    If a wheel was already built in the
-    prepare_metadata_for_build_editable fallback, this
-    will copy it rather than rebuilding the wheel.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.build_editable
-    except AttributeError:
-        raise HookMissing()
-    else:
-        prebuilt_whl = _find_already_built_wheel(metadata_directory)
-        if prebuilt_whl:
-            shutil.copy2(prebuilt_whl, wheel_directory)
-            return os.path.basename(prebuilt_whl)
-
-        return hook(wheel_directory, config_settings, metadata_directory)
-
-
-def get_requires_for_build_sdist(config_settings):
-    """Invoke the optional get_requires_for_build_wheel hook
-
-    Returns [] if the hook is not defined.
-    """
-    backend = _build_backend()
-    try:
-        hook = backend.get_requires_for_build_sdist
-    except AttributeError:
-        return []
-    else:
-        return hook(config_settings)
-
-
-class _DummyException(Exception):
-    """Nothing should ever raise this exception"""
-
-
-class GotUnsupportedOperation(Exception):
-    """For internal use when backend raises UnsupportedOperation"""
-    def __init__(self, traceback):
-        self.traceback = traceback
-
-
-def build_sdist(sdist_directory, config_settings):
-    """Invoke the mandatory build_sdist hook."""
-    backend = _build_backend()
-    try:
-        return backend.build_sdist(sdist_directory, config_settings)
-    except getattr(backend, 'UnsupportedOperation', _DummyException):
-        raise GotUnsupportedOperation(traceback.format_exc())
-
-
-HOOK_NAMES = {
-    'get_requires_for_build_wheel',
-    'prepare_metadata_for_build_wheel',
-    'build_wheel',
-    'get_requires_for_build_editable',
-    'prepare_metadata_for_build_editable',
-    'build_editable',
-    'get_requires_for_build_sdist',
-    'build_sdist',
-    '_supported_features',
-}
-
-
-def main():
-    if len(sys.argv) < 3:
-        sys.exit("Needs args: hook_name, control_dir")
-    hook_name = sys.argv[1]
-    control_dir = sys.argv[2]
-    if hook_name not in HOOK_NAMES:
-        sys.exit("Unknown hook: %s" % hook_name)
-    hook = globals()[hook_name]
-
-    hook_input = read_json(pjoin(control_dir, 'input.json'))
-
-    json_out = {'unsupported': False, 'return_val': None}
-    try:
-        json_out['return_val'] = hook(**hook_input['kwargs'])
-    except BackendUnavailable as e:
-        json_out['no_backend'] = True
-        json_out['traceback'] = e.traceback
-    except BackendInvalid as e:
-        json_out['backend_invalid'] = True
-        json_out['backend_error'] = e.message
-    except GotUnsupportedOperation as e:
-        json_out['unsupported'] = True
-        json_out['traceback'] = e.traceback
-    except HookMissing as e:
-        json_out['hook_missing'] = True
-        json_out['missing_hook_name'] = e.hook_name or hook_name
-
-    write_json(json_out, pjoin(control_dir, 'output.json'), indent=2)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/meta.py b/.venv/Lib/site-packages/pip/_vendor/pep517/meta.py
deleted file mode 100644
index 4afc3c0..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/meta.py
+++ /dev/null
@@ -1,93 +0,0 @@
-"""Build metadata for a project using PEP 517 hooks.
-"""
-import argparse
-import functools
-import logging
-import os
-import shutil
-import tempfile
-
-try:
-    import importlib.metadata as imp_meta
-except ImportError:
-    import importlib_metadata as imp_meta
-
-try:
-    from zipfile import Path
-except ImportError:
-    from zipp import Path
-
-from .build import compat_system, load_system, validate_system
-from .dirtools import dir_to_zipfile
-from .envbuild import BuildEnvironment
-from .wrappers import Pep517HookCaller, quiet_subprocess_runner
-
-log = logging.getLogger(__name__)
-
-
-def _prep_meta(hooks, env, dest):
-    reqs = hooks.get_requires_for_build_wheel({})
-    log.info('Got build requires: %s', reqs)
-
-    env.pip_install(reqs)
-    log.info('Installed dynamic build dependencies')
-
-    with tempfile.TemporaryDirectory() as td:
-        log.info('Trying to build metadata in %s', td)
-        filename = hooks.prepare_metadata_for_build_wheel(td, {})
-        source = os.path.join(td, filename)
-        shutil.move(source, os.path.join(dest, os.path.basename(filename)))
-
-
-def build(source_dir='.', dest=None, system=None):
-    system = system or load_system(source_dir)
-    dest = os.path.join(source_dir, dest or 'dist')
-    os.makedirs(dest, exist_ok=True)
-    validate_system(system)
-    hooks = Pep517HookCaller(
-        source_dir, system['build-backend'], system.get('backend-path')
-    )
-
-    with hooks.subprocess_runner(quiet_subprocess_runner):
-        with BuildEnvironment() as env:
-            env.pip_install(system['requires'])
-            _prep_meta(hooks, env, dest)
-
-
-def build_as_zip(builder=build):
-    with tempfile.TemporaryDirectory() as out_dir:
-        builder(dest=out_dir)
-        return dir_to_zipfile(out_dir)
-
-
-def load(root):
-    """
-    Given a source directory (root) of a package,
-    return an importlib.metadata.Distribution object
-    with metadata build from that package.
-    """
-    root = os.path.expanduser(root)
-    system = compat_system(root)
-    builder = functools.partial(build, source_dir=root, system=system)
-    path = Path(build_as_zip(builder))
-    return imp_meta.PathDistribution(path)
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    'source_dir',
-    help="A directory containing pyproject.toml",
-)
-parser.add_argument(
-    '--out-dir', '-o',
-    help="Destination in which to save the builds relative to source dir",
-)
-
-
-def main():
-    args = parser.parse_args()
-    build(args.source_dir, args.out_dir)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/.venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py b/.venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py
deleted file mode 100644
index 987a62a..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pep517/wrappers.py
+++ /dev/null
@@ -1,362 +0,0 @@
-import json
-import os
-import sys
-import tempfile
-import threading
-from contextlib import contextmanager
-from os.path import abspath
-from os.path import join as pjoin
-from subprocess import STDOUT, check_call, check_output
-
-from .in_process import _in_proc_script_path
-
-__all__ = [
-    'BackendUnavailable',
-    'BackendInvalid',
-    'HookMissing',
-    'UnsupportedOperation',
-    'default_subprocess_runner',
-    'quiet_subprocess_runner',
-    'Pep517HookCaller',
-]
-
-
-def write_json(obj, path, **kwargs):
-    with open(path, 'w', encoding='utf-8') as f:
-        json.dump(obj, f, **kwargs)
-
-
-def read_json(path):
-    with open(path, encoding='utf-8') as f:
-        return json.load(f)
-
-
-class BackendUnavailable(Exception):
-    """Will be raised if the backend cannot be imported in the hook process."""
-    def __init__(self, traceback):
-        self.traceback = traceback
-
-
-class BackendInvalid(Exception):
-    """Will be raised if the backend is invalid."""
-    def __init__(self, backend_name, backend_path, message):
-        self.backend_name = backend_name
-        self.backend_path = backend_path
-        self.message = message
-
-
-class HookMissing(Exception):
-    """Will be raised on missing hooks."""
-    def __init__(self, hook_name):
-        super().__init__(hook_name)
-        self.hook_name = hook_name
-
-
-class UnsupportedOperation(Exception):
-    """May be raised by build_sdist if the backend indicates that it can't."""
-    def __init__(self, traceback):
-        self.traceback = traceback
-
-
-def default_subprocess_runner(cmd, cwd=None, extra_environ=None):
-    """The default method of calling the wrapper subprocess."""
-    env = os.environ.copy()
-    if extra_environ:
-        env.update(extra_environ)
-
-    check_call(cmd, cwd=cwd, env=env)
-
-
-def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None):
-    """A method of calling the wrapper subprocess while suppressing output."""
-    env = os.environ.copy()
-    if extra_environ:
-        env.update(extra_environ)
-
-    check_output(cmd, cwd=cwd, env=env, stderr=STDOUT)
-
-
-def norm_and_check(source_tree, requested):
-    """Normalise and check a backend path.
-
-    Ensure that the requested backend path is specified as a relative path,
-    and resolves to a location under the given source tree.
-
-    Return an absolute version of the requested path.
-    """
-    if os.path.isabs(requested):
-        raise ValueError("paths must be relative")
-
-    abs_source = os.path.abspath(source_tree)
-    abs_requested = os.path.normpath(os.path.join(abs_source, requested))
-    # We have to use commonprefix for Python 2.7 compatibility. So we
-    # normalise case to avoid problems because commonprefix is a character
-    # based comparison :-(
-    norm_source = os.path.normcase(abs_source)
-    norm_requested = os.path.normcase(abs_requested)
-    if os.path.commonprefix([norm_source, norm_requested]) != norm_source:
-        raise ValueError("paths must be inside source tree")
-
-    return abs_requested
-
-
-class Pep517HookCaller:
-    """A wrapper around a source directory to be built with a PEP 517 backend.
-
-    :param source_dir: The path to the source directory, containing
-        pyproject.toml.
-    :param build_backend: The build backend spec, as per PEP 517, from
-        pyproject.toml.
-    :param backend_path: The backend path, as per PEP 517, from pyproject.toml.
-    :param runner: A callable that invokes the wrapper subprocess.
-    :param python_executable: The Python executable used to invoke the backend
-
-    The 'runner', if provided, must expect the following:
-
-    - cmd: a list of strings representing the command and arguments to
-      execute, as would be passed to e.g. 'subprocess.check_call'.
-    - cwd: a string representing the working directory that must be
-      used for the subprocess. Corresponds to the provided source_dir.
-    - extra_environ: a dict mapping environment variable names to values
-      which must be set for the subprocess execution.
-    """
-    def __init__(
-            self,
-            source_dir,
-            build_backend,
-            backend_path=None,
-            runner=None,
-            python_executable=None,
-    ):
-        if runner is None:
-            runner = default_subprocess_runner
-
-        self.source_dir = abspath(source_dir)
-        self.build_backend = build_backend
-        if backend_path:
-            backend_path = [
-                norm_and_check(self.source_dir, p) for p in backend_path
-            ]
-        self.backend_path = backend_path
-        self._subprocess_runner = runner
-        if not python_executable:
-            python_executable = sys.executable
-        self.python_executable = python_executable
-
-    @contextmanager
-    def subprocess_runner(self, runner):
-        """A context manager for temporarily overriding the default subprocess
-        runner.
-        """
-        prev = self._subprocess_runner
-        self._subprocess_runner = runner
-        try:
-            yield
-        finally:
-            self._subprocess_runner = prev
-
-    def _supported_features(self):
-        """Return the list of optional features supported by the backend."""
-        return self._call_hook('_supported_features', {})
-
-    def get_requires_for_build_wheel(self, config_settings=None):
-        """Identify packages required for building a wheel
-
-        Returns a list of dependency specifications, e.g.::
-
-            ["wheel >= 0.25", "setuptools"]
-
-        This does not include requirements specified in pyproject.toml.
-        It returns the result of calling the equivalently named hook in a
-        subprocess.
-        """
-        return self._call_hook('get_requires_for_build_wheel', {
-            'config_settings': config_settings
-        })
-
-    def prepare_metadata_for_build_wheel(
-            self, metadata_directory, config_settings=None,
-            _allow_fallback=True):
-        """Prepare a ``*.dist-info`` folder with metadata for this project.
-
-        Returns the name of the newly created folder.
-
-        If the build backend defines a hook with this name, it will be called
-        in a subprocess. If not, the backend will be asked to build a wheel,
-        and the dist-info extracted from that (unless _allow_fallback is
-        False).
-        """
-        return self._call_hook('prepare_metadata_for_build_wheel', {
-            'metadata_directory': abspath(metadata_directory),
-            'config_settings': config_settings,
-            '_allow_fallback': _allow_fallback,
-        })
-
-    def build_wheel(
-            self, wheel_directory, config_settings=None,
-            metadata_directory=None):
-        """Build a wheel from this project.
-
-        Returns the name of the newly created file.
-
-        In general, this will call the 'build_wheel' hook in the backend.
-        However, if that was previously called by
-        'prepare_metadata_for_build_wheel', and the same metadata_directory is
-        used, the previously built wheel will be copied to wheel_directory.
-        """
-        if metadata_directory is not None:
-            metadata_directory = abspath(metadata_directory)
-        return self._call_hook('build_wheel', {
-            'wheel_directory': abspath(wheel_directory),
-            'config_settings': config_settings,
-            'metadata_directory': metadata_directory,
-        })
-
-    def get_requires_for_build_editable(self, config_settings=None):
-        """Identify packages required for building an editable wheel
-
-        Returns a list of dependency specifications, e.g.::
-
-            ["wheel >= 0.25", "setuptools"]
-
-        This does not include requirements specified in pyproject.toml.
-        It returns the result of calling the equivalently named hook in a
-        subprocess.
-        """
-        return self._call_hook('get_requires_for_build_editable', {
-            'config_settings': config_settings
-        })
-
-    def prepare_metadata_for_build_editable(
-            self, metadata_directory, config_settings=None,
-            _allow_fallback=True):
-        """Prepare a ``*.dist-info`` folder with metadata for this project.
-
-        Returns the name of the newly created folder.
-
-        If the build backend defines a hook with this name, it will be called
-        in a subprocess. If not, the backend will be asked to build an editable
-        wheel, and the dist-info extracted from that (unless _allow_fallback is
-        False).
-        """
-        return self._call_hook('prepare_metadata_for_build_editable', {
-            'metadata_directory': abspath(metadata_directory),
-            'config_settings': config_settings,
-            '_allow_fallback': _allow_fallback,
-        })
-
-    def build_editable(
-            self, wheel_directory, config_settings=None,
-            metadata_directory=None):
-        """Build an editable wheel from this project.
-
-        Returns the name of the newly created file.
-
-        In general, this will call the 'build_editable' hook in the backend.
-        However, if that was previously called by
-        'prepare_metadata_for_build_editable', and the same metadata_directory
-        is used, the previously built wheel will be copied to wheel_directory.
-        """
-        if metadata_directory is not None:
-            metadata_directory = abspath(metadata_directory)
-        return self._call_hook('build_editable', {
-            'wheel_directory': abspath(wheel_directory),
-            'config_settings': config_settings,
-            'metadata_directory': metadata_directory,
-        })
-
-    def get_requires_for_build_sdist(self, config_settings=None):
-        """Identify packages required for building a wheel
-
-        Returns a list of dependency specifications, e.g.::
-
-            ["setuptools >= 26"]
-
-        This does not include requirements specified in pyproject.toml.
-        It returns the result of calling the equivalently named hook in a
-        subprocess.
-        """
-        return self._call_hook('get_requires_for_build_sdist', {
-            'config_settings': config_settings
-        })
-
-    def build_sdist(self, sdist_directory, config_settings=None):
-        """Build an sdist from this project.
-
-        Returns the name of the newly created file.
-
-        This calls the 'build_sdist' backend hook in a subprocess.
-        """
-        return self._call_hook('build_sdist', {
-            'sdist_directory': abspath(sdist_directory),
-            'config_settings': config_settings,
-        })
-
-    def _call_hook(self, hook_name, kwargs):
-        extra_environ = {'PEP517_BUILD_BACKEND': self.build_backend}
-
-        if self.backend_path:
-            backend_path = os.pathsep.join(self.backend_path)
-            extra_environ['PEP517_BACKEND_PATH'] = backend_path
-
-        with tempfile.TemporaryDirectory() as td:
-            hook_input = {'kwargs': kwargs}
-            write_json(hook_input, pjoin(td, 'input.json'), indent=2)
-
-            # Run the hook in a subprocess
-            with _in_proc_script_path() as script:
-                python = self.python_executable
-                self._subprocess_runner(
-                    [python, abspath(str(script)), hook_name, td],
-                    cwd=self.source_dir,
-                    extra_environ=extra_environ
-                )
-
-            data = read_json(pjoin(td, 'output.json'))
-            if data.get('unsupported'):
-                raise UnsupportedOperation(data.get('traceback', ''))
-            if data.get('no_backend'):
-                raise BackendUnavailable(data.get('traceback', ''))
-            if data.get('backend_invalid'):
-                raise BackendInvalid(
-                    backend_name=self.build_backend,
-                    backend_path=self.backend_path,
-                    message=data.get('backend_error', '')
-                )
-            if data.get('hook_missing'):
-                raise HookMissing(data.get('missing_hook_name') or hook_name)
-            return data['return_val']
-
-
-class LoggerWrapper(threading.Thread):
-    """
-    Read messages from a pipe and redirect them
-    to a logger (see python's logging module).
-    """
-
-    def __init__(self, logger, level):
-        threading.Thread.__init__(self)
-        self.daemon = True
-
-        self.logger = logger
-        self.level = level
-
-        # create the pipe and reader
-        self.fd_read, self.fd_write = os.pipe()
-        self.reader = os.fdopen(self.fd_read)
-
-        self.start()
-
-    def fileno(self):
-        return self.fd_write
-
-    @staticmethod
-    def remove_newline(msg):
-        return msg[:-1] if msg.endswith(os.linesep) else msg
-
-    def run(self):
-        for line in self.reader:
-            self._write(self.remove_newline(line))
-
-    def _write(self, message):
-        self.logger.log(self.level, message)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py
deleted file mode 100644
index 4cd562c..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pkg_resources/__init__.py
+++ /dev/null
@@ -1,3296 +0,0 @@
-# coding: utf-8
-"""
-Package resource API
---------------------
-
-A resource is a logical file contained within a package, or a logical
-subdirectory thereof.  The package resource API expects resource names
-to have their path parts separated with ``/``, *not* whatever the local
-path separator is.  Do not use os.path operations to manipulate resource
-names being passed into the API.
-
-The package resource API is designed to work with normal filesystem packages,
-.egg files, and unpacked .egg files.  It can also work in a limited way with
-.zip files and with custom PEP 302 loaders that support the ``get_data()``
-method.
-"""
-
-from __future__ import absolute_import
-
-import sys
-import os
-import io
-import time
-import re
-import types
-import zipfile
-import zipimport
-import warnings
-import stat
-import functools
-import pkgutil
-import operator
-import platform
-import collections
-import plistlib
-import email.parser
-import errno
-import tempfile
-import textwrap
-import itertools
-import inspect
-import ntpath
-import posixpath
-from pkgutil import get_importer
-
-try:
-    import _imp
-except ImportError:
-    # Python 3.2 compatibility
-    import imp as _imp
-
-try:
-    FileExistsError
-except NameError:
-    FileExistsError = OSError
-
-from pip._vendor import six
-from pip._vendor.six.moves import urllib, map, filter
-
-# capture these to bypass sandboxing
-from os import utime
-try:
-    from os import mkdir, rename, unlink
-    WRITE_SUPPORT = True
-except ImportError:
-    # no write support, probably under GAE
-    WRITE_SUPPORT = False
-
-from os import open as os_open
-from os.path import isdir, split
-
-try:
-    import importlib.machinery as importlib_machinery
-    # access attribute to force import under delayed import mechanisms.
-    importlib_machinery.__name__
-except ImportError:
-    importlib_machinery = None
-
-from . import py31compat
-from pip._vendor import platformdirs
-from pip._vendor import packaging
-__import__('pip._vendor.packaging.version')
-__import__('pip._vendor.packaging.specifiers')
-__import__('pip._vendor.packaging.requirements')
-__import__('pip._vendor.packaging.markers')
-
-
-__metaclass__ = type
-
-
-if (3, 0) < sys.version_info < (3, 5):
-    raise RuntimeError("Python 3.5 or later is required")
-
-if six.PY2:
-    # Those builtin exceptions are only defined in Python 3
-    PermissionError = None
-    NotADirectoryError = None
-
-# declare some globals that will be defined later to
-# satisfy the linters.
-require = None
-working_set = None
-add_activation_listener = None
-resources_stream = None
-cleanup_resources = None
-resource_dir = None
-resource_stream = None
-set_extraction_path = None
-resource_isdir = None
-resource_string = None
-iter_entry_points = None
-resource_listdir = None
-resource_filename = None
-resource_exists = None
-_distribution_finders = None
-_namespace_handlers = None
-_namespace_packages = None
-
-
-class PEP440Warning(RuntimeWarning):
-    """
-    Used when there is an issue with a version or specifier not complying with
-    PEP 440.
-    """
-
-
-def parse_version(v):
-    try:
-        return packaging.version.Version(v)
-    except packaging.version.InvalidVersion:
-        return packaging.version.LegacyVersion(v)
-
-
-_state_vars = {}
-
-
-def _declare_state(vartype, **kw):
-    globals().update(kw)
-    _state_vars.update(dict.fromkeys(kw, vartype))
-
-
-def __getstate__():
-    state = {}
-    g = globals()
-    for k, v in _state_vars.items():
-        state[k] = g['_sget_' + v](g[k])
-    return state
-
-
-def __setstate__(state):
-    g = globals()
-    for k, v in state.items():
-        g['_sset_' + _state_vars[k]](k, g[k], v)
-    return state
-
-
-def _sget_dict(val):
-    return val.copy()
-
-
-def _sset_dict(key, ob, state):
-    ob.clear()
-    ob.update(state)
-
-
-def _sget_object(val):
-    return val.__getstate__()
-
-
-def _sset_object(key, ob, state):
-    ob.__setstate__(state)
-
-
-_sget_none = _sset_none = lambda *args: None
-
-
-def get_supported_platform():
-    """Return this platform's maximum compatible version.
-
-    distutils.util.get_platform() normally reports the minimum version
-    of Mac OS X that would be required to *use* extensions produced by
-    distutils.  But what we want when checking compatibility is to know the
-    version of Mac OS X that we are *running*.  To allow usage of packages that
-    explicitly require a newer version of Mac OS X, we must also know the
-    current version of the OS.
-
-    If this condition occurs for any other platform with a version in its
-    platform strings, this function should be extended accordingly.
-    """
-    plat = get_build_platform()
-    m = macosVersionString.match(plat)
-    if m is not None and sys.platform == "darwin":
-        try:
-            plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3))
-        except ValueError:
-            # not Mac OS X
-            pass
-    return plat
-
-
-__all__ = [
-    # Basic resource access and distribution/entry point discovery
-    'require', 'run_script', 'get_provider', 'get_distribution',
-    'load_entry_point', 'get_entry_map', 'get_entry_info',
-    'iter_entry_points',
-    'resource_string', 'resource_stream', 'resource_filename',
-    'resource_listdir', 'resource_exists', 'resource_isdir',
-
-    # Environmental control
-    'declare_namespace', 'working_set', 'add_activation_listener',
-    'find_distributions', 'set_extraction_path', 'cleanup_resources',
-    'get_default_cache',
-
-    # Primary implementation classes
-    'Environment', 'WorkingSet', 'ResourceManager',
-    'Distribution', 'Requirement', 'EntryPoint',
-
-    # Exceptions
-    'ResolutionError', 'VersionConflict', 'DistributionNotFound',
-    'UnknownExtra', 'ExtractionError',
-
-    # Warnings
-    'PEP440Warning',
-
-    # Parsing functions and string utilities
-    'parse_requirements', 'parse_version', 'safe_name', 'safe_version',
-    'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections',
-    'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker',
-
-    # filesystem utilities
-    'ensure_directory', 'normalize_path',
-
-    # Distribution "precedence" constants
-    'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST',
-
-    # "Provider" interfaces, implementations, and registration/lookup APIs
-    'IMetadataProvider', 'IResourceProvider', 'FileMetadata',
-    'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider',
-    'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider',
-    'register_finder', 'register_namespace_handler', 'register_loader_type',
-    'fixup_namespace_packages', 'get_importer',
-
-    # Warnings
-    'PkgResourcesDeprecationWarning',
-
-    # Deprecated/backward compatibility only
-    'run_main', 'AvailableDistributions',
-]
-
-
-class ResolutionError(Exception):
-    """Abstract base for dependency resolution errors"""
-
-    def __repr__(self):
-        return self.__class__.__name__ + repr(self.args)
-
-
-class VersionConflict(ResolutionError):
-    """
-    An already-installed version conflicts with the requested version.
-
-    Should be initialized with the installed Distribution and the requested
-    Requirement.
-    """
-
-    _template = "{self.dist} is installed but {self.req} is required"
-
-    @property
-    def dist(self):
-        return self.args[0]
-
-    @property
-    def req(self):
-        return self.args[1]
-
-    def report(self):
-        return self._template.format(**locals())
-
-    def with_context(self, required_by):
-        """
-        If required_by is non-empty, return a version of self that is a
-        ContextualVersionConflict.
-        """
-        if not required_by:
-            return self
-        args = self.args + (required_by,)
-        return ContextualVersionConflict(*args)
-
-
-class ContextualVersionConflict(VersionConflict):
-    """
-    A VersionConflict that accepts a third parameter, the set of the
-    requirements that required the installed Distribution.
-    """
-
-    _template = VersionConflict._template + ' by {self.required_by}'
-
-    @property
-    def required_by(self):
-        return self.args[2]
-
-
-class DistributionNotFound(ResolutionError):
-    """A requested distribution was not found"""
-
-    _template = ("The '{self.req}' distribution was not found "
-                 "and is required by {self.requirers_str}")
-
-    @property
-    def req(self):
-        return self.args[0]
-
-    @property
-    def requirers(self):
-        return self.args[1]
-
-    @property
-    def requirers_str(self):
-        if not self.requirers:
-            return 'the application'
-        return ', '.join(self.requirers)
-
-    def report(self):
-        return self._template.format(**locals())
-
-    def __str__(self):
-        return self.report()
-
-
-class UnknownExtra(ResolutionError):
-    """Distribution doesn't have an "extra feature" of the given name"""
-
-
-_provider_factories = {}
-
-PY_MAJOR = '{}.{}'.format(*sys.version_info)
-EGG_DIST = 3
-BINARY_DIST = 2
-SOURCE_DIST = 1
-CHECKOUT_DIST = 0
-DEVELOP_DIST = -1
-
-
-def register_loader_type(loader_type, provider_factory):
-    """Register `provider_factory` to make providers for `loader_type`
-
-    `loader_type` is the type or class of a PEP 302 ``module.__loader__``,
-    and `provider_factory` is a function that, passed a *module* object,
-    returns an ``IResourceProvider`` for that module.
-    """
-    _provider_factories[loader_type] = provider_factory
-
-
-def get_provider(moduleOrReq):
-    """Return an IResourceProvider for the named module or requirement"""
-    if isinstance(moduleOrReq, Requirement):
-        return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0]
-    try:
-        module = sys.modules[moduleOrReq]
-    except KeyError:
-        __import__(moduleOrReq)
-        module = sys.modules[moduleOrReq]
-    loader = getattr(module, '__loader__', None)
-    return _find_adapter(_provider_factories, loader)(module)
-
-
-def _macosx_vers(_cache=[]):
-    if not _cache:
-        version = platform.mac_ver()[0]
-        # fallback for MacPorts
-        if version == '':
-            plist = '/System/Library/CoreServices/SystemVersion.plist'
-            if os.path.exists(plist):
-                if hasattr(plistlib, 'readPlist'):
-                    plist_content = plistlib.readPlist(plist)
-                    if 'ProductVersion' in plist_content:
-                        version = plist_content['ProductVersion']
-
-        _cache.append(version.split('.'))
-    return _cache[0]
-
-
-def _macosx_arch(machine):
-    return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine)
-
-
-def get_build_platform():
-    """Return this platform's string for platform-specific distributions
-
-    XXX Currently this is the same as ``distutils.util.get_platform()``, but it
-    needs some hacks for Linux and Mac OS X.
-    """
-    from sysconfig import get_platform
-
-    plat = get_platform()
-    if sys.platform == "darwin" and not plat.startswith('macosx-'):
-        try:
-            version = _macosx_vers()
-            machine = os.uname()[4].replace(" ", "_")
-            return "macosx-%d.%d-%s" % (
-                int(version[0]), int(version[1]),
-                _macosx_arch(machine),
-            )
-        except ValueError:
-            # if someone is running a non-Mac darwin system, this will fall
-            # through to the default implementation
-            pass
-    return plat
-
-
-macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)")
-darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)")
-# XXX backward compat
-get_platform = get_build_platform
-
-
-def compatible_platforms(provided, required):
-    """Can code for the `provided` platform run on the `required` platform?
-
-    Returns true if either platform is ``None``, or the platforms are equal.
-
-    XXX Needs compatibility checks for Linux and other unixy OSes.
-    """
-    if provided is None or required is None or provided == required:
-        # easy case
-        return True
-
-    # Mac OS X special cases
-    reqMac = macosVersionString.match(required)
-    if reqMac:
-        provMac = macosVersionString.match(provided)
-
-        # is this a Mac package?
-        if not provMac:
-            # this is backwards compatibility for packages built before
-            # setuptools 0.6. All packages built after this point will
-            # use the new macosx designation.
-            provDarwin = darwinVersionString.match(provided)
-            if provDarwin:
-                dversion = int(provDarwin.group(1))
-                macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2))
-                if dversion == 7 and macosversion >= "10.3" or \
-                        dversion == 8 and macosversion >= "10.4":
-                    return True
-            # egg isn't macosx or legacy darwin
-            return False
-
-        # are they the same major version and machine type?
-        if provMac.group(1) != reqMac.group(1) or \
-                provMac.group(3) != reqMac.group(3):
-            return False
-
-        # is the required OS major update >= the provided one?
-        if int(provMac.group(2)) > int(reqMac.group(2)):
-            return False
-
-        return True
-
-    # XXX Linux and other platforms' special cases should go here
-    return False
-
-
-def run_script(dist_spec, script_name):
-    """Locate distribution `dist_spec` and run its `script_name` script"""
-    ns = sys._getframe(1).f_globals
-    name = ns['__name__']
-    ns.clear()
-    ns['__name__'] = name
-    require(dist_spec)[0].run_script(script_name, ns)
-
-
-# backward compatibility
-run_main = run_script
-
-
-def get_distribution(dist):
-    """Return a current distribution object for a Requirement or string"""
-    if isinstance(dist, six.string_types):
-        dist = Requirement.parse(dist)
-    if isinstance(dist, Requirement):
-        dist = get_provider(dist)
-    if not isinstance(dist, Distribution):
-        raise TypeError("Expected string, Requirement, or Distribution", dist)
-    return dist
-
-
-def load_entry_point(dist, group, name):
-    """Return `name` entry point of `group` for `dist` or raise ImportError"""
-    return get_distribution(dist).load_entry_point(group, name)
-
-
-def get_entry_map(dist, group=None):
-    """Return the entry point map for `group`, or the full entry map"""
-    return get_distribution(dist).get_entry_map(group)
-
-
-def get_entry_info(dist, group, name):
-    """Return the EntryPoint object for `group`+`name`, or ``None``"""
-    return get_distribution(dist).get_entry_info(group, name)
-
-
-class IMetadataProvider:
-    def has_metadata(name):
-        """Does the package's distribution contain the named metadata?"""
-
-    def get_metadata(name):
-        """The named metadata resource as a string"""
-
-    def get_metadata_lines(name):
-        """Yield named metadata resource as list of non-blank non-comment lines
-
-       Leading and trailing whitespace is stripped from each line, and lines
-       with ``#`` as the first non-blank character are omitted."""
-
-    def metadata_isdir(name):
-        """Is the named metadata a directory?  (like ``os.path.isdir()``)"""
-
-    def metadata_listdir(name):
-        """List of metadata names in the directory (like ``os.listdir()``)"""
-
-    def run_script(script_name, namespace):
-        """Execute the named script in the supplied namespace dictionary"""
-
-
-class IResourceProvider(IMetadataProvider):
-    """An object that provides access to package resources"""
-
-    def get_resource_filename(manager, resource_name):
-        """Return a true filesystem path for `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def get_resource_stream(manager, resource_name):
-        """Return a readable file-like object for `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def get_resource_string(manager, resource_name):
-        """Return a string containing the contents of `resource_name`
-
-        `manager` must be an ``IResourceManager``"""
-
-    def has_resource(resource_name):
-        """Does the package contain the named resource?"""
-
-    def resource_isdir(resource_name):
-        """Is the named resource a directory?  (like ``os.path.isdir()``)"""
-
-    def resource_listdir(resource_name):
-        """List of resource names in the directory (like ``os.listdir()``)"""
-
-
-class WorkingSet:
-    """A collection of active distributions on sys.path (or a similar list)"""
-
-    def __init__(self, entries=None):
-        """Create working set from list of path entries (default=sys.path)"""
-        self.entries = []
-        self.entry_keys = {}
-        self.by_key = {}
-        self.callbacks = []
-
-        if entries is None:
-            entries = sys.path
-
-        for entry in entries:
-            self.add_entry(entry)
-
-    @classmethod
-    def _build_master(cls):
-        """
-        Prepare the master working set.
-        """
-        ws = cls()
-        try:
-            from __main__ import __requires__
-        except ImportError:
-            # The main program does not list any requirements
-            return ws
-
-        # ensure the requirements are met
-        try:
-            ws.require(__requires__)
-        except VersionConflict:
-            return cls._build_from_requirements(__requires__)
-
-        return ws
-
-    @classmethod
-    def _build_from_requirements(cls, req_spec):
-        """
-        Build a working set from a requirement spec. Rewrites sys.path.
-        """
-        # try it without defaults already on sys.path
-        # by starting with an empty path
-        ws = cls([])
-        reqs = parse_requirements(req_spec)
-        dists = ws.resolve(reqs, Environment())
-        for dist in dists:
-            ws.add(dist)
-
-        # add any missing entries from sys.path
-        for entry in sys.path:
-            if entry not in ws.entries:
-                ws.add_entry(entry)
-
-        # then copy back to sys.path
-        sys.path[:] = ws.entries
-        return ws
-
-    def add_entry(self, entry):
-        """Add a path item to ``.entries``, finding any distributions on it
-
-        ``find_distributions(entry, True)`` is used to find distributions
-        corresponding to the path entry, and they are added.  `entry` is
-        always appended to ``.entries``, even if it is already present.
-        (This is because ``sys.path`` can contain the same value more than
-        once, and the ``.entries`` of the ``sys.path`` WorkingSet should always
-        equal ``sys.path``.)
-        """
-        self.entry_keys.setdefault(entry, [])
-        self.entries.append(entry)
-        for dist in find_distributions(entry, True):
-            self.add(dist, entry, False)
-
-    def __contains__(self, dist):
-        """True if `dist` is the active distribution for its project"""
-        return self.by_key.get(dist.key) == dist
-
-    def find(self, req):
-        """Find a distribution matching requirement `req`
-
-        If there is an active distribution for the requested project, this
-        returns it as long as it meets the version requirement specified by
-        `req`.  But, if there is an active distribution for the project and it
-        does *not* meet the `req` requirement, ``VersionConflict`` is raised.
-        If there is no active distribution for the requested project, ``None``
-        is returned.
-        """
-        dist = self.by_key.get(req.key)
-        if dist is not None and dist not in req:
-            # XXX add more info
-            raise VersionConflict(dist, req)
-        return dist
-
-    def iter_entry_points(self, group, name=None):
-        """Yield entry point objects from `group` matching `name`
-
-        If `name` is None, yields all entry points in `group` from all
-        distributions in the working set, otherwise only ones matching
-        both `group` and `name` are yielded (in distribution order).
-        """
-        return (
-            entry
-            for dist in self
-            for entry in dist.get_entry_map(group).values()
-            if name is None or name == entry.name
-        )
-
-    def run_script(self, requires, script_name):
-        """Locate distribution for `requires` and run `script_name` script"""
-        ns = sys._getframe(1).f_globals
-        name = ns['__name__']
-        ns.clear()
-        ns['__name__'] = name
-        self.require(requires)[0].run_script(script_name, ns)
-
-    def __iter__(self):
-        """Yield distributions for non-duplicate projects in the working set
-
-        The yield order is the order in which the items' path entries were
-        added to the working set.
-        """
-        seen = {}
-        for item in self.entries:
-            if item not in self.entry_keys:
-                # workaround a cache issue
-                continue
-
-            for key in self.entry_keys[item]:
-                if key not in seen:
-                    seen[key] = 1
-                    yield self.by_key[key]
-
-    def add(self, dist, entry=None, insert=True, replace=False):
-        """Add `dist` to working set, associated with `entry`
-
-        If `entry` is unspecified, it defaults to the ``.location`` of `dist`.
-        On exit from this routine, `entry` is added to the end of the working
-        set's ``.entries`` (if it wasn't already present).
-
-        `dist` is only added to the working set if it's for a project that
-        doesn't already have a distribution in the set, unless `replace=True`.
-        If it's added, any callbacks registered with the ``subscribe()`` method
-        will be called.
-        """
-        if insert:
-            dist.insert_on(self.entries, entry, replace=replace)
-
-        if entry is None:
-            entry = dist.location
-        keys = self.entry_keys.setdefault(entry, [])
-        keys2 = self.entry_keys.setdefault(dist.location, [])
-        if not replace and dist.key in self.by_key:
-            # ignore hidden distros
-            return
-
-        self.by_key[dist.key] = dist
-        if dist.key not in keys:
-            keys.append(dist.key)
-        if dist.key not in keys2:
-            keys2.append(dist.key)
-        self._added_new(dist)
-
-    def resolve(self, requirements, env=None, installer=None,
-                replace_conflicting=False, extras=None):
-        """List all distributions needed to (recursively) meet `requirements`
-
-        `requirements` must be a sequence of ``Requirement`` objects.  `env`,
-        if supplied, should be an ``Environment`` instance.  If
-        not supplied, it defaults to all distributions available within any
-        entry or distribution in the working set.  `installer`, if supplied,
-        will be invoked with each requirement that cannot be met by an
-        already-installed distribution; it should return a ``Distribution`` or
-        ``None``.
-
-        Unless `replace_conflicting=True`, raises a VersionConflict exception
-        if
-        any requirements are found on the path that have the correct name but
-        the wrong version.  Otherwise, if an `installer` is supplied it will be
-        invoked to obtain the correct version of the requirement and activate
-        it.
-
-        `extras` is a list of the extras to be used with these requirements.
-        This is important because extra requirements may look like `my_req;
-        extra = "my_extra"`, which would otherwise be interpreted as a purely
-        optional requirement.  Instead, we want to be able to assert that these
-        requirements are truly required.
-        """
-
-        # set up the stack
-        requirements = list(requirements)[::-1]
-        # set of processed requirements
-        processed = {}
-        # key -> dist
-        best = {}
-        to_activate = []
-
-        req_extras = _ReqExtras()
-
-        # Mapping of requirement to set of distributions that required it;
-        # useful for reporting info about conflicts.
-        required_by = collections.defaultdict(set)
-
-        while requirements:
-            # process dependencies breadth-first
-            req = requirements.pop(0)
-            if req in processed:
-                # Ignore cyclic or redundant dependencies
-                continue
-
-            if not req_extras.markers_pass(req, extras):
-                continue
-
-            dist = best.get(req.key)
-            if dist is None:
-                # Find the best distribution and add it to the map
-                dist = self.by_key.get(req.key)
-                if dist is None or (dist not in req and replace_conflicting):
-                    ws = self
-                    if env is None:
-                        if dist is None:
-                            env = Environment(self.entries)
-                        else:
-                            # Use an empty environment and workingset to avoid
-                            # any further conflicts with the conflicting
-                            # distribution
-                            env = Environment([])
-                            ws = WorkingSet([])
-                    dist = best[req.key] = env.best_match(
-                        req, ws, installer,
-                        replace_conflicting=replace_conflicting
-                    )
-                    if dist is None:
-                        requirers = required_by.get(req, None)
-                        raise DistributionNotFound(req, requirers)
-                to_activate.append(dist)
-            if dist not in req:
-                # Oops, the "best" so far conflicts with a dependency
-                dependent_req = required_by[req]
-                raise VersionConflict(dist, req).with_context(dependent_req)
-
-            # push the new requirements onto the stack
-            new_requirements = dist.requires(req.extras)[::-1]
-            requirements.extend(new_requirements)
-
-            # Register the new requirements needed by req
-            for new_requirement in new_requirements:
-                required_by[new_requirement].add(req.project_name)
-                req_extras[new_requirement] = req.extras
-
-            processed[req] = True
-
-        # return list of distros to activate
-        return to_activate
-
-    def find_plugins(
-            self, plugin_env, full_env=None, installer=None, fallback=True):
-        """Find all activatable distributions in `plugin_env`
-
-        Example usage::
-
-            distributions, errors = working_set.find_plugins(
-                Environment(plugin_dirlist)
-            )
-            # add plugins+libs to sys.path
-            map(working_set.add, distributions)
-            # display errors
-            print('Could not load', errors)
-
-        The `plugin_env` should be an ``Environment`` instance that contains
-        only distributions that are in the project's "plugin directory" or
-        directories. The `full_env`, if supplied, should be an ``Environment``
-        contains all currently-available distributions.  If `full_env` is not
-        supplied, one is created automatically from the ``WorkingSet`` this
-        method is called on, which will typically mean that every directory on
-        ``sys.path`` will be scanned for distributions.
-
-        `installer` is a standard installer callback as used by the
-        ``resolve()`` method. The `fallback` flag indicates whether we should
-        attempt to resolve older versions of a plugin if the newest version
-        cannot be resolved.
-
-        This method returns a 2-tuple: (`distributions`, `error_info`), where
-        `distributions` is a list of the distributions found in `plugin_env`
-        that were loadable, along with any other distributions that are needed
-        to resolve their dependencies.  `error_info` is a dictionary mapping
-        unloadable plugin distributions to an exception instance describing the
-        error that occurred. Usually this will be a ``DistributionNotFound`` or
-        ``VersionConflict`` instance.
-        """
-
-        plugin_projects = list(plugin_env)
-        # scan project names in alphabetic order
-        plugin_projects.sort()
-
-        error_info = {}
-        distributions = {}
-
-        if full_env is None:
-            env = Environment(self.entries)
-            env += plugin_env
-        else:
-            env = full_env + plugin_env
-
-        shadow_set = self.__class__([])
-        # put all our entries in shadow_set
-        list(map(shadow_set.add, self))
-
-        for project_name in plugin_projects:
-
-            for dist in plugin_env[project_name]:
-
-                req = [dist.as_requirement()]
-
-                try:
-                    resolvees = shadow_set.resolve(req, env, installer)
-
-                except ResolutionError as v:
-                    # save error info
-                    error_info[dist] = v
-                    if fallback:
-                        # try the next older version of project
-                        continue
-                    else:
-                        # give up on this project, keep going
-                        break
-
-                else:
-                    list(map(shadow_set.add, resolvees))
-                    distributions.update(dict.fromkeys(resolvees))
-
-                    # success, no need to try any more versions of this project
-                    break
-
-        distributions = list(distributions)
-        distributions.sort()
-
-        return distributions, error_info
-
-    def require(self, *requirements):
-        """Ensure that distributions matching `requirements` are activated
-
-        `requirements` must be a string or a (possibly-nested) sequence
-        thereof, specifying the distributions and versions required.  The
-        return value is a sequence of the distributions that needed to be
-        activated to fulfill the requirements; all relevant distributions are
-        included, even if they were already activated in this working set.
-        """
-        needed = self.resolve(parse_requirements(requirements))
-
-        for dist in needed:
-            self.add(dist)
-
-        return needed
-
-    def subscribe(self, callback, existing=True):
-        """Invoke `callback` for all distributions
-
-        If `existing=True` (default),
-        call on all existing ones, as well.
-        """
-        if callback in self.callbacks:
-            return
-        self.callbacks.append(callback)
-        if not existing:
-            return
-        for dist in self:
-            callback(dist)
-
-    def _added_new(self, dist):
-        for callback in self.callbacks:
-            callback(dist)
-
-    def __getstate__(self):
-        return (
-            self.entries[:], self.entry_keys.copy(), self.by_key.copy(),
-            self.callbacks[:]
-        )
-
-    def __setstate__(self, e_k_b_c):
-        entries, keys, by_key, callbacks = e_k_b_c
-        self.entries = entries[:]
-        self.entry_keys = keys.copy()
-        self.by_key = by_key.copy()
-        self.callbacks = callbacks[:]
-
-
-class _ReqExtras(dict):
-    """
-    Map each requirement to the extras that demanded it.
-    """
-
-    def markers_pass(self, req, extras=None):
-        """
-        Evaluate markers for req against each extra that
-        demanded it.
-
-        Return False if the req has a marker and fails
-        evaluation. Otherwise, return True.
-        """
-        extra_evals = (
-            req.marker.evaluate({'extra': extra})
-            for extra in self.get(req, ()) + (extras or (None,))
-        )
-        return not req.marker or any(extra_evals)
-
-
-class Environment:
-    """Searchable snapshot of distributions on a search path"""
-
-    def __init__(
-            self, search_path=None, platform=get_supported_platform(),
-            python=PY_MAJOR):
-        """Snapshot distributions available on a search path
-
-        Any distributions found on `search_path` are added to the environment.
-        `search_path` should be a sequence of ``sys.path`` items.  If not
-        supplied, ``sys.path`` is used.
-
-        `platform` is an optional string specifying the name of the platform
-        that platform-specific distributions must be compatible with.  If
-        unspecified, it defaults to the current platform.  `python` is an
-        optional string naming the desired version of Python (e.g. ``'3.6'``);
-        it defaults to the current version.
-
-        You may explicitly set `platform` (and/or `python`) to ``None`` if you
-        wish to map *all* distributions, not just those compatible with the
-        running platform or Python version.
-        """
-        self._distmap = {}
-        self.platform = platform
-        self.python = python
-        self.scan(search_path)
-
-    def can_add(self, dist):
-        """Is distribution `dist` acceptable for this environment?
-
-        The distribution must match the platform and python version
-        requirements specified when this environment was created, or False
-        is returned.
-        """
-        py_compat = (
-            self.python is None
-            or dist.py_version is None
-            or dist.py_version == self.python
-        )
-        return py_compat and compatible_platforms(dist.platform, self.platform)
-
-    def remove(self, dist):
-        """Remove `dist` from the environment"""
-        self._distmap[dist.key].remove(dist)
-
-    def scan(self, search_path=None):
-        """Scan `search_path` for distributions usable in this environment
-
-        Any distributions found are added to the environment.
-        `search_path` should be a sequence of ``sys.path`` items.  If not
-        supplied, ``sys.path`` is used.  Only distributions conforming to
-        the platform/python version defined at initialization are added.
-        """
-        if search_path is None:
-            search_path = sys.path
-
-        for item in search_path:
-            for dist in find_distributions(item):
-                self.add(dist)
-
-    def __getitem__(self, project_name):
-        """Return a newest-to-oldest list of distributions for `project_name`
-
-        Uses case-insensitive `project_name` comparison, assuming all the
-        project's distributions use their project's name converted to all
-        lowercase as their key.
-
-        """
-        distribution_key = project_name.lower()
-        return self._distmap.get(distribution_key, [])
-
-    def add(self, dist):
-        """Add `dist` if we ``can_add()`` it and it has not already been added
-        """
-        if self.can_add(dist) and dist.has_version():
-            dists = self._distmap.setdefault(dist.key, [])
-            if dist not in dists:
-                dists.append(dist)
-                dists.sort(key=operator.attrgetter('hashcmp'), reverse=True)
-
-    def best_match(
-            self, req, working_set, installer=None, replace_conflicting=False):
-        """Find distribution best matching `req` and usable on `working_set`
-
-        This calls the ``find(req)`` method of the `working_set` to see if a
-        suitable distribution is already active.  (This may raise
-        ``VersionConflict`` if an unsuitable version of the project is already
-        active in the specified `working_set`.)  If a suitable distribution
-        isn't active, this method returns the newest distribution in the
-        environment that meets the ``Requirement`` in `req`.  If no suitable
-        distribution is found, and `installer` is supplied, then the result of
-        calling the environment's ``obtain(req, installer)`` method will be
-        returned.
-        """
-        try:
-            dist = working_set.find(req)
-        except VersionConflict:
-            if not replace_conflicting:
-                raise
-            dist = None
-        if dist is not None:
-            return dist
-        for dist in self[req.key]:
-            if dist in req:
-                return dist
-        # try to download/install
-        return self.obtain(req, installer)
-
-    def obtain(self, requirement, installer=None):
-        """Obtain a distribution matching `requirement` (e.g. via download)
-
-        Obtain a distro that matches requirement (e.g. via download).  In the
-        base ``Environment`` class, this routine just returns
-        ``installer(requirement)``, unless `installer` is None, in which case
-        None is returned instead.  This method is a hook that allows subclasses
-        to attempt other ways of obtaining a distribution before falling back
-        to the `installer` argument."""
-        if installer is not None:
-            return installer(requirement)
-
-    def __iter__(self):
-        """Yield the unique project names of the available distributions"""
-        for key in self._distmap.keys():
-            if self[key]:
-                yield key
-
-    def __iadd__(self, other):
-        """In-place addition of a distribution or environment"""
-        if isinstance(other, Distribution):
-            self.add(other)
-        elif isinstance(other, Environment):
-            for project in other:
-                for dist in other[project]:
-                    self.add(dist)
-        else:
-            raise TypeError("Can't add %r to environment" % (other,))
-        return self
-
-    def __add__(self, other):
-        """Add an environment or distribution to an environment"""
-        new = self.__class__([], platform=None, python=None)
-        for env in self, other:
-            new += env
-        return new
-
-
-# XXX backward compatibility
-AvailableDistributions = Environment
-
-
-class ExtractionError(RuntimeError):
-    """An error occurred extracting a resource
-
-    The following attributes are available from instances of this exception:
-
-    manager
-        The resource manager that raised this exception
-
-    cache_path
-        The base directory for resource extraction
-
-    original_error
-        The exception instance that caused extraction to fail
-    """
-
-
-class ResourceManager:
-    """Manage resource extraction and packages"""
-    extraction_path = None
-
-    def __init__(self):
-        self.cached_files = {}
-
-    def resource_exists(self, package_or_requirement, resource_name):
-        """Does the named resource exist?"""
-        return get_provider(package_or_requirement).has_resource(resource_name)
-
-    def resource_isdir(self, package_or_requirement, resource_name):
-        """Is the named resource an existing directory?"""
-        return get_provider(package_or_requirement).resource_isdir(
-            resource_name
-        )
-
-    def resource_filename(self, package_or_requirement, resource_name):
-        """Return a true filesystem path for specified resource"""
-        return get_provider(package_or_requirement).get_resource_filename(
-            self, resource_name
-        )
-
-    def resource_stream(self, package_or_requirement, resource_name):
-        """Return a readable file-like object for specified resource"""
-        return get_provider(package_or_requirement).get_resource_stream(
-            self, resource_name
-        )
-
-    def resource_string(self, package_or_requirement, resource_name):
-        """Return specified resource as a string"""
-        return get_provider(package_or_requirement).get_resource_string(
-            self, resource_name
-        )
-
-    def resource_listdir(self, package_or_requirement, resource_name):
-        """List the contents of the named resource directory"""
-        return get_provider(package_or_requirement).resource_listdir(
-            resource_name
-        )
-
-    def extraction_error(self):
-        """Give an error message for problems extracting file(s)"""
-
-        old_exc = sys.exc_info()[1]
-        cache_path = self.extraction_path or get_default_cache()
-
-        tmpl = textwrap.dedent("""
-            Can't extract file(s) to egg cache
-
-            The following error occurred while trying to extract file(s)
-            to the Python egg cache:
-
-              {old_exc}
-
-            The Python egg cache directory is currently set to:
-
-              {cache_path}
-
-            Perhaps your account does not have write access to this directory?
-            You can change the cache directory by setting the PYTHON_EGG_CACHE
-            environment variable to point to an accessible directory.
-            """).lstrip()
-        err = ExtractionError(tmpl.format(**locals()))
-        err.manager = self
-        err.cache_path = cache_path
-        err.original_error = old_exc
-        raise err
-
-    def get_cache_path(self, archive_name, names=()):
-        """Return absolute location in cache for `archive_name` and `names`
-
-        The parent directory of the resulting path will be created if it does
-        not already exist.  `archive_name` should be the base filename of the
-        enclosing egg (which may not be the name of the enclosing zipfile!),
-        including its ".egg" extension.  `names`, if provided, should be a
-        sequence of path name parts "under" the egg's extraction location.
-
-        This method should only be called by resource providers that need to
-        obtain an extraction location, and only for names they intend to
-        extract, as it tracks the generated names for possible cleanup later.
-        """
-        extract_path = self.extraction_path or get_default_cache()
-        target_path = os.path.join(extract_path, archive_name + '-tmp', *names)
-        try:
-            _bypass_ensure_directory(target_path)
-        except Exception:
-            self.extraction_error()
-
-        self._warn_unsafe_extraction_path(extract_path)
-
-        self.cached_files[target_path] = 1
-        return target_path
-
-    @staticmethod
-    def _warn_unsafe_extraction_path(path):
-        """
-        If the default extraction path is overridden and set to an insecure
-        location, such as /tmp, it opens up an opportunity for an attacker to
-        replace an extracted file with an unauthorized payload. Warn the user
-        if a known insecure location is used.
-
-        See Distribute #375 for more details.
-        """
-        if os.name == 'nt' and not path.startswith(os.environ['windir']):
-            # On Windows, permissions are generally restrictive by default
-            #  and temp directories are not writable by other users, so
-            #  bypass the warning.
-            return
-        mode = os.stat(path).st_mode
-        if mode & stat.S_IWOTH or mode & stat.S_IWGRP:
-            msg = (
-                "%s is writable by group/others and vulnerable to attack "
-                "when "
-                "used with get_resource_filename. Consider a more secure "
-                "location (set with .set_extraction_path or the "
-                "PYTHON_EGG_CACHE environment variable)." % path
-            )
-            warnings.warn(msg, UserWarning)
-
-    def postprocess(self, tempname, filename):
-        """Perform any platform-specific postprocessing of `tempname`
-
-        This is where Mac header rewrites should be done; other platforms don't
-        have anything special they should do.
-
-        Resource providers should call this method ONLY after successfully
-        extracting a compressed resource.  They must NOT call it on resources
-        that are already in the filesystem.
-
-        `tempname` is the current (temporary) name of the file, and `filename`
-        is the name it will be renamed to by the caller after this routine
-        returns.
-        """
-
-        if os.name == 'posix':
-            # Make the resource executable
-            mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777
-            os.chmod(tempname, mode)
-
-    def set_extraction_path(self, path):
-        """Set the base path where resources will be extracted to, if needed.
-
-        If you do not call this routine before any extractions take place, the
-        path defaults to the return value of ``get_default_cache()``.  (Which
-        is based on the ``PYTHON_EGG_CACHE`` environment variable, with various
-        platform-specific fallbacks.  See that routine's documentation for more
-        details.)
-
-        Resources are extracted to subdirectories of this path based upon
-        information given by the ``IResourceProvider``.  You may set this to a
-        temporary directory, but then you must call ``cleanup_resources()`` to
-        delete the extracted files when done.  There is no guarantee that
-        ``cleanup_resources()`` will be able to remove all extracted files.
-
-        (Note: you may not change the extraction path for a given resource
-        manager once resources have been extracted, unless you first call
-        ``cleanup_resources()``.)
-        """
-        if self.cached_files:
-            raise ValueError(
-                "Can't change extraction path, files already extracted"
-            )
-
-        self.extraction_path = path
-
-    def cleanup_resources(self, force=False):
-        """
-        Delete all extracted resource files and directories, returning a list
-        of the file and directory names that could not be successfully removed.
-        This function does not have any concurrency protection, so it should
-        generally only be called when the extraction path is a temporary
-        directory exclusive to a single process.  This method is not
-        automatically called; you must call it explicitly or register it as an
-        ``atexit`` function if you wish to ensure cleanup of a temporary
-        directory used for extractions.
-        """
-        # XXX
-
-
-def get_default_cache():
-    """
-    Return the ``PYTHON_EGG_CACHE`` environment variable
-    or a platform-relevant user cache dir for an app
-    named "Python-Eggs".
-    """
-    return (
-        os.environ.get('PYTHON_EGG_CACHE')
-        or platformdirs.user_cache_dir(appname='Python-Eggs')
-    )
-
-
-def safe_name(name):
-    """Convert an arbitrary string to a standard distribution name
-
-    Any runs of non-alphanumeric/. characters are replaced with a single '-'.
-    """
-    return re.sub('[^A-Za-z0-9.]+', '-', name)
-
-
-def safe_version(version):
-    """
-    Convert an arbitrary string to a standard version string
-    """
-    try:
-        # normalize the version
-        return str(packaging.version.Version(version))
-    except packaging.version.InvalidVersion:
-        version = version.replace(' ', '.')
-        return re.sub('[^A-Za-z0-9.]+', '-', version)
-
-
-def safe_extra(extra):
-    """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.
-    """
-    return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower()
-
-
-def to_filename(name):
-    """Convert a project or version name to its filename-escaped form
-
-    Any '-' characters are currently replaced with '_'.
-    """
-    return name.replace('-', '_')
-
-
-def invalid_marker(text):
-    """
-    Validate text as a PEP 508 environment marker; return an exception
-    if invalid or False otherwise.
-    """
-    try:
-        evaluate_marker(text)
-    except SyntaxError as e:
-        e.filename = None
-        e.lineno = None
-        return e
-    return False
-
-
-def evaluate_marker(text, extra=None):
-    """
-    Evaluate a PEP 508 environment marker.
-    Return a boolean indicating the marker result in this environment.
-    Raise SyntaxError if marker is invalid.
-
-    This implementation uses the 'pyparsing' module.
-    """
-    try:
-        marker = packaging.markers.Marker(text)
-        return marker.evaluate()
-    except packaging.markers.InvalidMarker as e:
-        raise SyntaxError(e)
-
-
-class NullProvider:
-    """Try to implement resources and metadata for arbitrary PEP 302 loaders"""
-
-    egg_name = None
-    egg_info = None
-    loader = None
-
-    def __init__(self, module):
-        self.loader = getattr(module, '__loader__', None)
-        self.module_path = os.path.dirname(getattr(module, '__file__', ''))
-
-    def get_resource_filename(self, manager, resource_name):
-        return self._fn(self.module_path, resource_name)
-
-    def get_resource_stream(self, manager, resource_name):
-        return io.BytesIO(self.get_resource_string(manager, resource_name))
-
-    def get_resource_string(self, manager, resource_name):
-        return self._get(self._fn(self.module_path, resource_name))
-
-    def has_resource(self, resource_name):
-        return self._has(self._fn(self.module_path, resource_name))
-
-    def _get_metadata_path(self, name):
-        return self._fn(self.egg_info, name)
-
-    def has_metadata(self, name):
-        if not self.egg_info:
-            return self.egg_info
-
-        path = self._get_metadata_path(name)
-        return self._has(path)
-
-    def get_metadata(self, name):
-        if not self.egg_info:
-            return ""
-        path = self._get_metadata_path(name)
-        value = self._get(path)
-        if six.PY2:
-            return value
-        try:
-            return value.decode('utf-8')
-        except UnicodeDecodeError as exc:
-            # Include the path in the error message to simplify
-            # troubleshooting, and without changing the exception type.
-            exc.reason += ' in {} file at path: {}'.format(name, path)
-            raise
-
-    def get_metadata_lines(self, name):
-        return yield_lines(self.get_metadata(name))
-
-    def resource_isdir(self, resource_name):
-        return self._isdir(self._fn(self.module_path, resource_name))
-
-    def metadata_isdir(self, name):
-        return self.egg_info and self._isdir(self._fn(self.egg_info, name))
-
-    def resource_listdir(self, resource_name):
-        return self._listdir(self._fn(self.module_path, resource_name))
-
-    def metadata_listdir(self, name):
-        if self.egg_info:
-            return self._listdir(self._fn(self.egg_info, name))
-        return []
-
-    def run_script(self, script_name, namespace):
-        script = 'scripts/' + script_name
-        if not self.has_metadata(script):
-            raise ResolutionError(
-                "Script {script!r} not found in metadata at {self.egg_info!r}"
-                .format(**locals()),
-            )
-        script_text = self.get_metadata(script).replace('\r\n', '\n')
-        script_text = script_text.replace('\r', '\n')
-        script_filename = self._fn(self.egg_info, script)
-        namespace['__file__'] = script_filename
-        if os.path.exists(script_filename):
-            source = open(script_filename).read()
-            code = compile(source, script_filename, 'exec')
-            exec(code, namespace, namespace)
-        else:
-            from linecache import cache
-            cache[script_filename] = (
-                len(script_text), 0, script_text.split('\n'), script_filename
-            )
-            script_code = compile(script_text, script_filename, 'exec')
-            exec(script_code, namespace, namespace)
-
-    def _has(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _isdir(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _listdir(self, path):
-        raise NotImplementedError(
-            "Can't perform this operation for unregistered loader type"
-        )
-
-    def _fn(self, base, resource_name):
-        self._validate_resource_path(resource_name)
-        if resource_name:
-            return os.path.join(base, *resource_name.split('/'))
-        return base
-
-    @staticmethod
-    def _validate_resource_path(path):
-        """
-        Validate the resource paths according to the docs.
-        https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access
-
-        >>> warned = getfixture('recwarn')
-        >>> warnings.simplefilter('always')
-        >>> vrp = NullProvider._validate_resource_path
-        >>> vrp('foo/bar.txt')
-        >>> bool(warned)
-        False
-        >>> vrp('../foo/bar.txt')
-        >>> bool(warned)
-        True
-        >>> warned.clear()
-        >>> vrp('/foo/bar.txt')
-        >>> bool(warned)
-        True
-        >>> vrp('foo/../../bar.txt')
-        >>> bool(warned)
-        True
-        >>> warned.clear()
-        >>> vrp('foo/f../bar.txt')
-        >>> bool(warned)
-        False
-
-        Windows path separators are straight-up disallowed.
-        >>> vrp(r'\\foo/bar.txt')
-        Traceback (most recent call last):
-        ...
-        ValueError: Use of .. or absolute path in a resource path \
-is not allowed.
-
-        >>> vrp(r'C:\\foo/bar.txt')
-        Traceback (most recent call last):
-        ...
-        ValueError: Use of .. or absolute path in a resource path \
-is not allowed.
-
-        Blank values are allowed
-
-        >>> vrp('')
-        >>> bool(warned)
-        False
-
-        Non-string values are not.
-
-        >>> vrp(None)
-        Traceback (most recent call last):
-        ...
-        AttributeError: ...
-        """
-        invalid = (
-            os.path.pardir in path.split(posixpath.sep) or
-            posixpath.isabs(path) or
-            ntpath.isabs(path)
-        )
-        if not invalid:
-            return
-
-        msg = "Use of .. or absolute path in a resource path is not allowed."
-
-        # Aggressively disallow Windows absolute paths
-        if ntpath.isabs(path) and not posixpath.isabs(path):
-            raise ValueError(msg)
-
-        # for compatibility, warn; in future
-        # raise ValueError(msg)
-        warnings.warn(
-            msg[:-1] + " and will raise exceptions in a future release.",
-            DeprecationWarning,
-            stacklevel=4,
-        )
-
-    def _get(self, path):
-        if hasattr(self.loader, 'get_data'):
-            return self.loader.get_data(path)
-        raise NotImplementedError(
-            "Can't perform this operation for loaders without 'get_data()'"
-        )
-
-
-register_loader_type(object, NullProvider)
-
-
-class EggProvider(NullProvider):
-    """Provider based on a virtual filesystem"""
-
-    def __init__(self, module):
-        NullProvider.__init__(self, module)
-        self._setup_prefix()
-
-    def _setup_prefix(self):
-        # we assume here that our metadata may be nested inside a "basket"
-        # of multiple eggs; that's why we use module_path instead of .archive
-        path = self.module_path
-        old = None
-        while path != old:
-            if _is_egg_path(path):
-                self.egg_name = os.path.basename(path)
-                self.egg_info = os.path.join(path, 'EGG-INFO')
-                self.egg_root = path
-                break
-            old = path
-            path, base = os.path.split(path)
-
-
-class DefaultProvider(EggProvider):
-    """Provides access to package resources in the filesystem"""
-
-    def _has(self, path):
-        return os.path.exists(path)
-
-    def _isdir(self, path):
-        return os.path.isdir(path)
-
-    def _listdir(self, path):
-        return os.listdir(path)
-
-    def get_resource_stream(self, manager, resource_name):
-        return open(self._fn(self.module_path, resource_name), 'rb')
-
-    def _get(self, path):
-        with open(path, 'rb') as stream:
-            return stream.read()
-
-    @classmethod
-    def _register(cls):
-        loader_names = 'SourceFileLoader', 'SourcelessFileLoader',
-        for name in loader_names:
-            loader_cls = getattr(importlib_machinery, name, type(None))
-            register_loader_type(loader_cls, cls)
-
-
-DefaultProvider._register()
-
-
-class EmptyProvider(NullProvider):
-    """Provider that returns nothing for all requests"""
-
-    module_path = None
-
-    _isdir = _has = lambda self, path: False
-
-    def _get(self, path):
-        return ''
-
-    def _listdir(self, path):
-        return []
-
-    def __init__(self):
-        pass
-
-
-empty_provider = EmptyProvider()
-
-
-class ZipManifests(dict):
-    """
-    zip manifest builder
-    """
-
-    @classmethod
-    def build(cls, path):
-        """
-        Build a dictionary similar to the zipimport directory
-        caches, except instead of tuples, store ZipInfo objects.
-
-        Use a platform-specific path separator (os.sep) for the path keys
-        for compatibility with pypy on Windows.
-        """
-        with zipfile.ZipFile(path) as zfile:
-            items = (
-                (
-                    name.replace('/', os.sep),
-                    zfile.getinfo(name),
-                )
-                for name in zfile.namelist()
-            )
-            return dict(items)
-
-    load = build
-
-
-class MemoizedZipManifests(ZipManifests):
-    """
-    Memoized zipfile manifests.
-    """
-    manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime')
-
-    def load(self, path):
-        """
-        Load a manifest at path or return a suitable manifest already loaded.
-        """
-        path = os.path.normpath(path)
-        mtime = os.stat(path).st_mtime
-
-        if path not in self or self[path].mtime != mtime:
-            manifest = self.build(path)
-            self[path] = self.manifest_mod(manifest, mtime)
-
-        return self[path].manifest
-
-
-class ZipProvider(EggProvider):
-    """Resource support for zips and eggs"""
-
-    eagers = None
-    _zip_manifests = MemoizedZipManifests()
-
-    def __init__(self, module):
-        EggProvider.__init__(self, module)
-        self.zip_pre = self.loader.archive + os.sep
-
-    def _zipinfo_name(self, fspath):
-        # Convert a virtual filename (full path to file) into a zipfile subpath
-        # usable with the zipimport directory cache for our target archive
-        fspath = fspath.rstrip(os.sep)
-        if fspath == self.loader.archive:
-            return ''
-        if fspath.startswith(self.zip_pre):
-            return fspath[len(self.zip_pre):]
-        raise AssertionError(
-            "%s is not a subpath of %s" % (fspath, self.zip_pre)
-        )
-
-    def _parts(self, zip_path):
-        # Convert a zipfile subpath into an egg-relative path part list.
-        # pseudo-fs path
-        fspath = self.zip_pre + zip_path
-        if fspath.startswith(self.egg_root + os.sep):
-            return fspath[len(self.egg_root) + 1:].split(os.sep)
-        raise AssertionError(
-            "%s is not a subpath of %s" % (fspath, self.egg_root)
-        )
-
-    @property
-    def zipinfo(self):
-        return self._zip_manifests.load(self.loader.archive)
-
-    def get_resource_filename(self, manager, resource_name):
-        if not self.egg_name:
-            raise NotImplementedError(
-                "resource_filename() only supported for .egg, not .zip"
-            )
-        # no need to lock for extraction, since we use temp names
-        zip_path = self._resource_to_zip(resource_name)
-        eagers = self._get_eager_resources()
-        if '/'.join(self._parts(zip_path)) in eagers:
-            for name in eagers:
-                self._extract_resource(manager, self._eager_to_zip(name))
-        return self._extract_resource(manager, zip_path)
-
-    @staticmethod
-    def _get_date_and_size(zip_stat):
-        size = zip_stat.file_size
-        # ymdhms+wday, yday, dst
-        date_time = zip_stat.date_time + (0, 0, -1)
-        # 1980 offset already done
-        timestamp = time.mktime(date_time)
-        return timestamp, size
-
-    def _extract_resource(self, manager, zip_path):
-
-        if zip_path in self._index():
-            for name in self._index()[zip_path]:
-                last = self._extract_resource(
-                    manager, os.path.join(zip_path, name)
-                )
-            # return the extracted directory name
-            return os.path.dirname(last)
-
-        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
-
-        if not WRITE_SUPPORT:
-            raise IOError('"os.rename" and "os.unlink" are not supported '
-                          'on this platform')
-        try:
-
-            real_path = manager.get_cache_path(
-                self.egg_name, self._parts(zip_path)
-            )
-
-            if self._is_current(real_path, zip_path):
-                return real_path
-
-            outf, tmpnam = _mkstemp(
-                ".$extract",
-                dir=os.path.dirname(real_path),
-            )
-            os.write(outf, self.loader.get_data(zip_path))
-            os.close(outf)
-            utime(tmpnam, (timestamp, timestamp))
-            manager.postprocess(tmpnam, real_path)
-
-            try:
-                rename(tmpnam, real_path)
-
-            except os.error:
-                if os.path.isfile(real_path):
-                    if self._is_current(real_path, zip_path):
-                        # the file became current since it was checked above,
-                        #  so proceed.
-                        return real_path
-                    # Windows, del old file and retry
-                    elif os.name == 'nt':
-                        unlink(real_path)
-                        rename(tmpnam, real_path)
-                        return real_path
-                raise
-
-        except os.error:
-            # report a user-friendly error
-            manager.extraction_error()
-
-        return real_path
-
-    def _is_current(self, file_path, zip_path):
-        """
-        Return True if the file_path is current for this zip_path
-        """
-        timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
-        if not os.path.isfile(file_path):
-            return False
-        stat = os.stat(file_path)
-        if stat.st_size != size or stat.st_mtime != timestamp:
-            return False
-        # check that the contents match
-        zip_contents = self.loader.get_data(zip_path)
-        with open(file_path, 'rb') as f:
-            file_contents = f.read()
-        return zip_contents == file_contents
-
-    def _get_eager_resources(self):
-        if self.eagers is None:
-            eagers = []
-            for name in ('native_libs.txt', 'eager_resources.txt'):
-                if self.has_metadata(name):
-                    eagers.extend(self.get_metadata_lines(name))
-            self.eagers = eagers
-        return self.eagers
-
-    def _index(self):
-        try:
-            return self._dirindex
-        except AttributeError:
-            ind = {}
-            for path in self.zipinfo:
-                parts = path.split(os.sep)
-                while parts:
-                    parent = os.sep.join(parts[:-1])
-                    if parent in ind:
-                        ind[parent].append(parts[-1])
-                        break
-                    else:
-                        ind[parent] = [parts.pop()]
-            self._dirindex = ind
-            return ind
-
-    def _has(self, fspath):
-        zip_path = self._zipinfo_name(fspath)
-        return zip_path in self.zipinfo or zip_path in self._index()
-
-    def _isdir(self, fspath):
-        return self._zipinfo_name(fspath) in self._index()
-
-    def _listdir(self, fspath):
-        return list(self._index().get(self._zipinfo_name(fspath), ()))
-
-    def _eager_to_zip(self, resource_name):
-        return self._zipinfo_name(self._fn(self.egg_root, resource_name))
-
-    def _resource_to_zip(self, resource_name):
-        return self._zipinfo_name(self._fn(self.module_path, resource_name))
-
-
-register_loader_type(zipimport.zipimporter, ZipProvider)
-
-
-class FileMetadata(EmptyProvider):
-    """Metadata handler for standalone PKG-INFO files
-
-    Usage::
-
-        metadata = FileMetadata("/path/to/PKG-INFO")
-
-    This provider rejects all data and metadata requests except for PKG-INFO,
-    which is treated as existing, and will be the contents of the file at
-    the provided location.
-    """
-
-    def __init__(self, path):
-        self.path = path
-
-    def _get_metadata_path(self, name):
-        return self.path
-
-    def has_metadata(self, name):
-        return name == 'PKG-INFO' and os.path.isfile(self.path)
-
-    def get_metadata(self, name):
-        if name != 'PKG-INFO':
-            raise KeyError("No metadata except PKG-INFO is available")
-
-        with io.open(self.path, encoding='utf-8', errors="replace") as f:
-            metadata = f.read()
-        self._warn_on_replacement(metadata)
-        return metadata
-
-    def _warn_on_replacement(self, metadata):
-        # Python 2.7 compat for: replacement_char = '�'
-        replacement_char = b'\xef\xbf\xbd'.decode('utf-8')
-        if replacement_char in metadata:
-            tmpl = "{self.path} could not be properly decoded in UTF-8"
-            msg = tmpl.format(**locals())
-            warnings.warn(msg)
-
-    def get_metadata_lines(self, name):
-        return yield_lines(self.get_metadata(name))
-
-
-class PathMetadata(DefaultProvider):
-    """Metadata provider for egg directories
-
-    Usage::
-
-        # Development eggs:
-
-        egg_info = "/path/to/PackageName.egg-info"
-        base_dir = os.path.dirname(egg_info)
-        metadata = PathMetadata(base_dir, egg_info)
-        dist_name = os.path.splitext(os.path.basename(egg_info))[0]
-        dist = Distribution(basedir, project_name=dist_name, metadata=metadata)
-
-        # Unpacked egg directories:
-
-        egg_path = "/path/to/PackageName-ver-pyver-etc.egg"
-        metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO'))
-        dist = Distribution.from_filename(egg_path, metadata=metadata)
-    """
-
-    def __init__(self, path, egg_info):
-        self.module_path = path
-        self.egg_info = egg_info
-
-
-class EggMetadata(ZipProvider):
-    """Metadata provider for .egg files"""
-
-    def __init__(self, importer):
-        """Create a metadata provider from a zipimporter"""
-
-        self.zip_pre = importer.archive + os.sep
-        self.loader = importer
-        if importer.prefix:
-            self.module_path = os.path.join(importer.archive, importer.prefix)
-        else:
-            self.module_path = importer.archive
-        self._setup_prefix()
-
-
-_declare_state('dict', _distribution_finders={})
-
-
-def register_finder(importer_type, distribution_finder):
-    """Register `distribution_finder` to find distributions in sys.path items
-
-    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
-    handler), and `distribution_finder` is a callable that, passed a path
-    item and the importer instance, yields ``Distribution`` instances found on
-    that path item.  See ``pkg_resources.find_on_path`` for an example."""
-    _distribution_finders[importer_type] = distribution_finder
-
-
-def find_distributions(path_item, only=False):
-    """Yield distributions accessible via `path_item`"""
-    importer = get_importer(path_item)
-    finder = _find_adapter(_distribution_finders, importer)
-    return finder(importer, path_item, only)
-
-
-def find_eggs_in_zip(importer, path_item, only=False):
-    """
-    Find eggs in zip files; possibly multiple nested eggs.
-    """
-    if importer.archive.endswith('.whl'):
-        # wheels are not supported with this finder
-        # they don't have PKG-INFO metadata, and won't ever contain eggs
-        return
-    metadata = EggMetadata(importer)
-    if metadata.has_metadata('PKG-INFO'):
-        yield Distribution.from_filename(path_item, metadata=metadata)
-    if only:
-        # don't yield nested distros
-        return
-    for subitem in metadata.resource_listdir(''):
-        if _is_egg_path(subitem):
-            subpath = os.path.join(path_item, subitem)
-            dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath)
-            for dist in dists:
-                yield dist
-        elif subitem.lower().endswith('.dist-info'):
-            subpath = os.path.join(path_item, subitem)
-            submeta = EggMetadata(zipimport.zipimporter(subpath))
-            submeta.egg_info = subpath
-            yield Distribution.from_location(path_item, subitem, submeta)
-
-
-register_finder(zipimport.zipimporter, find_eggs_in_zip)
-
-
-def find_nothing(importer, path_item, only=False):
-    return ()
-
-
-register_finder(object, find_nothing)
-
-
-def _by_version_descending(names):
-    """
-    Given a list of filenames, return them in descending order
-    by version number.
-
-    >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg'
-    >>> _by_version_descending(names)
-    ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar']
-    >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg'
-    >>> _by_version_descending(names)
-    ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg']
-    >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg'
-    >>> _by_version_descending(names)
-    ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg']
-    """
-    def _by_version(name):
-        """
-        Parse each component of the filename
-        """
-        name, ext = os.path.splitext(name)
-        parts = itertools.chain(name.split('-'), [ext])
-        return [packaging.version.parse(part) for part in parts]
-
-    return sorted(names, key=_by_version, reverse=True)
-
-
-def find_on_path(importer, path_item, only=False):
-    """Yield distributions accessible on a sys.path directory"""
-    path_item = _normalize_cached(path_item)
-
-    if _is_unpacked_egg(path_item):
-        yield Distribution.from_filename(
-            path_item, metadata=PathMetadata(
-                path_item, os.path.join(path_item, 'EGG-INFO')
-            )
-        )
-        return
-
-    entries = safe_listdir(path_item)
-
-    # for performance, before sorting by version,
-    # screen entries for only those that will yield
-    # distributions
-    filtered = (
-        entry
-        for entry in entries
-        if dist_factory(path_item, entry, only)
-    )
-
-    # scan for .egg and .egg-info in directory
-    path_item_entries = _by_version_descending(filtered)
-    for entry in path_item_entries:
-        fullpath = os.path.join(path_item, entry)
-        factory = dist_factory(path_item, entry, only)
-        for dist in factory(fullpath):
-            yield dist
-
-
-def dist_factory(path_item, entry, only):
-    """
-    Return a dist_factory for a path_item and entry
-    """
-    lower = entry.lower()
-    is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info')))
-    return (
-        distributions_from_metadata
-        if is_meta else
-        find_distributions
-        if not only and _is_egg_path(entry) else
-        resolve_egg_link
-        if not only and lower.endswith('.egg-link') else
-        NoDists()
-    )
-
-
-class NoDists:
-    """
-    >>> bool(NoDists())
-    False
-
-    >>> list(NoDists()('anything'))
-    []
-    """
-    def __bool__(self):
-        return False
-    if six.PY2:
-        __nonzero__ = __bool__
-
-    def __call__(self, fullpath):
-        return iter(())
-
-
-def safe_listdir(path):
-    """
-    Attempt to list contents of path, but suppress some exceptions.
-    """
-    try:
-        return os.listdir(path)
-    except (PermissionError, NotADirectoryError):
-        pass
-    except OSError as e:
-        # Ignore the directory if does not exist, not a directory or
-        # permission denied
-        ignorable = (
-            e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)
-            # Python 2 on Windows needs to be handled this way :(
-            or getattr(e, "winerror", None) == 267
-        )
-        if not ignorable:
-            raise
-    return ()
-
-
-def distributions_from_metadata(path):
-    root = os.path.dirname(path)
-    if os.path.isdir(path):
-        if len(os.listdir(path)) == 0:
-            # empty metadata dir; skip
-            return
-        metadata = PathMetadata(root, path)
-    else:
-        metadata = FileMetadata(path)
-    entry = os.path.basename(path)
-    yield Distribution.from_location(
-        root, entry, metadata, precedence=DEVELOP_DIST,
-    )
-
-
-def non_empty_lines(path):
-    """
-    Yield non-empty lines from file at path
-    """
-    with open(path) as f:
-        for line in f:
-            line = line.strip()
-            if line:
-                yield line
-
-
-def resolve_egg_link(path):
-    """
-    Given a path to an .egg-link, resolve distributions
-    present in the referenced path.
-    """
-    referenced_paths = non_empty_lines(path)
-    resolved_paths = (
-        os.path.join(os.path.dirname(path), ref)
-        for ref in referenced_paths
-    )
-    dist_groups = map(find_distributions, resolved_paths)
-    return next(dist_groups, ())
-
-
-register_finder(pkgutil.ImpImporter, find_on_path)
-
-if hasattr(importlib_machinery, 'FileFinder'):
-    register_finder(importlib_machinery.FileFinder, find_on_path)
-
-_declare_state('dict', _namespace_handlers={})
-_declare_state('dict', _namespace_packages={})
-
-
-def register_namespace_handler(importer_type, namespace_handler):
-    """Register `namespace_handler` to declare namespace packages
-
-    `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item
-    handler), and `namespace_handler` is a callable like this::
-
-        def namespace_handler(importer, path_entry, moduleName, module):
-            # return a path_entry to use for child packages
-
-    Namespace handlers are only called if the importer object has already
-    agreed that it can handle the relevant path item, and they should only
-    return a subpath if the module __path__ does not already contain an
-    equivalent subpath.  For an example namespace handler, see
-    ``pkg_resources.file_ns_handler``.
-    """
-    _namespace_handlers[importer_type] = namespace_handler
-
-
-def _handle_ns(packageName, path_item):
-    """Ensure that named package includes a subpath of path_item (if needed)"""
-
-    importer = get_importer(path_item)
-    if importer is None:
-        return None
-
-    # capture warnings due to #1111
-    with warnings.catch_warnings():
-        warnings.simplefilter("ignore")
-        loader = importer.find_module(packageName)
-
-    if loader is None:
-        return None
-    module = sys.modules.get(packageName)
-    if module is None:
-        module = sys.modules[packageName] = types.ModuleType(packageName)
-        module.__path__ = []
-        _set_parent_ns(packageName)
-    elif not hasattr(module, '__path__'):
-        raise TypeError("Not a package:", packageName)
-    handler = _find_adapter(_namespace_handlers, importer)
-    subpath = handler(importer, path_item, packageName, module)
-    if subpath is not None:
-        path = module.__path__
-        path.append(subpath)
-        loader.load_module(packageName)
-        _rebuild_mod_path(path, packageName, module)
-    return subpath
-
-
-def _rebuild_mod_path(orig_path, package_name, module):
-    """
-    Rebuild module.__path__ ensuring that all entries are ordered
-    corresponding to their sys.path order
-    """
-    sys_path = [_normalize_cached(p) for p in sys.path]
-
-    def safe_sys_path_index(entry):
-        """
-        Workaround for #520 and #513.
-        """
-        try:
-            return sys_path.index(entry)
-        except ValueError:
-            return float('inf')
-
-    def position_in_sys_path(path):
-        """
-        Return the ordinal of the path based on its position in sys.path
-        """
-        path_parts = path.split(os.sep)
-        module_parts = package_name.count('.') + 1
-        parts = path_parts[:-module_parts]
-        return safe_sys_path_index(_normalize_cached(os.sep.join(parts)))
-
-    new_path = sorted(orig_path, key=position_in_sys_path)
-    new_path = [_normalize_cached(p) for p in new_path]
-
-    if isinstance(module.__path__, list):
-        module.__path__[:] = new_path
-    else:
-        module.__path__ = new_path
-
-
-def declare_namespace(packageName):
-    """Declare that package 'packageName' is a namespace package"""
-
-    _imp.acquire_lock()
-    try:
-        if packageName in _namespace_packages:
-            return
-
-        path = sys.path
-        parent, _, _ = packageName.rpartition('.')
-
-        if parent:
-            declare_namespace(parent)
-            if parent not in _namespace_packages:
-                __import__(parent)
-            try:
-                path = sys.modules[parent].__path__
-            except AttributeError:
-                raise TypeError("Not a package:", parent)
-
-        # Track what packages are namespaces, so when new path items are added,
-        # they can be updated
-        _namespace_packages.setdefault(parent or None, []).append(packageName)
-        _namespace_packages.setdefault(packageName, [])
-
-        for path_item in path:
-            # Ensure all the parent's path items are reflected in the child,
-            # if they apply
-            _handle_ns(packageName, path_item)
-
-    finally:
-        _imp.release_lock()
-
-
-def fixup_namespace_packages(path_item, parent=None):
-    """Ensure that previously-declared namespace packages include path_item"""
-    _imp.acquire_lock()
-    try:
-        for package in _namespace_packages.get(parent, ()):
-            subpath = _handle_ns(package, path_item)
-            if subpath:
-                fixup_namespace_packages(subpath, package)
-    finally:
-        _imp.release_lock()
-
-
-def file_ns_handler(importer, path_item, packageName, module):
-    """Compute an ns-package subpath for a filesystem or zipfile importer"""
-
-    subpath = os.path.join(path_item, packageName.split('.')[-1])
-    normalized = _normalize_cached(subpath)
-    for item in module.__path__:
-        if _normalize_cached(item) == normalized:
-            break
-    else:
-        # Only return the path if it's not already there
-        return subpath
-
-
-register_namespace_handler(pkgutil.ImpImporter, file_ns_handler)
-register_namespace_handler(zipimport.zipimporter, file_ns_handler)
-
-if hasattr(importlib_machinery, 'FileFinder'):
-    register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler)
-
-
-def null_ns_handler(importer, path_item, packageName, module):
-    return None
-
-
-register_namespace_handler(object, null_ns_handler)
-
-
-def normalize_path(filename):
-    """Normalize a file/dir name for comparison purposes"""
-    return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
-
-
-def _cygwin_patch(filename):  # pragma: nocover
-    """
-    Contrary to POSIX 2008, on Cygwin, getcwd (3) contains
-    symlink components. Using
-    os.path.abspath() works around this limitation. A fix in os.getcwd()
-    would probably better, in Cygwin even more so, except
-    that this seems to be by design...
-    """
-    return os.path.abspath(filename) if sys.platform == 'cygwin' else filename
-
-
-def _normalize_cached(filename, _cache={}):
-    try:
-        return _cache[filename]
-    except KeyError:
-        _cache[filename] = result = normalize_path(filename)
-        return result
-
-
-def _is_egg_path(path):
-    """
-    Determine if given path appears to be an egg.
-    """
-    return path.lower().endswith('.egg')
-
-
-def _is_unpacked_egg(path):
-    """
-    Determine if given path appears to be an unpacked egg.
-    """
-    return (
-        _is_egg_path(path) and
-        os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO'))
-    )
-
-
-def _set_parent_ns(packageName):
-    parts = packageName.split('.')
-    name = parts.pop()
-    if parts:
-        parent = '.'.join(parts)
-        setattr(sys.modules[parent], name, sys.modules[packageName])
-
-
-def yield_lines(strs):
-    """Yield non-empty/non-comment lines of a string or sequence"""
-    if isinstance(strs, six.string_types):
-        for s in strs.splitlines():
-            s = s.strip()
-            # skip blank lines/comments
-            if s and not s.startswith('#'):
-                yield s
-    else:
-        for ss in strs:
-            for s in yield_lines(ss):
-                yield s
-
-
-MODULE = re.compile(r"\w+(\.\w+)*$").match
-EGG_NAME = re.compile(
-    r"""
-    (?P[^-]+) (
-        -(?P[^-]+) (
-            -py(?P[^-]+) (
-                -(?P.+)
-            )?
-        )?
-    )?
-    """,
-    re.VERBOSE | re.IGNORECASE,
-).match
-
-
-class EntryPoint:
-    """Object representing an advertised importable object"""
-
-    def __init__(self, name, module_name, attrs=(), extras=(), dist=None):
-        if not MODULE(module_name):
-            raise ValueError("Invalid module name", module_name)
-        self.name = name
-        self.module_name = module_name
-        self.attrs = tuple(attrs)
-        self.extras = tuple(extras)
-        self.dist = dist
-
-    def __str__(self):
-        s = "%s = %s" % (self.name, self.module_name)
-        if self.attrs:
-            s += ':' + '.'.join(self.attrs)
-        if self.extras:
-            s += ' [%s]' % ','.join(self.extras)
-        return s
-
-    def __repr__(self):
-        return "EntryPoint.parse(%r)" % str(self)
-
-    def load(self, require=True, *args, **kwargs):
-        """
-        Require packages for this EntryPoint, then resolve it.
-        """
-        if not require or args or kwargs:
-            warnings.warn(
-                "Parameters to load are deprecated.  Call .resolve and "
-                ".require separately.",
-                PkgResourcesDeprecationWarning,
-                stacklevel=2,
-            )
-        if require:
-            self.require(*args, **kwargs)
-        return self.resolve()
-
-    def resolve(self):
-        """
-        Resolve the entry point from its module and attrs.
-        """
-        module = __import__(self.module_name, fromlist=['__name__'], level=0)
-        try:
-            return functools.reduce(getattr, self.attrs, module)
-        except AttributeError as exc:
-            raise ImportError(str(exc))
-
-    def require(self, env=None, installer=None):
-        if self.extras and not self.dist:
-            raise UnknownExtra("Can't require() without a distribution", self)
-
-        # Get the requirements for this entry point with all its extras and
-        # then resolve them. We have to pass `extras` along when resolving so
-        # that the working set knows what extras we want. Otherwise, for
-        # dist-info distributions, the working set will assume that the
-        # requirements for that extra are purely optional and skip over them.
-        reqs = self.dist.requires(self.extras)
-        items = working_set.resolve(reqs, env, installer, extras=self.extras)
-        list(map(working_set.add, items))
-
-    pattern = re.compile(
-        r'\s*'
-        r'(?P.+?)\s*'
-        r'=\s*'
-        r'(?P[\w.]+)\s*'
-        r'(:\s*(?P[\w.]+))?\s*'
-        r'(?P\[.*\])?\s*$'
-    )
-
-    @classmethod
-    def parse(cls, src, dist=None):
-        """Parse a single entry point from string `src`
-
-        Entry point syntax follows the form::
-
-            name = some.module:some.attr [extra1, extra2]
-
-        The entry name and module name are required, but the ``:attrs`` and
-        ``[extras]`` parts are optional
-        """
-        m = cls.pattern.match(src)
-        if not m:
-            msg = "EntryPoint must be in 'name=module:attrs [extras]' format"
-            raise ValueError(msg, src)
-        res = m.groupdict()
-        extras = cls._parse_extras(res['extras'])
-        attrs = res['attr'].split('.') if res['attr'] else ()
-        return cls(res['name'], res['module'], attrs, extras, dist)
-
-    @classmethod
-    def _parse_extras(cls, extras_spec):
-        if not extras_spec:
-            return ()
-        req = Requirement.parse('x' + extras_spec)
-        if req.specs:
-            raise ValueError()
-        return req.extras
-
-    @classmethod
-    def parse_group(cls, group, lines, dist=None):
-        """Parse an entry point group"""
-        if not MODULE(group):
-            raise ValueError("Invalid group name", group)
-        this = {}
-        for line in yield_lines(lines):
-            ep = cls.parse(line, dist)
-            if ep.name in this:
-                raise ValueError("Duplicate entry point", group, ep.name)
-            this[ep.name] = ep
-        return this
-
-    @classmethod
-    def parse_map(cls, data, dist=None):
-        """Parse a map of entry point groups"""
-        if isinstance(data, dict):
-            data = data.items()
-        else:
-            data = split_sections(data)
-        maps = {}
-        for group, lines in data:
-            if group is None:
-                if not lines:
-                    continue
-                raise ValueError("Entry points must be listed in groups")
-            group = group.strip()
-            if group in maps:
-                raise ValueError("Duplicate group name", group)
-            maps[group] = cls.parse_group(group, lines, dist)
-        return maps
-
-
-def _remove_md5_fragment(location):
-    if not location:
-        return ''
-    parsed = urllib.parse.urlparse(location)
-    if parsed[-1].startswith('md5='):
-        return urllib.parse.urlunparse(parsed[:-1] + ('',))
-    return location
-
-
-def _version_from_file(lines):
-    """
-    Given an iterable of lines from a Metadata file, return
-    the value of the Version field, if present, or None otherwise.
-    """
-    def is_version_line(line):
-        return line.lower().startswith('version:')
-    version_lines = filter(is_version_line, lines)
-    line = next(iter(version_lines), '')
-    _, _, value = line.partition(':')
-    return safe_version(value.strip()) or None
-
-
-class Distribution:
-    """Wrap an actual or potential sys.path entry w/metadata"""
-    PKG_INFO = 'PKG-INFO'
-
-    def __init__(
-            self, location=None, metadata=None, project_name=None,
-            version=None, py_version=PY_MAJOR, platform=None,
-            precedence=EGG_DIST):
-        self.project_name = safe_name(project_name or 'Unknown')
-        if version is not None:
-            self._version = safe_version(version)
-        self.py_version = py_version
-        self.platform = platform
-        self.location = location
-        self.precedence = precedence
-        self._provider = metadata or empty_provider
-
-    @classmethod
-    def from_location(cls, location, basename, metadata=None, **kw):
-        project_name, version, py_version, platform = [None] * 4
-        basename, ext = os.path.splitext(basename)
-        if ext.lower() in _distributionImpl:
-            cls = _distributionImpl[ext.lower()]
-
-            match = EGG_NAME(basename)
-            if match:
-                project_name, version, py_version, platform = match.group(
-                    'name', 'ver', 'pyver', 'plat'
-                )
-        return cls(
-            location, metadata, project_name=project_name, version=version,
-            py_version=py_version, platform=platform, **kw
-        )._reload_version()
-
-    def _reload_version(self):
-        return self
-
-    @property
-    def hashcmp(self):
-        return (
-            self.parsed_version,
-            self.precedence,
-            self.key,
-            _remove_md5_fragment(self.location),
-            self.py_version or '',
-            self.platform or '',
-        )
-
-    def __hash__(self):
-        return hash(self.hashcmp)
-
-    def __lt__(self, other):
-        return self.hashcmp < other.hashcmp
-
-    def __le__(self, other):
-        return self.hashcmp <= other.hashcmp
-
-    def __gt__(self, other):
-        return self.hashcmp > other.hashcmp
-
-    def __ge__(self, other):
-        return self.hashcmp >= other.hashcmp
-
-    def __eq__(self, other):
-        if not isinstance(other, self.__class__):
-            # It's not a Distribution, so they are not equal
-            return False
-        return self.hashcmp == other.hashcmp
-
-    def __ne__(self, other):
-        return not self == other
-
-    # These properties have to be lazy so that we don't have to load any
-    # metadata until/unless it's actually needed.  (i.e., some distributions
-    # may not know their name or version without loading PKG-INFO)
-
-    @property
-    def key(self):
-        try:
-            return self._key
-        except AttributeError:
-            self._key = key = self.project_name.lower()
-            return key
-
-    @property
-    def parsed_version(self):
-        if not hasattr(self, "_parsed_version"):
-            self._parsed_version = parse_version(self.version)
-
-        return self._parsed_version
-
-    def _warn_legacy_version(self):
-        LV = packaging.version.LegacyVersion
-        is_legacy = isinstance(self._parsed_version, LV)
-        if not is_legacy:
-            return
-
-        # While an empty version is technically a legacy version and
-        # is not a valid PEP 440 version, it's also unlikely to
-        # actually come from someone and instead it is more likely that
-        # it comes from setuptools attempting to parse a filename and
-        # including it in the list. So for that we'll gate this warning
-        # on if the version is anything at all or not.
-        if not self.version:
-            return
-
-        tmpl = textwrap.dedent("""
-            '{project_name} ({version})' is being parsed as a legacy,
-            non PEP 440,
-            version. You may find odd behavior and sort order.
-            In particular it will be sorted as less than 0.0. It
-            is recommended to migrate to PEP 440 compatible
-            versions.
-            """).strip().replace('\n', ' ')
-
-        warnings.warn(tmpl.format(**vars(self)), PEP440Warning)
-
-    @property
-    def version(self):
-        try:
-            return self._version
-        except AttributeError:
-            version = self._get_version()
-            if version is None:
-                path = self._get_metadata_path_for_display(self.PKG_INFO)
-                msg = (
-                    "Missing 'Version:' header and/or {} file at path: {}"
-                ).format(self.PKG_INFO, path)
-                raise ValueError(msg, self)
-
-            return version
-
-    @property
-    def _dep_map(self):
-        """
-        A map of extra to its list of (direct) requirements
-        for this distribution, including the null extra.
-        """
-        try:
-            return self.__dep_map
-        except AttributeError:
-            self.__dep_map = self._filter_extras(self._build_dep_map())
-        return self.__dep_map
-
-    @staticmethod
-    def _filter_extras(dm):
-        """
-        Given a mapping of extras to dependencies, strip off
-        environment markers and filter out any dependencies
-        not matching the markers.
-        """
-        for extra in list(filter(None, dm)):
-            new_extra = extra
-            reqs = dm.pop(extra)
-            new_extra, _, marker = extra.partition(':')
-            fails_marker = marker and (
-                invalid_marker(marker)
-                or not evaluate_marker(marker)
-            )
-            if fails_marker:
-                reqs = []
-            new_extra = safe_extra(new_extra) or None
-
-            dm.setdefault(new_extra, []).extend(reqs)
-        return dm
-
-    def _build_dep_map(self):
-        dm = {}
-        for name in 'requires.txt', 'depends.txt':
-            for extra, reqs in split_sections(self._get_metadata(name)):
-                dm.setdefault(extra, []).extend(parse_requirements(reqs))
-        return dm
-
-    def requires(self, extras=()):
-        """List of Requirements needed for this distro if `extras` are used"""
-        dm = self._dep_map
-        deps = []
-        deps.extend(dm.get(None, ()))
-        for ext in extras:
-            try:
-                deps.extend(dm[safe_extra(ext)])
-            except KeyError:
-                raise UnknownExtra(
-                    "%s has no such extra feature %r" % (self, ext)
-                )
-        return deps
-
-    def _get_metadata_path_for_display(self, name):
-        """
-        Return the path to the given metadata file, if available.
-        """
-        try:
-            # We need to access _get_metadata_path() on the provider object
-            # directly rather than through this class's __getattr__()
-            # since _get_metadata_path() is marked private.
-            path = self._provider._get_metadata_path(name)
-
-        # Handle exceptions e.g. in case the distribution's metadata
-        # provider doesn't support _get_metadata_path().
-        except Exception:
-            return '[could not detect]'
-
-        return path
-
-    def _get_metadata(self, name):
-        if self.has_metadata(name):
-            for line in self.get_metadata_lines(name):
-                yield line
-
-    def _get_version(self):
-        lines = self._get_metadata(self.PKG_INFO)
-        version = _version_from_file(lines)
-
-        return version
-
-    def activate(self, path=None, replace=False):
-        """Ensure distribution is importable on `path` (default=sys.path)"""
-        if path is None:
-            path = sys.path
-        self.insert_on(path, replace=replace)
-        if path is sys.path:
-            fixup_namespace_packages(self.location)
-            for pkg in self._get_metadata('namespace_packages.txt'):
-                if pkg in sys.modules:
-                    declare_namespace(pkg)
-
-    def egg_name(self):
-        """Return what this distribution's standard .egg filename should be"""
-        filename = "%s-%s-py%s" % (
-            to_filename(self.project_name), to_filename(self.version),
-            self.py_version or PY_MAJOR
-        )
-
-        if self.platform:
-            filename += '-' + self.platform
-        return filename
-
-    def __repr__(self):
-        if self.location:
-            return "%s (%s)" % (self, self.location)
-        else:
-            return str(self)
-
-    def __str__(self):
-        try:
-            version = getattr(self, 'version', None)
-        except ValueError:
-            version = None
-        version = version or "[unknown version]"
-        return "%s %s" % (self.project_name, version)
-
-    def __getattr__(self, attr):
-        """Delegate all unrecognized public attributes to .metadata provider"""
-        if attr.startswith('_'):
-            raise AttributeError(attr)
-        return getattr(self._provider, attr)
-
-    def __dir__(self):
-        return list(
-            set(super(Distribution, self).__dir__())
-            | set(
-                attr for attr in self._provider.__dir__()
-                if not attr.startswith('_')
-            )
-        )
-
-    if not hasattr(object, '__dir__'):
-        # python 2.7 not supported
-        del __dir__
-
-    @classmethod
-    def from_filename(cls, filename, metadata=None, **kw):
-        return cls.from_location(
-            _normalize_cached(filename), os.path.basename(filename), metadata,
-            **kw
-        )
-
-    def as_requirement(self):
-        """Return a ``Requirement`` that matches this distribution exactly"""
-        if isinstance(self.parsed_version, packaging.version.Version):
-            spec = "%s==%s" % (self.project_name, self.parsed_version)
-        else:
-            spec = "%s===%s" % (self.project_name, self.parsed_version)
-
-        return Requirement.parse(spec)
-
-    def load_entry_point(self, group, name):
-        """Return the `name` entry point of `group` or raise ImportError"""
-        ep = self.get_entry_info(group, name)
-        if ep is None:
-            raise ImportError("Entry point %r not found" % ((group, name),))
-        return ep.load()
-
-    def get_entry_map(self, group=None):
-        """Return the entry point map for `group`, or the full entry map"""
-        try:
-            ep_map = self._ep_map
-        except AttributeError:
-            ep_map = self._ep_map = EntryPoint.parse_map(
-                self._get_metadata('entry_points.txt'), self
-            )
-        if group is not None:
-            return ep_map.get(group, {})
-        return ep_map
-
-    def get_entry_info(self, group, name):
-        """Return the EntryPoint object for `group`+`name`, or ``None``"""
-        return self.get_entry_map(group).get(name)
-
-    def insert_on(self, path, loc=None, replace=False):
-        """Ensure self.location is on path
-
-        If replace=False (default):
-            - If location is already in path anywhere, do nothing.
-            - Else:
-              - If it's an egg and its parent directory is on path,
-                insert just ahead of the parent.
-              - Else: add to the end of path.
-        If replace=True:
-            - If location is already on path anywhere (not eggs)
-              or higher priority than its parent (eggs)
-              do nothing.
-            - Else:
-              - If it's an egg and its parent directory is on path,
-                insert just ahead of the parent,
-                removing any lower-priority entries.
-              - Else: add it to the front of path.
-        """
-
-        loc = loc or self.location
-        if not loc:
-            return
-
-        nloc = _normalize_cached(loc)
-        bdir = os.path.dirname(nloc)
-        npath = [(p and _normalize_cached(p) or p) for p in path]
-
-        for p, item in enumerate(npath):
-            if item == nloc:
-                if replace:
-                    break
-                else:
-                    # don't modify path (even removing duplicates) if
-                    # found and not replace
-                    return
-            elif item == bdir and self.precedence == EGG_DIST:
-                # if it's an .egg, give it precedence over its directory
-                # UNLESS it's already been added to sys.path and replace=False
-                if (not replace) and nloc in npath[p:]:
-                    return
-                if path is sys.path:
-                    self.check_version_conflict()
-                path.insert(p, loc)
-                npath.insert(p, nloc)
-                break
-        else:
-            if path is sys.path:
-                self.check_version_conflict()
-            if replace:
-                path.insert(0, loc)
-            else:
-                path.append(loc)
-            return
-
-        # p is the spot where we found or inserted loc; now remove duplicates
-        while True:
-            try:
-                np = npath.index(nloc, p + 1)
-            except ValueError:
-                break
-            else:
-                del npath[np], path[np]
-                # ha!
-                p = np
-
-        return
-
-    def check_version_conflict(self):
-        if self.key == 'setuptools':
-            # ignore the inevitable setuptools self-conflicts  :(
-            return
-
-        nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt'))
-        loc = normalize_path(self.location)
-        for modname in self._get_metadata('top_level.txt'):
-            if (modname not in sys.modules or modname in nsp
-                    or modname in _namespace_packages):
-                continue
-            if modname in ('pkg_resources', 'setuptools', 'site'):
-                continue
-            fn = getattr(sys.modules[modname], '__file__', None)
-            if fn and (normalize_path(fn).startswith(loc) or
-                       fn.startswith(self.location)):
-                continue
-            issue_warning(
-                "Module %s was already imported from %s, but %s is being added"
-                " to sys.path" % (modname, fn, self.location),
-            )
-
-    def has_version(self):
-        try:
-            self.version
-        except ValueError:
-            issue_warning("Unbuilt egg for " + repr(self))
-            return False
-        return True
-
-    def clone(self, **kw):
-        """Copy this distribution, substituting in any changed keyword args"""
-        names = 'project_name version py_version platform location precedence'
-        for attr in names.split():
-            kw.setdefault(attr, getattr(self, attr, None))
-        kw.setdefault('metadata', self._provider)
-        return self.__class__(**kw)
-
-    @property
-    def extras(self):
-        return [dep for dep in self._dep_map if dep]
-
-
-class EggInfoDistribution(Distribution):
-    def _reload_version(self):
-        """
-        Packages installed by distutils (e.g. numpy or scipy),
-        which uses an old safe_version, and so
-        their version numbers can get mangled when
-        converted to filenames (e.g., 1.11.0.dev0+2329eae to
-        1.11.0.dev0_2329eae). These distributions will not be
-        parsed properly
-        downstream by Distribution and safe_version, so
-        take an extra step and try to get the version number from
-        the metadata file itself instead of the filename.
-        """
-        md_version = self._get_version()
-        if md_version:
-            self._version = md_version
-        return self
-
-
-class DistInfoDistribution(Distribution):
-    """
-    Wrap an actual or potential sys.path entry
-    w/metadata, .dist-info style.
-    """
-    PKG_INFO = 'METADATA'
-    EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])")
-
-    @property
-    def _parsed_pkg_info(self):
-        """Parse and cache metadata"""
-        try:
-            return self._pkg_info
-        except AttributeError:
-            metadata = self.get_metadata(self.PKG_INFO)
-            self._pkg_info = email.parser.Parser().parsestr(metadata)
-            return self._pkg_info
-
-    @property
-    def _dep_map(self):
-        try:
-            return self.__dep_map
-        except AttributeError:
-            self.__dep_map = self._compute_dependencies()
-            return self.__dep_map
-
-    def _compute_dependencies(self):
-        """Recompute this distribution's dependencies."""
-        dm = self.__dep_map = {None: []}
-
-        reqs = []
-        # Including any condition expressions
-        for req in self._parsed_pkg_info.get_all('Requires-Dist') or []:
-            reqs.extend(parse_requirements(req))
-
-        def reqs_for_extra(extra):
-            for req in reqs:
-                if not req.marker or req.marker.evaluate({'extra': extra}):
-                    yield req
-
-        common = frozenset(reqs_for_extra(None))
-        dm[None].extend(common)
-
-        for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []:
-            s_extra = safe_extra(extra.strip())
-            dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common)
-
-        return dm
-
-
-_distributionImpl = {
-    '.egg': Distribution,
-    '.egg-info': EggInfoDistribution,
-    '.dist-info': DistInfoDistribution,
-}
-
-
-def issue_warning(*args, **kw):
-    level = 1
-    g = globals()
-    try:
-        # find the first stack frame that is *not* code in
-        # the pkg_resources module, to use for the warning
-        while sys._getframe(level).f_globals is g:
-            level += 1
-    except ValueError:
-        pass
-    warnings.warn(stacklevel=level + 1, *args, **kw)
-
-
-class RequirementParseError(ValueError):
-    def __str__(self):
-        return ' '.join(self.args)
-
-
-def parse_requirements(strs):
-    """Yield ``Requirement`` objects for each specification in `strs`
-
-    `strs` must be a string, or a (possibly-nested) iterable thereof.
-    """
-    # create a steppable iterator, so we can handle \-continuations
-    lines = iter(yield_lines(strs))
-
-    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()
-            try:
-                line += next(lines)
-            except StopIteration:
-                return
-        yield Requirement(line)
-
-
-class Requirement(packaging.requirements.Requirement):
-    def __init__(self, requirement_string):
-        """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
-        try:
-            super(Requirement, self).__init__(requirement_string)
-        except packaging.requirements.InvalidRequirement as e:
-            raise RequirementParseError(str(e))
-        self.unsafe_name = self.name
-        project_name = safe_name(self.name)
-        self.project_name, self.key = project_name, project_name.lower()
-        self.specs = [
-            (spec.operator, spec.version) for spec in self.specifier]
-        self.extras = tuple(map(safe_extra, self.extras))
-        self.hashCmp = (
-            self.key,
-            self.url,
-            self.specifier,
-            frozenset(self.extras),
-            str(self.marker) if self.marker else None,
-        )
-        self.__hash = hash(self.hashCmp)
-
-    def __eq__(self, other):
-        return (
-            isinstance(other, Requirement) and
-            self.hashCmp == other.hashCmp
-        )
-
-    def __ne__(self, other):
-        return not self == other
-
-    def __contains__(self, item):
-        if isinstance(item, Distribution):
-            if item.key != self.key:
-                return False
-
-            item = item.version
-
-        # Allow prereleases always in order to match the previous behavior of
-        # this method. In the future this should be smarter and follow PEP 440
-        # more accurately.
-        return self.specifier.contains(item, prereleases=True)
-
-    def __hash__(self):
-        return self.__hash
-
-    def __repr__(self):
-        return "Requirement.parse(%r)" % str(self)
-
-    @staticmethod
-    def parse(s):
-        req, = parse_requirements(s)
-        return req
-
-
-def _always_object(classes):
-    """
-    Ensure object appears in the mro even
-    for old-style classes.
-    """
-    if object not in classes:
-        return classes + (object,)
-    return classes
-
-
-def _find_adapter(registry, ob):
-    """Return an adapter factory for `ob` from `registry`"""
-    types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob))))
-    for t in types:
-        if t in registry:
-            return registry[t]
-
-
-def ensure_directory(path):
-    """Ensure that the parent directory of `path` exists"""
-    dirname = os.path.dirname(path)
-    py31compat.makedirs(dirname, exist_ok=True)
-
-
-def _bypass_ensure_directory(path):
-    """Sandbox-bypassing version of ensure_directory()"""
-    if not WRITE_SUPPORT:
-        raise IOError('"os.mkdir" not supported on this platform.')
-    dirname, filename = split(path)
-    if dirname and filename and not isdir(dirname):
-        _bypass_ensure_directory(dirname)
-        try:
-            mkdir(dirname, 0o755)
-        except FileExistsError:
-            pass
-
-
-def split_sections(s):
-    """Split a string or iterable thereof into (section, content) pairs
-
-    Each ``section`` is a stripped version of the section header ("[section]")
-    and each ``content`` is a list of stripped lines excluding blank lines and
-    comment-only lines.  If there are any such lines before the first section
-    header, they're returned in a first ``section`` of ``None``.
-    """
-    section = None
-    content = []
-    for line in yield_lines(s):
-        if line.startswith("["):
-            if line.endswith("]"):
-                if section or content:
-                    yield section, content
-                section = line[1:-1].strip()
-                content = []
-            else:
-                raise ValueError("Invalid section heading", line)
-        else:
-            content.append(line)
-
-    # wrap up last segment
-    yield section, content
-
-
-def _mkstemp(*args, **kw):
-    old_open = os.open
-    try:
-        # temporarily bypass sandboxing
-        os.open = os_open
-        return tempfile.mkstemp(*args, **kw)
-    finally:
-        # and then put it back
-        os.open = old_open
-
-
-# Silence the PEP440Warning by default, so that end users don't get hit by it
-# randomly just because they use pkg_resources. We want to append the rule
-# because we want earlier uses of filterwarnings to take precedence over this
-# one.
-warnings.filterwarnings("ignore", category=PEP440Warning, append=True)
-
-
-# from jaraco.functools 1.3
-def _call_aside(f, *args, **kwargs):
-    f(*args, **kwargs)
-    return f
-
-
-@_call_aside
-def _initialize(g=globals()):
-    "Set up global resource manager (deliberately not state-saved)"
-    manager = ResourceManager()
-    g['_manager'] = manager
-    g.update(
-        (name, getattr(manager, name))
-        for name in dir(manager)
-        if not name.startswith('_')
-    )
-
-
-@_call_aside
-def _initialize_master_working_set():
-    """
-    Prepare the master working set and make the ``require()``
-    API available.
-
-    This function has explicit effects on the global state
-    of pkg_resources. It is intended to be invoked once at
-    the initialization of this module.
-
-    Invocation by other packages is unsupported and done
-    at their own risk.
-    """
-    working_set = WorkingSet._build_master()
-    _declare_state('object', working_set=working_set)
-
-    require = working_set.require
-    iter_entry_points = working_set.iter_entry_points
-    add_activation_listener = working_set.subscribe
-    run_script = working_set.run_script
-    # backward compatibility
-    run_main = run_script
-    # Activate all distributions already on sys.path with replace=False and
-    # ensure that all distributions added to the working set in the future
-    # (e.g. by calling ``require()``) will get activated as well,
-    # with higher priority (replace=True).
-    tuple(
-        dist.activate(replace=False)
-        for dist in working_set
-    )
-    add_activation_listener(
-        lambda dist: dist.activate(replace=True),
-        existing=False,
-    )
-    working_set.entries = []
-    # match order
-    list(map(working_set.add_entry, sys.path))
-    globals().update(locals())
-
-class PkgResourcesDeprecationWarning(Warning):
-    """
-    Base class for warning about deprecations in ``pkg_resources``
-
-    This class is not derived from ``DeprecationWarning``, and as such is
-    visible by default.
-    """
diff --git a/.venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py b/.venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py
deleted file mode 100644
index a2d3007..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pkg_resources/py31compat.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import os
-import errno
-import sys
-
-from pip._vendor import six
-
-
-def _makedirs_31(path, exist_ok=False):
-    try:
-        os.makedirs(path)
-    except OSError as exc:
-        if not exist_ok or exc.errno != errno.EEXIST:
-            raise
-
-
-# rely on compatibility behavior until mode considerations
-#  and exists_ok considerations are disentangled.
-# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663
-needs_makedirs = (
-    six.PY2 or
-    (3, 4) <= sys.version_info < (3, 4, 1)
-)
-makedirs = _makedirs_31 if needs_makedirs else os.makedirs
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py
deleted file mode 100644
index 9d513dc..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/__init__.py
+++ /dev/null
@@ -1,340 +0,0 @@
-"""
-Utilities for determining application-specific dirs. See  for details and
-usage.
-"""
-from __future__ import annotations
-
-import os
-import sys
-from pathlib import Path
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
-    from pip._vendor.typing_extensions import Literal  # pragma: no cover
-
-from .api import PlatformDirsABC
-from .version import __version__, __version_info__
-
-
-def _set_platform_dir_class() -> type[PlatformDirsABC]:
-    if sys.platform == "win32":
-        from pip._vendor.platformdirs.windows import Windows as Result
-    elif sys.platform == "darwin":
-        from pip._vendor.platformdirs.macos import MacOS as Result
-    else:
-        from pip._vendor.platformdirs.unix import Unix as Result
-
-    if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system":
-
-        if os.getenv("SHELL") is not None:
-            return Result
-
-        from pip._vendor.platformdirs.android import _android_folder
-
-        if _android_folder() is not None:
-            from pip._vendor.platformdirs.android import Android
-
-            return Android  # return to avoid redefinition of result
-
-    return Result
-
-
-PlatformDirs = _set_platform_dir_class()  #: Currently active platform
-AppDirs = PlatformDirs  #: Backwards compatibility with appdirs
-
-
-def user_data_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: data directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir
-
-
-def site_data_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    multipath: bool = False,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param multipath: See `roaming `.
-    :returns: data directory shared by users
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir
-
-
-def user_config_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: config directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir
-
-
-def site_config_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    multipath: bool = False,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param multipath: See `roaming `.
-    :returns: config directory shared by the users
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir
-
-
-def user_cache_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `roaming `.
-    :returns: cache directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir
-
-
-def user_state_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: state directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir
-
-
-def user_log_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `roaming `.
-    :returns: log directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir
-
-
-def user_documents_dir() -> str:
-    """
-    :returns: documents directory tied to the user
-    """
-    return PlatformDirs().user_documents_dir
-
-
-def user_runtime_dir(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> str:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `opinion `.
-    :returns: runtime directory tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir
-
-
-def user_data_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: data path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path
-
-
-def site_data_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    multipath: bool = False,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param multipath: See `multipath `.
-    :returns: data path shared by users
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path
-
-
-def user_config_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: config path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path
-
-
-def site_config_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    multipath: bool = False,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param multipath: See `roaming `.
-    :returns: config path shared by the users
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path
-
-
-def user_cache_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `roaming `.
-    :returns: cache path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path
-
-
-def user_state_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    roaming: bool = False,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param roaming: See `roaming `.
-    :returns: state path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path
-
-
-def user_log_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `roaming `.
-    :returns: log path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path
-
-
-def user_documents_path() -> Path:
-    """
-    :returns: documents path tied to the user
-    """
-    return PlatformDirs().user_documents_path
-
-
-def user_runtime_path(
-    appname: str | None = None,
-    appauthor: str | None | Literal[False] = None,
-    version: str | None = None,
-    opinion: bool = True,
-) -> Path:
-    """
-    :param appname: See `appname `.
-    :param appauthor: See `appauthor `.
-    :param version: See `version `.
-    :param opinion: See `opinion `.
-    :returns: runtime path tied to the user
-    """
-    return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path
-
-
-__all__ = [
-    "__version__",
-    "__version_info__",
-    "PlatformDirs",
-    "AppDirs",
-    "PlatformDirsABC",
-    "user_data_dir",
-    "user_config_dir",
-    "user_cache_dir",
-    "user_state_dir",
-    "user_log_dir",
-    "user_documents_dir",
-    "user_runtime_dir",
-    "site_data_dir",
-    "site_config_dir",
-    "user_data_path",
-    "user_config_path",
-    "user_cache_path",
-    "user_state_path",
-    "user_log_path",
-    "user_documents_path",
-    "user_runtime_path",
-    "site_data_path",
-    "site_config_path",
-]
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py
deleted file mode 100644
index 9c54bfb..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/__main__.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import annotations
-
-from pip._vendor.platformdirs import PlatformDirs, __version__
-
-PROPS = (
-    "user_data_dir",
-    "user_config_dir",
-    "user_cache_dir",
-    "user_state_dir",
-    "user_log_dir",
-    "user_documents_dir",
-    "user_runtime_dir",
-    "site_data_dir",
-    "site_config_dir",
-)
-
-
-def main() -> None:
-    app_name = "MyApp"
-    app_author = "MyCompany"
-
-    print(f"-- platformdirs {__version__} --")
-
-    print("-- app dirs (with optional 'version')")
-    dirs = PlatformDirs(app_name, app_author, version="1.0")
-    for prop in PROPS:
-        print(f"{prop}: {getattr(dirs, prop)}")
-
-    print("\n-- app dirs (without optional 'version')")
-    dirs = PlatformDirs(app_name, app_author)
-    for prop in PROPS:
-        print(f"{prop}: {getattr(dirs, prop)}")
-
-    print("\n-- app dirs (without optional 'appauthor')")
-    dirs = PlatformDirs(app_name)
-    for prop in PROPS:
-        print(f"{prop}: {getattr(dirs, prop)}")
-
-    print("\n-- app dirs (with disabled 'appauthor')")
-    dirs = PlatformDirs(app_name, appauthor=False)
-    for prop in PROPS:
-        print(f"{prop}: {getattr(dirs, prop)}")
-
-
-if __name__ == "__main__":
-    main()
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/android.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/android.py
deleted file mode 100644
index eda8093..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/android.py
+++ /dev/null
@@ -1,120 +0,0 @@
-from __future__ import annotations
-
-import os
-import re
-import sys
-from functools import lru_cache
-from typing import cast
-
-from .api import PlatformDirsABC
-
-
-class Android(PlatformDirsABC):
-    """
-    Follows the guidance `from here `_. Makes use of the
-    `appname ` and
-    `version `.
-    """
-
-    @property
-    def user_data_dir(self) -> str:
-        """:return: data directory tied to the user, e.g. ``/data/user///files/``"""
-        return self._append_app_name_and_version(cast(str, _android_folder()), "files")
-
-    @property
-    def site_data_dir(self) -> str:
-        """:return: data directory shared by users, same as `user_data_dir`"""
-        return self.user_data_dir
-
-    @property
-    def user_config_dir(self) -> str:
-        """
-        :return: config directory tied to the user, e.g. ``/data/user///shared_prefs/``
-        """
-        return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs")
-
-    @property
-    def site_config_dir(self) -> str:
-        """:return: config directory shared by the users, same as `user_config_dir`"""
-        return self.user_config_dir
-
-    @property
-    def user_cache_dir(self) -> str:
-        """:return: cache directory tied to the user, e.g. e.g. ``/data/user///cache/``"""
-        return self._append_app_name_and_version(cast(str, _android_folder()), "cache")
-
-    @property
-    def user_state_dir(self) -> str:
-        """:return: state directory tied to the user, same as `user_data_dir`"""
-        return self.user_data_dir
-
-    @property
-    def user_log_dir(self) -> str:
-        """
-        :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it,
-          e.g. ``/data/user///cache//log``
-        """
-        path = self.user_cache_dir
-        if self.opinion:
-            path = os.path.join(path, "log")
-        return path
-
-    @property
-    def user_documents_dir(self) -> str:
-        """
-        :return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``
-        """
-        return _android_documents_folder()
-
-    @property
-    def user_runtime_dir(self) -> str:
-        """
-        :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it,
-          e.g. ``/data/user///cache//tmp``
-        """
-        path = self.user_cache_dir
-        if self.opinion:
-            path = os.path.join(path, "tmp")
-        return path
-
-
-@lru_cache(maxsize=1)
-def _android_folder() -> str | None:
-    """:return: base folder for the Android OS or None if cannot be found"""
-    try:
-        # First try to get path to android app via pyjnius
-        from jnius import autoclass
-
-        Context = autoclass("android.content.Context")  # noqa: N806
-        result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath()
-    except Exception:
-        # if fails find an android folder looking path on the sys.path
-        pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files")
-        for path in sys.path:
-            if pattern.match(path):
-                result = path.split("/files")[0]
-                break
-        else:
-            result = None
-    return result
-
-
-@lru_cache(maxsize=1)
-def _android_documents_folder() -> str:
-    """:return: documents folder for the Android OS"""
-    # Get directories with pyjnius
-    try:
-        from jnius import autoclass
-
-        Context = autoclass("android.content.Context")  # noqa: N806
-        Environment = autoclass("android.os.Environment")  # noqa: N806
-        documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath()
-    except Exception:
-        documents_dir = "/storage/emulated/0/Documents"
-
-    return documents_dir
-
-
-__all__ = [
-    "Android",
-]
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/api.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/api.py
deleted file mode 100644
index 6f6e2c2..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/api.py
+++ /dev/null
@@ -1,156 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-from abc import ABC, abstractmethod
-from pathlib import Path
-
-if sys.version_info >= (3, 8):  # pragma: no branch
-    from typing import Literal  # pragma: no cover
-
-
-class PlatformDirsABC(ABC):
-    """
-    Abstract base class for platform directories.
-    """
-
-    def __init__(
-        self,
-        appname: str | None = None,
-        appauthor: str | None | Literal[False] = None,
-        version: str | None = None,
-        roaming: bool = False,
-        multipath: bool = False,
-        opinion: bool = True,
-    ):
-        """
-        Create a new platform directory.
-
-        :param appname: See `appname`.
-        :param appauthor: See `appauthor`.
-        :param version: See `version`.
-        :param roaming: See `roaming`.
-        :param multipath: See `multipath`.
-        :param opinion: See `opinion`.
-        """
-        self.appname = appname  #: The name of application.
-        self.appauthor = appauthor
-        """
-        The name of the app author or distributing body for this application. Typically, it is the owning company name.
-        Defaults to `appname`. You may pass ``False`` to disable it.
-        """
-        self.version = version
-        """
-        An optional version path element to append to the path. You might want to use this if you want multiple versions
-        of your app to be able to run independently. If used, this would typically be ``.``.
-        """
-        self.roaming = roaming
-        """
-        Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup
-        for roaming profiles, this user data will be synced on login (see
-        `here `_).
-        """
-        self.multipath = multipath
-        """
-        An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be
-        returned. By default, the first item would only be returned.
-        """
-        self.opinion = opinion  #: A flag to indicating to use opinionated values.
-
-    def _append_app_name_and_version(self, *base: str) -> str:
-        params = list(base[1:])
-        if self.appname:
-            params.append(self.appname)
-            if self.version:
-                params.append(self.version)
-        return os.path.join(base[0], *params)
-
-    @property
-    @abstractmethod
-    def user_data_dir(self) -> str:
-        """:return: data directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def site_data_dir(self) -> str:
-        """:return: data directory shared by users"""
-
-    @property
-    @abstractmethod
-    def user_config_dir(self) -> str:
-        """:return: config directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def site_config_dir(self) -> str:
-        """:return: config directory shared by the users"""
-
-    @property
-    @abstractmethod
-    def user_cache_dir(self) -> str:
-        """:return: cache directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def user_state_dir(self) -> str:
-        """:return: state directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def user_log_dir(self) -> str:
-        """:return: log directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def user_documents_dir(self) -> str:
-        """:return: documents directory tied to the user"""
-
-    @property
-    @abstractmethod
-    def user_runtime_dir(self) -> str:
-        """:return: runtime directory tied to the user"""
-
-    @property
-    def user_data_path(self) -> Path:
-        """:return: data path tied to the user"""
-        return Path(self.user_data_dir)
-
-    @property
-    def site_data_path(self) -> Path:
-        """:return: data path shared by users"""
-        return Path(self.site_data_dir)
-
-    @property
-    def user_config_path(self) -> Path:
-        """:return: config path tied to the user"""
-        return Path(self.user_config_dir)
-
-    @property
-    def site_config_path(self) -> Path:
-        """:return: config path shared by the users"""
-        return Path(self.site_config_dir)
-
-    @property
-    def user_cache_path(self) -> Path:
-        """:return: cache path tied to the user"""
-        return Path(self.user_cache_dir)
-
-    @property
-    def user_state_path(self) -> Path:
-        """:return: state path tied to the user"""
-        return Path(self.user_state_dir)
-
-    @property
-    def user_log_path(self) -> Path:
-        """:return: log path tied to the user"""
-        return Path(self.user_log_dir)
-
-    @property
-    def user_documents_path(self) -> Path:
-        """:return: documents path tied to the user"""
-        return Path(self.user_documents_dir)
-
-    @property
-    def user_runtime_path(self) -> Path:
-        """:return: runtime path tied to the user"""
-        return Path(self.user_runtime_dir)
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py
deleted file mode 100644
index a01337c..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/macos.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from __future__ import annotations
-
-import os
-
-from .api import PlatformDirsABC
-
-
-class MacOS(PlatformDirsABC):
-    """
-    Platform directories for the macOS operating system. Follows the guidance from `Apple documentation
-    `_.
-    Makes use of the `appname ` and
-    `version `.
-    """
-
-    @property
-    def user_data_dir(self) -> str:
-        """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``"""
-        return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support/"))
-
-    @property
-    def site_data_dir(self) -> str:
-        """:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``"""
-        return self._append_app_name_and_version("/Library/Application Support")
-
-    @property
-    def user_config_dir(self) -> str:
-        """:return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``"""
-        return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/"))
-
-    @property
-    def site_config_dir(self) -> str:
-        """:return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``"""
-        return self._append_app_name_and_version("/Library/Preferences")
-
-    @property
-    def user_cache_dir(self) -> str:
-        """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``"""
-        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches"))
-
-    @property
-    def user_state_dir(self) -> str:
-        """:return: state directory tied to the user, same as `user_data_dir`"""
-        return self.user_data_dir
-
-    @property
-    def user_log_dir(self) -> str:
-        """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``"""
-        return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs"))
-
-    @property
-    def user_documents_dir(self) -> str:
-        """:return: documents directory tied to the user, e.g. ``~/Documents``"""
-        return os.path.expanduser("~/Documents")
-
-    @property
-    def user_runtime_dir(self) -> str:
-        """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``"""
-        return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems"))
-
-
-__all__ = [
-    "MacOS",
-]
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py
deleted file mode 100644
index 2fbd4d4..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/unix.py
+++ /dev/null
@@ -1,181 +0,0 @@
-from __future__ import annotations
-
-import os
-import sys
-from configparser import ConfigParser
-from pathlib import Path
-
-from .api import PlatformDirsABC
-
-if sys.platform.startswith("linux"):  # pragma: no branch # no op check, only to please the type checker
-    from os import getuid
-else:
-
-    def getuid() -> int:
-        raise RuntimeError("should only be used on Linux")
-
-
-class Unix(PlatformDirsABC):
-    """
-    On Unix/Linux, we follow the
-    `XDG Basedir Spec `_. The spec allows
-    overriding directories with environment variables. The examples show are the default values, alongside the name of
-    the environment variable that overrides them. Makes use of the
-    `appname `,
-    `version `,
-    `multipath `,
-    `opinion `.
-    """
-
-    @property
-    def user_data_dir(self) -> str:
-        """
-        :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or
-         ``$XDG_DATA_HOME/$appname/$version``
-        """
-        path = os.environ.get("XDG_DATA_HOME", "")
-        if not path.strip():
-            path = os.path.expanduser("~/.local/share")
-        return self._append_app_name_and_version(path)
-
-    @property
-    def site_data_dir(self) -> str:
-        """
-        :return: data directories shared by users (if `multipath ` is
-         enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
-         path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version``
-        """
-        # XDG default for $XDG_DATA_DIRS; only first, if multipath is False
-        path = os.environ.get("XDG_DATA_DIRS", "")
-        if not path.strip():
-            path = f"/usr/local/share{os.pathsep}/usr/share"
-        return self._with_multi_path(path)
-
-    def _with_multi_path(self, path: str) -> str:
-        path_list = path.split(os.pathsep)
-        if not self.multipath:
-            path_list = path_list[0:1]
-        path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list]
-        return os.pathsep.join(path_list)
-
-    @property
-    def user_config_dir(self) -> str:
-        """
-        :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or
-         ``$XDG_CONFIG_HOME/$appname/$version``
-        """
-        path = os.environ.get("XDG_CONFIG_HOME", "")
-        if not path.strip():
-            path = os.path.expanduser("~/.config")
-        return self._append_app_name_and_version(path)
-
-    @property
-    def site_config_dir(self) -> str:
-        """
-        :return: config directories shared by users (if `multipath `
-         is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS
-         path separator), e.g. ``/etc/xdg/$appname/$version``
-        """
-        # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False
-        path = os.environ.get("XDG_CONFIG_DIRS", "")
-        if not path.strip():
-            path = "/etc/xdg"
-        return self._with_multi_path(path)
-
-    @property
-    def user_cache_dir(self) -> str:
-        """
-        :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or
-         ``~/$XDG_CACHE_HOME/$appname/$version``
-        """
-        path = os.environ.get("XDG_CACHE_HOME", "")
-        if not path.strip():
-            path = os.path.expanduser("~/.cache")
-        return self._append_app_name_and_version(path)
-
-    @property
-    def user_state_dir(self) -> str:
-        """
-        :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or
-         ``$XDG_STATE_HOME/$appname/$version``
-        """
-        path = os.environ.get("XDG_STATE_HOME", "")
-        if not path.strip():
-            path = os.path.expanduser("~/.local/state")
-        return self._append_app_name_and_version(path)
-
-    @property
-    def user_log_dir(self) -> str:
-        """
-        :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``log`` in it
-        """
-        path = self.user_cache_dir
-        if self.opinion:
-            path = os.path.join(path, "log")
-        return path
-
-    @property
-    def user_documents_dir(self) -> str:
-        """
-        :return: documents directory tied to the user, e.g. ``~/Documents``
-        """
-        documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR")
-        if documents_dir is None:
-            documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip()
-            if not documents_dir:
-                documents_dir = os.path.expanduser("~/Documents")
-
-        return documents_dir
-
-    @property
-    def user_runtime_dir(self) -> str:
-        """
-        :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or
-         ``$XDG_RUNTIME_DIR/$appname/$version``
-        """
-        path = os.environ.get("XDG_RUNTIME_DIR", "")
-        if not path.strip():
-            path = f"/run/user/{getuid()}"
-        return self._append_app_name_and_version(path)
-
-    @property
-    def site_data_path(self) -> Path:
-        """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``"""
-        return self._first_item_as_path_if_multipath(self.site_data_dir)
-
-    @property
-    def site_config_path(self) -> Path:
-        """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``"""
-        return self._first_item_as_path_if_multipath(self.site_config_dir)
-
-    def _first_item_as_path_if_multipath(self, directory: str) -> Path:
-        if self.multipath:
-            # If multipath is True, the first path is returned.
-            directory = directory.split(os.pathsep)[0]
-        return Path(directory)
-
-
-def _get_user_dirs_folder(key: str) -> str | None:
-    """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/"""
-    user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs")
-    if os.path.exists(user_dirs_config_path):
-        parser = ConfigParser()
-
-        with open(user_dirs_config_path) as stream:
-            # Add fake section header, so ConfigParser doesn't complain
-            parser.read_string(f"[top]\n{stream.read()}")
-
-        if key not in parser["top"]:
-            return None
-
-        path = parser["top"][key].strip('"')
-        # Handle relative home paths
-        path = path.replace("$HOME", os.path.expanduser("~"))
-        return path
-
-    return None
-
-
-__all__ = [
-    "Unix",
-]
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/version.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/version.py
deleted file mode 100644
index 4552c02..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/version.py
+++ /dev/null
@@ -1,4 +0,0 @@
-"""Version information"""
-
-__version__ = "2.5.2"
-__version_info__ = (2, 5, 2)
diff --git a/.venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py b/.venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py
deleted file mode 100644
index ef972bd..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/platformdirs/windows.py
+++ /dev/null
@@ -1,182 +0,0 @@
-from __future__ import annotations
-
-import ctypes
-import os
-from functools import lru_cache
-from typing import Callable
-
-from .api import PlatformDirsABC
-
-
-class Windows(PlatformDirsABC):
-    """`MSDN on where to store app data files
-    `_.
-    Makes use of the
-    `appname `,
-    `appauthor `,
-    `version `,
-    `roaming `,
-    `opinion `."""
-
-    @property
-    def user_data_dir(self) -> str:
-        """
-        :return: data directory tied to the user, e.g.
-         ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or
-         ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming)
-        """
-        const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA"
-        path = os.path.normpath(get_win_folder(const))
-        return self._append_parts(path)
-
-    def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str:
-        params = []
-        if self.appname:
-            if self.appauthor is not False:
-                author = self.appauthor or self.appname
-                params.append(author)
-            params.append(self.appname)
-            if opinion_value is not None and self.opinion:
-                params.append(opinion_value)
-            if self.version:
-                params.append(self.version)
-        return os.path.join(path, *params)
-
-    @property
-    def site_data_dir(self) -> str:
-        """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``"""
-        path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA"))
-        return self._append_parts(path)
-
-    @property
-    def user_config_dir(self) -> str:
-        """:return: config directory tied to the user, same as `user_data_dir`"""
-        return self.user_data_dir
-
-    @property
-    def site_config_dir(self) -> str:
-        """:return: config directory shared by the users, same as `site_data_dir`"""
-        return self.site_data_dir
-
-    @property
-    def user_cache_dir(self) -> str:
-        """
-        :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g.
-         ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version``
-        """
-        path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA"))
-        return self._append_parts(path, opinion_value="Cache")
-
-    @property
-    def user_state_dir(self) -> str:
-        """:return: state directory tied to the user, same as `user_data_dir`"""
-        return self.user_data_dir
-
-    @property
-    def user_log_dir(self) -> str:
-        """
-        :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it
-        """
-        path = self.user_data_dir
-        if self.opinion:
-            path = os.path.join(path, "Logs")
-        return path
-
-    @property
-    def user_documents_dir(self) -> str:
-        """
-        :return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``
-        """
-        return os.path.normpath(get_win_folder("CSIDL_PERSONAL"))
-
-    @property
-    def user_runtime_dir(self) -> str:
-        """
-        :return: runtime directory tied to the user, e.g.
-         ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname``
-        """
-        path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp"))
-        return self._append_parts(path)
-
-
-def get_win_folder_from_env_vars(csidl_name: str) -> str:
-    """Get folder from environment variables."""
-    if csidl_name == "CSIDL_PERSONAL":  # does not have an environment name
-        return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents")
-
-    env_var_name = {
-        "CSIDL_APPDATA": "APPDATA",
-        "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE",
-        "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA",
-    }.get(csidl_name)
-    if env_var_name is None:
-        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
-    result = os.environ.get(env_var_name)
-    if result is None:
-        raise ValueError(f"Unset environment variable: {env_var_name}")
-    return result
-
-
-def get_win_folder_from_registry(csidl_name: str) -> str:
-    """Get folder from the registry.
-
-    This is a fallback technique at best. I'm not sure if using the
-    registry for this guarantees us the correct answer for all CSIDL_*
-    names.
-    """
-    shell_folder_name = {
-        "CSIDL_APPDATA": "AppData",
-        "CSIDL_COMMON_APPDATA": "Common AppData",
-        "CSIDL_LOCAL_APPDATA": "Local AppData",
-        "CSIDL_PERSONAL": "Personal",
-    }.get(csidl_name)
-    if shell_folder_name is None:
-        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
-
-    import winreg
-
-    key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
-    directory, _ = winreg.QueryValueEx(key, shell_folder_name)
-    return str(directory)
-
-
-def get_win_folder_via_ctypes(csidl_name: str) -> str:
-    """Get folder with ctypes."""
-    csidl_const = {
-        "CSIDL_APPDATA": 26,
-        "CSIDL_COMMON_APPDATA": 35,
-        "CSIDL_LOCAL_APPDATA": 28,
-        "CSIDL_PERSONAL": 5,
-    }.get(csidl_name)
-    if csidl_const is None:
-        raise ValueError(f"Unknown CSIDL name: {csidl_name}")
-
-    buf = ctypes.create_unicode_buffer(1024)
-    windll = getattr(ctypes, "windll")  # noqa: B009 # using getattr to avoid false positive with mypy type checker
-    windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
-
-    # Downgrade to short path name if it has highbit chars.
-    if any(ord(c) > 255 for c in buf):
-        buf2 = ctypes.create_unicode_buffer(1024)
-        if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
-            buf = buf2
-
-    return buf.value
-
-
-def _pick_get_win_folder() -> Callable[[str], str]:
-    if hasattr(ctypes, "windll"):
-        return get_win_folder_via_ctypes
-    try:
-        import winreg  # noqa: F401
-    except ImportError:
-        return get_win_folder_from_env_vars
-    else:
-        return get_win_folder_from_registry
-
-
-get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder())
-
-__all__ = [
-    "Windows",
-]
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pygments/__init__.py
deleted file mode 100644
index 7185e53..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/__init__.py
+++ /dev/null
@@ -1,82 +0,0 @@
-"""
-    Pygments
-    ~~~~~~~~
-
-    Pygments is a syntax highlighting package written in Python.
-
-    It is a generic syntax highlighter for general use in all kinds of software
-    such as forum systems, wikis or other applications that need to prettify
-    source code. Highlights are:
-
-    * a wide range of common languages and markup formats is supported
-    * special attention is paid to details, increasing quality by a fair amount
-    * support for new languages and formats are added easily
-    * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image
-      formats that PIL supports, and ANSI sequences
-    * it is usable as a command-line tool and as a library
-    * ... and it highlights even Brainfuck!
-
-    The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``.
-
-    .. _Pygments master branch:
-       https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-from io import StringIO, BytesIO
-
-__version__ = '2.13.0'
-__docformat__ = 'restructuredtext'
-
-__all__ = ['lex', 'format', 'highlight']
-
-
-def lex(code, lexer):
-    """
-    Lex ``code`` with ``lexer`` and return an iterable of tokens.
-    """
-    try:
-        return lexer.get_tokens(code)
-    except TypeError:
-        # Heuristic to catch a common mistake.
-        from pip._vendor.pygments.lexer import RegexLexer
-        if isinstance(lexer, type) and issubclass(lexer, RegexLexer):
-            raise TypeError('lex() argument must be a lexer instance, '
-                            'not a class')
-        raise
-
-
-def format(tokens, formatter, outfile=None):  # pylint: disable=redefined-builtin
-    """
-    Format a tokenlist ``tokens`` with the formatter ``formatter``.
-
-    If ``outfile`` is given and a valid file object (an object
-    with a ``write`` method), the result will be written to it, otherwise
-    it is returned as a string.
-    """
-    try:
-        if not outfile:
-            realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO()
-            formatter.format(tokens, realoutfile)
-            return realoutfile.getvalue()
-        else:
-            formatter.format(tokens, outfile)
-    except TypeError:
-        # Heuristic to catch a common mistake.
-        from pip._vendor.pygments.formatter import Formatter
-        if isinstance(formatter, type) and issubclass(formatter, Formatter):
-            raise TypeError('format() argument must be a formatter instance, '
-                            'not a class')
-        raise
-
-
-def highlight(code, lexer, formatter, outfile=None):
-    """
-    Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``.
-
-    If ``outfile`` is given and a valid file object (an object
-    with a ``write`` method), the result will be written to it, otherwise
-    it is returned as a string.
-    """
-    return format(lex(code, lexer), formatter, outfile)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/__main__.py b/.venv/Lib/site-packages/pip/_vendor/pygments/__main__.py
deleted file mode 100644
index 90cafd9..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/__main__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-"""
-    pygments.__main__
-    ~~~~~~~~~~~~~~~~~
-
-    Main entry point for ``python -m pygments``.
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-import sys
-from pip._vendor.pygments.cmdline import main
-
-try:
-    sys.exit(main(sys.argv))
-except KeyboardInterrupt:
-    sys.exit(1)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py b/.venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py
deleted file mode 100644
index de73b06..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/cmdline.py
+++ /dev/null
@@ -1,668 +0,0 @@
-"""
-    pygments.cmdline
-    ~~~~~~~~~~~~~~~~
-
-    Command line interface.
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-import os
-import sys
-import shutil
-import argparse
-from textwrap import dedent
-
-from pip._vendor.pygments import __version__, highlight
-from pip._vendor.pygments.util import ClassNotFound, OptionError, docstring_headline, \
-    guess_decode, guess_decode_from_terminal, terminal_encoding, \
-    UnclosingTextIOWrapper
-from pip._vendor.pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \
-    load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename
-from pip._vendor.pygments.lexers.special import TextLexer
-from pip._vendor.pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter
-from pip._vendor.pygments.formatters import get_all_formatters, get_formatter_by_name, \
-    load_formatter_from_file, get_formatter_for_filename, find_formatter_class
-from pip._vendor.pygments.formatters.terminal import TerminalFormatter
-from pip._vendor.pygments.formatters.terminal256 import Terminal256Formatter, TerminalTrueColorFormatter
-from pip._vendor.pygments.filters import get_all_filters, find_filter_class
-from pip._vendor.pygments.styles import get_all_styles, get_style_by_name
-
-
-def _parse_options(o_strs):
-    opts = {}
-    if not o_strs:
-        return opts
-    for o_str in o_strs:
-        if not o_str.strip():
-            continue
-        o_args = o_str.split(',')
-        for o_arg in o_args:
-            o_arg = o_arg.strip()
-            try:
-                o_key, o_val = o_arg.split('=', 1)
-                o_key = o_key.strip()
-                o_val = o_val.strip()
-            except ValueError:
-                opts[o_arg] = True
-            else:
-                opts[o_key] = o_val
-    return opts
-
-
-def _parse_filters(f_strs):
-    filters = []
-    if not f_strs:
-        return filters
-    for f_str in f_strs:
-        if ':' in f_str:
-            fname, fopts = f_str.split(':', 1)
-            filters.append((fname, _parse_options([fopts])))
-        else:
-            filters.append((f_str, {}))
-    return filters
-
-
-def _print_help(what, name):
-    try:
-        if what == 'lexer':
-            cls = get_lexer_by_name(name)
-            print("Help on the %s lexer:" % cls.name)
-            print(dedent(cls.__doc__))
-        elif what == 'formatter':
-            cls = find_formatter_class(name)
-            print("Help on the %s formatter:" % cls.name)
-            print(dedent(cls.__doc__))
-        elif what == 'filter':
-            cls = find_filter_class(name)
-            print("Help on the %s filter:" % name)
-            print(dedent(cls.__doc__))
-        return 0
-    except (AttributeError, ValueError):
-        print("%s not found!" % what, file=sys.stderr)
-        return 1
-
-
-def _print_list(what):
-    if what == 'lexer':
-        print()
-        print("Lexers:")
-        print("~~~~~~~")
-
-        info = []
-        for fullname, names, exts, _ in get_all_lexers():
-            tup = (', '.join(names)+':', fullname,
-                   exts and '(filenames ' + ', '.join(exts) + ')' or '')
-            info.append(tup)
-        info.sort()
-        for i in info:
-            print(('* %s\n    %s %s') % i)
-
-    elif what == 'formatter':
-        print()
-        print("Formatters:")
-        print("~~~~~~~~~~~")
-
-        info = []
-        for cls in get_all_formatters():
-            doc = docstring_headline(cls)
-            tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
-                   '(filenames ' + ', '.join(cls.filenames) + ')' or '')
-            info.append(tup)
-        info.sort()
-        for i in info:
-            print(('* %s\n    %s %s') % i)
-
-    elif what == 'filter':
-        print()
-        print("Filters:")
-        print("~~~~~~~~")
-
-        for name in get_all_filters():
-            cls = find_filter_class(name)
-            print("* " + name + ':')
-            print("    %s" % docstring_headline(cls))
-
-    elif what == 'style':
-        print()
-        print("Styles:")
-        print("~~~~~~~")
-
-        for name in get_all_styles():
-            cls = get_style_by_name(name)
-            print("* " + name + ':')
-            print("    %s" % docstring_headline(cls))
-
-
-def _print_list_as_json(requested_items):
-    import json
-    result = {}
-    if 'lexer' in requested_items:
-        info = {}
-        for fullname, names, filenames, mimetypes in get_all_lexers():
-            info[fullname] = {
-                'aliases': names,
-                'filenames': filenames,
-                'mimetypes': mimetypes
-            }
-        result['lexers'] = info
-
-    if 'formatter' in requested_items:
-        info = {}
-        for cls in get_all_formatters():
-            doc = docstring_headline(cls)
-            info[cls.name] = {
-                'aliases': cls.aliases,
-                'filenames': cls.filenames,
-                'doc': doc
-            }
-        result['formatters'] = info
-
-    if 'filter' in requested_items:
-        info = {}
-        for name in get_all_filters():
-            cls = find_filter_class(name)
-            info[name] = {
-                'doc': docstring_headline(cls)
-            }
-        result['filters'] = info
-
-    if 'style' in requested_items:
-        info = {}
-        for name in get_all_styles():
-            cls = get_style_by_name(name)
-            info[name] = {
-                'doc': docstring_headline(cls)
-            }
-        result['styles'] = info
-
-    json.dump(result, sys.stdout)
-
-def main_inner(parser, argns):
-    if argns.help:
-        parser.print_help()
-        return 0
-
-    if argns.V:
-        print('Pygments version %s, (c) 2006-2022 by Georg Brandl, Matthäus '
-              'Chajdas and contributors.' % __version__)
-        return 0
-
-    def is_only_option(opt):
-        return not any(v for (k, v) in vars(argns).items() if k != opt)
-
-    # handle ``pygmentize -L``
-    if argns.L is not None:
-        arg_set = set()
-        for k, v in vars(argns).items():
-            if v:
-                arg_set.add(k)
-
-        arg_set.discard('L')
-        arg_set.discard('json')
-
-        if arg_set:
-            parser.print_help(sys.stderr)
-            return 2
-
-        # print version
-        if not argns.json:
-            main(['', '-V'])
-        allowed_types = {'lexer', 'formatter', 'filter', 'style'}
-        largs = [arg.rstrip('s') for arg in argns.L]
-        if any(arg not in allowed_types for arg in largs):
-            parser.print_help(sys.stderr)
-            return 0
-        if not largs:
-            largs = allowed_types
-        if not argns.json:
-            for arg in largs:
-                _print_list(arg)
-        else:
-            _print_list_as_json(largs)
-        return 0
-
-    # handle ``pygmentize -H``
-    if argns.H:
-        if not is_only_option('H'):
-            parser.print_help(sys.stderr)
-            return 2
-        what, name = argns.H
-        if what not in ('lexer', 'formatter', 'filter'):
-            parser.print_help(sys.stderr)
-            return 2
-        return _print_help(what, name)
-
-    # parse -O options
-    parsed_opts = _parse_options(argns.O or [])
-
-    # parse -P options
-    for p_opt in argns.P or []:
-        try:
-            name, value = p_opt.split('=', 1)
-        except ValueError:
-            parsed_opts[p_opt] = True
-        else:
-            parsed_opts[name] = value
-
-    # encodings
-    inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding'))
-    outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding'))
-
-    # handle ``pygmentize -N``
-    if argns.N:
-        lexer = find_lexer_class_for_filename(argns.N)
-        if lexer is None:
-            lexer = TextLexer
-
-        print(lexer.aliases[0])
-        return 0
-
-    # handle ``pygmentize -C``
-    if argns.C:
-        inp = sys.stdin.buffer.read()
-        try:
-            lexer = guess_lexer(inp, inencoding=inencoding)
-        except ClassNotFound:
-            lexer = TextLexer
-
-        print(lexer.aliases[0])
-        return 0
-
-    # handle ``pygmentize -S``
-    S_opt = argns.S
-    a_opt = argns.a
-    if S_opt is not None:
-        f_opt = argns.f
-        if not f_opt:
-            parser.print_help(sys.stderr)
-            return 2
-        if argns.l or argns.INPUTFILE:
-            parser.print_help(sys.stderr)
-            return 2
-
-        try:
-            parsed_opts['style'] = S_opt
-            fmter = get_formatter_by_name(f_opt, **parsed_opts)
-        except ClassNotFound as err:
-            print(err, file=sys.stderr)
-            return 1
-
-        print(fmter.get_style_defs(a_opt or ''))
-        return 0
-
-    # if no -S is given, -a is not allowed
-    if argns.a is not None:
-        parser.print_help(sys.stderr)
-        return 2
-
-    # parse -F options
-    F_opts = _parse_filters(argns.F or [])
-
-    # -x: allow custom (eXternal) lexers and formatters
-    allow_custom_lexer_formatter = bool(argns.x)
-
-    # select lexer
-    lexer = None
-
-    # given by name?
-    lexername = argns.l
-    if lexername:
-        # custom lexer, located relative to user's cwd
-        if allow_custom_lexer_formatter and '.py' in lexername:
-            try:
-                filename = None
-                name = None
-                if ':' in lexername:
-                    filename, name = lexername.rsplit(':', 1)
-
-                    if '.py' in name:
-                        # This can happen on Windows: If the lexername is
-                        # C:\lexer.py -- return to normal load path in that case
-                        name = None
-
-                if filename and name:
-                    lexer = load_lexer_from_file(filename, name,
-                                                 **parsed_opts)
-                else:
-                    lexer = load_lexer_from_file(lexername, **parsed_opts)
-            except ClassNotFound as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-        else:
-            try:
-                lexer = get_lexer_by_name(lexername, **parsed_opts)
-            except (OptionError, ClassNotFound) as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-
-    # read input code
-    code = None
-
-    if argns.INPUTFILE:
-        if argns.s:
-            print('Error: -s option not usable when input file specified',
-                  file=sys.stderr)
-            return 2
-
-        infn = argns.INPUTFILE
-        try:
-            with open(infn, 'rb') as infp:
-                code = infp.read()
-        except Exception as err:
-            print('Error: cannot read infile:', err, file=sys.stderr)
-            return 1
-        if not inencoding:
-            code, inencoding = guess_decode(code)
-
-        # do we have to guess the lexer?
-        if not lexer:
-            try:
-                lexer = get_lexer_for_filename(infn, code, **parsed_opts)
-            except ClassNotFound as err:
-                if argns.g:
-                    try:
-                        lexer = guess_lexer(code, **parsed_opts)
-                    except ClassNotFound:
-                        lexer = TextLexer(**parsed_opts)
-                else:
-                    print('Error:', err, file=sys.stderr)
-                    return 1
-            except OptionError as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-
-    elif not argns.s:  # treat stdin as full file (-s support is later)
-        # read code from terminal, always in binary mode since we want to
-        # decode ourselves and be tolerant with it
-        code = sys.stdin.buffer.read()  # use .buffer to get a binary stream
-        if not inencoding:
-            code, inencoding = guess_decode_from_terminal(code, sys.stdin)
-            # else the lexer will do the decoding
-        if not lexer:
-            try:
-                lexer = guess_lexer(code, **parsed_opts)
-            except ClassNotFound:
-                lexer = TextLexer(**parsed_opts)
-
-    else:  # -s option needs a lexer with -l
-        if not lexer:
-            print('Error: when using -s a lexer has to be selected with -l',
-                  file=sys.stderr)
-            return 2
-
-    # process filters
-    for fname, fopts in F_opts:
-        try:
-            lexer.add_filter(fname, **fopts)
-        except ClassNotFound as err:
-            print('Error:', err, file=sys.stderr)
-            return 1
-
-    # select formatter
-    outfn = argns.o
-    fmter = argns.f
-    if fmter:
-        # custom formatter, located relative to user's cwd
-        if allow_custom_lexer_formatter and '.py' in fmter:
-            try:
-                filename = None
-                name = None
-                if ':' in fmter:
-                    # Same logic as above for custom lexer
-                    filename, name = fmter.rsplit(':', 1)
-
-                    if '.py' in name:
-                        name = None
-
-                if filename and name:
-                    fmter = load_formatter_from_file(filename, name,
-                                                     **parsed_opts)
-                else:
-                    fmter = load_formatter_from_file(fmter, **parsed_opts)
-            except ClassNotFound as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-        else:
-            try:
-                fmter = get_formatter_by_name(fmter, **parsed_opts)
-            except (OptionError, ClassNotFound) as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-
-    if outfn:
-        if not fmter:
-            try:
-                fmter = get_formatter_for_filename(outfn, **parsed_opts)
-            except (OptionError, ClassNotFound) as err:
-                print('Error:', err, file=sys.stderr)
-                return 1
-        try:
-            outfile = open(outfn, 'wb')
-        except Exception as err:
-            print('Error: cannot open outfile:', err, file=sys.stderr)
-            return 1
-    else:
-        if not fmter:
-            if os.environ.get('COLORTERM','') in ('truecolor', '24bit'):
-                fmter = TerminalTrueColorFormatter(**parsed_opts)
-            elif '256' in os.environ.get('TERM', ''):
-                fmter = Terminal256Formatter(**parsed_opts)
-            else:
-                fmter = TerminalFormatter(**parsed_opts)
-        outfile = sys.stdout.buffer
-
-    # determine output encoding if not explicitly selected
-    if not outencoding:
-        if outfn:
-            # output file? use lexer encoding for now (can still be None)
-            fmter.encoding = inencoding
-        else:
-            # else use terminal encoding
-            fmter.encoding = terminal_encoding(sys.stdout)
-
-    # provide coloring under Windows, if possible
-    if not outfn and sys.platform in ('win32', 'cygwin') and \
-       fmter.name in ('Terminal', 'Terminal256'):  # pragma: no cover
-        # unfortunately colorama doesn't support binary streams on Py3
-        outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding)
-        fmter.encoding = None
-        try:
-            import pip._vendor.colorama.initialise as colorama_initialise
-        except ImportError:
-            pass
-        else:
-            outfile = colorama_initialise.wrap_stream(
-                outfile, convert=None, strip=None, autoreset=False, wrap=True)
-
-    # When using the LaTeX formatter and the option `escapeinside` is
-    # specified, we need a special lexer which collects escaped text
-    # before running the chosen language lexer.
-    escapeinside = parsed_opts.get('escapeinside', '')
-    if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter):
-        left = escapeinside[0]
-        right = escapeinside[1]
-        lexer = LatexEmbeddedLexer(left, right, lexer)
-
-    # ... and do it!
-    if not argns.s:
-        # process whole input as per normal...
-        try:
-            highlight(code, lexer, fmter, outfile)
-        finally:
-            if outfn:
-                outfile.close()
-        return 0
-    else:
-        # line by line processing of stdin (eg: for 'tail -f')...
-        try:
-            while 1:
-                line = sys.stdin.buffer.readline()
-                if not line:
-                    break
-                if not inencoding:
-                    line = guess_decode_from_terminal(line, sys.stdin)[0]
-                highlight(line, lexer, fmter, outfile)
-                if hasattr(outfile, 'flush'):
-                    outfile.flush()
-            return 0
-        except KeyboardInterrupt:  # pragma: no cover
-            return 0
-        finally:
-            if outfn:
-                outfile.close()
-
-
-class HelpFormatter(argparse.HelpFormatter):
-    def __init__(self, prog, indent_increment=2, max_help_position=16, width=None):
-        if width is None:
-            try:
-                width = shutil.get_terminal_size().columns - 2
-            except Exception:
-                pass
-        argparse.HelpFormatter.__init__(self, prog, indent_increment,
-                                        max_help_position, width)
-
-
-def main(args=sys.argv):
-    """
-    Main command line entry point.
-    """
-    desc = "Highlight an input file and write the result to an output file."
-    parser = argparse.ArgumentParser(description=desc, add_help=False,
-                                     formatter_class=HelpFormatter)
-
-    operation = parser.add_argument_group('Main operation')
-    lexersel = operation.add_mutually_exclusive_group()
-    lexersel.add_argument(
-        '-l', metavar='LEXER',
-        help='Specify the lexer to use.  (Query names with -L.)  If not '
-        'given and -g is not present, the lexer is guessed from the filename.')
-    lexersel.add_argument(
-        '-g', action='store_true',
-        help='Guess the lexer from the file contents, or pass through '
-        'as plain text if nothing can be guessed.')
-    operation.add_argument(
-        '-F', metavar='FILTER[:options]', action='append',
-        help='Add a filter to the token stream.  (Query names with -L.) '
-        'Filter options are given after a colon if necessary.')
-    operation.add_argument(
-        '-f', metavar='FORMATTER',
-        help='Specify the formatter to use.  (Query names with -L.) '
-        'If not given, the formatter is guessed from the output filename, '
-        'and defaults to the terminal formatter if the output is to the '
-        'terminal or an unknown file extension.')
-    operation.add_argument(
-        '-O', metavar='OPTION=value[,OPTION=value,...]', action='append',
-        help='Give options to the lexer and formatter as a comma-separated '
-        'list of key-value pairs. '
-        'Example: `-O bg=light,python=cool`.')
-    operation.add_argument(
-        '-P', metavar='OPTION=value', action='append',
-        help='Give a single option to the lexer and formatter - with this '
-        'you can pass options whose value contains commas and equal signs. '
-        'Example: `-P "heading=Pygments, the Python highlighter"`.')
-    operation.add_argument(
-        '-o', metavar='OUTPUTFILE',
-        help='Where to write the output.  Defaults to standard output.')
-
-    operation.add_argument(
-        'INPUTFILE', nargs='?',
-        help='Where to read the input.  Defaults to standard input.')
-
-    flags = parser.add_argument_group('Operation flags')
-    flags.add_argument(
-        '-v', action='store_true',
-        help='Print a detailed traceback on unhandled exceptions, which '
-        'is useful for debugging and bug reports.')
-    flags.add_argument(
-        '-s', action='store_true',
-        help='Process lines one at a time until EOF, rather than waiting to '
-        'process the entire file.  This only works for stdin, only for lexers '
-        'with no line-spanning constructs, and is intended for streaming '
-        'input such as you get from `tail -f`. '
-        'Example usage: `tail -f sql.log | pygmentize -s -l sql`.')
-    flags.add_argument(
-        '-x', action='store_true',
-        help='Allow custom lexers and formatters to be loaded from a .py file '
-        'relative to the current working directory. For example, '
-        '`-l ./customlexer.py -x`. By default, this option expects a file '
-        'with a class named CustomLexer or CustomFormatter; you can also '
-        'specify your own class name with a colon (`-l ./lexer.py:MyLexer`). '
-        'Users should be very careful not to use this option with untrusted '
-        'files, because it will import and run them.')
-    flags.add_argument('--json', help='Output as JSON. This can '
-        'be only used in conjunction with -L.',
-        default=False,
-        action='store_true')
-
-    special_modes_group = parser.add_argument_group(
-        'Special modes - do not do any highlighting')
-    special_modes = special_modes_group.add_mutually_exclusive_group()
-    special_modes.add_argument(
-        '-S', metavar='STYLE -f formatter',
-        help='Print style definitions for STYLE for a formatter '
-        'given with -f. The argument given by -a is formatter '
-        'dependent.')
-    special_modes.add_argument(
-        '-L', nargs='*', metavar='WHAT',
-        help='List lexers, formatters, styles or filters -- '
-        'give additional arguments for the thing(s) you want to list '
-        '(e.g. "styles"), or omit them to list everything.')
-    special_modes.add_argument(
-        '-N', metavar='FILENAME',
-        help='Guess and print out a lexer name based solely on the given '
-        'filename. Does not take input or highlight anything. If no specific '
-        'lexer can be determined, "text" is printed.')
-    special_modes.add_argument(
-        '-C', action='store_true',
-        help='Like -N, but print out a lexer name based solely on '
-        'a given content from standard input.')
-    special_modes.add_argument(
-        '-H', action='store', nargs=2, metavar=('NAME', 'TYPE'),
-        help='Print detailed help for the object  of type , '
-        'where  is one of "lexer", "formatter" or "filter".')
-    special_modes.add_argument(
-        '-V', action='store_true',
-        help='Print the package version.')
-    special_modes.add_argument(
-        '-h', '--help', action='store_true',
-        help='Print this help.')
-    special_modes_group.add_argument(
-        '-a', metavar='ARG',
-        help='Formatter-specific additional argument for the -S (print '
-        'style sheet) mode.')
-
-    argns = parser.parse_args(args[1:])
-
-    try:
-        return main_inner(parser, argns)
-    except BrokenPipeError:
-        # someone closed our stdout, e.g. by quitting a pager.
-        return 0
-    except Exception:
-        if argns.v:
-            print(file=sys.stderr)
-            print('*' * 65, file=sys.stderr)
-            print('An unhandled exception occurred while highlighting.',
-                  file=sys.stderr)
-            print('Please report the whole traceback to the issue tracker at',
-                  file=sys.stderr)
-            print('.',
-                  file=sys.stderr)
-            print('*' * 65, file=sys.stderr)
-            print(file=sys.stderr)
-            raise
-        import traceback
-        info = traceback.format_exception(*sys.exc_info())
-        msg = info[-1].strip()
-        if len(info) >= 3:
-            # extract relevant file and position info
-            msg += '\n   (f%s)' % info[-2].split('\n')[0].strip()[1:]
-        print(file=sys.stderr)
-        print('*** Error while highlighting:', file=sys.stderr)
-        print(msg, file=sys.stderr)
-        print('*** If this is a bug you want to report, please rerun with -v.',
-              file=sys.stderr)
-        return 1
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/console.py b/.venv/Lib/site-packages/pip/_vendor/pygments/console.py
deleted file mode 100644
index 2ada68e..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/console.py
+++ /dev/null
@@ -1,70 +0,0 @@
-"""
-    pygments.console
-    ~~~~~~~~~~~~~~~~
-
-    Format colored console output.
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-esc = "\x1b["
-
-codes = {}
-codes[""] = ""
-codes["reset"] = esc + "39;49;00m"
-
-codes["bold"] = esc + "01m"
-codes["faint"] = esc + "02m"
-codes["standout"] = esc + "03m"
-codes["underline"] = esc + "04m"
-codes["blink"] = esc + "05m"
-codes["overline"] = esc + "06m"
-
-dark_colors = ["black", "red", "green", "yellow", "blue",
-               "magenta", "cyan", "gray"]
-light_colors = ["brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
-                "brightmagenta", "brightcyan", "white"]
-
-x = 30
-for d, l in zip(dark_colors, light_colors):
-    codes[d] = esc + "%im" % x
-    codes[l] = esc + "%im" % (60 + x)
-    x += 1
-
-del d, l, x
-
-codes["white"] = codes["bold"]
-
-
-def reset_color():
-    return codes["reset"]
-
-
-def colorize(color_key, text):
-    return codes[color_key] + text + codes["reset"]
-
-
-def ansiformat(attr, text):
-    """
-    Format ``text`` with a color and/or some attributes::
-
-        color       normal color
-        *color*     bold color
-        _color_     underlined color
-        +color+     blinking color
-    """
-    result = []
-    if attr[:1] == attr[-1:] == '+':
-        result.append(codes['blink'])
-        attr = attr[1:-1]
-    if attr[:1] == attr[-1:] == '*':
-        result.append(codes['bold'])
-        attr = attr[1:-1]
-    if attr[:1] == attr[-1:] == '_':
-        result.append(codes['underline'])
-        attr = attr[1:-1]
-    result.append(codes[attr])
-    result.append(text)
-    result.append(codes['reset'])
-    return ''.join(result)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/filter.py b/.venv/Lib/site-packages/pip/_vendor/pygments/filter.py
deleted file mode 100644
index e5c9664..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/filter.py
+++ /dev/null
@@ -1,71 +0,0 @@
-"""
-    pygments.filter
-    ~~~~~~~~~~~~~~~
-
-    Module that implements the default filter.
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-
-def apply_filters(stream, filters, lexer=None):
-    """
-    Use this method to apply an iterable of filters to
-    a stream. If lexer is given it's forwarded to the
-    filter, otherwise the filter receives `None`.
-    """
-    def _apply(filter_, stream):
-        yield from filter_.filter(lexer, stream)
-    for filter_ in filters:
-        stream = _apply(filter_, stream)
-    return stream
-
-
-def simplefilter(f):
-    """
-    Decorator that converts a function into a filter::
-
-        @simplefilter
-        def lowercase(self, lexer, stream, options):
-            for ttype, value in stream:
-                yield ttype, value.lower()
-    """
-    return type(f.__name__, (FunctionFilter,), {
-        '__module__': getattr(f, '__module__'),
-        '__doc__': f.__doc__,
-        'function': f,
-    })
-
-
-class Filter:
-    """
-    Default filter. Subclass this class or use the `simplefilter`
-    decorator to create own filters.
-    """
-
-    def __init__(self, **options):
-        self.options = options
-
-    def filter(self, lexer, stream):
-        raise NotImplementedError()
-
-
-class FunctionFilter(Filter):
-    """
-    Abstract class used by `simplefilter` to create simple
-    function filters on the fly. The `simplefilter` decorator
-    automatically creates subclasses of this class for
-    functions passed to it.
-    """
-    function = None
-
-    def __init__(self, **options):
-        if not hasattr(self, 'function'):
-            raise TypeError('%r used without bound function' %
-                            self.__class__.__name__)
-        Filter.__init__(self, **options)
-
-    def filter(self, lexer, stream):
-        # pylint: disable=not-callable
-        yield from self.function(lexer, stream, self.options)
diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py
deleted file mode 100644
index c302a6c..0000000
--- a/.venv/Lib/site-packages/pip/_vendor/pygments/filters/__init__.py
+++ /dev/null
@@ -1,940 +0,0 @@
-"""
-    pygments.filters
-    ~~~~~~~~~~~~~~~~
-
-    Module containing filter lookup functions and default
-    filters.
-
-    :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
-    :license: BSD, see LICENSE for details.
-"""
-
-import re
-
-from pip._vendor.pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \
-    string_to_tokentype
-from pip._vendor.pygments.filter import Filter
-from pip._vendor.pygments.util import get_list_opt, get_int_opt, get_bool_opt, \
-    get_choice_opt, ClassNotFound, OptionError
-from pip._vendor.pygments.plugin import find_plugin_filters
-
-
-def find_filter_class(filtername):
-    """Lookup a filter by name. Return None if not found."""
-    if filtername in FILTERS:
-        return FILTERS[filtername]
-    for name, cls in find_plugin_filters():
-        if name == filtername:
-            return cls
-    return None
-
-
-def get_filter_by_name(filtername, **options):
-    """Return an instantiated filter.
-
-    Options are passed to the filter initializer if wanted.
-    Raise a ClassNotFound if not found.
-    """
-    cls = find_filter_class(filtername)
-    if cls:
-        return cls(**options)
-    else:
-        raise ClassNotFound('filter %r not found' % filtername)
-
-
-def get_all_filters():
-    """Return a generator of all filter names."""
-    yield from FILTERS
-    for name, _ in find_plugin_filters():
-        yield name
-
-
-def _replace_special(ttype, value, regex, specialttype,
-                     replacefunc=lambda x: x):
-    last = 0
-    for match in regex.finditer(value):
-        start, end = match.start(), match.end()
-        if start != last:
-            yield ttype, value[last:start]
-        yield specialttype, replacefunc(value[start:end])
-        last = end
-    if last != len(value):
-        yield ttype, value[last:]
-
-
-class CodeTagFilter(Filter):
-    """Highlight special code tags in comments and docstrings.
-
-    Options accepted:
-
-    `codetags` : list of strings
-       A list of strings that are flagged as code tags.  The default is to
-       highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``.
-
-    .. versionchanged:: 2.13
-       Now recognizes ``FIXME`` by default.
-    """
-
-    def __init__(self, **options):
-        Filter.__init__(self, **options)
-        tags = get_list_opt(options, 'codetags',
-                            ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE'])
-        self.tag_re = re.compile(r'\b(%s)\b' % '|'.join([
-            re.escape(tag) for tag in tags if tag
-        ]))
-
-    def filter(self, lexer, stream):
-        regex = self.tag_re
-        for ttype, value in stream:
-            if ttype in String.Doc or \
-               ttype in Comment and \
-               ttype not in Comment.Preproc:
-                yield from _replace_special(ttype, value, regex, Comment.Special)
-            else:
-                yield ttype, value
-
-
-class SymbolFilter(Filter):
-    """Convert mathematical symbols such as \\ in Isabelle
-    or \\longrightarrow in LaTeX into Unicode characters.
-
-    This is mostly useful for HTML or console output when you want to
-    approximate the source rendering you'd see in an IDE.
-
-    Options accepted:
-
-    `lang` : string
-       The symbol language. Must be one of ``'isabelle'`` or
-       ``'latex'``.  The default is ``'isabelle'``.
-    """
-
-    latex_symbols = {
-        '\\alpha'                : '\U000003b1',
-        '\\beta'                 : '\U000003b2',
-        '\\gamma'                : '\U000003b3',
-        '\\delta'                : '\U000003b4',
-        '\\varepsilon'           : '\U000003b5',
-        '\\zeta'                 : '\U000003b6',
-        '\\eta'                  : '\U000003b7',
-        '\\vartheta'             : '\U000003b8',
-        '\\iota'                 : '\U000003b9',
-        '\\kappa'                : '\U000003ba',
-        '\\lambda'               : '\U000003bb',
-        '\\mu'                   : '\U000003bc',
-        '\\nu'                   : '\U000003bd',
-        '\\xi'                   : '\U000003be',
-        '\\pi'                   : '\U000003c0',
-        '\\varrho'               : '\U000003c1',
-        '\\sigma'                : '\U000003c3',
-        '\\tau'                  : '\U000003c4',
-        '\\upsilon'              : '\U000003c5',
-        '\\varphi'               : '\U000003c6',
-        '\\chi'                  : '\U000003c7',
-        '\\psi'                  : '\U000003c8',
-        '\\omega'                : '\U000003c9',
-        '\\Gamma'                : '\U00000393',
-        '\\Delta'                : '\U00000394',
-        '\\Theta'                : '\U00000398',
-        '\\Lambda'               : '\U0000039b',
-        '\\Xi'                   : '\U0000039e',
-        '\\Pi'                   : '\U000003a0',
-        '\\Sigma'                : '\U000003a3',
-        '\\Upsilon'              : '\U000003a5',
-        '\\Phi'                  : '\U000003a6',
-        '\\Psi'                  : '\U000003a8',
-        '\\Omega'                : '\U000003a9',
-        '\\leftarrow'            : '\U00002190',
-        '\\longleftarrow'        : '\U000027f5',
-        '\\rightarrow'           : '\U00002192',
-        '\\longrightarrow'       : '\U000027f6',
-        '\\Leftarrow'            : '\U000021d0',
-        '\\Longleftarrow'        : '\U000027f8',
-        '\\Rightarrow'           : '\U000021d2',
-        '\\Longrightarrow'       : '\U000027f9',
-        '\\leftrightarrow'       : '\U00002194',
-        '\\longleftrightarrow'   : '\U000027f7',
-        '\\Leftrightarrow'       : '\U000021d4',
-        '\\Longleftrightarrow'   : '\U000027fa',
-        '\\mapsto'               : '\U000021a6',
-        '\\longmapsto'           : '\U000027fc',
-        '\\relbar'               : '\U00002500',
-        '\\Relbar'               : '\U00002550',
-        '\\hookleftarrow'        : '\U000021a9',
-        '\\hookrightarrow'       : '\U000021aa',
-        '\\leftharpoondown'      : '\U000021bd',
-        '\\rightharpoondown'     : '\U000021c1',
-        '\\leftharpoonup'        : '\U000021bc',
-        '\\rightharpoonup'       : '\U000021c0',
-        '\\rightleftharpoons'    : '\U000021cc',
-        '\\leadsto'              : '\U0000219d',
-        '\\downharpoonleft'      : '\U000021c3',
-        '\\downharpoonright'     : '\U000021c2',
-        '\\upharpoonleft'        : '\U000021bf',
-        '\\upharpoonright'       : '\U000021be',
-        '\\restriction'          : '\U000021be',
-        '\\uparrow'              : '\U00002191',
-        '\\Uparrow'              : '\U000021d1',
-        '\\downarrow'            : '\U00002193',
-        '\\Downarrow'            : '\U000021d3',
-        '\\updownarrow'          : '\U00002195',
-        '\\Updownarrow'          : '\U000021d5',
-        '\\langle'               : '\U000027e8',
-        '\\rangle'               : '\U000027e9',
-        '\\lceil'                : '\U00002308',
-        '\\rceil'                : '\U00002309',
-        '\\lfloor'               : '\U0000230a',
-        '\\rfloor'               : '\U0000230b',
-        '\\flqq'                 : '\U000000ab',
-        '\\frqq'                 : '\U000000bb',
-        '\\bot'                  : '\U000022a5',
-        '\\top'                  : '\U000022a4',
-        '\\wedge'                : '\U00002227',
-        '\\bigwedge'             : '\U000022c0',
-        '\\vee'                  : '\U00002228',
-        '\\bigvee'               : '\U000022c1',
-        '\\forall'               : '\U00002200',
-        '\\exists'               : '\U00002203',
-        '\\nexists'              : '\U00002204',
-        '\\neg'                  : '\U000000ac',
-        '\\Box'                  : '\U000025a1',
-        '\\Diamond'              : '\U000025c7',
-        '\\vdash'                : '\U000022a2',
-        '\\models'               : '\U000022a8',
-        '\\dashv'                : '\U000022a3',
-        '\\surd'                 : '\U0000221a',
-        '\\le'                   : '\U00002264',
-        '\\ge'                   : '\U00002265',
-        '\\ll'                   : '\U0000226a',
-        '\\gg'                   : '\U0000226b',
-        '\\lesssim'              : '\U00002272',
-        '\\gtrsim'               : '\U00002273',
-        '\\lessapprox'           : '\U00002a85',
-        '\\gtrapprox'            : '\U00002a86',
-        '\\in'                   : '\U00002208',
-        '\\notin'                : '\U00002209',
-        '\\subset'               : '\U00002282',
-        '\\supset'               : '\U00002283',
-        '\\subseteq'             : '\U00002286',
-        '\\supseteq'             : '\U00002287',
-        '\\sqsubset'             : '\U0000228f',
-        '\\sqsupset'             : '\U00002290',
-        '\\sqsubseteq'           : '\U00002291',
-        '\\sqsupseteq'           : '\U00002292',
-        '\\cap'                  : '\U00002229',
-        '\\bigcap'               : '\U000022c2',
-        '\\cup'                  : '\U0000222a',
-        '\\bigcup'               : '\U000022c3',
-        '\\sqcup'                : '\U00002294',
-        '\\bigsqcup'             : '\U00002a06',
-        '\\sqcap'                : '\U00002293',
-        '\\Bigsqcap'             : '\U00002a05',
-        '\\setminus'             : '\U00002216',
-        '\\propto'               : '\U0000221d',
-        '\\uplus'                : '\U0000228e',
-        '\\bigplus'              : '\U00002a04',
-        '\\sim'                  : '\U0000223c',
-        '\\doteq'                : '\U00002250',
-        '\\simeq'                : '\U00002243',
-        '\\approx'               : '\U00002248',
-        '\\asymp'                : '\U0000224d',
-        '\\cong'                 : '\U00002245',
-        '\\equiv'                : '\U00002261',
-        '\\Join'                 : '\U000022c8',
-        '\\bowtie'               : '\U00002a1d',
-        '\\prec'                 : '\U0000227a',
-        '\\succ'                 : '\U0000227b',
-        '\\preceq'               : '\U0000227c',
-        '\\succeq'               : '\U0000227d',
-        '\\parallel'             : '\U00002225',
-        '\\mid'                  : '\U000000a6',
-        '\\pm'                   : '\U000000b1',
-        '\\mp'                   : '\U00002213',
-        '\\times'                : '\U000000d7',
-        '\\div'                  : '\U000000f7',
-        '\\cdot'                 : '\U000022c5',
-        '\\star'                 : '\U000022c6',
-        '\\circ'                 : '\U00002218',
-        '\\dagger'               : '\U00002020',
-        '\\ddagger'              : '\U00002021',
-        '\\lhd'                  : '\U000022b2',
-        '\\rhd'                  : '\U000022b3',
-        '\\unlhd'                : '\U000022b4',
-        '\\unrhd'                : '\U000022b5',
-        '\\triangleleft'         : '\U000025c3',
-        '\\triangleright'        : '\U000025b9',
-        '\\triangle'             : '\U000025b3',
-        '\\triangleq'            : '\U0000225c',
-        '\\oplus'                : '\U00002295',
-        '\\bigoplus'             : '\U00002a01',
-        '\\otimes'               : '\U00002297',
-        '\\bigotimes'            : '\U00002a02',
-        '\\odot'                 : '\U00002299',
-        '\\bigodot'              : '\U00002a00',
-        '\\ominus'               : '\U00002296',
-        '\\oslash'               : '\U00002298',
-        '\\dots'                 : '\U00002026',
-        '\\cdots'                : '\U000022ef',
-        '\\sum'                  : '\U00002211',
-        '\\prod'                 : '\U0000220f',
-        '\\coprod'               : '\U00002210',
-        '\\infty'                : '\U0000221e',
-        '\\int'                  : '\U0000222b',
-        '\\oint'                 : '\U0000222e',
-        '\\clubsuit'             : '\U00002663',
-        '\\diamondsuit'          : '\U00002662',
-        '\\heartsuit'            : '\U00002661',
-        '\\spadesuit'            : '\U00002660',
-        '\\aleph'                : '\U00002135',
-        '\\emptyset'             : '\U00002205',
-        '\\nabla'                : '\U00002207',
-        '\\partial'              : '\U00002202',
-        '\\flat'                 : '\U0000266d',
-        '\\natural'              : '\U0000266e',
-        '\\sharp'                : '\U0000266f',
-        '\\angle'                : '\U00002220',
-        '\\copyright'            : '\U000000a9',
-        '\\textregistered'       : '\U000000ae',
-        '\\textonequarter'       : '\U000000bc',
-        '\\textonehalf'          : '\U000000bd',
-        '\\textthreequarters'    : '\U000000be',
-        '\\textordfeminine'      : '\U000000aa',
-        '\\textordmasculine'     : '\U000000ba',
-        '\\euro'                 : '\U000020ac',
-        '\\pounds'               : '\U000000a3',
-        '\\yen'                  : '\U000000a5',
-        '\\textcent'             : '\U000000a2',
-        '\\textcurrency'         : '\U000000a4',
-        '\\textdegree'           : '\U000000b0',
-    }
-
-    isabelle_symbols = {
-        '\\'                 : '\U0001d7ec',
-        '\\'                  : '\U0001d7ed',
-        '\\'                  : '\U0001d7ee',
-        '\\'                : '\U0001d7ef',
-        '\\'                 : '\U0001d7f0',
-        '\\'                 : '\U0001d7f1',
-        '\\'                  : '\U0001d7f2',
-        '\\'                : '\U0001d7f3',
-        '\\'                : '\U0001d7f4',
-        '\\'                 : '\U0001d7f5',
-        '\\'                    : '\U0001d49c',
-        '\\'                    : '\U0000212c',
-        '\\'                    : '\U0001d49e',
-        '\\'                    : '\U0001d49f',
-        '\\'                    : '\U00002130',
-        '\\'                    : '\U00002131',
-        '\\'                    : '\U0001d4a2',
-        '\\'                    : '\U0000210b',
-        '\\'                    : '\U00002110',
-        '\\'                    : '\U0001d4a5',
-        '\\'                    : '\U0001d4a6',
-        '\\'                    : '\U00002112',
-        '\\'                    : '\U00002133',
-        '\\'                    : '\U0001d4a9',
-        '\\'                    : '\U0001d4aa',
-        '\\

' : '\U0001d5c9', - '\\' : '\U0001d5ca', - '\\' : '\U0001d5cb', - '\\' : '\U0001d5cc', - '\\' : '\U0001d5cd', - '\\' : '\U0001d5ce', - '\\' : '\U0001d5cf', - '\\' : '\U0001d5d0', - '\\' : '\U0001d5d1', - '\\' : '\U0001d5d2', - '\\' : '\U0001d5d3', - '\\' : '\U0001d504', - '\\' : '\U0001d505', - '\\' : '\U0000212d', - '\\

' : '\U0001d507', - '\\' : '\U0001d508', - '\\' : '\U0001d509', - '\\' : '\U0001d50a', - '\\' : '\U0000210c', - '\\' : '\U00002111', - '\\' : '\U0001d50d', - '\\' : '\U0001d50e', - '\\' : '\U0001d50f', - '\\' : '\U0001d510', - '\\' : '\U0001d511', - '\\' : '\U0001d512', - '\\' : '\U0001d513', - '\\' : '\U0001d514', - '\\' : '\U0000211c', - '\\' : '\U0001d516', - '\\' : '\U0001d517', - '\\' : '\U0001d518', - '\\' : '\U0001d519', - '\\' : '\U0001d51a', - '\\' : '\U0001d51b', - '\\' : '\U0001d51c', - '\\' : '\U00002128', - '\\' : '\U0001d51e', - '\\' : '\U0001d51f', - '\\' : '\U0001d520', - '\\

' : '\U0001d4ab', - '\\' : '\U0001d4ac', - '\\' : '\U0000211b', - '\\' : '\U0001d4ae', - '\\' : '\U0001d4af', - '\\' : '\U0001d4b0', - '\\' : '\U0001d4b1', - '\\' : '\U0001d4b2', - '\\' : '\U0001d4b3', - '\\' : '\U0001d4b4', - '\\' : '\U0001d4b5', - '\\' : '\U0001d5ba', - '\\' : '\U0001d5bb', - '\\' : '\U0001d5bc', - '\\' : '\U0001d5bd', - '\\' : '\U0001d5be', - '\\' : '\U0001d5bf', - '\\' : '\U0001d5c0', - '\\' : '\U0001d5c1', - '\\' : '\U0001d5c2', - '\\' : '\U0001d5c3', - '\\' : '\U0001d5c4', - '\\' : '\U0001d5c5', - '\\' : '\U0001d5c6', - '\\' : '\U0001d5c7', - '\\' : '\U0001d5c8', - '\\

' : '\U0001d521', - '\\' : '\U0001d522', - '\\' : '\U0001d523', - '\\' : '\U0001d524', - '\\' : '\U0001d525', - '\\' : '\U0001d526', - '\\' : '\U0001d527', - '\\' : '\U0001d528', - '\\' : '\U0001d529', - '\\' : '\U0001d52a', - '\\' : '\U0001d52b', - '\\' : '\U0001d52c', - '\\' : '\U0001d52d', - '\\' : '\U0001d52e', - '\\' : '\U0001d52f', - '\\' : '\U0001d530', - '\\' : '\U0001d531', - '\\' : '\U0001d532', - '\\' : '\U0001d533', - '\\' : '\U0001d534', - '\\' : '\U0001d535', - '\\' : '\U0001d536', - '\\' : '\U0001d537', - '\\' : '\U000003b1', - '\\' : '\U000003b2', - '\\' : '\U000003b3', - '\\' : '\U000003b4', - '\\' : '\U000003b5', - '\\' : '\U000003b6', - '\\' : '\U000003b7', - '\\' : '\U000003b8', - '\\' : '\U000003b9', - '\\' : '\U000003ba', - '\\' : '\U000003bb', - '\\' : '\U000003bc', - '\\' : '\U000003bd', - '\\' : '\U000003be', - '\\' : '\U000003c0', - '\\' : '\U000003c1', - '\\' : '\U000003c3', - '\\' : '\U000003c4', - '\\' : '\U000003c5', - '\\' : '\U000003c6', - '\\' : '\U000003c7', - '\\' : '\U000003c8', - '\\' : '\U000003c9', - '\\' : '\U00000393', - '\\' : '\U00000394', - '\\' : '\U00000398', - '\\' : '\U0000039b', - '\\' : '\U0000039e', - '\\' : '\U000003a0', - '\\' : '\U000003a3', - '\\' : '\U000003a5', - '\\' : '\U000003a6', - '\\' : '\U000003a8', - '\\' : '\U000003a9', - '\\' : '\U0001d539', - '\\' : '\U00002102', - '\\' : '\U00002115', - '\\' : '\U0000211a', - '\\' : '\U0000211d', - '\\' : '\U00002124', - '\\' : '\U00002190', - '\\' : '\U000027f5', - '\\' : '\U00002192', - '\\' : '\U000027f6', - '\\' : '\U000021d0', - '\\' : '\U000027f8', - '\\' : '\U000021d2', - '\\' : '\U000027f9', - '\\' : '\U00002194', - '\\' : '\U000027f7', - '\\' : '\U000021d4', - '\\' : '\U000027fa', - '\\' : '\U000021a6', - '\\' : '\U000027fc', - '\\' : '\U00002500', - '\\' : '\U00002550', - '\\' : '\U000021a9', - '\\' : '\U000021aa', - '\\' : '\U000021bd', - '\\' : '\U000021c1', - '\\' : '\U000021bc', - '\\' : '\U000021c0', - '\\' : '\U000021cc', - '\\' : '\U0000219d', - '\\' : '\U000021c3', - '\\' : '\U000021c2', - '\\' : '\U000021bf', - '\\' : '\U000021be', - '\\' : '\U000021be', - '\\' : '\U00002237', - '\\' : '\U00002191', - '\\' : '\U000021d1', - '\\' : '\U00002193', - '\\' : '\U000021d3', - '\\' : '\U00002195', - '\\' : '\U000021d5', - '\\' : '\U000027e8', - '\\' : '\U000027e9', - '\\' : '\U00002308', - '\\' : '\U00002309', - '\\' : '\U0000230a', - '\\' : '\U0000230b', - '\\' : '\U00002987', - '\\' : '\U00002988', - '\\' : '\U000027e6', - '\\' : '\U000027e7', - '\\' : '\U00002983', - '\\' : '\U00002984', - '\\' : '\U000000ab', - '\\' : '\U000000bb', - '\\' : '\U000022a5', - '\\' : '\U000022a4', - '\\' : '\U00002227', - '\\' : '\U000022c0', - '\\' : '\U00002228', - '\\' : '\U000022c1', - '\\' : '\U00002200', - '\\' : '\U00002203', - '\\' : '\U00002204', - '\\' : '\U000000ac', - '\\' : '\U000025a1', - '\\' : '\U000025c7', - '\\' : '\U000022a2', - '\\' : '\U000022a8', - '\\' : '\U000022a9', - '\\' : '\U000022ab', - '\\' : '\U000022a3', - '\\' : '\U0000221a', - '\\' : '\U00002264', - '\\' : '\U00002265', - '\\' : '\U0000226a', - '\\' : '\U0000226b', - '\\' : '\U00002272', - '\\' : '\U00002273', - '\\' : '\U00002a85', - '\\' : '\U00002a86', - '\\' : '\U00002208', - '\\' : '\U00002209', - '\\' : '\U00002282', - '\\' : '\U00002283', - '\\' : '\U00002286', - '\\' : '\U00002287', - '\\' : '\U0000228f', - '\\' : '\U00002290', - '\\' : '\U00002291', - '\\' : '\U00002292', - '\\' : '\U00002229', - '\\' : '\U000022c2', - '\\' : '\U0000222a', - '\\' : '\U000022c3', - '\\' : '\U00002294', - '\\' : '\U00002a06', - '\\' : '\U00002293', - '\\' : '\U00002a05', - '\\' : '\U00002216', - '\\' : '\U0000221d', - '\\' : '\U0000228e', - '\\' : '\U00002a04', - '\\' : '\U00002260', - '\\' : '\U0000223c', - '\\' : '\U00002250', - '\\' : '\U00002243', - '\\' : '\U00002248', - '\\' : '\U0000224d', - '\\' : '\U00002245', - '\\' : '\U00002323', - '\\' : '\U00002261', - '\\' : '\U00002322', - '\\' : '\U000022c8', - '\\' : '\U00002a1d', - '\\' : '\U0000227a', - '\\' : '\U0000227b', - '\\' : '\U0000227c', - '\\' : '\U0000227d', - '\\' : '\U00002225', - '\\' : '\U000000a6', - '\\' : '\U000000b1', - '\\' : '\U00002213', - '\\' : '\U000000d7', - '\\
' : '\U000000f7', - '\\' : '\U000022c5', - '\\' : '\U000022c6', - '\\' : '\U00002219', - '\\' : '\U00002218', - '\\' : '\U00002020', - '\\' : '\U00002021', - '\\' : '\U000022b2', - '\\' : '\U000022b3', - '\\' : '\U000022b4', - '\\' : '\U000022b5', - '\\' : '\U000025c3', - '\\' : '\U000025b9', - '\\' : '\U000025b3', - '\\' : '\U0000225c', - '\\' : '\U00002295', - '\\' : '\U00002a01', - '\\' : '\U00002297', - '\\' : '\U00002a02', - '\\' : '\U00002299', - '\\' : '\U00002a00', - '\\' : '\U00002296', - '\\' : '\U00002298', - '\\' : '\U00002026', - '\\' : '\U000022ef', - '\\' : '\U00002211', - '\\' : '\U0000220f', - '\\' : '\U00002210', - '\\' : '\U0000221e', - '\\' : '\U0000222b', - '\\' : '\U0000222e', - '\\' : '\U00002663', - '\\' : '\U00002662', - '\\' : '\U00002661', - '\\' : '\U00002660', - '\\' : '\U00002135', - '\\' : '\U00002205', - '\\' : '\U00002207', - '\\' : '\U00002202', - '\\' : '\U0000266d', - '\\' : '\U0000266e', - '\\' : '\U0000266f', - '\\' : '\U00002220', - '\\' : '\U000000a9', - '\\' : '\U000000ae', - '\\' : '\U000000ad', - '\\' : '\U000000af', - '\\' : '\U000000bc', - '\\' : '\U000000bd', - '\\' : '\U000000be', - '\\' : '\U000000aa', - '\\' : '\U000000ba', - '\\
' : '\U000000a7', - '\\' : '\U000000b6', - '\\' : '\U000000a1', - '\\' : '\U000000bf', - '\\' : '\U000020ac', - '\\' : '\U000000a3', - '\\' : '\U000000a5', - '\\' : '\U000000a2', - '\\' : '\U000000a4', - '\\' : '\U000000b0', - '\\' : '\U00002a3f', - '\\' : '\U00002127', - '\\' : '\U000025ca', - '\\' : '\U00002118', - '\\' : '\U00002240', - '\\' : '\U000022c4', - '\\' : '\U000000b4', - '\\' : '\U00000131', - '\\' : '\U000000a8', - '\\' : '\U000000b8', - '\\' : '\U000002dd', - '\\' : '\U000003f5', - '\\' : '\U000023ce', - '\\' : '\U00002039', - '\\' : '\U0000203a', - '\\' : '\U00002302', - '\\<^sub>' : '\U000021e9', - '\\<^sup>' : '\U000021e7', - '\\<^bold>' : '\U00002759', - '\\<^bsub>' : '\U000021d8', - '\\<^esub>' : '\U000021d9', - '\\<^bsup>' : '\U000021d7', - '\\<^esup>' : '\U000021d6', - } - - lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols} - - def __init__(self, **options): - Filter.__init__(self, **options) - lang = get_choice_opt(options, 'lang', - ['isabelle', 'latex'], 'isabelle') - self.symbols = self.lang_map[lang] - - def filter(self, lexer, stream): - for ttype, value in stream: - if value in self.symbols: - yield ttype, self.symbols[value] - else: - yield ttype, value - - -class KeywordCaseFilter(Filter): - """Convert keywords to lowercase or uppercase or capitalize them, which - means first letter uppercase, rest lowercase. - - This can be useful e.g. if you highlight Pascal code and want to adapt the - code to your styleguide. - - Options accepted: - - `case` : string - The casing to convert keywords to. Must be one of ``'lower'``, - ``'upper'`` or ``'capitalize'``. The default is ``'lower'``. - """ - - def __init__(self, **options): - Filter.__init__(self, **options) - case = get_choice_opt(options, 'case', - ['lower', 'upper', 'capitalize'], 'lower') - self.convert = getattr(str, case) - - def filter(self, lexer, stream): - for ttype, value in stream: - if ttype in Keyword: - yield ttype, self.convert(value) - else: - yield ttype, value - - -class NameHighlightFilter(Filter): - """Highlight a normal Name (and Name.*) token with a different token type. - - Example:: - - filter = NameHighlightFilter( - names=['foo', 'bar', 'baz'], - tokentype=Name.Function, - ) - - This would highlight the names "foo", "bar" and "baz" - as functions. `Name.Function` is the default token type. - - Options accepted: - - `names` : list of strings - A list of names that should be given the different token type. - There is no default. - `tokentype` : TokenType or string - A token type or a string containing a token type name that is - used for highlighting the strings in `names`. The default is - `Name.Function`. - """ - - def __init__(self, **options): - Filter.__init__(self, **options) - self.names = set(get_list_opt(options, 'names', [])) - tokentype = options.get('tokentype') - if tokentype: - self.tokentype = string_to_tokentype(tokentype) - else: - self.tokentype = Name.Function - - def filter(self, lexer, stream): - for ttype, value in stream: - if ttype in Name and value in self.names: - yield self.tokentype, value - else: - yield ttype, value - - -class ErrorToken(Exception): - pass - - -class RaiseOnErrorTokenFilter(Filter): - """Raise an exception when the lexer generates an error token. - - Options accepted: - - `excclass` : Exception class - The exception class to raise. - The default is `pygments.filters.ErrorToken`. - - .. versionadded:: 0.8 - """ - - def __init__(self, **options): - Filter.__init__(self, **options) - self.exception = options.get('excclass', ErrorToken) - try: - # issubclass() will raise TypeError if first argument is not a class - if not issubclass(self.exception, Exception): - raise TypeError - except TypeError: - raise OptionError('excclass option is not an exception class') - - def filter(self, lexer, stream): - for ttype, value in stream: - if ttype is Error: - raise self.exception(value) - yield ttype, value - - -class VisibleWhitespaceFilter(Filter): - """Convert tabs, newlines and/or spaces to visible characters. - - Options accepted: - - `spaces` : string or bool - If this is a one-character string, spaces will be replaces by this string. - If it is another true value, spaces will be replaced by ``·`` (unicode - MIDDLE DOT). If it is a false value, spaces will not be replaced. The - default is ``False``. - `tabs` : string or bool - The same as for `spaces`, but the default replacement character is ``»`` - (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value - is ``False``. Note: this will not work if the `tabsize` option for the - lexer is nonzero, as tabs will already have been expanded then. - `tabsize` : int - If tabs are to be replaced by this filter (see the `tabs` option), this - is the total number of characters that a tab should be expanded to. - The default is ``8``. - `newlines` : string or bool - The same as for `spaces`, but the default replacement character is ``¶`` - (unicode PILCROW SIGN). The default value is ``False``. - `wstokentype` : bool - If true, give whitespace the special `Whitespace` token type. This allows - styling the visible whitespace differently (e.g. greyed out), but it can - disrupt background colors. The default is ``True``. - - .. versionadded:: 0.8 - """ - - def __init__(self, **options): - Filter.__init__(self, **options) - for name, default in [('spaces', '·'), - ('tabs', '»'), - ('newlines', '¶')]: - opt = options.get(name, False) - if isinstance(opt, str) and len(opt) == 1: - setattr(self, name, opt) - else: - setattr(self, name, (opt and default or '')) - tabsize = get_int_opt(options, 'tabsize', 8) - if self.tabs: - self.tabs += ' ' * (tabsize - 1) - if self.newlines: - self.newlines += '\n' - self.wstt = get_bool_opt(options, 'wstokentype', True) - - def filter(self, lexer, stream): - if self.wstt: - spaces = self.spaces or ' ' - tabs = self.tabs or '\t' - newlines = self.newlines or '\n' - regex = re.compile(r'\s') - - def replacefunc(wschar): - if wschar == ' ': - return spaces - elif wschar == '\t': - return tabs - elif wschar == '\n': - return newlines - return wschar - - for ttype, value in stream: - yield from _replace_special(ttype, value, regex, Whitespace, - replacefunc) - else: - spaces, tabs, newlines = self.spaces, self.tabs, self.newlines - # simpler processing - for ttype, value in stream: - if spaces: - value = value.replace(' ', spaces) - if tabs: - value = value.replace('\t', tabs) - if newlines: - value = value.replace('\n', newlines) - yield ttype, value - - -class GobbleFilter(Filter): - """Gobbles source code lines (eats initial characters). - - This filter drops the first ``n`` characters off every line of code. This - may be useful when the source code fed to the lexer is indented by a fixed - amount of space that isn't desired in the output. - - Options accepted: - - `n` : int - The number of characters to gobble. - - .. versionadded:: 1.2 - """ - def __init__(self, **options): - Filter.__init__(self, **options) - self.n = get_int_opt(options, 'n', 0) - - def gobble(self, value, left): - if left < len(value): - return value[left:], 0 - else: - return '', left - len(value) - - def filter(self, lexer, stream): - n = self.n - left = n # How many characters left to gobble. - for ttype, value in stream: - # Remove ``left`` tokens from first line, ``n`` from all others. - parts = value.split('\n') - (parts[0], left) = self.gobble(parts[0], left) - for i in range(1, len(parts)): - (parts[i], left) = self.gobble(parts[i], n) - value = '\n'.join(parts) - - if value != '': - yield ttype, value - - -class TokenMergeFilter(Filter): - """Merges consecutive tokens with the same token type in the output - stream of a lexer. - - .. versionadded:: 1.2 - """ - def __init__(self, **options): - Filter.__init__(self, **options) - - def filter(self, lexer, stream): - current_type = None - current_value = None - for ttype, value in stream: - if ttype is current_type: - current_value += value - else: - if current_type is not None: - yield current_type, current_value - current_type = ttype - current_value = value - if current_type is not None: - yield current_type, current_value - - -FILTERS = { - 'codetagify': CodeTagFilter, - 'keywordcase': KeywordCaseFilter, - 'highlight': NameHighlightFilter, - 'raiseonerror': RaiseOnErrorTokenFilter, - 'whitespace': VisibleWhitespaceFilter, - 'gobble': GobbleFilter, - 'tokenmerge': TokenMergeFilter, - 'symbols': SymbolFilter, -} diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatter.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatter.py deleted file mode 100644 index a2349ef..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatter.py +++ /dev/null @@ -1,94 +0,0 @@ -""" - pygments.formatter - ~~~~~~~~~~~~~~~~~~ - - Base formatter class. - - :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import codecs - -from pip._vendor.pygments.util import get_bool_opt -from pip._vendor.pygments.styles import get_style_by_name - -__all__ = ['Formatter'] - - -def _lookup_style(style): - if isinstance(style, str): - return get_style_by_name(style) - return style - - -class Formatter: - """ - Converts a token stream to text. - - Options accepted: - - ``style`` - The style to use, can be a string or a Style subclass - (default: "default"). Not used by e.g. the - TerminalFormatter. - ``full`` - Tells the formatter to output a "full" document, i.e. - a complete self-contained document. This doesn't have - any effect for some formatters (default: false). - ``title`` - If ``full`` is true, the title that should be used to - caption the document (default: ''). - ``encoding`` - If given, must be an encoding name. This will be used to - convert the Unicode token strings to byte strings in the - output. If it is "" or None, Unicode strings will be written - to the output file, which most file-like objects do not - support (default: None). - ``outencoding`` - Overrides ``encoding`` if given. - """ - - #: Name of the formatter - name = None - - #: Shortcuts for the formatter - aliases = [] - - #: fn match rules - filenames = [] - - #: If True, this formatter outputs Unicode strings when no encoding - #: option is given. - unicodeoutput = True - - def __init__(self, **options): - self.style = _lookup_style(options.get('style', 'default')) - self.full = get_bool_opt(options, 'full', False) - self.title = options.get('title', '') - self.encoding = options.get('encoding', None) or None - if self.encoding in ('guess', 'chardet'): - # can happen for e.g. pygmentize -O encoding=guess - self.encoding = 'utf-8' - self.encoding = options.get('outencoding') or self.encoding - self.options = options - - def get_style_defs(self, arg=''): - """ - Return the style definitions for the current style as a string. - - ``arg`` is an additional argument whose meaning depends on the - formatter used. Note that ``arg`` can also be a list or tuple - for some formatters like the html formatter. - """ - return '' - - def format(self, tokensource, outfile): - """ - Format ``tokensource``, an iterable of ``(tokentype, tokenstring)`` - tuples and write it into ``outfile``. - """ - if self.encoding: - # wrap the outfile in a StreamWriter - outfile = codecs.lookup(self.encoding)[3](outfile) - return self.format_unencoded(tokensource, outfile) diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py deleted file mode 100644 index 43c4c89..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/__init__.py +++ /dev/null @@ -1,143 +0,0 @@ -""" - pygments.formatters - ~~~~~~~~~~~~~~~~~~~ - - Pygments formatters. - - :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import re -import sys -import types -from fnmatch import fnmatch -from os.path import basename - -from pip._vendor.pygments.formatters._mapping import FORMATTERS -from pip._vendor.pygments.plugin import find_plugin_formatters -from pip._vendor.pygments.util import ClassNotFound - -__all__ = ['get_formatter_by_name', 'get_formatter_for_filename', - 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS) - -_formatter_cache = {} # classes by name - -def _load_formatters(module_name): - """Load a formatter (and all others in the module too).""" - mod = __import__(module_name, None, None, ['__all__']) - for formatter_name in mod.__all__: - cls = getattr(mod, formatter_name) - _formatter_cache[cls.name] = cls - - -def get_all_formatters(): - """Return a generator for all formatter classes.""" - # NB: this returns formatter classes, not info like get_all_lexers(). - for info in FORMATTERS.values(): - if info[1] not in _formatter_cache: - _load_formatters(info[0]) - yield _formatter_cache[info[1]] - for _, formatter in find_plugin_formatters(): - yield formatter - - -def find_formatter_class(alias): - """Lookup a formatter by alias. - - Returns None if not found. - """ - for module_name, name, aliases, _, _ in FORMATTERS.values(): - if alias in aliases: - if name not in _formatter_cache: - _load_formatters(module_name) - return _formatter_cache[name] - for _, cls in find_plugin_formatters(): - if alias in cls.aliases: - return cls - - -def get_formatter_by_name(_alias, **options): - """Lookup and instantiate a formatter by alias. - - Raises ClassNotFound if not found. - """ - cls = find_formatter_class(_alias) - if cls is None: - raise ClassNotFound("no formatter found for name %r" % _alias) - return cls(**options) - - -def load_formatter_from_file(filename, formattername="CustomFormatter", - **options): - """Load a formatter from a file. - - This method expects a file located relative to the current working - directory, which contains a class named CustomFormatter. By default, - it expects the Formatter to be named CustomFormatter; you can specify - your own class name as the second argument to this function. - - Users should be very careful with the input, because this method - is equivalent to running eval on the input file. - - Raises ClassNotFound if there are any problems importing the Formatter. - - .. versionadded:: 2.2 - """ - try: - # This empty dict will contain the namespace for the exec'd file - custom_namespace = {} - with open(filename, 'rb') as f: - exec(f.read(), custom_namespace) - # Retrieve the class `formattername` from that namespace - if formattername not in custom_namespace: - raise ClassNotFound('no valid %s class found in %s' % - (formattername, filename)) - formatter_class = custom_namespace[formattername] - # And finally instantiate it with the options - return formatter_class(**options) - except OSError as err: - raise ClassNotFound('cannot read %s: %s' % (filename, err)) - except ClassNotFound: - raise - except Exception as err: - raise ClassNotFound('error when loading custom formatter: %s' % err) - - -def get_formatter_for_filename(fn, **options): - """Lookup and instantiate a formatter by filename pattern. - - Raises ClassNotFound if not found. - """ - fn = basename(fn) - for modname, name, _, filenames, _ in FORMATTERS.values(): - for filename in filenames: - if fnmatch(fn, filename): - if name not in _formatter_cache: - _load_formatters(modname) - return _formatter_cache[name](**options) - for cls in find_plugin_formatters(): - for filename in cls.filenames: - if fnmatch(fn, filename): - return cls(**options) - raise ClassNotFound("no formatter found for file name %r" % fn) - - -class _automodule(types.ModuleType): - """Automatically import formatters.""" - - def __getattr__(self, name): - info = FORMATTERS.get(name) - if info: - _load_formatters(info[0]) - cls = _formatter_cache[info[1]] - setattr(self, name, cls) - return cls - raise AttributeError(name) - - -oldmod = sys.modules[__name__] -newmod = _automodule(__name__) -newmod.__dict__.update(oldmod.__dict__) -sys.modules[__name__] = newmod -del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py deleted file mode 100644 index 6e34f96..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/_mapping.py +++ /dev/null @@ -1,23 +0,0 @@ -# Automatically generated by scripts/gen_mapfiles.py. -# DO NOT EDIT BY HAND; run `make mapfiles` instead. - -FORMATTERS = { - 'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'), - 'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), - 'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), - 'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'), - 'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ```` tags within a ``
`` tag, wrapped in a ``
`` tag. The ``
``'s CSS class can be set by the `cssclass` option."), - 'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'), - 'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), - 'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), - 'LatexFormatter': ('pygments.formatters.latex', 'LaTeX', ('latex', 'tex'), ('*.tex',), 'Format tokens as LaTeX code. This needs the `fancyvrb` and `color` standard packages.'), - 'NullFormatter': ('pygments.formatters.other', 'Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'), - 'PangoMarkupFormatter': ('pygments.formatters.pangomarkup', 'Pango Markup', ('pango', 'pangomarkup'), (), 'Format tokens as Pango Markup code. It can then be rendered to an SVG.'), - 'RawTokenFormatter': ('pygments.formatters.other', 'Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'), - 'RtfFormatter': ('pygments.formatters.rtf', 'RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft(R) Word(R) documents.'), - 'SvgFormatter': ('pygments.formatters.svg', 'SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file. This formatter is still experimental. Each line of code is a ```` element with explicit ``x`` and ``y`` coordinates containing ```` elements with the individual token styles.'), - 'Terminal256Formatter': ('pygments.formatters.terminal256', 'Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), - 'TerminalFormatter': ('pygments.formatters.terminal', 'Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.'), - 'TerminalTrueColorFormatter': ('pygments.formatters.terminal256', 'TerminalTrueColor', ('terminal16m', 'console16m', '16m'), (), 'Format tokens with ANSI color sequences, for output in a true-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), - 'TestcaseFormatter': ('pygments.formatters.other', 'Testcase', ('testcase',), (), 'Format tokens as appropriate for a new testcase.'), -} diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py deleted file mode 100644 index 2be2b4e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/bbcode.py +++ /dev/null @@ -1,108 +0,0 @@ -""" - pygments.formatters.bbcode - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - BBcode formatter. - - :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - - -from pip._vendor.pygments.formatter import Formatter -from pip._vendor.pygments.util import get_bool_opt - -__all__ = ['BBCodeFormatter'] - - -class BBCodeFormatter(Formatter): - """ - Format tokens with BBcodes. These formatting codes are used by many - bulletin boards, so you can highlight your sourcecode with pygments before - posting it there. - - This formatter has no support for background colors and borders, as there - are no common BBcode tags for that. - - Some board systems (e.g. phpBB) don't support colors in their [code] tag, - so you can't use the highlighting together with that tag. - Text in a [code] tag usually is shown with a monospace font (which this - formatter can do with the ``monofont`` option) and no spaces (which you - need for indentation) are removed. - - Additional options accepted: - - `style` - The style to use, can be a string or a Style subclass (default: - ``'default'``). - - `codetag` - If set to true, put the output into ``[code]`` tags (default: - ``false``) - - `monofont` - If set to true, add a tag to show the code with a monospace font - (default: ``false``). - """ - name = 'BBCode' - aliases = ['bbcode', 'bb'] - filenames = [] - - def __init__(self, **options): - Formatter.__init__(self, **options) - self._code = get_bool_opt(options, 'codetag', False) - self._mono = get_bool_opt(options, 'monofont', False) - - self.styles = {} - self._make_styles() - - def _make_styles(self): - for ttype, ndef in self.style: - start = end = '' - if ndef['color']: - start += '[color=#%s]' % ndef['color'] - end = '[/color]' + end - if ndef['bold']: - start += '[b]' - end = '[/b]' + end - if ndef['italic']: - start += '[i]' - end = '[/i]' + end - if ndef['underline']: - start += '[u]' - end = '[/u]' + end - # there are no common BBcodes for background-color and border - - self.styles[ttype] = start, end - - def format_unencoded(self, tokensource, outfile): - if self._code: - outfile.write('[code]') - if self._mono: - outfile.write('[font=monospace]') - - lastval = '' - lasttype = None - - for ttype, value in tokensource: - while ttype not in self.styles: - ttype = ttype.parent - if ttype == lasttype: - lastval += value - else: - if lastval: - start, end = self.styles[lasttype] - outfile.write(''.join((start, lastval, end))) - lastval = value - lasttype = ttype - - if lastval: - start, end = self.styles[lasttype] - outfile.write(''.join((start, lastval, end))) - - if self._mono: - outfile.write('[/font]') - if self._code: - outfile.write('[/code]') - if self._code or self._mono: - outfile.write('\n') diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py deleted file mode 100644 index f3dcbce..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/groff.py +++ /dev/null @@ -1,170 +0,0 @@ -""" - pygments.formatters.groff - ~~~~~~~~~~~~~~~~~~~~~~~~~ - - Formatter for groff output. - - :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import math -from pip._vendor.pygments.formatter import Formatter -from pip._vendor.pygments.util import get_bool_opt, get_int_opt - -__all__ = ['GroffFormatter'] - - -class GroffFormatter(Formatter): - """ - Format tokens with groff escapes to change their color and font style. - - .. versionadded:: 2.11 - - Additional options accepted: - - `style` - The style to use, can be a string or a Style subclass (default: - ``'default'``). - - `monospaced` - If set to true, monospace font will be used (default: ``true``). - - `linenos` - If set to true, print the line numbers (default: ``false``). - - `wrap` - Wrap lines to the specified number of characters. Disabled if set to 0 - (default: ``0``). - """ - - name = 'groff' - aliases = ['groff','troff','roff'] - filenames = [] - - def __init__(self, **options): - Formatter.__init__(self, **options) - - self.monospaced = get_bool_opt(options, 'monospaced', True) - self.linenos = get_bool_opt(options, 'linenos', False) - self._lineno = 0 - self.wrap = get_int_opt(options, 'wrap', 0) - self._linelen = 0 - - self.styles = {} - self._make_styles() - - - def _make_styles(self): - regular = '\\f[CR]' if self.monospaced else '\\f[R]' - bold = '\\f[CB]' if self.monospaced else '\\f[B]' - italic = '\\f[CI]' if self.monospaced else '\\f[I]' - - for ttype, ndef in self.style: - start = end = '' - if ndef['color']: - start += '\\m[%s]' % ndef['color'] - end = '\\m[]' + end - if ndef['bold']: - start += bold - end = regular + end - if ndef['italic']: - start += italic - end = regular + end - if ndef['bgcolor']: - start += '\\M[%s]' % ndef['bgcolor'] - end = '\\M[]' + end - - self.styles[ttype] = start, end - - - def _define_colors(self, outfile): - colors = set() - for _, ndef in self.style: - if ndef['color'] is not None: - colors.add(ndef['color']) - - for color in colors: - outfile.write('.defcolor ' + color + ' rgb #' + color + '\n') - - - def _write_lineno(self, outfile): - self._lineno += 1 - outfile.write("%s% 4d " % (self._lineno != 1 and '\n' or '', self._lineno)) - - - def _wrap_line(self, line): - length = len(line.rstrip('\n')) - space = ' ' if self.linenos else '' - newline = '' - - if length > self.wrap: - for i in range(0, math.floor(length / self.wrap)): - chunk = line[i*self.wrap:i*self.wrap+self.wrap] - newline += (chunk + '\n' + space) - remainder = length % self.wrap - if remainder > 0: - newline += line[-remainder-1:] - self._linelen = remainder - elif self._linelen + length > self.wrap: - newline = ('\n' + space) + line - self._linelen = length - else: - newline = line - self._linelen += length - - return newline - - - def _escape_chars(self, text): - text = text.replace('\\', '\\[u005C]'). \ - replace('.', '\\[char46]'). \ - replace('\'', '\\[u0027]'). \ - replace('`', '\\[u0060]'). \ - replace('~', '\\[u007E]') - copy = text - - for char in copy: - if len(char) != len(char.encode()): - uni = char.encode('unicode_escape') \ - .decode()[1:] \ - .replace('x', 'u00') \ - .upper() - text = text.replace(char, '\\[u' + uni[1:] + ']') - - return text - - - def format_unencoded(self, tokensource, outfile): - self._define_colors(outfile) - - outfile.write('.nf\n\\f[CR]\n') - - if self.linenos: - self._write_lineno(outfile) - - for ttype, value in tokensource: - while ttype not in self.styles: - ttype = ttype.parent - start, end = self.styles[ttype] - - for line in value.splitlines(True): - if self.wrap > 0: - line = self._wrap_line(line) - - if start and end: - text = self._escape_chars(line.rstrip('\n')) - if text != '': - outfile.write(''.join((start, text, end))) - else: - outfile.write(self._escape_chars(line.rstrip('\n'))) - - if line.endswith('\n'): - if self.linenos: - self._write_lineno(outfile) - self._linelen = 0 - else: - outfile.write('\n') - self._linelen = 0 - - outfile.write('\n.fi') diff --git a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py b/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py deleted file mode 100644 index d5cda4c..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pygments/formatters/html.py +++ /dev/null @@ -1,989 +0,0 @@ -""" - pygments.formatters.html - ~~~~~~~~~~~~~~~~~~~~~~~~ - - Formatter for HTML output. - - :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import functools -import os -import sys -import os.path -from io import StringIO - -from pip._vendor.pygments.formatter import Formatter -from pip._vendor.pygments.token import Token, Text, STANDARD_TYPES -from pip._vendor.pygments.util import get_bool_opt, get_int_opt, get_list_opt - -try: - import ctags -except ImportError: - ctags = None - -__all__ = ['HtmlFormatter'] - - -_escape_html_table = { - ord('&'): '&', - ord('<'): '<', - ord('>'): '>', - ord('"'): '"', - ord("'"): ''', -} - - -def escape_html(text, table=_escape_html_table): - """Escape &, <, > as well as single and double quotes for HTML.""" - return text.translate(table) - - -def webify(color): - if color.startswith('calc') or color.startswith('var'): - return color - else: - return '#' + color - - -def _get_ttype_class(ttype): - fname = STANDARD_TYPES.get(ttype) - if fname: - return fname - aname = '' - while fname is None: - aname = '-' + ttype[-1] + aname - ttype = ttype.parent - fname = STANDARD_TYPES.get(ttype) - return fname + aname - - -CSSFILE_TEMPLATE = '''\ -/* -generated by Pygments -Copyright 2006-2022 by the Pygments team. -Licensed under the BSD license, see LICENSE for details. -*/ -%(styledefs)s -''' - -DOC_HEADER = '''\ - - - - - %(title)s - - - - -

%(title)s

- -''' - -DOC_HEADER_EXTERNALCSS = '''\ - - - - - %(title)s - - - - -

%(title)s

- -''' - -DOC_FOOTER = '''\ - - -''' - - -class HtmlFormatter(Formatter): - r""" - Format tokens as HTML 4 ```` tags within a ``
`` tag, wrapped
-    in a ``
`` tag. The ``
``'s CSS class can be set by the `cssclass` - option. - - If the `linenos` option is set to ``"table"``, the ``
`` is
-    additionally wrapped inside a ```` which has one row and two
-    cells: one containing the line numbers and one containing the code.
-    Example:
-
-    .. sourcecode:: html
-
-        
-
- - -
-
1
-            2
-
-
def foo(bar):
-              pass
-            
-
- - (whitespace added to improve clarity). - - Wrapping can be disabled using the `nowrap` option. - - A list of lines can be specified using the `hl_lines` option to make these - lines highlighted (as of Pygments 0.11). - - With the `full` option, a complete HTML 4 document is output, including - the style definitions inside a `` - {% else %} - {{ head | safe }} - {% endif %} - - -{{ body | safe }} -{% for diagram in diagrams %} -
-

{{ diagram.title }}

-
{{ diagram.text }}
-
- {{ diagram.svg }} -
-
-{% endfor %} - - -""" - -template = Template(jinja2_template_source) - -# Note: ideally this would be a dataclass, but we're supporting Python 3.5+ so we can't do this yet -NamedDiagram = NamedTuple( - "NamedDiagram", - [("name", str), ("diagram", typing.Optional[railroad.DiagramItem]), ("index", int)], -) -""" -A simple structure for associating a name with a railroad diagram -""" - -T = TypeVar("T") - - -class EachItem(railroad.Group): - """ - Custom railroad item to compose a: - - Group containing a - - OneOrMore containing a - - Choice of the elements in the Each - with the group label indicating that all must be matched - """ - - all_label = "[ALL]" - - def __init__(self, *items): - choice_item = railroad.Choice(len(items) - 1, *items) - one_or_more_item = railroad.OneOrMore(item=choice_item) - super().__init__(one_or_more_item, label=self.all_label) - - -class AnnotatedItem(railroad.Group): - """ - Simple subclass of Group that creates an annotation label - """ - - def __init__(self, label: str, item): - super().__init__(item=item, label="[{}]".format(label) if label else label) - - -class EditablePartial(Generic[T]): - """ - Acts like a functools.partial, but can be edited. In other words, it represents a type that hasn't yet been - constructed. - """ - - # We need this here because the railroad constructors actually transform the data, so can't be called until the - # entire tree is assembled - - def __init__(self, func: Callable[..., T], args: list, kwargs: dict): - self.func = func - self.args = args - self.kwargs = kwargs - - @classmethod - def from_call(cls, func: Callable[..., T], *args, **kwargs) -> "EditablePartial[T]": - """ - If you call this function in the same way that you would call the constructor, it will store the arguments - as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3) - """ - return EditablePartial(func=func, args=list(args), kwargs=kwargs) - - @property - def name(self): - return self.kwargs["name"] - - def __call__(self) -> T: - """ - Evaluate the partial and return the result - """ - args = self.args.copy() - kwargs = self.kwargs.copy() - - # This is a helpful hack to allow you to specify varargs parameters (e.g. *args) as keyword args (e.g. - # args=['list', 'of', 'things']) - arg_spec = inspect.getfullargspec(self.func) - if arg_spec.varargs in self.kwargs: - args += kwargs.pop(arg_spec.varargs) - - return self.func(*args, **kwargs) - - -def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str: - """ - Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams - :params kwargs: kwargs to be passed in to the template - """ - data = [] - for diagram in diagrams: - if diagram.diagram is None: - continue - io = StringIO() - diagram.diagram.writeSvg(io.write) - title = diagram.name - if diagram.index == 0: - title += " (root)" - data.append({"title": title, "text": "", "svg": io.getvalue()}) - - return template.render(diagrams=data, **kwargs) - - -def resolve_partial(partial: "EditablePartial[T]") -> T: - """ - Recursively resolves a collection of Partials into whatever type they are - """ - if isinstance(partial, EditablePartial): - partial.args = resolve_partial(partial.args) - partial.kwargs = resolve_partial(partial.kwargs) - return partial() - elif isinstance(partial, list): - return [resolve_partial(x) for x in partial] - elif isinstance(partial, dict): - return {key: resolve_partial(x) for key, x in partial.items()} - else: - return partial - - -def to_railroad( - element: pyparsing.ParserElement, - diagram_kwargs: typing.Optional[dict] = None, - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, -) -> List[NamedDiagram]: - """ - Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram - creation if you want to access the Railroad tree before it is converted to HTML - :param element: base element of the parser being diagrammed - :param diagram_kwargs: kwargs to pass to the Diagram() constructor - :param vertical: (optional) - int - limit at which number of alternatives should be - shown vertically instead of horizontally - :param show_results_names - bool to indicate whether results name annotations should be - included in the diagram - :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled - surrounding box - """ - # Convert the whole tree underneath the root - lookup = ConverterState(diagram_kwargs=diagram_kwargs or {}) - _to_diagram_element( - element, - lookup=lookup, - parent=None, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - root_id = id(element) - # Convert the root if it hasn't been already - if root_id in lookup: - if not element.customName: - lookup[root_id].name = "" - lookup[root_id].mark_for_extraction(root_id, lookup, force=True) - - # Now that we're finished, we can convert from intermediate structures into Railroad elements - diags = list(lookup.diagrams.values()) - if len(diags) > 1: - # collapse out duplicate diags with the same name - seen = set() - deduped_diags = [] - for d in diags: - # don't extract SkipTo elements, they are uninformative as subdiagrams - if d.name == "...": - continue - if d.name is not None and d.name not in seen: - seen.add(d.name) - deduped_diags.append(d) - resolved = [resolve_partial(partial) for partial in deduped_diags] - else: - # special case - if just one diagram, always display it, even if - # it has no name - resolved = [resolve_partial(partial) for partial in diags] - return sorted(resolved, key=lambda diag: diag.index) - - -def _should_vertical( - specification: int, exprs: Iterable[pyparsing.ParserElement] -) -> bool: - """ - Returns true if we should return a vertical list of elements - """ - if specification is None: - return False - else: - return len(_visible_exprs(exprs)) >= specification - - -class ElementState: - """ - State recorded for an individual pyparsing Element - """ - - # Note: this should be a dataclass, but we have to support Python 3.5 - def __init__( - self, - element: pyparsing.ParserElement, - converted: EditablePartial, - parent: EditablePartial, - number: int, - name: str = None, - parent_index: typing.Optional[int] = None, - ): - #: The pyparsing element that this represents - self.element: pyparsing.ParserElement = element - #: The name of the element - self.name: typing.Optional[str] = name - #: The output Railroad element in an unconverted state - self.converted: EditablePartial = converted - #: The parent Railroad element, which we store so that we can extract this if it's duplicated - self.parent: EditablePartial = parent - #: The order in which we found this element, used for sorting diagrams if this is extracted into a diagram - self.number: int = number - #: The index of this inside its parent - self.parent_index: typing.Optional[int] = parent_index - #: If true, we should extract this out into a subdiagram - self.extract: bool = False - #: If true, all of this element's children have been filled out - self.complete: bool = False - - def mark_for_extraction( - self, el_id: int, state: "ConverterState", name: str = None, force: bool = False - ): - """ - Called when this instance has been seen twice, and thus should eventually be extracted into a sub-diagram - :param el_id: id of the element - :param state: element/diagram state tracker - :param name: name to use for this element's text - :param force: If true, force extraction now, regardless of the state of this. Only useful for extracting the - root element when we know we're finished - """ - self.extract = True - - # Set the name - if not self.name: - if name: - # Allow forcing a custom name - self.name = name - elif self.element.customName: - self.name = self.element.customName - else: - self.name = "" - - # Just because this is marked for extraction doesn't mean we can do it yet. We may have to wait for children - # to be added - # Also, if this is just a string literal etc, don't bother extracting it - if force or (self.complete and _worth_extracting(self.element)): - state.extract_into_diagram(el_id) - - -class ConverterState: - """ - Stores some state that persists between recursions into the element tree - """ - - def __init__(self, diagram_kwargs: typing.Optional[dict] = None): - #: A dictionary mapping ParserElements to state relating to them - self._element_diagram_states: Dict[int, ElementState] = {} - #: A dictionary mapping ParserElement IDs to subdiagrams generated from them - self.diagrams: Dict[int, EditablePartial[NamedDiagram]] = {} - #: The index of the next unnamed element - self.unnamed_index: int = 1 - #: The index of the next element. This is used for sorting - self.index: int = 0 - #: Shared kwargs that are used to customize the construction of diagrams - self.diagram_kwargs: dict = diagram_kwargs or {} - self.extracted_diagram_names: Set[str] = set() - - def __setitem__(self, key: int, value: ElementState): - self._element_diagram_states[key] = value - - def __getitem__(self, key: int) -> ElementState: - return self._element_diagram_states[key] - - def __delitem__(self, key: int): - del self._element_diagram_states[key] - - def __contains__(self, key: int): - return key in self._element_diagram_states - - def generate_unnamed(self) -> int: - """ - Generate a number used in the name of an otherwise unnamed diagram - """ - self.unnamed_index += 1 - return self.unnamed_index - - def generate_index(self) -> int: - """ - Generate a number used to index a diagram - """ - self.index += 1 - return self.index - - def extract_into_diagram(self, el_id: int): - """ - Used when we encounter the same token twice in the same tree. When this - happens, we replace all instances of that token with a terminal, and - create a new subdiagram for the token - """ - position = self[el_id] - - # Replace the original definition of this element with a regular block - if position.parent: - ret = EditablePartial.from_call(railroad.NonTerminal, text=position.name) - if "item" in position.parent.kwargs: - position.parent.kwargs["item"] = ret - elif "items" in position.parent.kwargs: - position.parent.kwargs["items"][position.parent_index] = ret - - # If the element we're extracting is a group, skip to its content but keep the title - if position.converted.func == railroad.Group: - content = position.converted.kwargs["item"] - else: - content = position.converted - - self.diagrams[el_id] = EditablePartial.from_call( - NamedDiagram, - name=position.name, - diagram=EditablePartial.from_call( - railroad.Diagram, content, **self.diagram_kwargs - ), - index=position.number, - ) - - del self[el_id] - - -def _worth_extracting(element: pyparsing.ParserElement) -> bool: - """ - Returns true if this element is worth having its own sub-diagram. Simply, if any of its children - themselves have children, then its complex enough to extract - """ - children = element.recurse() - return any(child.recurse() for child in children) - - -def _apply_diagram_item_enhancements(fn): - """ - decorator to ensure enhancements to a diagram item (such as results name annotations) - get applied on return from _to_diagram_element (we do this since there are several - returns in _to_diagram_element) - """ - - def _inner( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, - ) -> typing.Optional[EditablePartial]: - - ret = fn( - element, - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ) - - # apply annotation for results name, if present - if show_results_names and ret is not None: - element_results_name = element.resultsName - if element_results_name: - # add "*" to indicate if this is a "list all results" name - element_results_name += "" if element.modalResults else "*" - ret = EditablePartial.from_call( - railroad.Group, item=ret, label=element_results_name - ) - - return ret - - return _inner - - -def _visible_exprs(exprs: Iterable[pyparsing.ParserElement]): - non_diagramming_exprs = ( - pyparsing.ParseElementEnhance, - pyparsing.PositionToken, - pyparsing.And._ErrorStop, - ) - return [ - e - for e in exprs - if not (e.customName or e.resultsName or isinstance(e, non_diagramming_exprs)) - ] - - -@_apply_diagram_item_enhancements -def _to_diagram_element( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, -) -> typing.Optional[EditablePartial]: - """ - Recursively converts a PyParsing Element to a railroad Element - :param lookup: The shared converter state that keeps track of useful things - :param index: The index of this element within the parent - :param parent: The parent of this element in the output tree - :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default), - it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never - do so - :param name_hint: If provided, this will override the generated name - :param show_results_names: bool flag indicating whether to add annotations for results names - :returns: The converted version of the input element, but as a Partial that hasn't yet been constructed - :param show_groups: bool flag indicating whether to show groups using bounding box - """ - exprs = element.recurse() - name = name_hint or element.customName or element.__class__.__name__ - - # Python's id() is used to provide a unique identifier for elements - el_id = id(element) - - element_results_name = element.resultsName - - # Here we basically bypass processing certain wrapper elements if they contribute nothing to the diagram - if not element.customName: - if isinstance( - element, - ( - # pyparsing.TokenConverter, - # pyparsing.Forward, - pyparsing.Located, - ), - ): - # However, if this element has a useful custom name, and its child does not, we can pass it on to the child - if exprs: - if not exprs[0].customName: - propagated_name = name - else: - propagated_name = None - - return _to_diagram_element( - element.expr, - parent=parent, - lookup=lookup, - vertical=vertical, - index=index, - name_hint=propagated_name, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # If the element isn't worth extracting, we always treat it as the first time we say it - if _worth_extracting(element): - if el_id in lookup: - # If we've seen this element exactly once before, we are only just now finding out that it's a duplicate, - # so we have to extract it into a new diagram. - looked_up = lookup[el_id] - looked_up.mark_for_extraction(el_id, lookup, name=name_hint) - ret = EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name) - return ret - - elif el_id in lookup.diagrams: - # If we have seen the element at least twice before, and have already extracted it into a subdiagram, we - # just put in a marker element that refers to the sub-diagram - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - return ret - - # Recursively convert child elements - # Here we find the most relevant Railroad element for matching pyparsing Element - # We use ``items=[]`` here to hold the place for where the child elements will go once created - if isinstance(element, pyparsing.And): - # detect And's created with ``expr*N`` notation - for these use a OneOrMore with a repeat - # (all will have the same name, and resultsName) - if not exprs: - return None - if len(set((e.name, e.resultsName) for e in exprs)) == 1: - ret = EditablePartial.from_call( - railroad.OneOrMore, item="", repeat=str(len(exprs)) - ) - elif _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Stack, items=[]) - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)): - if not exprs: - return None - if _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Choice, 0, items=[]) - else: - ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[]) - elif isinstance(element, pyparsing.Each): - if not exprs: - return None - ret = EditablePartial.from_call(EachItem, items=[]) - elif isinstance(element, pyparsing.NotAny): - ret = EditablePartial.from_call(AnnotatedItem, label="NOT", item="") - elif isinstance(element, pyparsing.FollowedBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKAHEAD", item="") - elif isinstance(element, pyparsing.PrecededBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKBEHIND", item="") - elif isinstance(element, pyparsing.Group): - if show_groups: - ret = EditablePartial.from_call(AnnotatedItem, label="", item="") - else: - ret = EditablePartial.from_call(railroad.Group, label="", item="") - elif isinstance(element, pyparsing.TokenConverter): - ret = EditablePartial.from_call( - AnnotatedItem, label=type(element).__name__.lower(), item="" - ) - elif isinstance(element, pyparsing.Opt): - ret = EditablePartial.from_call(railroad.Optional, item="") - elif isinstance(element, pyparsing.OneOrMore): - ret = EditablePartial.from_call(railroad.OneOrMore, item="") - elif isinstance(element, pyparsing.ZeroOrMore): - ret = EditablePartial.from_call(railroad.ZeroOrMore, item="") - elif isinstance(element, pyparsing.Group): - ret = EditablePartial.from_call( - railroad.Group, item=None, label=element_results_name - ) - elif isinstance(element, pyparsing.Empty) and not element.customName: - # Skip unnamed "Empty" elements - ret = None - elif len(exprs) > 1: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif len(exprs) > 0 and not element_results_name: - ret = EditablePartial.from_call(railroad.Group, item="", label=name) - else: - terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName) - ret = terminal - - if ret is None: - return - - # Indicate this element's position in the tree so we can extract it if necessary - lookup[el_id] = ElementState( - element=element, - converted=ret, - parent=parent, - parent_index=index, - number=lookup.generate_index(), - ) - if element.customName: - lookup[el_id].mark_for_extraction(el_id, lookup, element.customName) - - i = 0 - for expr in exprs: - # Add a placeholder index in case we have to extract the child before we even add it to the parent - if "items" in ret.kwargs: - ret.kwargs["items"].insert(i, None) - - item = _to_diagram_element( - expr, - parent=ret, - lookup=lookup, - vertical=vertical, - index=i, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # Some elements don't need to be shown in the diagram - if item is not None: - if "item" in ret.kwargs: - ret.kwargs["item"] = item - elif "items" in ret.kwargs: - # If we've already extracted the child, don't touch this index, since it's occupied by a nonterminal - ret.kwargs["items"][i] = item - i += 1 - elif "items" in ret.kwargs: - # If we're supposed to skip this element, remove it from the parent - del ret.kwargs["items"][i] - - # If all this items children are none, skip this item - if ret and ( - ("items" in ret.kwargs and len(ret.kwargs["items"]) == 0) - or ("item" in ret.kwargs and ret.kwargs["item"] is None) - ): - ret = EditablePartial.from_call(railroad.Terminal, name) - - # Mark this element as "complete", ie it has all of its children - if el_id in lookup: - lookup[el_id].complete = True - - if el_id in lookup and lookup[el_id].extract and lookup[el_id].complete: - lookup.extract_into_diagram(el_id) - if ret is not None: - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - - return ret diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py deleted file mode 100644 index a38447b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/exceptions.py +++ /dev/null @@ -1,267 +0,0 @@ -# exceptions.py - -import re -import sys -import typing - -from .util import col, line, lineno, _collapse_string_to_ranges -from .unicode import pyparsing_unicode as ppu - - -class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): - pass - - -_extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) -_exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") - - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, - pstr: str, - loc: int = 0, - msg: typing.Optional[str] = None, - elem=None, - ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parser_element = self.parserElement = elem - self.args = (pstr, loc, msg) - - @staticmethod - def explain_exception(exc, depth=16): - """ - Method to take an exception and translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - exc - exception raised during parsing (need not be a ParseException, in support - of Python exceptions that might be raised in a parse action) - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - """ - import inspect - from .core import ParserElement - - if depth is None: - depth = sys.getrecursionlimit() - ret = [] - if isinstance(exc, ParseBaseException): - ret.append(exc.line) - ret.append(" " * (exc.column - 1) + "^") - ret.append("{}: {}".format(type(exc).__name__, exc)) - - if depth > 0: - callers = inspect.getinnerframes(exc.__traceback__, context=depth) - seen = set() - for i, ff in enumerate(callers[-depth:]): - frm = ff[0] - - f_self = frm.f_locals.get("self", None) - if isinstance(f_self, ParserElement): - if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"): - continue - if id(f_self) in seen: - continue - seen.add(id(f_self)) - - self_type = type(f_self) - ret.append( - "{}.{} - {}".format( - self_type.__module__, self_type.__name__, f_self - ) - ) - - elif f_self is not None: - self_type = type(f_self) - ret.append("{}.{}".format(self_type.__module__, self_type.__name__)) - - else: - code = frm.f_code - if code.co_name in ("wrapper", ""): - continue - - ret.append("{}".format(code.co_name)) - - depth -= 1 - if not depth: - break - - return "\n".join(ret) - - @classmethod - def _from_exception(cls, pe): - """ - internal factory method to simplify creating one type of ParseException - from another - avoids having __init__ signature conflicts among subclasses - """ - return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) - - @property - def line(self) -> str: - """ - Return the line of text where the exception occurred. - """ - return line(self.loc, self.pstr) - - @property - def lineno(self) -> int: - """ - Return the 1-based line number of text where the exception occurred. - """ - return lineno(self.loc, self.pstr) - - @property - def col(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - @property - def column(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - found_match = _exception_word_extractor.match(self.pstr, self.loc) - if found_match is not None: - found = found_match.group(0) - else: - found = self.pstr[self.loc : self.loc + 1] - foundstr = (", found %r" % found).replace(r"\\", "\\") - else: - foundstr = "" - return "{}{} (at char {}), (line:{}, col:{})".format( - self.msg, foundstr, self.loc, self.lineno, self.column - ) - - def __repr__(self): - return str(self) - - def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str: - """ - Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - markerString = marker_string if marker_string is not None else markerString - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( - (line_str[:line_column], markerString, line_str[line_column:]) - ) - return line_str.strip() - - def explain(self, depth=16) -> str: - """ - Method to translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - - Example:: - - expr = pp.Word(pp.nums) * 3 - try: - expr.parse_string("123 456 A789") - except pp.ParseException as pe: - print(pe.explain(depth=0)) - - prints:: - - 123 456 A789 - ^ - ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) - - Note: the diagnostic output will include string representations of the expressions - that failed to parse. These representations will be more helpful if you use `set_name` to - give identifiable names to your expressions. Otherwise they will use the default string - forms, which may be cryptic to read. - - Note: pyparsing's default truncation of exception tracebacks may also truncate the - stack of expressions that are displayed in the ``explain`` output. To get the full listing - of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` - """ - return self.explain_exception(self, depth) - - markInputline = mark_input_line - - -class ParseException(ParseBaseException): - """ - Exception thrown when a parse expression doesn't match the input string - - Example:: - - try: - Word(nums).set_name("integer").parse_string("ABC") - except ParseException as pe: - print(pe) - print("column: {}".format(pe.column)) - - prints:: - - Expected integer (at char 0), (line:1, col:1) - column: 1 - - """ - - -class ParseFatalException(ParseBaseException): - """ - User-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately - """ - - -class ParseSyntaxException(ParseFatalException): - """ - Just like :class:`ParseFatalException`, but thrown internally - when an :class:`ErrorStop` ('-' operator) indicates - that parsing is to stop immediately because an unbacktrackable - syntax error has been found. - """ - - -class RecursiveGrammarException(Exception): - """ - Exception thrown by :class:`ParserElement.validate` if the - grammar could be left-recursive; parser may need to enable - left recursion using :class:`ParserElement.enable_left_recursion` - """ - - def __init__(self, parseElementList): - self.parseElementTrace = parseElementList - - def __str__(self) -> str: - return "RecursiveGrammarException: {}".format(self.parseElementTrace) diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py deleted file mode 100644 index 9588b3b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/helpers.py +++ /dev/null @@ -1,1088 +0,0 @@ -# helpers.py -import html.entities -import re -import typing - -from . import __diag__ -from .core import * -from .util import _bslash, _flatten, _escape_regex_range_chars - - -# -# global helpers -# -def delimited_list( - expr: Union[str, ParserElement], - delim: Union[str, ParserElement] = ",", - combine: bool = False, - min: typing.Optional[int] = None, - max: typing.Optional[int] = None, - *, - allow_trailing_delim: bool = False, -) -> ParserElement: - """Helper to define a delimited list of expressions - the delimiter - defaults to ','. By default, the list elements and delimiters can - have intervening whitespace, and comments, but this can be - overridden by passing ``combine=True`` in the constructor. If - ``combine`` is set to ``True``, the matching tokens are - returned as a single token string, with the delimiters included; - otherwise, the matching tokens are returned as a list of tokens, - with the delimiters suppressed. - - If ``allow_trailing_delim`` is set to True, then the list may end with - a delimiter. - - Example:: - - delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc'] - delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] - """ - if isinstance(expr, str_type): - expr = ParserElement._literalStringClass(expr) - - dlName = "{expr} [{delim} {expr}]...{end}".format( - expr=str(expr.copy().streamline()), - delim=str(delim), - end=" [{}]".format(str(delim)) if allow_trailing_delim else "", - ) - - if not combine: - delim = Suppress(delim) - - if min is not None: - if min < 1: - raise ValueError("min must be greater than 0") - min -= 1 - if max is not None: - if min is not None and max <= min: - raise ValueError("max must be greater than, or equal to min") - max -= 1 - delimited_list_expr = expr + (delim + expr)[min, max] - - if allow_trailing_delim: - delimited_list_expr += Opt(delim) - - if combine: - return Combine(delimited_list_expr).set_name(dlName) - else: - return delimited_list_expr.set_name(dlName) - - -def counted_array( - expr: ParserElement, - int_expr: typing.Optional[ParserElement] = None, - *, - intExpr: typing.Optional[ParserElement] = None, -) -> ParserElement: - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``int_expr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2)) - counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd'] - - # if other fields must be parsed after the count but before the - # list items, give the fields results names and they will - # be preserved in the returned ParseResults: - count_with_metadata = integer + Word(alphas)("type") - typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items") - result = typed_array.parse_string("3 bool True True False") - print(result.dump()) - - # prints - # ['True', 'True', 'False'] - # - items: ['True', 'True', 'False'] - # - type: 'bool' - """ - intExpr = intExpr or int_expr - array_expr = Forward() - - def count_field_parse_action(s, l, t): - nonlocal array_expr - n = t[0] - array_expr <<= (expr * n) if n else Empty() - # clear list contents, but keep any named results - del t[:] - - if intExpr is None: - intExpr = Word(nums).set_parse_action(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.set_name("arrayLen") - intExpr.add_parse_action(count_field_parse_action, call_during_try=True) - return (intExpr + array_expr).set_name("(len) " + str(expr) + "...") - - -def match_previous_literal(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_literal(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`match_previous_expr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - - def copy_token_to_repeater(s, l, t): - if t: - if len(t) == 1: - rep << t[0] - else: - # flatten t tokens - tflat = _flatten(t.as_list()) - rep << And(Literal(tt) for tt in tflat) - else: - rep << Empty() - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def match_previous_expr(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_expr(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - - def copy_token_to_repeater(s, l, t): - matchTokens = _flatten(t.as_list()) - - def must_match_these_tokens(s, l, t): - theseTokens = _flatten(t.as_list()) - if theseTokens != matchTokens: - raise ParseException( - s, l, "Expected {}, found{}".format(matchTokens, theseTokens) - ) - - rep.set_parse_action(must_match_these_tokens, callDuringTry=True) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def one_of( - strs: Union[typing.Iterable[str], str], - caseless: bool = False, - use_regex: bool = True, - as_keyword: bool = False, - *, - useRegex: bool = True, - asKeyword: bool = False, -) -> ParserElement: - """Helper to quickly define a set of alternative :class:`Literal` s, - and makes sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - ``strs`` - a string of space-delimited literals, or a collection of - string literals - - ``caseless`` - treat all literals as caseless - (default= ``False``) - - ``use_regex`` - as an optimization, will - generate a :class:`Regex` object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if - creating a :class:`Regex` raises an exception) - (default= ``True``) - - ``as_keyword`` - enforce :class:`Keyword`-style matching on the - generated expressions - (default= ``False``) - - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility, - but will be removed in a future release - - Example:: - - comp_oper = one_of("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - asKeyword = asKeyword or as_keyword - useRegex = useRegex and use_regex - - if ( - isinstance(caseless, str_type) - and __diag__.warn_on_multiple_string_args_to_oneof - ): - warnings.warn( - "More than one string argument passed to one_of, pass" - " choices as a list or space-delimited string", - stacklevel=2, - ) - - if caseless: - isequal = lambda a, b: a.upper() == b.upper() - masks = lambda a, b: b.upper().startswith(a.upper()) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = lambda a, b: a == b - masks = lambda a, b: b.startswith(a) - parseElementClass = Keyword if asKeyword else Literal - - symbols: List[str] = [] - if isinstance(strs, str_type): - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - raise TypeError("Invalid argument to one_of, expected string or iterable") - if not symbols: - return NoMatch() - - # reorder given symbols to take care to avoid masking longer choices with shorter ones - # (but only if the given symbols are not just single characters) - if any(len(sym) > 1 for sym in symbols): - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1 :]): - if isequal(other, cur): - del symbols[i + j + 1] - break - elif masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if useRegex: - re_flags: int = re.IGNORECASE if caseless else 0 - - try: - if all(len(sym) == 1 for sym in symbols): - # symbols are just single characters, create range regex pattern - patt = "[{}]".format( - "".join(_escape_regex_range_chars(sym) for sym in symbols) - ) - else: - patt = "|".join(re.escape(sym) for sym in symbols) - - # wrap with \b word break markers if defining as keywords - if asKeyword: - patt = r"\b(?:{})\b".format(patt) - - ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols)) - - if caseless: - # add parse action to return symbols as specified, not in random - # casing as found in input string - symbol_map = {sym.lower(): sym for sym in symbols} - ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()]) - - return ret - - except re.error: - warnings.warn( - "Exception creating Regex for one_of, building MatchFirst", stacklevel=2 - ) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).set_name( - " | ".join(symbols) - ) - - -def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - print(attr_expr[1, ...].parse_string(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join) - - # similar to Dict, but simpler call format - result = dict_of(attr_label, attr_value).parse_string(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.as_dict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - - -def original_text_for( - expr: ParserElement, as_string: bool = True, *, asString: bool = True -) -> ParserElement: - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns astring containing the original parsed text. - - If the optional ``as_string`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`original_text_for` contains expressions with defined - results names, you must set ``as_string`` to ``False`` if you - want to preserve those results name values. - - The ``asString`` pre-PEP8 argument is retained for compatibility, - but will be removed in a future release. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = make_html_tags(tag) - patt = original_text_for(opener + SkipTo(closer) + closer) - print(patt.search_string(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - asString = asString and as_string - - locMarker = Empty().set_parse_action(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start : t._original_end] - else: - - def extractText(s, l, t): - t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]] - - matchExpr.set_parse_action(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection) - return matchExpr - - -def ungroup(expr: ParserElement) -> ParserElement: - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).add_parse_action(lambda t: t[0]) - - -def locatedExpr(expr: ParserElement) -> ParserElement: - """ - (DEPRECATED - future code should use the Located class) - Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parseWithTabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().set_parse_action(lambda ss, ll, tt: ll) - return Group( - locator("locn_start") - + expr("value") - + locator.copy().leaveWhitespace()("locn_end") - ) - - -def nested_expr( - opener: Union[str, ParserElement] = "(", - closer: Union[str, ParserElement] = ")", - content: typing.Optional[ParserElement] = None, - ignore_expr: ParserElement = quoted_string(), - *, - ignoreExpr: ParserElement = quoted_string(), -) -> ParserElement: - """Helper method for defining nested lists enclosed in opening and - closing delimiters (``"("`` and ``")"`` are the default). - - Parameters: - - ``opener`` - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - ``closer`` - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - ``content`` - expression for items within the nested lists - (default= ``None``) - - ``ignore_expr`` - expression for ignoring opening and closing delimiters - (default= :class:`quoted_string`) - - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility - but will be removed in a future release - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignore_expr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quoted_string or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quoted_string`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = one_of("void int short long char float double") - decl_data_type = Combine(data_type + Opt(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Opt(delimited_list(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(c_style_comment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.search_string(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if ignoreExpr != ignore_expr: - ignoreExpr = ignore_expr if ignoreExpr == quoted_string() else ignoreExpr - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, str_type) and isinstance(closer, str_type): - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS, - exact=1, - ) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = empty.copy() + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS - ).set_parse_action(lambda t: t[0].strip()) - else: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = Combine( - OneOrMore( - ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - raise ValueError( - "opening and closing arguments must be strings if no content expression is given" - ) - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( - Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer) - ) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name("nested %s%s expression" % (opener, closer)) - return ret - - -def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, str_type): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - else: - tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word( - printables, exclude_chars=">" - ) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict( - ZeroOrMore( - Group( - tagAttrName.set_parse_action(lambda t: t[0].lower()) - + Opt(Suppress("=") + tagAttrValue) - ) - ) - ) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - closeTag = Combine(Literal("", adjacent=False) - - openTag.set_name("<%s>" % resname) - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.add_parse_action( - lambda t: t.__setitem__( - "start" + "".join(resname.replace(":", " ").title().split()), t.copy() - ) - ) - closeTag = closeTag( - "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name("" % resname) - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - - -def make_html_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the
pyparsing wiki page' - # make_html_tags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = make_html_tags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.search_string(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tag_str, False) - - -def make_xml_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`make_html_tags` - """ - return _makeTags(tag_str, True) - - -any_open_tag: ParserElement -any_close_tag: ParserElement -any_open_tag, any_close_tag = make_html_tags( - Word(alphas, alphanums + "_:").set_name("any tag") -) - -_htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()} -common_html_entity = Regex("&(?P" + "|".join(_htmlEntityMap) + ");").set_name( - "common HTML entity" -) - - -def replace_html_entity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - - -class OpAssoc(Enum): - LEFT = 1 - RIGHT = 2 - - -InfixNotationOperatorArgType = Union[ - ParserElement, str, Tuple[Union[ParserElement, str], Union[ParserElement, str]] -] -InfixNotationOperatorSpec = Union[ - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - typing.Optional[ParseAction], - ], - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - ], -] - - -def infix_notation( - base_expr: ParserElement, - op_list: List[InfixNotationOperatorSpec], - lpar: Union[str, ParserElement] = Suppress("("), - rpar: Union[str, ParserElement] = Suppress(")"), -) -> ParserElement: - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infix_notation. See - :class:`ParserElement.enable_packrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - ``base_expr`` - expression representing the most basic operand to - be used in the expression - - ``op_list`` - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(op_expr, - num_operands, right_left_assoc, (optional)parse_action)``, where: - - - ``op_expr`` is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if ``num_operands`` - is 3, ``op_expr`` is a tuple of two expressions, for the two - operators separating the 3 terms - - ``num_operands`` is the number of terms for this operator (must be 1, - 2, or 3) - - ``right_left_assoc`` is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``. - - ``parse_action`` is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``set_parse_action(*fn)`` - (:class:`ParserElement.set_parse_action`) - - ``lpar`` - expression for matching left-parentheses; if passed as a - str, then will be parsed as Suppress(lpar). If lpar is passed as - an expression (such as ``Literal('(')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress('(')``) - - ``rpar`` - expression for matching right-parentheses; if passed as a - str, then will be parsed as Suppress(rpar). If rpar is passed as - an expression (such as ``Literal(')')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infix_notation(integer | varname, - [ - ('-', 1, OpAssoc.RIGHT), - (one_of('* /'), 2, OpAssoc.LEFT), - (one_of('+ -'), 2, OpAssoc.LEFT), - ]) - - arith_expr.run_tests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', full_dump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.try_parse(instring, loc) - return loc, [] - - _FB.__name__ = "FollowedBy>" - - ret = Forward() - if isinstance(lpar, str): - lpar = Suppress(lpar) - if isinstance(rpar, str): - rpar = Suppress(rpar) - - # if lpar and rpar are not suppressed, wrap in group - if not (isinstance(rpar, Suppress) and isinstance(rpar, Suppress)): - lastExpr = base_expr | Group(lpar + ret + rpar) - else: - lastExpr = base_expr | (lpar + ret + rpar) - - for i, operDef in enumerate(op_list): - opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] - if isinstance(opExpr, str_type): - opExpr = ParserElement._literalStringClass(opExpr) - if arity == 3: - if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions" - ) - opExpr1, opExpr2 = opExpr - term_name = "{}{} term".format(opExpr1, opExpr2) - else: - term_name = "{} term".format(opExpr) - - if not 1 <= arity <= 3: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT): - raise ValueError("operator must indicate right or left associativity") - - thisExpr: Forward = Forward().set_name(term_name) - if rightLeftAssoc is OpAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...]) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( - lastExpr + (opExpr + lastExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr[2, ...]) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr - ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)) - elif rightLeftAssoc is OpAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Opt): - opExpr = Opt(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( - lastExpr + (opExpr + thisExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group( - lastExpr + thisExpr[1, ...] - ) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr - ) + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.set_parse_action(*pa) - else: - matchExpr.set_parse_action(pa) - thisExpr <<= (matchExpr | lastExpr).setName(term_name) - lastExpr = thisExpr - ret <<= lastExpr - return ret - - -def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]): - """ - (DEPRECATED - use IndentedBlock class instead) - Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - ``blockStatementExpr`` - expression defining syntax of statement that - is repeated within the indented block - - ``indentStack`` - list created by caller to manage indentation stack - (multiple ``statementWithIndentedBlock`` expressions within a single - grammar should share a common ``indentStack``) - - ``indent`` - boolean indicating whether block must be indented beyond - the current level; set to ``False`` for block of left-most statements - (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - (Note that indentedBlock uses internal parse actions which make it - incompatible with packrat parsing.) - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = stmt[1, ...] - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stacks.append(indentStack[:]) - - def reset_stack(): - indentStack[:] = backup_stacks[-1] - - def checkPeerIndent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if not (indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress()) - INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT") - PEER = Empty().set_parse_action(checkPeerIndent).set_name("") - UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT") - if indent: - smExpr = Group( - Opt(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + UNDENT - ) - else: - smExpr = Group( - Opt(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + Opt(UNDENT) - ) - - # add a parse action to remove backup_stack from list of backups - smExpr.add_parse_action( - lambda: backup_stacks.pop(-1) and None if backup_stacks else None - ) - smExpr.set_fail_action(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.set_name("indented block") - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -c_style_comment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/").set_name( - "C style comment" -) -"Comment of the form ``/* ... */``" - -html_comment = Regex(r"").set_name("HTML comment") -"Comment of the form ````" - -rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line") -dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment") -"Comment of the form ``// ... (to end of line)``" - -cpp_style_comment = Combine( - Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/" | dbl_slash_comment -).set_name("C++ style comment") -"Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`" - -java_style_comment = cpp_style_comment -"Same as :class:`cpp_style_comment`" - -python_style_comment = Regex(r"#.*").set_name("Python style comment") -"Comment of the form ``# ... (to end of line)``" - - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - - -# pre-PEP8 compatible names -delimitedList = delimited_list -countedArray = counted_array -matchPreviousLiteral = match_previous_literal -matchPreviousExpr = match_previous_expr -oneOf = one_of -dictOf = dict_of -originalTextFor = original_text_for -nestedExpr = nested_expr -makeHTMLTags = make_html_tags -makeXMLTags = make_xml_tags -anyOpenTag, anyCloseTag = any_open_tag, any_close_tag -commonHTMLEntity = common_html_entity -replaceHTMLEntity = replace_html_entity -opAssoc = OpAssoc -infixNotation = infix_notation -cStyleComment = c_style_comment -htmlComment = html_comment -restOfLine = rest_of_line -dblSlashComment = dbl_slash_comment -cppStyleComment = cpp_style_comment -javaStyleComment = java_style_comment -pythonStyleComment = python_style_comment diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/results.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/results.py deleted file mode 100644 index 00c9421..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/results.py +++ /dev/null @@ -1,760 +0,0 @@ -# results.py -from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator -import pprint -from weakref import ref as wkref -from typing import Tuple, Any - -str_type: Tuple[type, ...] = (str, bytes) -_generator_type = type((_ for _ in ())) - - -class _ParseResultsWithOffset: - __slots__ = ["tup"] - - def __init__(self, p1, p2): - self.tup = (p1, p2) - - def __getitem__(self, i): - return self.tup[i] - - def __getstate__(self): - return self.tup - - def __setstate__(self, *args): - self.tup = args[0] - - -class ParseResults: - """Structured parse results, to provide multiple means of access to - the parsed data: - - - as a list (``len(results)``) - - by list index (``results[0], results[1]``, etc.) - - by attribute (``results.`` - see :class:`ParserElement.set_results_name`) - - Example:: - - integer = Word(nums) - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - # equivalent form: - # date_str = (integer("year") + '/' - # + integer("month") + '/' - # + integer("day")) - - # parse_string returns a ParseResults object - result = date_str.parse_string("1999/12/31") - - def test(s, fn=repr): - print("{} -> {}".format(s, fn(eval(s)))) - test("list(result)") - test("result[0]") - test("result['month']") - test("result.day") - test("'month' in result") - test("'minutes' in result") - test("result.dump()", str) - - prints:: - - list(result) -> ['1999', '/', '12', '/', '31'] - result[0] -> '1999' - result['month'] -> '12' - result.day -> '31' - 'month' in result -> True - 'minutes' in result -> False - result.dump() -> ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - - _null_values: Tuple[Any, ...] = (None, [], "", ()) - - __slots__ = [ - "_name", - "_parent", - "_all_names", - "_modal", - "_toklist", - "_tokdict", - "__weakref__", - ] - - class List(list): - """ - Simple wrapper class to distinguish parsed list results that should be preserved - as actual Python lists, instead of being converted to :class:`ParseResults`: - - LBRACK, RBRACK = map(pp.Suppress, "[]") - element = pp.Forward() - item = ppc.integer - element_list = LBRACK + pp.delimited_list(element) + RBRACK - - # add parse actions to convert from ParseResults to actual Python collection types - def as_python_list(t): - return pp.ParseResults.List(t.as_list()) - element_list.add_parse_action(as_python_list) - - element <<= item | element_list - - element.run_tests(''' - 100 - [2,3,4] - [[2, 1],3,4] - [(2, 1),3,4] - (2,3,4) - ''', post_parse=lambda s, r: (r[0], type(r[0]))) - - prints: - - 100 - (100, ) - - [2,3,4] - ([2, 3, 4], ) - - [[2, 1],3,4] - ([[2, 1], 3, 4], ) - - (Used internally by :class:`Group` when `aslist=True`.) - """ - - def __new__(cls, contained=None): - if contained is None: - contained = [] - - if not isinstance(contained, list): - raise TypeError( - "{} may only be constructed with a list," - " not {}".format(cls.__name__, type(contained).__name__) - ) - - return list.__new__(cls) - - def __new__(cls, toklist=None, name=None, **kwargs): - if isinstance(toklist, ParseResults): - return toklist - self = object.__new__(cls) - self._name = None - self._parent = None - self._all_names = set() - - if toklist is None: - self._toklist = [] - elif isinstance(toklist, (list, _generator_type)): - self._toklist = ( - [toklist[:]] - if isinstance(toklist, ParseResults.List) - else list(toklist) - ) - else: - self._toklist = [toklist] - self._tokdict = dict() - return self - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance - ): - self._modal = modal - if name is not None and name != "": - if isinstance(name, int): - name = str(name) - if not modal: - self._all_names = {name} - self._name = name - if toklist not in self._null_values: - if isinstance(toklist, (str_type, type)): - toklist = [toklist] - if asList: - if isinstance(toklist, ParseResults): - self[name] = _ParseResultsWithOffset( - ParseResults(toklist._toklist), 0 - ) - else: - self[name] = _ParseResultsWithOffset( - ParseResults(toklist[0]), 0 - ) - self[name]._name = name - else: - try: - self[name] = toklist[0] - except (KeyError, TypeError, IndexError): - if toklist is not self: - self[name] = toklist - else: - self._name = name - - def __getitem__(self, i): - if isinstance(i, (int, slice)): - return self._toklist[i] - else: - if i not in self._all_names: - return self._tokdict[i][-1][0] - else: - return ParseResults([v[0] for v in self._tokdict[i]]) - - def __setitem__(self, k, v, isinstance=isinstance): - if isinstance(v, _ParseResultsWithOffset): - self._tokdict[k] = self._tokdict.get(k, list()) + [v] - sub = v[0] - elif isinstance(k, (int, slice)): - self._toklist[k] = v - sub = v - else: - self._tokdict[k] = self._tokdict.get(k, list()) + [ - _ParseResultsWithOffset(v, 0) - ] - sub = v - if isinstance(sub, ParseResults): - sub._parent = wkref(self) - - def __delitem__(self, i): - if isinstance(i, (int, slice)): - mylen = len(self._toklist) - del self._toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i + 1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position - (position > j) - ) - else: - del self._tokdict[i] - - def __contains__(self, k) -> bool: - return k in self._tokdict - - def __len__(self) -> int: - return len(self._toklist) - - def __bool__(self) -> bool: - return not not (self._toklist or self._tokdict) - - def __iter__(self) -> Iterator: - return iter(self._toklist) - - def __reversed__(self) -> Iterator: - return iter(self._toklist[::-1]) - - def keys(self): - return iter(self._tokdict) - - def values(self): - return (self[k] for k in self.keys()) - - def items(self): - return ((k, self[k]) for k in self.keys()) - - def haskeys(self) -> bool: - """ - Since ``keys()`` returns an iterator, this method is helpful in bypassing - code that looks for the existence of any defined results names.""" - return bool(self._tokdict) - - def pop(self, *args, **kwargs): - """ - Removes and returns item at specified index (default= ``last``). - Supports both ``list`` and ``dict`` semantics for ``pop()``. If - passed no argument or an integer argument, it will use ``list`` - semantics and pop tokens from the list of parsed tokens. If passed - a non-integer argument (most likely a string), it will use ``dict`` - semantics and pop the corresponding value from any defined results - names. A second default return value argument is supported, just as in - ``dict.pop()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - def remove_first(tokens): - tokens.pop(0) - numlist.add_parse_action(remove_first) - print(numlist.parse_string("0 123 321")) # -> ['123', '321'] - - label = Word(alphas) - patt = label("LABEL") + Word(nums)[1, ...] - print(patt.parse_string("AAB 123 321").dump()) - - # Use pop() in a parse action to remove named result (note that corresponding value is not - # removed from list form of results) - def remove_LABEL(tokens): - tokens.pop("LABEL") - return tokens - patt.add_parse_action(remove_LABEL) - print(patt.parse_string("AAB 123 321").dump()) - - prints:: - - ['AAB', '123', '321'] - - LABEL: 'AAB' - - ['AAB', '123', '321'] - """ - if not args: - args = [-1] - for k, v in kwargs.items(): - if k == "default": - args = (args[0], v) - else: - raise TypeError( - "pop() got an unexpected keyword argument {!r}".format(k) - ) - if isinstance(args[0], int) or len(args) == 1 or args[0] in self: - index = args[0] - ret = self[index] - del self[index] - return ret - else: - defaultvalue = args[1] - return defaultvalue - - def get(self, key, default_value=None): - """ - Returns named result matching the given key, or if there is no - such name, then returns the given ``default_value`` or ``None`` if no - ``default_value`` is specified. - - Similar to ``dict.get()``. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string("1999/12/31") - print(result.get("year")) # -> '1999' - print(result.get("hour", "not specified")) # -> 'not specified' - print(result.get("hour")) # -> None - """ - if key in self: - return self[key] - else: - return default_value - - def insert(self, index, ins_string): - """ - Inserts new element at location index in the list of parsed tokens. - - Similar to ``list.insert()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to insert the parse location in the front of the parsed results - def insert_locn(locn, tokens): - tokens.insert(0, locn) - numlist.add_parse_action(insert_locn) - print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321'] - """ - self._toklist.insert(index, ins_string) - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position + (position > index) - ) - - def append(self, item): - """ - Add single element to end of ``ParseResults`` list of elements. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to compute the sum of the parsed integers, and add it to the end - def append_sum(tokens): - tokens.append(sum(map(int, tokens))) - numlist.add_parse_action(append_sum) - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444] - """ - self._toklist.append(item) - - def extend(self, itemseq): - """ - Add sequence of elements to end of ``ParseResults`` list of elements. - - Example:: - - patt = Word(alphas)[1, ...] - - # use a parse action to append the reverse of the matched strings, to make a palindrome - def make_palindrome(tokens): - tokens.extend(reversed([t[::-1] for t in tokens])) - return ''.join(tokens) - patt.add_parse_action(make_palindrome) - print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' - """ - if isinstance(itemseq, ParseResults): - self.__iadd__(itemseq) - else: - self._toklist.extend(itemseq) - - def clear(self): - """ - Clear all elements and results names. - """ - del self._toklist[:] - self._tokdict.clear() - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - if name.startswith("__"): - raise AttributeError(name) - return "" - - def __add__(self, other) -> "ParseResults": - ret = self.copy() - ret += other - return ret - - def __iadd__(self, other) -> "ParseResults": - if other._tokdict: - offset = len(self._toklist) - addoffset = lambda a: offset if a < 0 else a + offset - otheritems = other._tokdict.items() - otherdictitems = [ - (k, _ParseResultsWithOffset(v[0], addoffset(v[1]))) - for k, vlist in otheritems - for v in vlist - ] - for k, v in otherdictitems: - self[k] = v - if isinstance(v[0], ParseResults): - v[0]._parent = wkref(self) - - self._toklist += other._toklist - self._all_names |= other._all_names - return self - - def __radd__(self, other) -> "ParseResults": - if isinstance(other, int) and other == 0: - # useful for merging many ParseResults using sum() builtin - return self.copy() - else: - # this may raise a TypeError - so be it - return other + self - - def __repr__(self) -> str: - return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.as_dict()) - - def __str__(self) -> str: - return ( - "[" - + ", ".join( - [ - str(i) if isinstance(i, ParseResults) else repr(i) - for i in self._toklist - ] - ) - + "]" - ) - - def _asStringList(self, sep=""): - out = [] - for item in self._toklist: - if out and sep: - out.append(sep) - if isinstance(item, ParseResults): - out += item._asStringList() - else: - out.append(str(item)) - return out - - def as_list(self) -> list: - """ - Returns the parse results as a nested list of matching tokens, all converted to strings. - - Example:: - - patt = Word(alphas)[1, ...] - result = patt.parse_string("sldkj lsdkj sldkj") - # even though the result prints in string-like form, it is actually a pyparsing ParseResults - print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] - - # Use as_list() to create an actual list - result_list = result.as_list() - print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] - """ - return [ - res.as_list() if isinstance(res, ParseResults) else res - for res in self._toklist - ] - - def as_dict(self) -> dict: - """ - Returns the named parse results as a nested dictionary. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('12/31/1999') - print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) - - result_dict = result.as_dict() - print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} - - # even though a ParseResults supports dict-like access, sometime you just need to have a dict - import json - print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable - print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"} - """ - - def to_item(obj): - if isinstance(obj, ParseResults): - return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj] - else: - return obj - - return dict((k, to_item(v)) for k, v in self.items()) - - def copy(self) -> "ParseResults": - """ - Returns a new copy of a :class:`ParseResults` object. - """ - ret = ParseResults(self._toklist) - ret._tokdict = self._tokdict.copy() - ret._parent = self._parent - ret._all_names |= self._all_names - ret._name = self._name - return ret - - def get_name(self): - r""" - Returns the results name for this token expression. Useful when several - different expressions might match at a particular location. - - Example:: - - integer = Word(nums) - ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") - house_number_expr = Suppress('#') + Word(nums, alphanums) - user_data = (Group(house_number_expr)("house_number") - | Group(ssn_expr)("ssn") - | Group(integer)("age")) - user_info = user_data[1, ...] - - result = user_info.parse_string("22 111-22-3333 #221B") - for item in result: - print(item.get_name(), ':', item[0]) - - prints:: - - age : 22 - ssn : 111-22-3333 - house_number : 221B - """ - if self._name: - return self._name - elif self._parent: - par = self._parent() - - def find_in_parent(sub): - return next( - ( - k - for k, vlist in par._tokdict.items() - for v, loc in vlist - if sub is v - ), - None, - ) - - return find_in_parent(self) if par else None - elif ( - len(self) == 1 - and len(self._tokdict) == 1 - and next(iter(self._tokdict.values()))[0][1] in (0, -1) - ): - return next(iter(self._tokdict.keys())) - else: - return None - - def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: - """ - Diagnostic method for listing out the contents of - a :class:`ParseResults`. Accepts an optional ``indent`` argument so - that this string can be embedded in a nested display of other data. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('1999/12/31') - print(result.dump()) - - prints:: - - ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - out = [] - NL = "\n" - out.append(indent + str(self.as_list()) if include_list else "") - - if full: - if self.haskeys(): - items = sorted((str(k), v) for k, v in self.items()) - for k, v in items: - if out: - out.append(NL) - out.append("{}{}- {}: ".format(indent, (" " * _depth), k)) - if isinstance(v, ParseResults): - if v: - out.append( - v.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - ) - else: - out.append(str(v)) - else: - out.append(repr(v)) - if any(isinstance(vv, ParseResults) for vv in self): - v = self - for i, vv in enumerate(v): - if isinstance(vv, ParseResults): - out.append( - "\n{}{}[{}]:\n{}{}{}".format( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - vv.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ), - ) - ) - else: - out.append( - "\n%s%s[%d]:\n%s%s%s" - % ( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - str(vv), - ) - ) - - return "".join(out) - - def pprint(self, *args, **kwargs): - """ - Pretty-printer for parsed results as a list, using the - `pprint `_ module. - Accepts additional positional or keyword args as defined for - `pprint.pprint `_ . - - Example:: - - ident = Word(alphas, alphanums) - num = Word(nums) - func = Forward() - term = ident | num | Group('(' + func + ')') - func <<= ident + Group(Optional(delimited_list(term))) - result = func.parse_string("fna a,b,(fnb c,d,200),100") - result.pprint(width=40) - - prints:: - - ['fna', - ['a', - 'b', - ['(', 'fnb', ['c', 'd', '200'], ')'], - '100']] - """ - pprint.pprint(self.as_list(), *args, **kwargs) - - # add support for pickle protocol - def __getstate__(self): - return ( - self._toklist, - ( - self._tokdict.copy(), - self._parent is not None and self._parent() or None, - self._all_names, - self._name, - ), - ) - - def __setstate__(self, state): - self._toklist, (self._tokdict, par, inAccumNames, self._name) = state - self._all_names = set(inAccumNames) - if par is not None: - self._parent = wkref(par) - else: - self._parent = None - - def __getnewargs__(self): - return self._toklist, self._name - - def __dir__(self): - return dir(type(self)) + list(self.keys()) - - @classmethod - def from_dict(cls, other, name=None) -> "ParseResults": - """ - Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the - name-value relations as results names. If an optional ``name`` argument is - given, a nested ``ParseResults`` will be returned. - """ - - def is_iterable(obj): - try: - iter(obj) - except Exception: - return False - else: - return not isinstance(obj, str_type) - - ret = cls([]) - for k, v in other.items(): - if isinstance(v, Mapping): - ret += cls.from_dict(v, name=k) - else: - ret += cls([v], name=k, asList=is_iterable(v)) - if name is not None: - ret = cls([ret], name=name) - return ret - - asList = as_list - asDict = as_dict - getName = get_name - - -MutableMapping.register(ParseResults) -MutableSequence.register(ParseResults) diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py deleted file mode 100644 index 84a0ef1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/testing.py +++ /dev/null @@ -1,331 +0,0 @@ -# testing.py - -from contextlib import contextmanager -import typing - -from .core import ( - ParserElement, - ParseException, - Keyword, - __diag__, - __compat__, -) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - bounded recursion parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example:: - - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - - self._save_context[ - "literal_string_class" - ] = ParserElement._literalStringClass - - self._save_context["verbose_stacktrace"] = ParserElement.verbose_stacktrace - - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - if ParserElement._packratEnabled: - self._save_context[ - "packrat_cache_size" - ] = ParserElement.packrat_cache.size - else: - self._save_context["packrat_cache_size"] = None - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context[ - "recursion_enabled" - ] = ParserElement._left_recursion_enabled - - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.set_default_whitespace_chars( - self._save_context["default_whitespace"] - ) - - ParserElement.verbose_stacktrace = self._save_context["verbose_stacktrace"] - - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - - for name, value in self._save_context["__diag__"].items(): - (__diag__.enable if value else __diag__.disable)(name) - - ParserElement._packratEnabled = False - if self._save_context["packrat_enabled"]: - ParserElement.enable_packrat(self._save_context["packrat_cache_size"]) - else: - ParserElement._parse = self._save_context["packrat_parse"] - ParserElement._left_recursion_enabled = self._save_context[ - "recursion_enabled" - ] - - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - return self - - def copy(self): - ret = type(self)() - ret._save_context.update(self._save_context) - return ret - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a :class:`ParseResults` object with an optional ``expected_list``, - and compare any defined results names with an optional ``expected_dict``. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.as_list(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.as_dict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asList()`` is equal to the ``expected_list``. - """ - result = expr.parse_string(test_string, parse_all=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``. - """ - result = expr.parse_string(test_string, parseAll=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of - list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped - with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``. - Finally, asserts that the overall ``runTests()`` success value is ``True``. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is not None: - merged = [ - (*rpt, expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next( - (exp for exp in expected if isinstance(exp, str)), None - ) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print("no validation for {!r}".format(test_string)) - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException(self, exc_type=ParseException, msg=None): - with self.assertRaises(exc_type, msg=msg): - yield - - @staticmethod - def with_line_numbers( - s: str, - start_line: typing.Optional[int] = None, - end_line: typing.Optional[int] = None, - expand_tabs: bool = True, - eol_mark: str = "|", - mark_spaces: typing.Optional[str] = None, - mark_control: typing.Optional[str] = None, - ) -> str: - """ - Helpful method for debugging a parser - prints a string with line and column numbers. - (Line and column numbers are 1-based.) - - :param s: tuple(bool, str - string to be printed with line and column numbers - :param start_line: int - (optional) starting line number in s to print (default=1) - :param end_line: int - (optional) ending line number in s to print (default=len(s)) - :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default - :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|") - :param mark_spaces: str - (optional) special character to display in place of spaces - :param mark_control: str - (optional) convert non-printing control characters to a placeholding - character; valid values: - - "unicode" - replaces control chars with Unicode symbols, such as "␍" and "␊" - - any single character string - replace control characters with given string - - None (default) - string is displayed as-is - - :return: str - input string with leading line numbers and column number headers - """ - if expand_tabs: - s = s.expandtabs() - if mark_control is not None: - if mark_control == "unicode": - tbl = str.maketrans( - {c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))} - | {127: 0x2421} - ) - eol_mark = "" - else: - tbl = str.maketrans( - {c: mark_control for c in list(range(0, 32)) + [127]} - ) - s = s.translate(tbl) - if mark_spaces is not None and mark_spaces != " ": - if mark_spaces == "unicode": - tbl = str.maketrans({9: 0x2409, 32: 0x2423}) - s = s.translate(tbl) - else: - s = s.replace(" ", mark_spaces) - if start_line is None: - start_line = 1 - if end_line is None: - end_line = len(s) - end_line = min(end_line, len(s)) - start_line = min(max(1, start_line), end_line) - - if mark_control != "unicode": - s_lines = s.splitlines()[start_line - 1 : end_line] - else: - s_lines = [line + "␊" for line in s.split("␊")[start_line - 1 : end_line]] - if not s_lines: - return "" - - lineno_width = len(str(end_line)) - max_line_len = max(len(line) for line in s_lines) - lead = " " * (lineno_width + 1) - if max_line_len >= 99: - header0 = ( - lead - + "".join( - "{}{}".format(" " * 99, (i + 1) % 100) - for i in range(max(max_line_len // 100, 1)) - ) - + "\n" - ) - else: - header0 = "" - header1 = ( - header0 - + lead - + "".join( - " {}".format((i + 1) % 10) - for i in range(-(-max_line_len // 10)) - ) - + "\n" - ) - header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n" - return ( - header1 - + header2 - + "\n".join( - "{:{}d}:{}{}".format(i, lineno_width, line, eol_mark) - for i, line in enumerate(s_lines, start=start_line) - ) - + "\n" - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py deleted file mode 100644 index 0652620..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/unicode.py +++ /dev/null @@ -1,352 +0,0 @@ -# unicode.py - -import sys -from itertools import filterfalse -from typing import List, Tuple, Union - - -class _lazyclassproperty: - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, "_intern") or any( - cls._intern is getattr(superclass, "_intern", []) - for superclass in cls.__mro__[1:] - ): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]] - - -class unicode_set: - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``. Ranges can be specified using - 2-tuples or a 1-tuple, such as:: - - _ranges = [ - (0x0020, 0x007e), - (0x00a0, 0x00ff), - (0x0100,), - ] - - Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x). - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - - _ranges: UnicodeRangeList = [] - - @_lazyclassproperty - def _chars_for_ranges(cls): - ret = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in getattr(cc, "_ranges", ()): - ret.extend(range(rr[0], rr[-1] + 1)) - return [chr(c) for c in sorted(set(ret))] - - @_lazyclassproperty - def printables(cls): - "all non-whitespace characters in this range" - return "".join(filterfalse(str.isspace, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphas(cls): - "all alphabetic characters in this range" - return "".join(filter(str.isalpha, cls._chars_for_ranges)) - - @_lazyclassproperty - def nums(cls): - "all numeric digit characters in this range" - return "".join(filter(str.isdigit, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphanums(cls): - "all alphanumeric characters in this range" - return cls.alphas + cls.nums - - @_lazyclassproperty - def identchars(cls): - "all characters in this range that are valid identifier characters, plus underscore '_'" - return "".join( - sorted( - set( - "".join(filter(str.isidentifier, cls._chars_for_ranges)) - + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº" - + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" - + "_" - ) - ) - ) - - @_lazyclassproperty - def identbodychars(cls): - """ - all characters in this range that are valid identifier body characters, - plus the digits 0-9 - """ - return "".join( - sorted( - set( - cls.identchars - + "0123456789" - + "".join( - [c for c in cls._chars_for_ranges if ("_" + c).isidentifier()] - ) - ) - ) - ) - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - - # fmt: off - - # define ranges in language character sets - _ranges: UnicodeRangeList = [ - (0x0020, sys.maxunicode), - ] - - class BasicMultilingualPlane(unicode_set): - "Unicode set for the Basic Multilingual Plane" - _ranges: UnicodeRangeList = [ - (0x0020, 0xFFFF), - ] - - class Latin1(unicode_set): - "Unicode set for Latin-1 Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0020, 0x007E), - (0x00A0, 0x00FF), - ] - - class LatinA(unicode_set): - "Unicode set for Latin-A Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0100, 0x017F), - ] - - class LatinB(unicode_set): - "Unicode set for Latin-B Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0180, 0x024F), - ] - - class Greek(unicode_set): - "Unicode set for Greek Unicode Character Ranges" - _ranges: UnicodeRangeList = [ - (0x0342, 0x0345), - (0x0370, 0x0377), - (0x037A, 0x037F), - (0x0384, 0x038A), - (0x038C,), - (0x038E, 0x03A1), - (0x03A3, 0x03E1), - (0x03F0, 0x03FF), - (0x1D26, 0x1D2A), - (0x1D5E,), - (0x1D60,), - (0x1D66, 0x1D6A), - (0x1F00, 0x1F15), - (0x1F18, 0x1F1D), - (0x1F20, 0x1F45), - (0x1F48, 0x1F4D), - (0x1F50, 0x1F57), - (0x1F59,), - (0x1F5B,), - (0x1F5D,), - (0x1F5F, 0x1F7D), - (0x1F80, 0x1FB4), - (0x1FB6, 0x1FC4), - (0x1FC6, 0x1FD3), - (0x1FD6, 0x1FDB), - (0x1FDD, 0x1FEF), - (0x1FF2, 0x1FF4), - (0x1FF6, 0x1FFE), - (0x2129,), - (0x2719, 0x271A), - (0xAB65,), - (0x10140, 0x1018D), - (0x101A0,), - (0x1D200, 0x1D245), - (0x1F7A1, 0x1F7A7), - ] - - class Cyrillic(unicode_set): - "Unicode set for Cyrillic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0400, 0x052F), - (0x1C80, 0x1C88), - (0x1D2B,), - (0x1D78,), - (0x2DE0, 0x2DFF), - (0xA640, 0xA672), - (0xA674, 0xA69F), - (0xFE2E, 0xFE2F), - ] - - class Chinese(unicode_set): - "Unicode set for Chinese Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x2E80, 0x2E99), - (0x2E9B, 0x2EF3), - (0x31C0, 0x31E3), - (0x3400, 0x4DB5), - (0x4E00, 0x9FEF), - (0xA700, 0xA707), - (0xF900, 0xFA6D), - (0xFA70, 0xFAD9), - (0x16FE2, 0x16FE3), - (0x1F210, 0x1F212), - (0x1F214, 0x1F23B), - (0x1F240, 0x1F248), - (0x20000, 0x2A6D6), - (0x2A700, 0x2B734), - (0x2B740, 0x2B81D), - (0x2B820, 0x2CEA1), - (0x2CEB0, 0x2EBE0), - (0x2F800, 0x2FA1D), - ] - - class Japanese(unicode_set): - "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" - _ranges: UnicodeRangeList = [] - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x4E00, 0x9FBF), - (0x3000, 0x303F), - ] - - class Hiragana(unicode_set): - "Unicode set for Hiragana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3041, 0x3096), - (0x3099, 0x30A0), - (0x30FC,), - (0xFF70,), - (0x1B001,), - (0x1B150, 0x1B152), - (0x1F200,), - ] - - class Katakana(unicode_set): - "Unicode set for Katakana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3099, 0x309C), - (0x30A0, 0x30FF), - (0x31F0, 0x31FF), - (0x32D0, 0x32FE), - (0xFF65, 0xFF9F), - (0x1B000,), - (0x1B164, 0x1B167), - (0x1F201, 0x1F202), - (0x1F213,), - ] - - class Hangul(unicode_set): - "Unicode set for Hangul (Korean) Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x1100, 0x11FF), - (0x302E, 0x302F), - (0x3131, 0x318E), - (0x3200, 0x321C), - (0x3260, 0x327B), - (0x327E,), - (0xA960, 0xA97C), - (0xAC00, 0xD7A3), - (0xD7B0, 0xD7C6), - (0xD7CB, 0xD7FB), - (0xFFA0, 0xFFBE), - (0xFFC2, 0xFFC7), - (0xFFCA, 0xFFCF), - (0xFFD2, 0xFFD7), - (0xFFDA, 0xFFDC), - ] - - Korean = Hangul - - class CJK(Chinese, Japanese, Hangul): - "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" - - class Thai(unicode_set): - "Unicode set for Thai Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0E01, 0x0E3A), - (0x0E3F, 0x0E5B) - ] - - class Arabic(unicode_set): - "Unicode set for Arabic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0600, 0x061B), - (0x061E, 0x06FF), - (0x0700, 0x077F), - ] - - class Hebrew(unicode_set): - "Unicode set for Hebrew Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0591, 0x05C7), - (0x05D0, 0x05EA), - (0x05EF, 0x05F4), - (0xFB1D, 0xFB36), - (0xFB38, 0xFB3C), - (0xFB3E,), - (0xFB40, 0xFB41), - (0xFB43, 0xFB44), - (0xFB46, 0xFB4F), - ] - - class Devanagari(unicode_set): - "Unicode set for Devanagari Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0900, 0x097F), - (0xA8E0, 0xA8FF) - ] - - # fmt: on - - -pyparsing_unicode.Japanese._ranges = ( - pyparsing_unicode.Japanese.Kanji._ranges - + pyparsing_unicode.Japanese.Hiragana._ranges - + pyparsing_unicode.Japanese.Katakana._ranges -) - -pyparsing_unicode.BMP = pyparsing_unicode.BasicMultilingualPlane - -# add language identifiers using language Unicode -pyparsing_unicode.العربية = pyparsing_unicode.Arabic -pyparsing_unicode.中文 = pyparsing_unicode.Chinese -pyparsing_unicode.кириллица = pyparsing_unicode.Cyrillic -pyparsing_unicode.Ελληνικά = pyparsing_unicode.Greek -pyparsing_unicode.עִברִית = pyparsing_unicode.Hebrew -pyparsing_unicode.日本語 = pyparsing_unicode.Japanese -pyparsing_unicode.Japanese.漢字 = pyparsing_unicode.Japanese.Kanji -pyparsing_unicode.Japanese.カタカナ = pyparsing_unicode.Japanese.Katakana -pyparsing_unicode.Japanese.ひらがな = pyparsing_unicode.Japanese.Hiragana -pyparsing_unicode.한국어 = pyparsing_unicode.Korean -pyparsing_unicode.ไทย = pyparsing_unicode.Thai -pyparsing_unicode.देवनागरी = pyparsing_unicode.Devanagari diff --git a/.venv/Lib/site-packages/pip/_vendor/pyparsing/util.py b/.venv/Lib/site-packages/pip/_vendor/pyparsing/util.py deleted file mode 100644 index 34ce092..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/pyparsing/util.py +++ /dev/null @@ -1,235 +0,0 @@ -# util.py -import warnings -import types -import collections -import itertools -from functools import lru_cache -from typing import List, Union, Iterable - -_bslash = chr(92) - - -class __config_flags: - """Internal class for defining compatibility and debugging flags""" - - _all_names: List[str] = [] - _fixed_names: List[str] = [] - _type_desc = "configuration" - - @classmethod - def _set(cls, dname, value): - if dname in cls._fixed_names: - warnings.warn( - "{}.{} {} is {} and cannot be overridden".format( - cls.__name__, - dname, - cls._type_desc, - str(getattr(cls, dname)).upper(), - ) - ) - return - if dname in cls._all_names: - setattr(cls, dname, value) - else: - raise ValueError("no such {} {!r}".format(cls._type_desc, dname)) - - enable = classmethod(lambda cls, name: cls._set(name, True)) - disable = classmethod(lambda cls, name: cls._set(name, False)) - - -@lru_cache(maxsize=128) -def col(loc: int, strg: str) -> int: - """ - Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See - :class:`ParserElement.parseString` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - """ - s = strg - return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc) - - -@lru_cache(maxsize=128) -def lineno(loc: int, strg: str) -> int: - """Returns current line number within a string, counting newlines as line separators. - The first line is number 1. - - Note - the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`ParserElement.parseString` - for more information on parsing strings containing ```` s, and - suggested methods to maintain a consistent view of the parsed string, the - parse location, and line and column positions within the parsed string. - """ - return strg.count("\n", 0, loc) + 1 - - -@lru_cache(maxsize=128) -def line(loc: int, strg: str) -> str: - """ - Returns the line of text containing loc within a string, counting newlines as line separators. - """ - last_cr = strg.rfind("\n", 0, loc) - next_cr = strg.find("\n", loc) - return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :] - - -class _UnboundedCache: - def __init__(self): - cache = {} - cache_get = cache.get - self.not_in_cache = not_in_cache = object() - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - - def clear(_): - cache.clear() - - self.size = None - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class _FifoCache: - def __init__(self, size): - self.not_in_cache = not_in_cache = object() - cache = collections.OrderedDict() - cache_get = cache.get - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - while len(cache) > size: - cache.popitem(last=False) - - def clear(_): - cache.clear() - - self.size = size - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class LRUMemo: - """ - A memoizing mapping that retains `capacity` deleted items - - The memo tracks retained items by their access order; once `capacity` items - are retained, the least recently used item is discarded. - """ - - def __init__(self, capacity): - self._capacity = capacity - self._active = {} - self._memory = collections.OrderedDict() - - def __getitem__(self, key): - try: - return self._active[key] - except KeyError: - self._memory.move_to_end(key) - return self._memory[key] - - def __setitem__(self, key, value): - self._memory.pop(key, None) - self._active[key] = value - - def __delitem__(self, key): - try: - value = self._active.pop(key) - except KeyError: - pass - else: - while len(self._memory) >= self._capacity: - self._memory.popitem(last=False) - self._memory[key] = value - - def clear(self): - self._active.clear() - self._memory.clear() - - -class UnboundedMemo(dict): - """ - A memoizing mapping that retains all deleted items - """ - - def __delitem__(self, key): - pass - - -def _escape_regex_range_chars(s: str) -> str: - # escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return str(s) - - -def _collapse_string_to_ranges( - s: Union[str, Iterable[str]], re_escape: bool = True -) -> str: - def is_consecutive(c): - c_int = ord(c) - is_consecutive.prev, prev = c_int, is_consecutive.prev - if c_int - prev > 1: - is_consecutive.value = next(is_consecutive.counter) - return is_consecutive.value - - is_consecutive.prev = 0 - is_consecutive.counter = itertools.count() - is_consecutive.value = -1 - - def escape_re_range_char(c): - return "\\" + c if c in r"\^-][" else c - - def no_escape_re_range_char(c): - return c - - if not re_escape: - escape_re_range_char = no_escape_re_range_char - - ret = [] - s = "".join(sorted(set(s))) - if len(s) > 3: - for _, chars in itertools.groupby(s, key=is_consecutive): - first = last = next(chars) - last = collections.deque( - itertools.chain(iter([last]), chars), maxlen=1 - ).pop() - if first == last: - ret.append(escape_re_range_char(first)) - else: - sep = "" if ord(last) == ord(first) + 1 else "-" - ret.append( - "{}{}{}".format( - escape_re_range_char(first), sep, escape_re_range_char(last) - ) - ) - else: - ret = [escape_re_range_char(c) for c in s] - - return "".join(ret) - - -def _flatten(ll: list) -> list: - ret = [] - for i in ll: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/__init__.py b/.venv/Lib/site-packages/pip/_vendor/requests/__init__.py deleted file mode 100644 index 9e97059..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/__init__.py +++ /dev/null @@ -1,182 +0,0 @@ -# __ -# /__) _ _ _ _ _/ _ -# / ( (- (/ (/ (- _) / _) -# / - -""" -Requests HTTP Library -~~~~~~~~~~~~~~~~~~~~~ - -Requests is an HTTP library, written in Python, for human beings. -Basic GET usage: - - >>> import requests - >>> r = requests.get('https://www.python.org') - >>> r.status_code - 200 - >>> b'Python is a programming language' in r.content - True - -... or POST: - - >>> payload = dict(key1='value1', key2='value2') - >>> r = requests.post('https://httpbin.org/post', data=payload) - >>> print(r.text) - { - ... - "form": { - "key1": "value1", - "key2": "value2" - }, - ... - } - -The other HTTP methods are supported - see `requests.api`. Full documentation -is at . - -:copyright: (c) 2017 by Kenneth Reitz. -:license: Apache 2.0, see LICENSE for more details. -""" - -import warnings - -from pip._vendor import urllib3 - -from .exceptions import RequestsDependencyWarning - -charset_normalizer_version = None - -try: - from pip._vendor.chardet import __version__ as chardet_version -except ImportError: - chardet_version = None - - -def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): - urllib3_version = urllib3_version.split(".") - assert urllib3_version != ["dev"] # Verify urllib3 isn't installed from git. - - # Sometimes, urllib3 only reports its version as 16.1. - if len(urllib3_version) == 2: - urllib3_version.append("0") - - # Check urllib3 for compatibility. - major, minor, patch = urllib3_version # noqa: F811 - major, minor, patch = int(major), int(minor), int(patch) - # urllib3 >= 1.21.1, <= 1.26 - assert major == 1 - assert minor >= 21 - assert minor <= 26 - - # Check charset_normalizer for compatibility. - if chardet_version: - major, minor, patch = chardet_version.split(".")[:3] - major, minor, patch = int(major), int(minor), int(patch) - # chardet_version >= 3.0.2, < 6.0.0 - assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0) - elif charset_normalizer_version: - major, minor, patch = charset_normalizer_version.split(".")[:3] - major, minor, patch = int(major), int(minor), int(patch) - # charset_normalizer >= 2.0.0 < 3.0.0 - assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0) - else: - raise Exception("You need either charset_normalizer or chardet installed") - - -def _check_cryptography(cryptography_version): - # cryptography < 1.3.4 - try: - cryptography_version = list(map(int, cryptography_version.split("."))) - except ValueError: - return - - if cryptography_version < [1, 3, 4]: - warning = "Old version of cryptography ({}) may cause slowdown.".format( - cryptography_version - ) - warnings.warn(warning, RequestsDependencyWarning) - - -# Check imported dependencies for compatibility. -try: - check_compatibility( - urllib3.__version__, chardet_version, charset_normalizer_version - ) -except (AssertionError, ValueError): - warnings.warn( - "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " - "version!".format( - urllib3.__version__, chardet_version, charset_normalizer_version - ), - RequestsDependencyWarning, - ) - -# Attempt to enable urllib3's fallback for SNI support -# if the standard library doesn't support SNI or the -# 'ssl' library isn't available. -try: - # Note: This logic prevents upgrading cryptography on Windows, if imported - # as part of pip. - from pip._internal.utils.compat import WINDOWS - if not WINDOWS: - raise ImportError("pip internals: don't import cryptography on Windows") - try: - import ssl - except ImportError: - ssl = None - - if not getattr(ssl, "HAS_SNI", False): - from pip._vendor.urllib3.contrib import pyopenssl - - pyopenssl.inject_into_urllib3() - - # Check cryptography version - from cryptography import __version__ as cryptography_version - - _check_cryptography(cryptography_version) -except ImportError: - pass - -# urllib3's DependencyWarnings should be silenced. -from pip._vendor.urllib3.exceptions import DependencyWarning - -warnings.simplefilter("ignore", DependencyWarning) - -# Set default logging handler to avoid "No handler found" warnings. -import logging -from logging import NullHandler - -from . import packages, utils -from .__version__ import ( - __author__, - __author_email__, - __build__, - __cake__, - __copyright__, - __description__, - __license__, - __title__, - __url__, - __version__, -) -from .api import delete, get, head, options, patch, post, put, request -from .exceptions import ( - ConnectionError, - ConnectTimeout, - FileModeWarning, - HTTPError, - JSONDecodeError, - ReadTimeout, - RequestException, - Timeout, - TooManyRedirects, - URLRequired, -) -from .models import PreparedRequest, Request, Response -from .sessions import Session, session -from .status_codes import codes - -logging.getLogger(__name__).addHandler(NullHandler()) - -# FileModeWarnings go off per the default. -warnings.simplefilter("default", FileModeWarning, append=True) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/__version__.py b/.venv/Lib/site-packages/pip/_vendor/requests/__version__.py deleted file mode 100644 index e725ada..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/__version__.py +++ /dev/null @@ -1,14 +0,0 @@ -# .-. .-. .-. . . .-. .-. .-. .-. -# |( |- |.| | | |- `-. | `-. -# ' ' `-' `-`.`-' `-' `-' ' `-' - -__title__ = "requests" -__description__ = "Python HTTP for Humans." -__url__ = "https://requests.readthedocs.io" -__version__ = "2.28.1" -__build__ = 0x022801 -__author__ = "Kenneth Reitz" -__author_email__ = "me@kennethreitz.org" -__license__ = "Apache 2.0" -__copyright__ = "Copyright 2022 Kenneth Reitz" -__cake__ = "\u2728 \U0001f370 \u2728" diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py b/.venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py deleted file mode 100644 index 7dc9bc5..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -requests._internal_utils -~~~~~~~~~~~~~~ - -Provides utility functions that are consumed internally by Requests -which depend on extremely few external helpers (such as compat) -""" -import re - -from .compat import builtin_str - -_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$") -_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$") -_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$") -_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$") - -HEADER_VALIDATORS = { - bytes: (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE), - str: (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR), -} - - -def to_native_string(string, encoding="ascii"): - """Given a string object, regardless of type, returns a representation of - that string in the native string type, encoding and decoding where - necessary. This assumes ASCII unless told otherwise. - """ - if isinstance(string, builtin_str): - out = string - else: - out = string.decode(encoding) - - return out - - -def unicode_is_ascii(u_string): - """Determine if unicode string only contains ASCII characters. - - :param str u_string: unicode string to check. Must be unicode - and not Python 2 `str`. - :rtype: bool - """ - assert isinstance(u_string, str) - try: - u_string.encode("ascii") - return True - except UnicodeEncodeError: - return False diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/adapters.py b/.venv/Lib/site-packages/pip/_vendor/requests/adapters.py deleted file mode 100644 index f68f7d4..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/adapters.py +++ /dev/null @@ -1,584 +0,0 @@ -""" -requests.adapters -~~~~~~~~~~~~~~~~~ - -This module contains the transport adapters that Requests uses to define -and maintain connections. -""" - -import os.path -import socket # noqa: F401 - -from pip._vendor.urllib3.exceptions import ClosedPoolError, ConnectTimeoutError -from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError -from pip._vendor.urllib3.exceptions import InvalidHeader as _InvalidHeader -from pip._vendor.urllib3.exceptions import ( - LocationValueError, - MaxRetryError, - NewConnectionError, - ProtocolError, -) -from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError -from pip._vendor.urllib3.exceptions import ReadTimeoutError, ResponseError -from pip._vendor.urllib3.exceptions import SSLError as _SSLError -from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url -from pip._vendor.urllib3.response import HTTPResponse -from pip._vendor.urllib3.util import Timeout as TimeoutSauce -from pip._vendor.urllib3.util import parse_url -from pip._vendor.urllib3.util.retry import Retry - -from .auth import _basic_auth_str -from .compat import basestring, urlparse -from .cookies import extract_cookies_to_jar -from .exceptions import ( - ConnectionError, - ConnectTimeout, - InvalidHeader, - InvalidProxyURL, - InvalidSchema, - InvalidURL, - ProxyError, - ReadTimeout, - RetryError, - SSLError, -) -from .models import Response -from .structures import CaseInsensitiveDict -from .utils import ( - DEFAULT_CA_BUNDLE_PATH, - extract_zipped_paths, - get_auth_from_url, - get_encoding_from_headers, - prepend_scheme_if_needed, - select_proxy, - urldefragauth, -) - -try: - from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager -except ImportError: - - def SOCKSProxyManager(*args, **kwargs): - raise InvalidSchema("Missing dependencies for SOCKS support.") - - -DEFAULT_POOLBLOCK = False -DEFAULT_POOLSIZE = 10 -DEFAULT_RETRIES = 0 -DEFAULT_POOL_TIMEOUT = None - - -class BaseAdapter: - """The Base Transport Adapter""" - - def __init__(self): - super().__init__() - - def send( - self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None - ): - """Sends PreparedRequest object. Returns Response object. - - :param request: The :class:`PreparedRequest ` being sent. - :param stream: (optional) Whether to stream the request content. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use - :param cert: (optional) Any user-provided SSL certificate to be trusted. - :param proxies: (optional) The proxies dictionary to apply to the request. - """ - raise NotImplementedError - - def close(self): - """Cleans up adapter specific items.""" - raise NotImplementedError - - -class HTTPAdapter(BaseAdapter): - """The built-in HTTP Adapter for urllib3. - - Provides a general-case interface for Requests sessions to contact HTTP and - HTTPS urls by implementing the Transport Adapter interface. This class will - usually be created by the :class:`Session ` class under the - covers. - - :param pool_connections: The number of urllib3 connection pools to cache. - :param pool_maxsize: The maximum number of connections to save in the pool. - :param max_retries: The maximum number of retries each connection - should attempt. Note, this applies only to failed DNS lookups, socket - connections and connection timeouts, never to requests where data has - made it to the server. By default, Requests does not retry failed - connections. If you need granular control over the conditions under - which we retry a request, import urllib3's ``Retry`` class and pass - that instead. - :param pool_block: Whether the connection pool should block for connections. - - Usage:: - - >>> import requests - >>> s = requests.Session() - >>> a = requests.adapters.HTTPAdapter(max_retries=3) - >>> s.mount('http://', a) - """ - - __attrs__ = [ - "max_retries", - "config", - "_pool_connections", - "_pool_maxsize", - "_pool_block", - ] - - def __init__( - self, - pool_connections=DEFAULT_POOLSIZE, - pool_maxsize=DEFAULT_POOLSIZE, - max_retries=DEFAULT_RETRIES, - pool_block=DEFAULT_POOLBLOCK, - ): - if max_retries == DEFAULT_RETRIES: - self.max_retries = Retry(0, read=False) - else: - self.max_retries = Retry.from_int(max_retries) - self.config = {} - self.proxy_manager = {} - - super().__init__() - - self._pool_connections = pool_connections - self._pool_maxsize = pool_maxsize - self._pool_block = pool_block - - self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) - - def __getstate__(self): - return {attr: getattr(self, attr, None) for attr in self.__attrs__} - - def __setstate__(self, state): - # Can't handle by adding 'proxy_manager' to self.__attrs__ because - # self.poolmanager uses a lambda function, which isn't pickleable. - self.proxy_manager = {} - self.config = {} - - for attr, value in state.items(): - setattr(self, attr, value) - - self.init_poolmanager( - self._pool_connections, self._pool_maxsize, block=self._pool_block - ) - - def init_poolmanager( - self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs - ): - """Initializes a urllib3 PoolManager. - - This method should not be called from user code, and is only - exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param connections: The number of urllib3 connection pools to cache. - :param maxsize: The maximum number of connections to save in the pool. - :param block: Block when no free connections are available. - :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. - """ - # save these values for pickling - self._pool_connections = connections - self._pool_maxsize = maxsize - self._pool_block = block - - self.poolmanager = PoolManager( - num_pools=connections, - maxsize=maxsize, - block=block, - strict=True, - **pool_kwargs, - ) - - def proxy_manager_for(self, proxy, **proxy_kwargs): - """Return urllib3 ProxyManager for the given proxy. - - This method should not be called from user code, and is only - exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param proxy: The proxy to return a urllib3 ProxyManager for. - :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. - :returns: ProxyManager - :rtype: urllib3.ProxyManager - """ - if proxy in self.proxy_manager: - manager = self.proxy_manager[proxy] - elif proxy.lower().startswith("socks"): - username, password = get_auth_from_url(proxy) - manager = self.proxy_manager[proxy] = SOCKSProxyManager( - proxy, - username=username, - password=password, - num_pools=self._pool_connections, - maxsize=self._pool_maxsize, - block=self._pool_block, - **proxy_kwargs, - ) - else: - proxy_headers = self.proxy_headers(proxy) - manager = self.proxy_manager[proxy] = proxy_from_url( - proxy, - proxy_headers=proxy_headers, - num_pools=self._pool_connections, - maxsize=self._pool_maxsize, - block=self._pool_block, - **proxy_kwargs, - ) - - return manager - - def cert_verify(self, conn, url, verify, cert): - """Verify a SSL certificate. This method should not be called from user - code, and is only exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param conn: The urllib3 connection object associated with the cert. - :param url: The requested URL. - :param verify: Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use - :param cert: The SSL certificate to verify. - """ - if url.lower().startswith("https") and verify: - - cert_loc = None - - # Allow self-specified cert location. - if verify is not True: - cert_loc = verify - - if not cert_loc: - cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) - - if not cert_loc or not os.path.exists(cert_loc): - raise OSError( - f"Could not find a suitable TLS CA certificate bundle, " - f"invalid path: {cert_loc}" - ) - - conn.cert_reqs = "CERT_REQUIRED" - - if not os.path.isdir(cert_loc): - conn.ca_certs = cert_loc - else: - conn.ca_cert_dir = cert_loc - else: - conn.cert_reqs = "CERT_NONE" - conn.ca_certs = None - conn.ca_cert_dir = None - - if cert: - if not isinstance(cert, basestring): - conn.cert_file = cert[0] - conn.key_file = cert[1] - else: - conn.cert_file = cert - conn.key_file = None - if conn.cert_file and not os.path.exists(conn.cert_file): - raise OSError( - f"Could not find the TLS certificate file, " - f"invalid path: {conn.cert_file}" - ) - if conn.key_file and not os.path.exists(conn.key_file): - raise OSError( - f"Could not find the TLS key file, invalid path: {conn.key_file}" - ) - - def build_response(self, req, resp): - """Builds a :class:`Response ` object from a urllib3 - response. This should not be called from user code, and is only exposed - for use when subclassing the - :class:`HTTPAdapter ` - - :param req: The :class:`PreparedRequest ` used to generate the response. - :param resp: The urllib3 response object. - :rtype: requests.Response - """ - response = Response() - - # Fallback to None if there's no status_code, for whatever reason. - response.status_code = getattr(resp, "status", None) - - # Make headers case-insensitive. - response.headers = CaseInsensitiveDict(getattr(resp, "headers", {})) - - # Set encoding. - response.encoding = get_encoding_from_headers(response.headers) - response.raw = resp - response.reason = response.raw.reason - - if isinstance(req.url, bytes): - response.url = req.url.decode("utf-8") - else: - response.url = req.url - - # Add new cookies from the server. - extract_cookies_to_jar(response.cookies, req, resp) - - # Give the Response some context. - response.request = req - response.connection = self - - return response - - def get_connection(self, url, proxies=None): - """Returns a urllib3 connection for the given URL. This should not be - called from user code, and is only exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param url: The URL to connect to. - :param proxies: (optional) A Requests-style dictionary of proxies used on this request. - :rtype: urllib3.ConnectionPool - """ - proxy = select_proxy(url, proxies) - - if proxy: - proxy = prepend_scheme_if_needed(proxy, "http") - proxy_url = parse_url(proxy) - if not proxy_url.host: - raise InvalidProxyURL( - "Please check proxy URL. It is malformed " - "and could be missing the host." - ) - proxy_manager = self.proxy_manager_for(proxy) - conn = proxy_manager.connection_from_url(url) - else: - # Only scheme should be lower case - parsed = urlparse(url) - url = parsed.geturl() - conn = self.poolmanager.connection_from_url(url) - - return conn - - def close(self): - """Disposes of any internal state. - - Currently, this closes the PoolManager and any active ProxyManager, - which closes any pooled connections. - """ - self.poolmanager.clear() - for proxy in self.proxy_manager.values(): - proxy.clear() - - def request_url(self, request, proxies): - """Obtain the url to use when making the final request. - - If the message is being sent through a HTTP proxy, the full URL has to - be used. Otherwise, we should only use the path portion of the URL. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param request: The :class:`PreparedRequest ` being sent. - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. - :rtype: str - """ - proxy = select_proxy(request.url, proxies) - scheme = urlparse(request.url).scheme - - is_proxied_http_request = proxy and scheme != "https" - using_socks_proxy = False - if proxy: - proxy_scheme = urlparse(proxy).scheme.lower() - using_socks_proxy = proxy_scheme.startswith("socks") - - url = request.path_url - if is_proxied_http_request and not using_socks_proxy: - url = urldefragauth(request.url) - - return url - - def add_headers(self, request, **kwargs): - """Add any headers needed by the connection. As of v2.0 this does - nothing by default, but is left for overriding by users that subclass - the :class:`HTTPAdapter `. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param request: The :class:`PreparedRequest ` to add headers to. - :param kwargs: The keyword arguments from the call to send(). - """ - pass - - def proxy_headers(self, proxy): - """Returns a dictionary of the headers to add to any request sent - through a proxy. This works with urllib3 magic to ensure that they are - correctly sent to the proxy, rather than in a tunnelled request if - CONNECT is being used. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param proxy: The url of the proxy being used for this request. - :rtype: dict - """ - headers = {} - username, password = get_auth_from_url(proxy) - - if username: - headers["Proxy-Authorization"] = _basic_auth_str(username, password) - - return headers - - def send( - self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None - ): - """Sends PreparedRequest object. Returns Response object. - - :param request: The :class:`PreparedRequest ` being sent. - :param stream: (optional) Whether to stream the request content. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple or urllib3 Timeout object - :param verify: (optional) Either a boolean, in which case it controls whether - we verify the server's TLS certificate, or a string, in which case it - must be a path to a CA bundle to use - :param cert: (optional) Any user-provided SSL certificate to be trusted. - :param proxies: (optional) The proxies dictionary to apply to the request. - :rtype: requests.Response - """ - - try: - conn = self.get_connection(request.url, proxies) - except LocationValueError as e: - raise InvalidURL(e, request=request) - - self.cert_verify(conn, request.url, verify, cert) - url = self.request_url(request, proxies) - self.add_headers( - request, - stream=stream, - timeout=timeout, - verify=verify, - cert=cert, - proxies=proxies, - ) - - chunked = not (request.body is None or "Content-Length" in request.headers) - - if isinstance(timeout, tuple): - try: - connect, read = timeout - timeout = TimeoutSauce(connect=connect, read=read) - except ValueError: - raise ValueError( - f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " - f"or a single float to set both timeouts to the same value." - ) - elif isinstance(timeout, TimeoutSauce): - pass - else: - timeout = TimeoutSauce(connect=timeout, read=timeout) - - try: - if not chunked: - resp = conn.urlopen( - method=request.method, - url=url, - body=request.body, - headers=request.headers, - redirect=False, - assert_same_host=False, - preload_content=False, - decode_content=False, - retries=self.max_retries, - timeout=timeout, - ) - - # Send the request. - else: - if hasattr(conn, "proxy_pool"): - conn = conn.proxy_pool - - low_conn = conn._get_conn(timeout=DEFAULT_POOL_TIMEOUT) - - try: - skip_host = "Host" in request.headers - low_conn.putrequest( - request.method, - url, - skip_accept_encoding=True, - skip_host=skip_host, - ) - - for header, value in request.headers.items(): - low_conn.putheader(header, value) - - low_conn.endheaders() - - for i in request.body: - low_conn.send(hex(len(i))[2:].encode("utf-8")) - low_conn.send(b"\r\n") - low_conn.send(i) - low_conn.send(b"\r\n") - low_conn.send(b"0\r\n\r\n") - - # Receive the response from the server - r = low_conn.getresponse() - - resp = HTTPResponse.from_httplib( - r, - pool=conn, - connection=low_conn, - preload_content=False, - decode_content=False, - ) - except Exception: - # If we hit any problems here, clean up the connection. - # Then, raise so that we can handle the actual exception. - low_conn.close() - raise - - except (ProtocolError, OSError) as err: - raise ConnectionError(err, request=request) - - except MaxRetryError as e: - if isinstance(e.reason, ConnectTimeoutError): - # TODO: Remove this in 3.0.0: see #2811 - if not isinstance(e.reason, NewConnectionError): - raise ConnectTimeout(e, request=request) - - if isinstance(e.reason, ResponseError): - raise RetryError(e, request=request) - - if isinstance(e.reason, _ProxyError): - raise ProxyError(e, request=request) - - if isinstance(e.reason, _SSLError): - # This branch is for urllib3 v1.22 and later. - raise SSLError(e, request=request) - - raise ConnectionError(e, request=request) - - except ClosedPoolError as e: - raise ConnectionError(e, request=request) - - except _ProxyError as e: - raise ProxyError(e) - - except (_SSLError, _HTTPError) as e: - if isinstance(e, _SSLError): - # This branch is for urllib3 versions earlier than v1.22 - raise SSLError(e, request=request) - elif isinstance(e, ReadTimeoutError): - raise ReadTimeout(e, request=request) - elif isinstance(e, _InvalidHeader): - raise InvalidHeader(e, request=request) - else: - raise - - return self.build_response(request, resp) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/api.py b/.venv/Lib/site-packages/pip/_vendor/requests/api.py deleted file mode 100644 index 2f71aae..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/api.py +++ /dev/null @@ -1,157 +0,0 @@ -""" -requests.api -~~~~~~~~~~~~ - -This module implements the Requests API. - -:copyright: (c) 2012 by Kenneth Reitz. -:license: Apache2, see LICENSE for more details. -""" - -from . import sessions - - -def request(method, url, **kwargs): - """Constructs and sends a :class:`Request `. - - :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary, list of tuples or bytes to send - in the query string for the :class:`Request`. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. - :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. - :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. - :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. - ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` - or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string - defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers - to add for the file. - :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) How many seconds to wait for the server to send data - before giving up, as a float, or a :ref:`(connect timeout, read - timeout) ` tuple. - :type timeout: float or tuple - :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. - :type allow_redirects: bool - :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. - :param stream: (optional) if ``False``, the response content will be immediately downloaded. - :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. - :return: :class:`Response ` object - :rtype: requests.Response - - Usage:: - - >>> import requests - >>> req = requests.request('GET', 'https://httpbin.org/get') - >>> req - - """ - - # By using the 'with' statement we are sure the session is closed, thus we - # avoid leaving sockets open which can trigger a ResourceWarning in some - # cases, and look like a memory leak in others. - with sessions.Session() as session: - return session.request(method=method, url=url, **kwargs) - - -def get(url, params=None, **kwargs): - r"""Sends a GET request. - - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary, list of tuples or bytes to send - in the query string for the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("get", url, params=params, **kwargs) - - -def options(url, **kwargs): - r"""Sends an OPTIONS request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("options", url, **kwargs) - - -def head(url, **kwargs): - r"""Sends a HEAD request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. If - `allow_redirects` is not provided, it will be set to `False` (as - opposed to the default :meth:`request` behavior). - :return: :class:`Response ` object - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", False) - return request("head", url, **kwargs) - - -def post(url, data=None, json=None, **kwargs): - r"""Sends a POST request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json data to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("post", url, data=data, json=json, **kwargs) - - -def put(url, data=None, **kwargs): - r"""Sends a PUT request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json data to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("put", url, data=data, **kwargs) - - -def patch(url, data=None, **kwargs): - r"""Sends a PATCH request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json data to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("patch", url, data=data, **kwargs) - - -def delete(url, **kwargs): - r"""Sends a DELETE request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("delete", url, **kwargs) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/auth.py b/.venv/Lib/site-packages/pip/_vendor/requests/auth.py deleted file mode 100644 index 9733686..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/auth.py +++ /dev/null @@ -1,315 +0,0 @@ -""" -requests.auth -~~~~~~~~~~~~~ - -This module contains the authentication handlers for Requests. -""" - -import hashlib -import os -import re -import threading -import time -import warnings -from base64 import b64encode - -from ._internal_utils import to_native_string -from .compat import basestring, str, urlparse -from .cookies import extract_cookies_to_jar -from .utils import parse_dict_header - -CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded" -CONTENT_TYPE_MULTI_PART = "multipart/form-data" - - -def _basic_auth_str(username, password): - """Returns a Basic Auth string.""" - - # "I want us to put a big-ol' comment on top of it that - # says that this behaviour is dumb but we need to preserve - # it because people are relying on it." - # - Lukasa - # - # These are here solely to maintain backwards compatibility - # for things like ints. This will be removed in 3.0.0. - if not isinstance(username, basestring): - warnings.warn( - "Non-string usernames will no longer be supported in Requests " - "3.0.0. Please convert the object you've passed in ({!r}) to " - "a string or bytes object in the near future to avoid " - "problems.".format(username), - category=DeprecationWarning, - ) - username = str(username) - - if not isinstance(password, basestring): - warnings.warn( - "Non-string passwords will no longer be supported in Requests " - "3.0.0. Please convert the object you've passed in ({!r}) to " - "a string or bytes object in the near future to avoid " - "problems.".format(type(password)), - category=DeprecationWarning, - ) - password = str(password) - # -- End Removal -- - - if isinstance(username, str): - username = username.encode("latin1") - - if isinstance(password, str): - password = password.encode("latin1") - - authstr = "Basic " + to_native_string( - b64encode(b":".join((username, password))).strip() - ) - - return authstr - - -class AuthBase: - """Base class that all auth implementations derive from""" - - def __call__(self, r): - raise NotImplementedError("Auth hooks must be callable.") - - -class HTTPBasicAuth(AuthBase): - """Attaches HTTP Basic Authentication to the given Request object.""" - - def __init__(self, username, password): - self.username = username - self.password = password - - def __eq__(self, other): - return all( - [ - self.username == getattr(other, "username", None), - self.password == getattr(other, "password", None), - ] - ) - - def __ne__(self, other): - return not self == other - - def __call__(self, r): - r.headers["Authorization"] = _basic_auth_str(self.username, self.password) - return r - - -class HTTPProxyAuth(HTTPBasicAuth): - """Attaches HTTP Proxy Authentication to a given Request object.""" - - def __call__(self, r): - r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password) - return r - - -class HTTPDigestAuth(AuthBase): - """Attaches HTTP Digest Authentication to the given Request object.""" - - def __init__(self, username, password): - self.username = username - self.password = password - # Keep state in per-thread local storage - self._thread_local = threading.local() - - def init_per_thread_state(self): - # Ensure state is initialized just once per-thread - if not hasattr(self._thread_local, "init"): - self._thread_local.init = True - self._thread_local.last_nonce = "" - self._thread_local.nonce_count = 0 - self._thread_local.chal = {} - self._thread_local.pos = None - self._thread_local.num_401_calls = None - - def build_digest_header(self, method, url): - """ - :rtype: str - """ - - realm = self._thread_local.chal["realm"] - nonce = self._thread_local.chal["nonce"] - qop = self._thread_local.chal.get("qop") - algorithm = self._thread_local.chal.get("algorithm") - opaque = self._thread_local.chal.get("opaque") - hash_utf8 = None - - if algorithm is None: - _algorithm = "MD5" - else: - _algorithm = algorithm.upper() - # lambdas assume digest modules are imported at the top level - if _algorithm == "MD5" or _algorithm == "MD5-SESS": - - def md5_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.md5(x).hexdigest() - - hash_utf8 = md5_utf8 - elif _algorithm == "SHA": - - def sha_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha1(x).hexdigest() - - hash_utf8 = sha_utf8 - elif _algorithm == "SHA-256": - - def sha256_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha256(x).hexdigest() - - hash_utf8 = sha256_utf8 - elif _algorithm == "SHA-512": - - def sha512_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha512(x).hexdigest() - - hash_utf8 = sha512_utf8 - - KD = lambda s, d: hash_utf8(f"{s}:{d}") # noqa:E731 - - if hash_utf8 is None: - return None - - # XXX not implemented yet - entdig = None - p_parsed = urlparse(url) - #: path is request-uri defined in RFC 2616 which should not be empty - path = p_parsed.path or "/" - if p_parsed.query: - path += f"?{p_parsed.query}" - - A1 = f"{self.username}:{realm}:{self.password}" - A2 = f"{method}:{path}" - - HA1 = hash_utf8(A1) - HA2 = hash_utf8(A2) - - if nonce == self._thread_local.last_nonce: - self._thread_local.nonce_count += 1 - else: - self._thread_local.nonce_count = 1 - ncvalue = f"{self._thread_local.nonce_count:08x}" - s = str(self._thread_local.nonce_count).encode("utf-8") - s += nonce.encode("utf-8") - s += time.ctime().encode("utf-8") - s += os.urandom(8) - - cnonce = hashlib.sha1(s).hexdigest()[:16] - if _algorithm == "MD5-SESS": - HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}") - - if not qop: - respdig = KD(HA1, f"{nonce}:{HA2}") - elif qop == "auth" or "auth" in qop.split(","): - noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}" - respdig = KD(HA1, noncebit) - else: - # XXX handle auth-int. - return None - - self._thread_local.last_nonce = nonce - - # XXX should the partial digests be encoded too? - base = ( - f'username="{self.username}", realm="{realm}", nonce="{nonce}", ' - f'uri="{path}", response="{respdig}"' - ) - if opaque: - base += f', opaque="{opaque}"' - if algorithm: - base += f', algorithm="{algorithm}"' - if entdig: - base += f', digest="{entdig}"' - if qop: - base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"' - - return f"Digest {base}" - - def handle_redirect(self, r, **kwargs): - """Reset num_401_calls counter on redirects.""" - if r.is_redirect: - self._thread_local.num_401_calls = 1 - - def handle_401(self, r, **kwargs): - """ - Takes the given response and tries digest-auth, if needed. - - :rtype: requests.Response - """ - - # If response is not 4xx, do not auth - # See https://github.com/psf/requests/issues/3772 - if not 400 <= r.status_code < 500: - self._thread_local.num_401_calls = 1 - return r - - if self._thread_local.pos is not None: - # Rewind the file position indicator of the body to where - # it was to resend the request. - r.request.body.seek(self._thread_local.pos) - s_auth = r.headers.get("www-authenticate", "") - - if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2: - - self._thread_local.num_401_calls += 1 - pat = re.compile(r"digest ", flags=re.IGNORECASE) - self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1)) - - # Consume content and release the original connection - # to allow our new request to reuse the same one. - r.content - r.close() - prep = r.request.copy() - extract_cookies_to_jar(prep._cookies, r.request, r.raw) - prep.prepare_cookies(prep._cookies) - - prep.headers["Authorization"] = self.build_digest_header( - prep.method, prep.url - ) - _r = r.connection.send(prep, **kwargs) - _r.history.append(r) - _r.request = prep - - return _r - - self._thread_local.num_401_calls = 1 - return r - - def __call__(self, r): - # Initialize per-thread state, if needed - self.init_per_thread_state() - # If we have a saved nonce, skip the 401 - if self._thread_local.last_nonce: - r.headers["Authorization"] = self.build_digest_header(r.method, r.url) - try: - self._thread_local.pos = r.body.tell() - except AttributeError: - # In the case of HTTPDigestAuth being reused and the body of - # the previous request was a file-like object, pos has the - # file position of the previous body. Ensure it's set to - # None. - self._thread_local.pos = None - r.register_hook("response", self.handle_401) - r.register_hook("response", self.handle_redirect) - self._thread_local.num_401_calls = 1 - - return r - - def __eq__(self, other): - return all( - [ - self.username == getattr(other, "username", None), - self.password == getattr(other, "password", None), - ] - ) - - def __ne__(self, other): - return not self == other diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/certs.py b/.venv/Lib/site-packages/pip/_vendor/requests/certs.py deleted file mode 100644 index 38696a1..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/certs.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -""" -requests.certs -~~~~~~~~~~~~~~ - -This module returns the preferred default CA certificate bundle. There is -only one — the one from the certifi package. - -If you are packaging Requests, e.g., for a Linux distribution or a managed -environment, you can change the definition of where() to return a separately -packaged CA bundle. -""" - -import os - -if "_PIP_STANDALONE_CERT" not in os.environ: - from pip._vendor.certifi import where -else: - def where(): - return os.environ["_PIP_STANDALONE_CERT"] - -if __name__ == "__main__": - print(where()) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/compat.py b/.venv/Lib/site-packages/pip/_vendor/requests/compat.py deleted file mode 100644 index 9ab2bb4..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/compat.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -requests.compat -~~~~~~~~~~~~~~~ - -This module previously handled import compatibility issues -between Python 2 and Python 3. It remains for backwards -compatibility until the next major version. -""" - -from pip._vendor import chardet - -import sys - -# ------- -# Pythons -# ------- - -# Syntax sugar. -_ver = sys.version_info - -#: Python 2.x? -is_py2 = _ver[0] == 2 - -#: Python 3.x? -is_py3 = _ver[0] == 3 - -# Note: We've patched out simplejson support in pip because it prevents -# upgrading simplejson on Windows. -import json -from json import JSONDecodeError - -# Keep OrderedDict for backwards compatibility. -from collections import OrderedDict -from collections.abc import Callable, Mapping, MutableMapping -from http import cookiejar as cookielib -from http.cookies import Morsel -from io import StringIO - -# -------------- -# Legacy Imports -# -------------- -from urllib.parse import ( - quote, - quote_plus, - unquote, - unquote_plus, - urldefrag, - urlencode, - urljoin, - urlparse, - urlsplit, - urlunparse, -) -from urllib.request import ( - getproxies, - getproxies_environment, - parse_http_list, - proxy_bypass, - proxy_bypass_environment, -) - -builtin_str = str -str = str -bytes = bytes -basestring = (str, bytes) -numeric_types = (int, float) -integer_types = (int,) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/cookies.py b/.venv/Lib/site-packages/pip/_vendor/requests/cookies.py deleted file mode 100644 index bf54ab2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/cookies.py +++ /dev/null @@ -1,561 +0,0 @@ -""" -requests.cookies -~~~~~~~~~~~~~~~~ - -Compatibility code to be able to use `cookielib.CookieJar` with requests. - -requests.utils imports from here, so be careful with imports. -""" - -import calendar -import copy -import time - -from ._internal_utils import to_native_string -from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse - -try: - import threading -except ImportError: - import dummy_threading as threading - - -class MockRequest: - """Wraps a `requests.Request` to mimic a `urllib2.Request`. - - The code in `cookielib.CookieJar` expects this interface in order to correctly - manage cookie policies, i.e., determine whether a cookie can be set, given the - domains of the request and the cookie. - - The original request object is read-only. The client is responsible for collecting - the new headers via `get_new_headers()` and interpreting them appropriately. You - probably want `get_cookie_header`, defined below. - """ - - def __init__(self, request): - self._r = request - self._new_headers = {} - self.type = urlparse(self._r.url).scheme - - def get_type(self): - return self.type - - def get_host(self): - return urlparse(self._r.url).netloc - - def get_origin_req_host(self): - return self.get_host() - - def get_full_url(self): - # Only return the response's URL if the user hadn't set the Host - # header - if not self._r.headers.get("Host"): - return self._r.url - # If they did set it, retrieve it and reconstruct the expected domain - host = to_native_string(self._r.headers["Host"], encoding="utf-8") - parsed = urlparse(self._r.url) - # Reconstruct the URL as we expect it - return urlunparse( - [ - parsed.scheme, - host, - parsed.path, - parsed.params, - parsed.query, - parsed.fragment, - ] - ) - - def is_unverifiable(self): - return True - - def has_header(self, name): - return name in self._r.headers or name in self._new_headers - - def get_header(self, name, default=None): - return self._r.headers.get(name, self._new_headers.get(name, default)) - - def add_header(self, key, val): - """cookielib has no legitimate use for this method; add it back if you find one.""" - raise NotImplementedError( - "Cookie headers should be added with add_unredirected_header()" - ) - - def add_unredirected_header(self, name, value): - self._new_headers[name] = value - - def get_new_headers(self): - return self._new_headers - - @property - def unverifiable(self): - return self.is_unverifiable() - - @property - def origin_req_host(self): - return self.get_origin_req_host() - - @property - def host(self): - return self.get_host() - - -class MockResponse: - """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. - - ...what? Basically, expose the parsed HTTP headers from the server response - the way `cookielib` expects to see them. - """ - - def __init__(self, headers): - """Make a MockResponse for `cookielib` to read. - - :param headers: a httplib.HTTPMessage or analogous carrying the headers - """ - self._headers = headers - - def info(self): - return self._headers - - def getheaders(self, name): - self._headers.getheaders(name) - - -def extract_cookies_to_jar(jar, request, response): - """Extract the cookies from the response into a CookieJar. - - :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar) - :param request: our own requests.Request object - :param response: urllib3.HTTPResponse object - """ - if not (hasattr(response, "_original_response") and response._original_response): - return - # the _original_response field is the wrapped httplib.HTTPResponse object, - req = MockRequest(request) - # pull out the HTTPMessage with the headers and put it in the mock: - res = MockResponse(response._original_response.msg) - jar.extract_cookies(res, req) - - -def get_cookie_header(jar, request): - """ - Produce an appropriate Cookie header string to be sent with `request`, or None. - - :rtype: str - """ - r = MockRequest(request) - jar.add_cookie_header(r) - return r.get_new_headers().get("Cookie") - - -def remove_cookie_by_name(cookiejar, name, domain=None, path=None): - """Unsets a cookie by name, by default over all domains and paths. - - Wraps CookieJar.clear(), is O(n). - """ - clearables = [] - for cookie in cookiejar: - if cookie.name != name: - continue - if domain is not None and domain != cookie.domain: - continue - if path is not None and path != cookie.path: - continue - clearables.append((cookie.domain, cookie.path, cookie.name)) - - for domain, path, name in clearables: - cookiejar.clear(domain, path, name) - - -class CookieConflictError(RuntimeError): - """There are two cookies that meet the criteria specified in the cookie jar. - Use .get and .set and include domain and path args in order to be more specific. - """ - - -class RequestsCookieJar(cookielib.CookieJar, MutableMapping): - """Compatibility class; is a cookielib.CookieJar, but exposes a dict - interface. - - This is the CookieJar we create by default for requests and sessions that - don't specify one, since some clients may expect response.cookies and - session.cookies to support dict operations. - - Requests does not use the dict interface internally; it's just for - compatibility with external client code. All requests code should work - out of the box with externally provided instances of ``CookieJar``, e.g. - ``LWPCookieJar`` and ``FileCookieJar``. - - Unlike a regular CookieJar, this class is pickleable. - - .. warning:: dictionary operations that are normally O(1) may be O(n). - """ - - def get(self, name, default=None, domain=None, path=None): - """Dict-like get() that also supports optional domain and path args in - order to resolve naming collisions from using one cookie jar over - multiple domains. - - .. warning:: operation is O(n), not O(1). - """ - try: - return self._find_no_duplicates(name, domain, path) - except KeyError: - return default - - def set(self, name, value, **kwargs): - """Dict-like set() that also supports optional domain and path args in - order to resolve naming collisions from using one cookie jar over - multiple domains. - """ - # support client code that unsets cookies by assignment of a None value: - if value is None: - remove_cookie_by_name( - self, name, domain=kwargs.get("domain"), path=kwargs.get("path") - ) - return - - if isinstance(value, Morsel): - c = morsel_to_cookie(value) - else: - c = create_cookie(name, value, **kwargs) - self.set_cookie(c) - return c - - def iterkeys(self): - """Dict-like iterkeys() that returns an iterator of names of cookies - from the jar. - - .. seealso:: itervalues() and iteritems(). - """ - for cookie in iter(self): - yield cookie.name - - def keys(self): - """Dict-like keys() that returns a list of names of cookies from the - jar. - - .. seealso:: values() and items(). - """ - return list(self.iterkeys()) - - def itervalues(self): - """Dict-like itervalues() that returns an iterator of values of cookies - from the jar. - - .. seealso:: iterkeys() and iteritems(). - """ - for cookie in iter(self): - yield cookie.value - - def values(self): - """Dict-like values() that returns a list of values of cookies from the - jar. - - .. seealso:: keys() and items(). - """ - return list(self.itervalues()) - - def iteritems(self): - """Dict-like iteritems() that returns an iterator of name-value tuples - from the jar. - - .. seealso:: iterkeys() and itervalues(). - """ - for cookie in iter(self): - yield cookie.name, cookie.value - - def items(self): - """Dict-like items() that returns a list of name-value tuples from the - jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a - vanilla python dict of key value pairs. - - .. seealso:: keys() and values(). - """ - return list(self.iteritems()) - - def list_domains(self): - """Utility method to list all the domains in the jar.""" - domains = [] - for cookie in iter(self): - if cookie.domain not in domains: - domains.append(cookie.domain) - return domains - - def list_paths(self): - """Utility method to list all the paths in the jar.""" - paths = [] - for cookie in iter(self): - if cookie.path not in paths: - paths.append(cookie.path) - return paths - - def multiple_domains(self): - """Returns True if there are multiple domains in the jar. - Returns False otherwise. - - :rtype: bool - """ - domains = [] - for cookie in iter(self): - if cookie.domain is not None and cookie.domain in domains: - return True - domains.append(cookie.domain) - return False # there is only one domain in jar - - def get_dict(self, domain=None, path=None): - """Takes as an argument an optional domain and path and returns a plain - old Python dict of name-value pairs of cookies that meet the - requirements. - - :rtype: dict - """ - dictionary = {} - for cookie in iter(self): - if (domain is None or cookie.domain == domain) and ( - path is None or cookie.path == path - ): - dictionary[cookie.name] = cookie.value - return dictionary - - def __contains__(self, name): - try: - return super().__contains__(name) - except CookieConflictError: - return True - - def __getitem__(self, name): - """Dict-like __getitem__() for compatibility with client code. Throws - exception if there are more than one cookie with name. In that case, - use the more explicit get() method instead. - - .. warning:: operation is O(n), not O(1). - """ - return self._find_no_duplicates(name) - - def __setitem__(self, name, value): - """Dict-like __setitem__ for compatibility with client code. Throws - exception if there is already a cookie of that name in the jar. In that - case, use the more explicit set() method instead. - """ - self.set(name, value) - - def __delitem__(self, name): - """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s - ``remove_cookie_by_name()``. - """ - remove_cookie_by_name(self, name) - - def set_cookie(self, cookie, *args, **kwargs): - if ( - hasattr(cookie.value, "startswith") - and cookie.value.startswith('"') - and cookie.value.endswith('"') - ): - cookie.value = cookie.value.replace('\\"', "") - return super().set_cookie(cookie, *args, **kwargs) - - def update(self, other): - """Updates this jar with cookies from another CookieJar or dict-like""" - if isinstance(other, cookielib.CookieJar): - for cookie in other: - self.set_cookie(copy.copy(cookie)) - else: - super().update(other) - - def _find(self, name, domain=None, path=None): - """Requests uses this method internally to get cookie values. - - If there are conflicting cookies, _find arbitrarily chooses one. - See _find_no_duplicates if you want an exception thrown if there are - conflicting cookies. - - :param name: a string containing name of cookie - :param domain: (optional) string containing domain of cookie - :param path: (optional) string containing path of cookie - :return: cookie.value - """ - for cookie in iter(self): - if cookie.name == name: - if domain is None or cookie.domain == domain: - if path is None or cookie.path == path: - return cookie.value - - raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") - - def _find_no_duplicates(self, name, domain=None, path=None): - """Both ``__get_item__`` and ``get`` call this function: it's never - used elsewhere in Requests. - - :param name: a string containing name of cookie - :param domain: (optional) string containing domain of cookie - :param path: (optional) string containing path of cookie - :raises KeyError: if cookie is not found - :raises CookieConflictError: if there are multiple cookies - that match name and optionally domain and path - :return: cookie.value - """ - toReturn = None - for cookie in iter(self): - if cookie.name == name: - if domain is None or cookie.domain == domain: - if path is None or cookie.path == path: - if toReturn is not None: - # if there are multiple cookies that meet passed in criteria - raise CookieConflictError( - f"There are multiple cookies with name, {name!r}" - ) - # we will eventually return this as long as no cookie conflict - toReturn = cookie.value - - if toReturn: - return toReturn - raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") - - def __getstate__(self): - """Unlike a normal CookieJar, this class is pickleable.""" - state = self.__dict__.copy() - # remove the unpickleable RLock object - state.pop("_cookies_lock") - return state - - def __setstate__(self, state): - """Unlike a normal CookieJar, this class is pickleable.""" - self.__dict__.update(state) - if "_cookies_lock" not in self.__dict__: - self._cookies_lock = threading.RLock() - - def copy(self): - """Return a copy of this RequestsCookieJar.""" - new_cj = RequestsCookieJar() - new_cj.set_policy(self.get_policy()) - new_cj.update(self) - return new_cj - - def get_policy(self): - """Return the CookiePolicy instance used.""" - return self._policy - - -def _copy_cookie_jar(jar): - if jar is None: - return None - - if hasattr(jar, "copy"): - # We're dealing with an instance of RequestsCookieJar - return jar.copy() - # We're dealing with a generic CookieJar instance - new_jar = copy.copy(jar) - new_jar.clear() - for cookie in jar: - new_jar.set_cookie(copy.copy(cookie)) - return new_jar - - -def create_cookie(name, value, **kwargs): - """Make a cookie from underspecified parameters. - - By default, the pair of `name` and `value` will be set for the domain '' - and sent on every request (this is sometimes called a "supercookie"). - """ - result = { - "version": 0, - "name": name, - "value": value, - "port": None, - "domain": "", - "path": "/", - "secure": False, - "expires": None, - "discard": True, - "comment": None, - "comment_url": None, - "rest": {"HttpOnly": None}, - "rfc2109": False, - } - - badargs = set(kwargs) - set(result) - if badargs: - raise TypeError( - f"create_cookie() got unexpected keyword arguments: {list(badargs)}" - ) - - result.update(kwargs) - result["port_specified"] = bool(result["port"]) - result["domain_specified"] = bool(result["domain"]) - result["domain_initial_dot"] = result["domain"].startswith(".") - result["path_specified"] = bool(result["path"]) - - return cookielib.Cookie(**result) - - -def morsel_to_cookie(morsel): - """Convert a Morsel object into a Cookie containing the one k/v pair.""" - - expires = None - if morsel["max-age"]: - try: - expires = int(time.time() + int(morsel["max-age"])) - except ValueError: - raise TypeError(f"max-age: {morsel['max-age']} must be integer") - elif morsel["expires"]: - time_template = "%a, %d-%b-%Y %H:%M:%S GMT" - expires = calendar.timegm(time.strptime(morsel["expires"], time_template)) - return create_cookie( - comment=morsel["comment"], - comment_url=bool(morsel["comment"]), - discard=False, - domain=morsel["domain"], - expires=expires, - name=morsel.key, - path=morsel["path"], - port=None, - rest={"HttpOnly": morsel["httponly"]}, - rfc2109=False, - secure=bool(morsel["secure"]), - value=morsel.value, - version=morsel["version"] or 0, - ) - - -def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): - """Returns a CookieJar from a key/value dictionary. - - :param cookie_dict: Dict of key/values to insert into CookieJar. - :param cookiejar: (optional) A cookiejar to add the cookies to. - :param overwrite: (optional) If False, will not replace cookies - already in the jar with new ones. - :rtype: CookieJar - """ - if cookiejar is None: - cookiejar = RequestsCookieJar() - - if cookie_dict is not None: - names_from_jar = [cookie.name for cookie in cookiejar] - for name in cookie_dict: - if overwrite or (name not in names_from_jar): - cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) - - return cookiejar - - -def merge_cookies(cookiejar, cookies): - """Add cookies to cookiejar and returns a merged CookieJar. - - :param cookiejar: CookieJar object to add the cookies to. - :param cookies: Dictionary or CookieJar object to be added. - :rtype: CookieJar - """ - if not isinstance(cookiejar, cookielib.CookieJar): - raise ValueError("You can only merge into CookieJar") - - if isinstance(cookies, dict): - cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False) - elif isinstance(cookies, cookielib.CookieJar): - try: - cookiejar.update(cookies) - except AttributeError: - for cookie_in_jar in cookies: - cookiejar.set_cookie(cookie_in_jar) - - return cookiejar diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/exceptions.py b/.venv/Lib/site-packages/pip/_vendor/requests/exceptions.py deleted file mode 100644 index 168d073..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/exceptions.py +++ /dev/null @@ -1,141 +0,0 @@ -""" -requests.exceptions -~~~~~~~~~~~~~~~~~~~ - -This module contains the set of Requests' exceptions. -""" -from pip._vendor.urllib3.exceptions import HTTPError as BaseHTTPError - -from .compat import JSONDecodeError as CompatJSONDecodeError - - -class RequestException(IOError): - """There was an ambiguous exception that occurred while handling your - request. - """ - - def __init__(self, *args, **kwargs): - """Initialize RequestException with `request` and `response` objects.""" - response = kwargs.pop("response", None) - self.response = response - self.request = kwargs.pop("request", None) - if response is not None and not self.request and hasattr(response, "request"): - self.request = self.response.request - super().__init__(*args, **kwargs) - - -class InvalidJSONError(RequestException): - """A JSON error occurred.""" - - -class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): - """Couldn't decode the text into json""" - - def __init__(self, *args, **kwargs): - """ - Construct the JSONDecodeError instance first with all - args. Then use it's args to construct the IOError so that - the json specific args aren't used as IOError specific args - and the error message from JSONDecodeError is preserved. - """ - CompatJSONDecodeError.__init__(self, *args) - InvalidJSONError.__init__(self, *self.args, **kwargs) - - -class HTTPError(RequestException): - """An HTTP error occurred.""" - - -class ConnectionError(RequestException): - """A Connection error occurred.""" - - -class ProxyError(ConnectionError): - """A proxy error occurred.""" - - -class SSLError(ConnectionError): - """An SSL error occurred.""" - - -class Timeout(RequestException): - """The request timed out. - - Catching this error will catch both - :exc:`~requests.exceptions.ConnectTimeout` and - :exc:`~requests.exceptions.ReadTimeout` errors. - """ - - -class ConnectTimeout(ConnectionError, Timeout): - """The request timed out while trying to connect to the remote server. - - Requests that produced this error are safe to retry. - """ - - -class ReadTimeout(Timeout): - """The server did not send any data in the allotted amount of time.""" - - -class URLRequired(RequestException): - """A valid URL is required to make a request.""" - - -class TooManyRedirects(RequestException): - """Too many redirects.""" - - -class MissingSchema(RequestException, ValueError): - """The URL scheme (e.g. http or https) is missing.""" - - -class InvalidSchema(RequestException, ValueError): - """The URL scheme provided is either invalid or unsupported.""" - - -class InvalidURL(RequestException, ValueError): - """The URL provided was somehow invalid.""" - - -class InvalidHeader(RequestException, ValueError): - """The header value provided was somehow invalid.""" - - -class InvalidProxyURL(InvalidURL): - """The proxy URL provided is invalid.""" - - -class ChunkedEncodingError(RequestException): - """The server declared chunked encoding but sent an invalid chunk.""" - - -class ContentDecodingError(RequestException, BaseHTTPError): - """Failed to decode response content.""" - - -class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed.""" - - -class RetryError(RequestException): - """Custom retries logic failed""" - - -class UnrewindableBodyError(RequestException): - """Requests encountered an error when trying to rewind a body.""" - - -# Warnings - - -class RequestsWarning(Warning): - """Base warning for Requests.""" - - -class FileModeWarning(RequestsWarning, DeprecationWarning): - """A file was opened in text mode, but Requests determined its binary length.""" - - -class RequestsDependencyWarning(RequestsWarning): - """An imported dependency doesn't match the expected version range.""" diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/help.py b/.venv/Lib/site-packages/pip/_vendor/requests/help.py deleted file mode 100644 index 2d292c2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/help.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Module containing bug report helper(s).""" - -import json -import platform -import ssl -import sys - -from pip._vendor import idna -from pip._vendor import urllib3 - -from . import __version__ as requests_version - -charset_normalizer = None - -try: - from pip._vendor import chardet -except ImportError: - chardet = None - -try: - from pip._vendor.urllib3.contrib import pyopenssl -except ImportError: - pyopenssl = None - OpenSSL = None - cryptography = None -else: - import cryptography - import OpenSSL - - -def _implementation(): - """Return a dict with the Python implementation and version. - - Provide both the name and the version of the Python implementation - currently running. For example, on CPython 3.10.3 it will return - {'name': 'CPython', 'version': '3.10.3'}. - - This function works best on CPython and PyPy: in particular, it probably - doesn't work for Jython or IronPython. Future investigation should be done - to work out the correct shape of the code for those platforms. - """ - implementation = platform.python_implementation() - - if implementation == "CPython": - implementation_version = platform.python_version() - elif implementation == "PyPy": - implementation_version = "{}.{}.{}".format( - sys.pypy_version_info.major, - sys.pypy_version_info.minor, - sys.pypy_version_info.micro, - ) - if sys.pypy_version_info.releaselevel != "final": - implementation_version = "".join( - [implementation_version, sys.pypy_version_info.releaselevel] - ) - elif implementation == "Jython": - implementation_version = platform.python_version() # Complete Guess - elif implementation == "IronPython": - implementation_version = platform.python_version() # Complete Guess - else: - implementation_version = "Unknown" - - return {"name": implementation, "version": implementation_version} - - -def info(): - """Generate information for a bug report.""" - try: - platform_info = { - "system": platform.system(), - "release": platform.release(), - } - except OSError: - platform_info = { - "system": "Unknown", - "release": "Unknown", - } - - implementation_info = _implementation() - urllib3_info = {"version": urllib3.__version__} - charset_normalizer_info = {"version": None} - chardet_info = {"version": None} - if charset_normalizer: - charset_normalizer_info = {"version": charset_normalizer.__version__} - if chardet: - chardet_info = {"version": chardet.__version__} - - pyopenssl_info = { - "version": None, - "openssl_version": "", - } - if OpenSSL: - pyopenssl_info = { - "version": OpenSSL.__version__, - "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}", - } - cryptography_info = { - "version": getattr(cryptography, "__version__", ""), - } - idna_info = { - "version": getattr(idna, "__version__", ""), - } - - system_ssl = ssl.OPENSSL_VERSION_NUMBER - system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} - - return { - "platform": platform_info, - "implementation": implementation_info, - "system_ssl": system_ssl_info, - "using_pyopenssl": pyopenssl is not None, - "using_charset_normalizer": chardet is None, - "pyOpenSSL": pyopenssl_info, - "urllib3": urllib3_info, - "chardet": chardet_info, - "charset_normalizer": charset_normalizer_info, - "cryptography": cryptography_info, - "idna": idna_info, - "requests": { - "version": requests_version, - }, - } - - -def main(): - """Pretty-print the bug information as JSON.""" - print(json.dumps(info(), sort_keys=True, indent=2)) - - -if __name__ == "__main__": - main() diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/hooks.py b/.venv/Lib/site-packages/pip/_vendor/requests/hooks.py deleted file mode 100644 index d181ba2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/hooks.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -requests.hooks -~~~~~~~~~~~~~~ - -This module provides the capabilities for the Requests hooks system. - -Available hooks: - -``response``: - The response generated from a Request. -""" -HOOKS = ["response"] - - -def default_hooks(): - return {event: [] for event in HOOKS} - - -# TODO: response is the only one - - -def dispatch_hook(key, hooks, hook_data, **kwargs): - """Dispatches a hook dictionary on a given piece of data.""" - hooks = hooks or {} - hooks = hooks.get(key) - if hooks: - if hasattr(hooks, "__call__"): - hooks = [hooks] - for hook in hooks: - _hook_data = hook(hook_data, **kwargs) - if _hook_data is not None: - hook_data = _hook_data - return hook_data diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/models.py b/.venv/Lib/site-packages/pip/_vendor/requests/models.py deleted file mode 100644 index b45e810..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/models.py +++ /dev/null @@ -1,1034 +0,0 @@ -""" -requests.models -~~~~~~~~~~~~~~~ - -This module contains the primary objects that power Requests. -""" - -import datetime - -# Import encoding now, to avoid implicit import later. -# Implicit import within threads may cause LookupError when standard library is in a ZIP, -# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. -import encodings.idna # noqa: F401 -from io import UnsupportedOperation - -from pip._vendor.urllib3.exceptions import ( - DecodeError, - LocationParseError, - ProtocolError, - ReadTimeoutError, - SSLError, -) -from pip._vendor.urllib3.fields import RequestField -from pip._vendor.urllib3.filepost import encode_multipart_formdata -from pip._vendor.urllib3.util import parse_url - -from ._internal_utils import to_native_string, unicode_is_ascii -from .auth import HTTPBasicAuth -from .compat import ( - Callable, - JSONDecodeError, - Mapping, - basestring, - builtin_str, - chardet, - cookielib, -) -from .compat import json as complexjson -from .compat import urlencode, urlsplit, urlunparse -from .cookies import _copy_cookie_jar, cookiejar_from_dict, get_cookie_header -from .exceptions import ( - ChunkedEncodingError, - ConnectionError, - ContentDecodingError, - HTTPError, - InvalidJSONError, - InvalidURL, -) -from .exceptions import JSONDecodeError as RequestsJSONDecodeError -from .exceptions import MissingSchema -from .exceptions import SSLError as RequestsSSLError -from .exceptions import StreamConsumedError -from .hooks import default_hooks -from .status_codes import codes -from .structures import CaseInsensitiveDict -from .utils import ( - check_header_validity, - get_auth_from_url, - guess_filename, - guess_json_utf, - iter_slices, - parse_header_links, - requote_uri, - stream_decode_response_unicode, - super_len, - to_key_val_list, -) - -#: The set of HTTP status codes that indicate an automatically -#: processable redirect. -REDIRECT_STATI = ( - codes.moved, # 301 - codes.found, # 302 - codes.other, # 303 - codes.temporary_redirect, # 307 - codes.permanent_redirect, # 308 -) - -DEFAULT_REDIRECT_LIMIT = 30 -CONTENT_CHUNK_SIZE = 10 * 1024 -ITER_CHUNK_SIZE = 512 - - -class RequestEncodingMixin: - @property - def path_url(self): - """Build the path URL to use.""" - - url = [] - - p = urlsplit(self.url) - - path = p.path - if not path: - path = "/" - - url.append(path) - - query = p.query - if query: - url.append("?") - url.append(query) - - return "".join(url) - - @staticmethod - def _encode_params(data): - """Encode parameters in a piece of data. - - Will successfully encode parameters when passed as a dict or a list of - 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary - if parameters are supplied as a dict. - """ - - if isinstance(data, (str, bytes)): - return data - elif hasattr(data, "read"): - return data - elif hasattr(data, "__iter__"): - result = [] - for k, vs in to_key_val_list(data): - if isinstance(vs, basestring) or not hasattr(vs, "__iter__"): - vs = [vs] - for v in vs: - if v is not None: - result.append( - ( - k.encode("utf-8") if isinstance(k, str) else k, - v.encode("utf-8") if isinstance(v, str) else v, - ) - ) - return urlencode(result, doseq=True) - else: - return data - - @staticmethod - def _encode_files(files, data): - """Build the body for a multipart/form-data request. - - Will successfully encode files when passed as a dict or a list of - tuples. Order is retained if data is a list of tuples but arbitrary - if parameters are supplied as a dict. - The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) - or 4-tuples (filename, fileobj, contentype, custom_headers). - """ - if not files: - raise ValueError("Files must be provided.") - elif isinstance(data, basestring): - raise ValueError("Data must not be a string.") - - new_fields = [] - fields = to_key_val_list(data or {}) - files = to_key_val_list(files or {}) - - for field, val in fields: - if isinstance(val, basestring) or not hasattr(val, "__iter__"): - val = [val] - for v in val: - if v is not None: - # Don't call str() on bytestrings: in Py3 it all goes wrong. - if not isinstance(v, bytes): - v = str(v) - - new_fields.append( - ( - field.decode("utf-8") - if isinstance(field, bytes) - else field, - v.encode("utf-8") if isinstance(v, str) else v, - ) - ) - - for (k, v) in files: - # support for explicit filename - ft = None - fh = None - if isinstance(v, (tuple, list)): - if len(v) == 2: - fn, fp = v - elif len(v) == 3: - fn, fp, ft = v - else: - fn, fp, ft, fh = v - else: - fn = guess_filename(v) or k - fp = v - - if isinstance(fp, (str, bytes, bytearray)): - fdata = fp - elif hasattr(fp, "read"): - fdata = fp.read() - elif fp is None: - continue - else: - fdata = fp - - rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) - rf.make_multipart(content_type=ft) - new_fields.append(rf) - - body, content_type = encode_multipart_formdata(new_fields) - - return body, content_type - - -class RequestHooksMixin: - def register_hook(self, event, hook): - """Properly register a hook.""" - - if event not in self.hooks: - raise ValueError(f'Unsupported event specified, with event name "{event}"') - - if isinstance(hook, Callable): - self.hooks[event].append(hook) - elif hasattr(hook, "__iter__"): - self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) - - def deregister_hook(self, event, hook): - """Deregister a previously registered hook. - Returns True if the hook existed, False if not. - """ - - try: - self.hooks[event].remove(hook) - return True - except ValueError: - return False - - -class Request(RequestHooksMixin): - """A user-created :class:`Request ` object. - - Used to prepare a :class:`PreparedRequest `, which is sent to the server. - - :param method: HTTP method to use. - :param url: URL to send. - :param headers: dictionary of headers to send. - :param files: dictionary of {filename: fileobject} files to multipart upload. - :param data: the body to attach to the request. If a dictionary or - list of tuples ``[(key, value)]`` is provided, form-encoding will - take place. - :param json: json for the body to attach to the request (if files or data is not specified). - :param params: URL parameters to append to the URL. If a dictionary or - list of tuples ``[(key, value)]`` is provided, form-encoding will - take place. - :param auth: Auth handler or (user, pass) tuple. - :param cookies: dictionary or CookieJar of cookies to attach to this request. - :param hooks: dictionary of callback hooks, for internal usage. - - Usage:: - - >>> import requests - >>> req = requests.Request('GET', 'https://httpbin.org/get') - >>> req.prepare() - - """ - - def __init__( - self, - method=None, - url=None, - headers=None, - files=None, - data=None, - params=None, - auth=None, - cookies=None, - hooks=None, - json=None, - ): - - # Default empty dicts for dict params. - data = [] if data is None else data - files = [] if files is None else files - headers = {} if headers is None else headers - params = {} if params is None else params - hooks = {} if hooks is None else hooks - - self.hooks = default_hooks() - for (k, v) in list(hooks.items()): - self.register_hook(event=k, hook=v) - - self.method = method - self.url = url - self.headers = headers - self.files = files - self.data = data - self.json = json - self.params = params - self.auth = auth - self.cookies = cookies - - def __repr__(self): - return f"" - - def prepare(self): - """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" - p = PreparedRequest() - p.prepare( - method=self.method, - url=self.url, - headers=self.headers, - files=self.files, - data=self.data, - json=self.json, - params=self.params, - auth=self.auth, - cookies=self.cookies, - hooks=self.hooks, - ) - return p - - -class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): - """The fully mutable :class:`PreparedRequest ` object, - containing the exact bytes that will be sent to the server. - - Instances are generated from a :class:`Request ` object, and - should not be instantiated manually; doing so may produce undesirable - effects. - - Usage:: - - >>> import requests - >>> req = requests.Request('GET', 'https://httpbin.org/get') - >>> r = req.prepare() - >>> r - - - >>> s = requests.Session() - >>> s.send(r) - - """ - - def __init__(self): - #: HTTP verb to send to the server. - self.method = None - #: HTTP URL to send the request to. - self.url = None - #: dictionary of HTTP headers. - self.headers = None - # The `CookieJar` used to create the Cookie header will be stored here - # after prepare_cookies is called - self._cookies = None - #: request body to send to the server. - self.body = None - #: dictionary of callback hooks, for internal usage. - self.hooks = default_hooks() - #: integer denoting starting position of a readable file-like body. - self._body_position = None - - def prepare( - self, - method=None, - url=None, - headers=None, - files=None, - data=None, - params=None, - auth=None, - cookies=None, - hooks=None, - json=None, - ): - """Prepares the entire request with the given parameters.""" - - self.prepare_method(method) - self.prepare_url(url, params) - self.prepare_headers(headers) - self.prepare_cookies(cookies) - self.prepare_body(data, files, json) - self.prepare_auth(auth, url) - - # Note that prepare_auth must be last to enable authentication schemes - # such as OAuth to work on a fully prepared request. - - # This MUST go after prepare_auth. Authenticators could add a hook - self.prepare_hooks(hooks) - - def __repr__(self): - return f"" - - def copy(self): - p = PreparedRequest() - p.method = self.method - p.url = self.url - p.headers = self.headers.copy() if self.headers is not None else None - p._cookies = _copy_cookie_jar(self._cookies) - p.body = self.body - p.hooks = self.hooks - p._body_position = self._body_position - return p - - def prepare_method(self, method): - """Prepares the given HTTP method.""" - self.method = method - if self.method is not None: - self.method = to_native_string(self.method.upper()) - - @staticmethod - def _get_idna_encoded_host(host): - from pip._vendor import idna - - try: - host = idna.encode(host, uts46=True).decode("utf-8") - except idna.IDNAError: - raise UnicodeError - return host - - def prepare_url(self, url, params): - """Prepares the given HTTP URL.""" - #: Accept objects that have string representations. - #: We're unable to blindly call unicode/str functions - #: as this will include the bytestring indicator (b'') - #: on python 3.x. - #: https://github.com/psf/requests/pull/2238 - if isinstance(url, bytes): - url = url.decode("utf8") - else: - url = str(url) - - # Remove leading whitespaces from url - url = url.lstrip() - - # Don't do any URL preparation for non-HTTP schemes like `mailto`, - # `data` etc to work around exceptions from `url_parse`, which - # handles RFC 3986 only. - if ":" in url and not url.lower().startswith("http"): - self.url = url - return - - # Support for unicode domain names and paths. - try: - scheme, auth, host, port, path, query, fragment = parse_url(url) - except LocationParseError as e: - raise InvalidURL(*e.args) - - if not scheme: - raise MissingSchema( - f"Invalid URL {url!r}: No scheme supplied. " - f"Perhaps you meant http://{url}?" - ) - - if not host: - raise InvalidURL(f"Invalid URL {url!r}: No host supplied") - - # In general, we want to try IDNA encoding the hostname if the string contains - # non-ASCII characters. This allows users to automatically get the correct IDNA - # behaviour. For strings containing only ASCII characters, we need to also verify - # it doesn't start with a wildcard (*), before allowing the unencoded hostname. - if not unicode_is_ascii(host): - try: - host = self._get_idna_encoded_host(host) - except UnicodeError: - raise InvalidURL("URL has an invalid label.") - elif host.startswith(("*", ".")): - raise InvalidURL("URL has an invalid label.") - - # Carefully reconstruct the network location - netloc = auth or "" - if netloc: - netloc += "@" - netloc += host - if port: - netloc += f":{port}" - - # Bare domains aren't valid URLs. - if not path: - path = "/" - - if isinstance(params, (str, bytes)): - params = to_native_string(params) - - enc_params = self._encode_params(params) - if enc_params: - if query: - query = f"{query}&{enc_params}" - else: - query = enc_params - - url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) - self.url = url - - def prepare_headers(self, headers): - """Prepares the given HTTP headers.""" - - self.headers = CaseInsensitiveDict() - if headers: - for header in headers.items(): - # Raise exception on invalid header value. - check_header_validity(header) - name, value = header - self.headers[to_native_string(name)] = value - - def prepare_body(self, data, files, json=None): - """Prepares the given HTTP body data.""" - - # Check if file, fo, generator, iterator. - # If not, run through normal process. - - # Nottin' on you. - body = None - content_type = None - - if not data and json is not None: - # urllib3 requires a bytes-like body. Python 2's json.dumps - # provides this natively, but Python 3 gives a Unicode string. - content_type = "application/json" - - try: - body = complexjson.dumps(json, allow_nan=False) - except ValueError as ve: - raise InvalidJSONError(ve, request=self) - - if not isinstance(body, bytes): - body = body.encode("utf-8") - - is_stream = all( - [ - hasattr(data, "__iter__"), - not isinstance(data, (basestring, list, tuple, Mapping)), - ] - ) - - if is_stream: - try: - length = super_len(data) - except (TypeError, AttributeError, UnsupportedOperation): - length = None - - body = data - - if getattr(body, "tell", None) is not None: - # Record the current file position before reading. - # This will allow us to rewind a file in the event - # of a redirect. - try: - self._body_position = body.tell() - except OSError: - # This differentiates from None, allowing us to catch - # a failed `tell()` later when trying to rewind the body - self._body_position = object() - - if files: - raise NotImplementedError( - "Streamed bodies and files are mutually exclusive." - ) - - if length: - self.headers["Content-Length"] = builtin_str(length) - else: - self.headers["Transfer-Encoding"] = "chunked" - else: - # Multi-part file uploads. - if files: - (body, content_type) = self._encode_files(files, data) - else: - if data: - body = self._encode_params(data) - if isinstance(data, basestring) or hasattr(data, "read"): - content_type = None - else: - content_type = "application/x-www-form-urlencoded" - - self.prepare_content_length(body) - - # Add content-type if it wasn't explicitly provided. - if content_type and ("content-type" not in self.headers): - self.headers["Content-Type"] = content_type - - self.body = body - - def prepare_content_length(self, body): - """Prepare Content-Length header based on request method and body""" - if body is not None: - length = super_len(body) - if length: - # If length exists, set it. Otherwise, we fallback - # to Transfer-Encoding: chunked. - self.headers["Content-Length"] = builtin_str(length) - elif ( - self.method not in ("GET", "HEAD") - and self.headers.get("Content-Length") is None - ): - # Set Content-Length to 0 for methods that can have a body - # but don't provide one. (i.e. not GET or HEAD) - self.headers["Content-Length"] = "0" - - def prepare_auth(self, auth, url=""): - """Prepares the given HTTP auth data.""" - - # If no Auth is explicitly provided, extract it from the URL first. - if auth is None: - url_auth = get_auth_from_url(self.url) - auth = url_auth if any(url_auth) else None - - if auth: - if isinstance(auth, tuple) and len(auth) == 2: - # special-case basic HTTP auth - auth = HTTPBasicAuth(*auth) - - # Allow auth to make its changes. - r = auth(self) - - # Update self to reflect the auth changes. - self.__dict__.update(r.__dict__) - - # Recompute Content-Length - self.prepare_content_length(self.body) - - def prepare_cookies(self, cookies): - """Prepares the given HTTP cookie data. - - This function eventually generates a ``Cookie`` header from the - given cookies using cookielib. Due to cookielib's design, the header - will not be regenerated if it already exists, meaning this function - can only be called once for the life of the - :class:`PreparedRequest ` object. Any subsequent calls - to ``prepare_cookies`` will have no actual effect, unless the "Cookie" - header is removed beforehand. - """ - if isinstance(cookies, cookielib.CookieJar): - self._cookies = cookies - else: - self._cookies = cookiejar_from_dict(cookies) - - cookie_header = get_cookie_header(self._cookies, self) - if cookie_header is not None: - self.headers["Cookie"] = cookie_header - - def prepare_hooks(self, hooks): - """Prepares the given hooks.""" - # hooks can be passed as None to the prepare method and to this - # method. To prevent iterating over None, simply use an empty list - # if hooks is False-y - hooks = hooks or [] - for event in hooks: - self.register_hook(event, hooks[event]) - - -class Response: - """The :class:`Response ` object, which contains a - server's response to an HTTP request. - """ - - __attrs__ = [ - "_content", - "status_code", - "headers", - "url", - "history", - "encoding", - "reason", - "cookies", - "elapsed", - "request", - ] - - def __init__(self): - self._content = False - self._content_consumed = False - self._next = None - - #: Integer Code of responded HTTP Status, e.g. 404 or 200. - self.status_code = None - - #: Case-insensitive Dictionary of Response Headers. - #: For example, ``headers['content-encoding']`` will return the - #: value of a ``'Content-Encoding'`` response header. - self.headers = CaseInsensitiveDict() - - #: File-like object representation of response (for advanced usage). - #: Use of ``raw`` requires that ``stream=True`` be set on the request. - #: This requirement does not apply for use internally to Requests. - self.raw = None - - #: Final URL location of Response. - self.url = None - - #: Encoding to decode with when accessing r.text. - self.encoding = None - - #: A list of :class:`Response ` objects from - #: the history of the Request. Any redirect responses will end - #: up here. The list is sorted from the oldest to the most recent request. - self.history = [] - - #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". - self.reason = None - - #: A CookieJar of Cookies the server sent back. - self.cookies = cookiejar_from_dict({}) - - #: The amount of time elapsed between sending the request - #: and the arrival of the response (as a timedelta). - #: This property specifically measures the time taken between sending - #: the first byte of the request and finishing parsing the headers. It - #: is therefore unaffected by consuming the response content or the - #: value of the ``stream`` keyword argument. - self.elapsed = datetime.timedelta(0) - - #: The :class:`PreparedRequest ` object to which this - #: is a response. - self.request = None - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def __getstate__(self): - # Consume everything; accessing the content attribute makes - # sure the content has been fully read. - if not self._content_consumed: - self.content - - return {attr: getattr(self, attr, None) for attr in self.__attrs__} - - def __setstate__(self, state): - for name, value in state.items(): - setattr(self, name, value) - - # pickled objects do not have .raw - setattr(self, "_content_consumed", True) - setattr(self, "raw", None) - - def __repr__(self): - return f"" - - def __bool__(self): - """Returns True if :attr:`status_code` is less than 400. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code, is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - return self.ok - - def __nonzero__(self): - """Returns True if :attr:`status_code` is less than 400. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code, is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - return self.ok - - def __iter__(self): - """Allows you to use a response as an iterator.""" - return self.iter_content(128) - - @property - def ok(self): - """Returns True if :attr:`status_code` is less than 400, False if not. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - try: - self.raise_for_status() - except HTTPError: - return False - return True - - @property - def is_redirect(self): - """True if this Response is a well-formed HTTP redirect that could have - been processed automatically (by :meth:`Session.resolve_redirects`). - """ - return "location" in self.headers and self.status_code in REDIRECT_STATI - - @property - def is_permanent_redirect(self): - """True if this Response one of the permanent versions of redirect.""" - return "location" in self.headers and self.status_code in ( - codes.moved_permanently, - codes.permanent_redirect, - ) - - @property - def next(self): - """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" - return self._next - - @property - def apparent_encoding(self): - """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" - return chardet.detect(self.content)["encoding"] - - def iter_content(self, chunk_size=1, decode_unicode=False): - """Iterates over the response data. When stream=True is set on the - request, this avoids reading the content at once into memory for - large responses. The chunk size is the number of bytes it should - read into memory. This is not necessarily the length of each item - returned as decoding can take place. - - chunk_size must be of type int or None. A value of None will - function differently depending on the value of `stream`. - stream=True will read data as it arrives in whatever size the - chunks are received. If stream=False, data is returned as - a single chunk. - - If decode_unicode is True, content will be decoded using the best - available encoding based on the response. - """ - - def generate(): - # Special case for urllib3. - if hasattr(self.raw, "stream"): - try: - yield from self.raw.stream(chunk_size, decode_content=True) - except ProtocolError as e: - raise ChunkedEncodingError(e) - except DecodeError as e: - raise ContentDecodingError(e) - except ReadTimeoutError as e: - raise ConnectionError(e) - except SSLError as e: - raise RequestsSSLError(e) - else: - # Standard file-like object. - while True: - chunk = self.raw.read(chunk_size) - if not chunk: - break - yield chunk - - self._content_consumed = True - - if self._content_consumed and isinstance(self._content, bool): - raise StreamConsumedError() - elif chunk_size is not None and not isinstance(chunk_size, int): - raise TypeError( - f"chunk_size must be an int, it is instead a {type(chunk_size)}." - ) - # simulate reading small chunks of the content - reused_chunks = iter_slices(self._content, chunk_size) - - stream_chunks = generate() - - chunks = reused_chunks if self._content_consumed else stream_chunks - - if decode_unicode: - chunks = stream_decode_response_unicode(chunks, self) - - return chunks - - def iter_lines( - self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None - ): - """Iterates over the response data, one line at a time. When - stream=True is set on the request, this avoids reading the - content at once into memory for large responses. - - .. note:: This method is not reentrant safe. - """ - - pending = None - - for chunk in self.iter_content( - chunk_size=chunk_size, decode_unicode=decode_unicode - ): - - if pending is not None: - chunk = pending + chunk - - if delimiter: - lines = chunk.split(delimiter) - else: - lines = chunk.splitlines() - - if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: - pending = lines.pop() - else: - pending = None - - yield from lines - - if pending is not None: - yield pending - - @property - def content(self): - """Content of the response, in bytes.""" - - if self._content is False: - # Read the contents. - if self._content_consumed: - raise RuntimeError("The content for this response was already consumed") - - if self.status_code == 0 or self.raw is None: - self._content = None - else: - self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" - - self._content_consumed = True - # don't need to release the connection; that's been handled by urllib3 - # since we exhausted the data. - return self._content - - @property - def text(self): - """Content of the response, in unicode. - - If Response.encoding is None, encoding will be guessed using - ``charset_normalizer`` or ``chardet``. - - The encoding of the response content is determined based solely on HTTP - headers, following RFC 2616 to the letter. If you can take advantage of - non-HTTP knowledge to make a better guess at the encoding, you should - set ``r.encoding`` appropriately before accessing this property. - """ - - # Try charset from content-type - content = None - encoding = self.encoding - - if not self.content: - return "" - - # Fallback to auto-detected encoding. - if self.encoding is None: - encoding = self.apparent_encoding - - # Decode unicode from given encoding. - try: - content = str(self.content, encoding, errors="replace") - except (LookupError, TypeError): - # A LookupError is raised if the encoding was not found which could - # indicate a misspelling or similar mistake. - # - # A TypeError can be raised if encoding is None - # - # So we try blindly encoding. - content = str(self.content, errors="replace") - - return content - - def json(self, **kwargs): - r"""Returns the json-encoded content of a response, if any. - - :param \*\*kwargs: Optional arguments that ``json.loads`` takes. - :raises requests.exceptions.JSONDecodeError: If the response body does not - contain valid json. - """ - - if not self.encoding and self.content and len(self.content) > 3: - # No encoding set. JSON RFC 4627 section 3 states we should expect - # UTF-8, -16 or -32. Detect which one to use; If the detection or - # decoding fails, fall back to `self.text` (using charset_normalizer to make - # a best guess). - encoding = guess_json_utf(self.content) - if encoding is not None: - try: - return complexjson.loads(self.content.decode(encoding), **kwargs) - except UnicodeDecodeError: - # Wrong UTF codec detected; usually because it's not UTF-8 - # but some other 8-bit codec. This is an RFC violation, - # and the server didn't bother to tell us what codec *was* - # used. - pass - except JSONDecodeError as e: - raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) - - try: - return complexjson.loads(self.text, **kwargs) - except JSONDecodeError as e: - # Catch JSON-related errors and raise as requests.JSONDecodeError - # This aliases json.JSONDecodeError and simplejson.JSONDecodeError - raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) - - @property - def links(self): - """Returns the parsed header links of the response, if any.""" - - header = self.headers.get("link") - - resolved_links = {} - - if header: - links = parse_header_links(header) - - for link in links: - key = link.get("rel") or link.get("url") - resolved_links[key] = link - - return resolved_links - - def raise_for_status(self): - """Raises :class:`HTTPError`, if one occurred.""" - - http_error_msg = "" - if isinstance(self.reason, bytes): - # We attempt to decode utf-8 first because some servers - # choose to localize their reason strings. If the string - # isn't utf-8, we fall back to iso-8859-1 for all other - # encodings. (See PR #3538) - try: - reason = self.reason.decode("utf-8") - except UnicodeDecodeError: - reason = self.reason.decode("iso-8859-1") - else: - reason = self.reason - - if 400 <= self.status_code < 500: - http_error_msg = ( - f"{self.status_code} Client Error: {reason} for url: {self.url}" - ) - - elif 500 <= self.status_code < 600: - http_error_msg = ( - f"{self.status_code} Server Error: {reason} for url: {self.url}" - ) - - if http_error_msg: - raise HTTPError(http_error_msg, response=self) - - def close(self): - """Releases the connection back to the pool. Once this method has been - called the underlying ``raw`` object must not be accessed again. - - *Note: Should not normally need to be called explicitly.* - """ - if not self._content_consumed: - self.raw.close() - - release_conn = getattr(self.raw, "release_conn", None) - if release_conn is not None: - release_conn() diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/packages.py b/.venv/Lib/site-packages/pip/_vendor/requests/packages.py deleted file mode 100644 index 9582fa7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/packages.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys - -# This code exists for backwards compatibility reasons. -# I don't like it either. Just look the other way. :) - -for package in ('urllib3', 'idna', 'chardet'): - vendored_package = "pip._vendor." + package - locals()[package] = __import__(vendored_package) - # This traversal is apparently necessary such that the identities are - # preserved (requests.packages.urllib3.* is urllib3.*) - for mod in list(sys.modules): - if mod == vendored_package or mod.startswith(vendored_package + '.'): - unprefixed_mod = mod[len("pip._vendor."):] - sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod] - -# Kinda cool, though, right? diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/sessions.py b/.venv/Lib/site-packages/pip/_vendor/requests/sessions.py deleted file mode 100644 index 6cb3b4d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/sessions.py +++ /dev/null @@ -1,831 +0,0 @@ -""" -requests.sessions -~~~~~~~~~~~~~~~~~ - -This module provides a Session object to manage and persist settings across -requests (cookies, auth, proxies). -""" -import os -import sys -import time -from collections import OrderedDict -from datetime import timedelta - -from ._internal_utils import to_native_string -from .adapters import HTTPAdapter -from .auth import _basic_auth_str -from .compat import Mapping, cookielib, urljoin, urlparse -from .cookies import ( - RequestsCookieJar, - cookiejar_from_dict, - extract_cookies_to_jar, - merge_cookies, -) -from .exceptions import ( - ChunkedEncodingError, - ContentDecodingError, - InvalidSchema, - TooManyRedirects, -) -from .hooks import default_hooks, dispatch_hook - -# formerly defined here, reexposed here for backward compatibility -from .models import ( # noqa: F401 - DEFAULT_REDIRECT_LIMIT, - REDIRECT_STATI, - PreparedRequest, - Request, -) -from .status_codes import codes -from .structures import CaseInsensitiveDict -from .utils import ( # noqa: F401 - DEFAULT_PORTS, - default_headers, - get_auth_from_url, - get_environ_proxies, - get_netrc_auth, - requote_uri, - resolve_proxies, - rewind_body, - should_bypass_proxies, - to_key_val_list, -) - -# Preferred clock, based on which one is more accurate on a given system. -if sys.platform == "win32": - preferred_clock = time.perf_counter -else: - preferred_clock = time.time - - -def merge_setting(request_setting, session_setting, dict_class=OrderedDict): - """Determines appropriate setting for a given request, taking into account - the explicit setting on that request, and the setting in the session. If a - setting is a dictionary, they will be merged together using `dict_class` - """ - - if session_setting is None: - return request_setting - - if request_setting is None: - return session_setting - - # Bypass if not a dictionary (e.g. verify) - if not ( - isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping) - ): - return request_setting - - merged_setting = dict_class(to_key_val_list(session_setting)) - merged_setting.update(to_key_val_list(request_setting)) - - # Remove keys that are set to None. Extract keys first to avoid altering - # the dictionary during iteration. - none_keys = [k for (k, v) in merged_setting.items() if v is None] - for key in none_keys: - del merged_setting[key] - - return merged_setting - - -def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): - """Properly merges both requests and session hooks. - - This is necessary because when request_hooks == {'response': []}, the - merge breaks Session hooks entirely. - """ - if session_hooks is None or session_hooks.get("response") == []: - return request_hooks - - if request_hooks is None or request_hooks.get("response") == []: - return session_hooks - - return merge_setting(request_hooks, session_hooks, dict_class) - - -class SessionRedirectMixin: - def get_redirect_target(self, resp): - """Receives a Response. Returns a redirect URI or ``None``""" - # Due to the nature of how requests processes redirects this method will - # be called at least once upon the original response and at least twice - # on each subsequent redirect response (if any). - # If a custom mixin is used to handle this logic, it may be advantageous - # to cache the redirect location onto the response object as a private - # attribute. - if resp.is_redirect: - location = resp.headers["location"] - # Currently the underlying http module on py3 decode headers - # in latin1, but empirical evidence suggests that latin1 is very - # rarely used with non-ASCII characters in HTTP headers. - # It is more likely to get UTF8 header rather than latin1. - # This causes incorrect handling of UTF8 encoded location headers. - # To solve this, we re-encode the location in latin1. - location = location.encode("latin1") - return to_native_string(location, "utf8") - return None - - def should_strip_auth(self, old_url, new_url): - """Decide whether Authorization header should be removed when redirecting""" - old_parsed = urlparse(old_url) - new_parsed = urlparse(new_url) - if old_parsed.hostname != new_parsed.hostname: - return True - # Special case: allow http -> https redirect when using the standard - # ports. This isn't specified by RFC 7235, but is kept to avoid - # breaking backwards compatibility with older versions of requests - # that allowed any redirects on the same host. - if ( - old_parsed.scheme == "http" - and old_parsed.port in (80, None) - and new_parsed.scheme == "https" - and new_parsed.port in (443, None) - ): - return False - - # Handle default port usage corresponding to scheme. - changed_port = old_parsed.port != new_parsed.port - changed_scheme = old_parsed.scheme != new_parsed.scheme - default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) - if ( - not changed_scheme - and old_parsed.port in default_port - and new_parsed.port in default_port - ): - return False - - # Standard case: root URI must match - return changed_port or changed_scheme - - def resolve_redirects( - self, - resp, - req, - stream=False, - timeout=None, - verify=True, - cert=None, - proxies=None, - yield_requests=False, - **adapter_kwargs, - ): - """Receives a Response. Returns a generator of Responses or Requests.""" - - hist = [] # keep track of history - - url = self.get_redirect_target(resp) - previous_fragment = urlparse(req.url).fragment - while url: - prepared_request = req.copy() - - # Update history and keep track of redirects. - # resp.history must ignore the original request in this loop - hist.append(resp) - resp.history = hist[1:] - - try: - resp.content # Consume socket so it can be released - except (ChunkedEncodingError, ContentDecodingError, RuntimeError): - resp.raw.read(decode_content=False) - - if len(resp.history) >= self.max_redirects: - raise TooManyRedirects( - f"Exceeded {self.max_redirects} redirects.", response=resp - ) - - # Release the connection back into the pool. - resp.close() - - # Handle redirection without scheme (see: RFC 1808 Section 4) - if url.startswith("//"): - parsed_rurl = urlparse(resp.url) - url = ":".join([to_native_string(parsed_rurl.scheme), url]) - - # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) - parsed = urlparse(url) - if parsed.fragment == "" and previous_fragment: - parsed = parsed._replace(fragment=previous_fragment) - elif parsed.fragment: - previous_fragment = parsed.fragment - url = parsed.geturl() - - # Facilitate relative 'location' headers, as allowed by RFC 7231. - # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') - # Compliant with RFC3986, we percent encode the url. - if not parsed.netloc: - url = urljoin(resp.url, requote_uri(url)) - else: - url = requote_uri(url) - - prepared_request.url = to_native_string(url) - - self.rebuild_method(prepared_request, resp) - - # https://github.com/psf/requests/issues/1084 - if resp.status_code not in ( - codes.temporary_redirect, - codes.permanent_redirect, - ): - # https://github.com/psf/requests/issues/3490 - purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding") - for header in purged_headers: - prepared_request.headers.pop(header, None) - prepared_request.body = None - - headers = prepared_request.headers - headers.pop("Cookie", None) - - # Extract any cookies sent on the response to the cookiejar - # in the new request. Because we've mutated our copied prepared - # request, use the old one that we haven't yet touched. - extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) - merge_cookies(prepared_request._cookies, self.cookies) - prepared_request.prepare_cookies(prepared_request._cookies) - - # Rebuild auth and proxy information. - proxies = self.rebuild_proxies(prepared_request, proxies) - self.rebuild_auth(prepared_request, resp) - - # A failed tell() sets `_body_position` to `object()`. This non-None - # value ensures `rewindable` will be True, allowing us to raise an - # UnrewindableBodyError, instead of hanging the connection. - rewindable = prepared_request._body_position is not None and ( - "Content-Length" in headers or "Transfer-Encoding" in headers - ) - - # Attempt to rewind consumed file-like object. - if rewindable: - rewind_body(prepared_request) - - # Override the original request. - req = prepared_request - - if yield_requests: - yield req - else: - - resp = self.send( - req, - stream=stream, - timeout=timeout, - verify=verify, - cert=cert, - proxies=proxies, - allow_redirects=False, - **adapter_kwargs, - ) - - extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) - - # extract redirect url, if any, for the next loop - url = self.get_redirect_target(resp) - yield resp - - def rebuild_auth(self, prepared_request, response): - """When being redirected we may want to strip authentication from the - request to avoid leaking credentials. This method intelligently removes - and reapplies authentication where possible to avoid credential loss. - """ - headers = prepared_request.headers - url = prepared_request.url - - if "Authorization" in headers and self.should_strip_auth( - response.request.url, url - ): - # If we get redirected to a new host, we should strip out any - # authentication headers. - del headers["Authorization"] - - # .netrc might have more auth for us on our new host. - new_auth = get_netrc_auth(url) if self.trust_env else None - if new_auth is not None: - prepared_request.prepare_auth(new_auth) - - def rebuild_proxies(self, prepared_request, proxies): - """This method re-evaluates the proxy configuration by considering the - environment variables. If we are redirected to a URL covered by - NO_PROXY, we strip the proxy configuration. Otherwise, we set missing - proxy keys for this URL (in case they were stripped by a previous - redirect). - - This method also replaces the Proxy-Authorization header where - necessary. - - :rtype: dict - """ - headers = prepared_request.headers - scheme = urlparse(prepared_request.url).scheme - new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env) - - if "Proxy-Authorization" in headers: - del headers["Proxy-Authorization"] - - try: - username, password = get_auth_from_url(new_proxies[scheme]) - except KeyError: - username, password = None, None - - if username and password: - headers["Proxy-Authorization"] = _basic_auth_str(username, password) - - return new_proxies - - def rebuild_method(self, prepared_request, response): - """When being redirected we may want to change the method of the request - based on certain specs or browser behavior. - """ - method = prepared_request.method - - # https://tools.ietf.org/html/rfc7231#section-6.4.4 - if response.status_code == codes.see_other and method != "HEAD": - method = "GET" - - # Do what the browsers do, despite standards... - # First, turn 302s into GETs. - if response.status_code == codes.found and method != "HEAD": - method = "GET" - - # Second, if a POST is responded to with a 301, turn it into a GET. - # This bizarre behaviour is explained in Issue 1704. - if response.status_code == codes.moved and method == "POST": - method = "GET" - - prepared_request.method = method - - -class Session(SessionRedirectMixin): - """A Requests session. - - Provides cookie persistence, connection-pooling, and configuration. - - Basic Usage:: - - >>> import requests - >>> s = requests.Session() - >>> s.get('https://httpbin.org/get') - - - Or as a context manager:: - - >>> with requests.Session() as s: - ... s.get('https://httpbin.org/get') - - """ - - __attrs__ = [ - "headers", - "cookies", - "auth", - "proxies", - "hooks", - "params", - "verify", - "cert", - "adapters", - "stream", - "trust_env", - "max_redirects", - ] - - def __init__(self): - - #: A case-insensitive dictionary of headers to be sent on each - #: :class:`Request ` sent from this - #: :class:`Session `. - self.headers = default_headers() - - #: Default Authentication tuple or object to attach to - #: :class:`Request `. - self.auth = None - - #: Dictionary mapping protocol or protocol and host to the URL of the proxy - #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to - #: be used on each :class:`Request `. - self.proxies = {} - - #: Event-handling hooks. - self.hooks = default_hooks() - - #: Dictionary of querystring data to attach to each - #: :class:`Request `. The dictionary values may be lists for - #: representing multivalued query parameters. - self.params = {} - - #: Stream response content default. - self.stream = False - - #: SSL Verification default. - #: Defaults to `True`, requiring requests to verify the TLS certificate at the - #: remote end. - #: If verify is set to `False`, requests will accept any TLS certificate - #: presented by the server, and will ignore hostname mismatches and/or - #: expired certificates, which will make your application vulnerable to - #: man-in-the-middle (MitM) attacks. - #: Only set this to `False` for testing. - self.verify = True - - #: SSL client certificate default, if String, path to ssl client - #: cert file (.pem). If Tuple, ('cert', 'key') pair. - self.cert = None - - #: Maximum number of redirects allowed. If the request exceeds this - #: limit, a :class:`TooManyRedirects` exception is raised. - #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is - #: 30. - self.max_redirects = DEFAULT_REDIRECT_LIMIT - - #: Trust environment settings for proxy configuration, default - #: authentication and similar. - self.trust_env = True - - #: A CookieJar containing all currently outstanding cookies set on this - #: session. By default it is a - #: :class:`RequestsCookieJar `, but - #: may be any other ``cookielib.CookieJar`` compatible object. - self.cookies = cookiejar_from_dict({}) - - # Default connection adapters. - self.adapters = OrderedDict() - self.mount("https://", HTTPAdapter()) - self.mount("http://", HTTPAdapter()) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def prepare_request(self, request): - """Constructs a :class:`PreparedRequest ` for - transmission and returns it. The :class:`PreparedRequest` has settings - merged from the :class:`Request ` instance and those of the - :class:`Session`. - - :param request: :class:`Request` instance to prepare with this - session's settings. - :rtype: requests.PreparedRequest - """ - cookies = request.cookies or {} - - # Bootstrap CookieJar. - if not isinstance(cookies, cookielib.CookieJar): - cookies = cookiejar_from_dict(cookies) - - # Merge with session cookies - merged_cookies = merge_cookies( - merge_cookies(RequestsCookieJar(), self.cookies), cookies - ) - - # Set environment's basic authentication if not explicitly set. - auth = request.auth - if self.trust_env and not auth and not self.auth: - auth = get_netrc_auth(request.url) - - p = PreparedRequest() - p.prepare( - method=request.method.upper(), - url=request.url, - files=request.files, - data=request.data, - json=request.json, - headers=merge_setting( - request.headers, self.headers, dict_class=CaseInsensitiveDict - ), - params=merge_setting(request.params, self.params), - auth=merge_setting(auth, self.auth), - cookies=merged_cookies, - hooks=merge_hooks(request.hooks, self.hooks), - ) - return p - - def request( - self, - method, - url, - params=None, - data=None, - headers=None, - cookies=None, - files=None, - auth=None, - timeout=None, - allow_redirects=True, - proxies=None, - hooks=None, - stream=None, - verify=None, - cert=None, - json=None, - ): - """Constructs a :class:`Request `, prepares it and sends it. - Returns :class:`Response ` object. - - :param method: method for the new :class:`Request` object. - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary or bytes to be sent in the query - string for the :class:`Request`. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json to send in the body of the - :class:`Request`. - :param headers: (optional) Dictionary of HTTP Headers to send with the - :class:`Request`. - :param cookies: (optional) Dict or CookieJar object to send with the - :class:`Request`. - :param files: (optional) Dictionary of ``'filename': file-like-objects`` - for multipart encoding upload. - :param auth: (optional) Auth tuple or callable to enable - Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple - :param allow_redirects: (optional) Set to True by default. - :type allow_redirects: bool - :param proxies: (optional) Dictionary mapping protocol or protocol and - hostname to the URL of the proxy. - :param stream: (optional) whether to immediately download the response - content. Defaults to ``False``. - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. When set to - ``False``, requests will accept any TLS certificate presented by - the server, and will ignore hostname mismatches and/or expired - certificates, which will make your application vulnerable to - man-in-the-middle (MitM) attacks. Setting verify to ``False`` - may be useful during local development or testing. - :param cert: (optional) if String, path to ssl client cert file (.pem). - If Tuple, ('cert', 'key') pair. - :rtype: requests.Response - """ - # Create the Request. - req = Request( - method=method.upper(), - url=url, - headers=headers, - files=files, - data=data or {}, - json=json, - params=params or {}, - auth=auth, - cookies=cookies, - hooks=hooks, - ) - prep = self.prepare_request(req) - - proxies = proxies or {} - - settings = self.merge_environment_settings( - prep.url, proxies, stream, verify, cert - ) - - # Send the request. - send_kwargs = { - "timeout": timeout, - "allow_redirects": allow_redirects, - } - send_kwargs.update(settings) - resp = self.send(prep, **send_kwargs) - - return resp - - def get(self, url, **kwargs): - r"""Sends a GET request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", True) - return self.request("GET", url, **kwargs) - - def options(self, url, **kwargs): - r"""Sends a OPTIONS request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", True) - return self.request("OPTIONS", url, **kwargs) - - def head(self, url, **kwargs): - r"""Sends a HEAD request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", False) - return self.request("HEAD", url, **kwargs) - - def post(self, url, data=None, json=None, **kwargs): - r"""Sends a POST request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("POST", url, data=data, json=json, **kwargs) - - def put(self, url, data=None, **kwargs): - r"""Sends a PUT request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("PUT", url, data=data, **kwargs) - - def patch(self, url, data=None, **kwargs): - r"""Sends a PATCH request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("PATCH", url, data=data, **kwargs) - - def delete(self, url, **kwargs): - r"""Sends a DELETE request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("DELETE", url, **kwargs) - - def send(self, request, **kwargs): - """Send a given PreparedRequest. - - :rtype: requests.Response - """ - # Set defaults that the hooks can utilize to ensure they always have - # the correct parameters to reproduce the previous request. - kwargs.setdefault("stream", self.stream) - kwargs.setdefault("verify", self.verify) - kwargs.setdefault("cert", self.cert) - if "proxies" not in kwargs: - kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env) - - # It's possible that users might accidentally send a Request object. - # Guard against that specific failure case. - if isinstance(request, Request): - raise ValueError("You can only send PreparedRequests.") - - # Set up variables needed for resolve_redirects and dispatching of hooks - allow_redirects = kwargs.pop("allow_redirects", True) - stream = kwargs.get("stream") - hooks = request.hooks - - # Get the appropriate adapter to use - adapter = self.get_adapter(url=request.url) - - # Start time (approximately) of the request - start = preferred_clock() - - # Send the request - r = adapter.send(request, **kwargs) - - # Total elapsed time of the request (approximately) - elapsed = preferred_clock() - start - r.elapsed = timedelta(seconds=elapsed) - - # Response manipulation hooks - r = dispatch_hook("response", hooks, r, **kwargs) - - # Persist cookies - if r.history: - - # If the hooks create history then we want those cookies too - for resp in r.history: - extract_cookies_to_jar(self.cookies, resp.request, resp.raw) - - extract_cookies_to_jar(self.cookies, request, r.raw) - - # Resolve redirects if allowed. - if allow_redirects: - # Redirect resolving generator. - gen = self.resolve_redirects(r, request, **kwargs) - history = [resp for resp in gen] - else: - history = [] - - # Shuffle things around if there's history. - if history: - # Insert the first (original) request at the start - history.insert(0, r) - # Get the last request made - r = history.pop() - r.history = history - - # If redirects aren't being followed, store the response on the Request for Response.next(). - if not allow_redirects: - try: - r._next = next( - self.resolve_redirects(r, request, yield_requests=True, **kwargs) - ) - except StopIteration: - pass - - if not stream: - r.content - - return r - - def merge_environment_settings(self, url, proxies, stream, verify, cert): - """ - Check the environment and merge it with some settings. - - :rtype: dict - """ - # Gather clues from the surrounding environment. - if self.trust_env: - # Set environment's proxies. - no_proxy = proxies.get("no_proxy") if proxies is not None else None - env_proxies = get_environ_proxies(url, no_proxy=no_proxy) - for (k, v) in env_proxies.items(): - proxies.setdefault(k, v) - - # Look for requests environment configuration - # and be compatible with cURL. - if verify is True or verify is None: - verify = ( - os.environ.get("REQUESTS_CA_BUNDLE") - or os.environ.get("CURL_CA_BUNDLE") - or verify - ) - - # Merge all the kwargs. - proxies = merge_setting(proxies, self.proxies) - stream = merge_setting(stream, self.stream) - verify = merge_setting(verify, self.verify) - cert = merge_setting(cert, self.cert) - - return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert} - - def get_adapter(self, url): - """ - Returns the appropriate connection adapter for the given URL. - - :rtype: requests.adapters.BaseAdapter - """ - for (prefix, adapter) in self.adapters.items(): - - if url.lower().startswith(prefix.lower()): - return adapter - - # Nothing matches :-/ - raise InvalidSchema(f"No connection adapters were found for {url!r}") - - def close(self): - """Closes all adapters and as such the session""" - for v in self.adapters.values(): - v.close() - - def mount(self, prefix, adapter): - """Registers a connection adapter to a prefix. - - Adapters are sorted in descending order by prefix length. - """ - self.adapters[prefix] = adapter - keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] - - for key in keys_to_move: - self.adapters[key] = self.adapters.pop(key) - - def __getstate__(self): - state = {attr: getattr(self, attr, None) for attr in self.__attrs__} - return state - - def __setstate__(self, state): - for attr, value in state.items(): - setattr(self, attr, value) - - -def session(): - """ - Returns a :class:`Session` for context-management. - - .. deprecated:: 1.0.0 - - This method has been deprecated since version 1.0.0 and is only kept for - backwards compatibility. New code should use :class:`~requests.sessions.Session` - to create a session. This may be removed at a future date. - - :rtype: Session - """ - return Session() diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/status_codes.py b/.venv/Lib/site-packages/pip/_vendor/requests/status_codes.py deleted file mode 100644 index 4bd072b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/status_codes.py +++ /dev/null @@ -1,128 +0,0 @@ -r""" -The ``codes`` object defines a mapping from common names for HTTP statuses -to their numerical codes, accessible either as attributes or as dictionary -items. - -Example:: - - >>> import requests - >>> requests.codes['temporary_redirect'] - 307 - >>> requests.codes.teapot - 418 - >>> requests.codes['\o/'] - 200 - -Some codes have multiple names, and both upper- and lower-case versions of -the names are allowed. For example, ``codes.ok``, ``codes.OK``, and -``codes.okay`` all correspond to the HTTP status code 200. -""" - -from .structures import LookupDict - -_codes = { - # Informational. - 100: ("continue",), - 101: ("switching_protocols",), - 102: ("processing",), - 103: ("checkpoint",), - 122: ("uri_too_long", "request_uri_too_long"), - 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), - 201: ("created",), - 202: ("accepted",), - 203: ("non_authoritative_info", "non_authoritative_information"), - 204: ("no_content",), - 205: ("reset_content", "reset"), - 206: ("partial_content", "partial"), - 207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"), - 208: ("already_reported",), - 226: ("im_used",), - # Redirection. - 300: ("multiple_choices",), - 301: ("moved_permanently", "moved", "\\o-"), - 302: ("found",), - 303: ("see_other", "other"), - 304: ("not_modified",), - 305: ("use_proxy",), - 306: ("switch_proxy",), - 307: ("temporary_redirect", "temporary_moved", "temporary"), - 308: ( - "permanent_redirect", - "resume_incomplete", - "resume", - ), # "resume" and "resume_incomplete" to be removed in 3.0 - # Client Error. - 400: ("bad_request", "bad"), - 401: ("unauthorized",), - 402: ("payment_required", "payment"), - 403: ("forbidden",), - 404: ("not_found", "-o-"), - 405: ("method_not_allowed", "not_allowed"), - 406: ("not_acceptable",), - 407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"), - 408: ("request_timeout", "timeout"), - 409: ("conflict",), - 410: ("gone",), - 411: ("length_required",), - 412: ("precondition_failed", "precondition"), - 413: ("request_entity_too_large",), - 414: ("request_uri_too_large",), - 415: ("unsupported_media_type", "unsupported_media", "media_type"), - 416: ( - "requested_range_not_satisfiable", - "requested_range", - "range_not_satisfiable", - ), - 417: ("expectation_failed",), - 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), - 421: ("misdirected_request",), - 422: ("unprocessable_entity", "unprocessable"), - 423: ("locked",), - 424: ("failed_dependency", "dependency"), - 425: ("unordered_collection", "unordered"), - 426: ("upgrade_required", "upgrade"), - 428: ("precondition_required", "precondition"), - 429: ("too_many_requests", "too_many"), - 431: ("header_fields_too_large", "fields_too_large"), - 444: ("no_response", "none"), - 449: ("retry_with", "retry"), - 450: ("blocked_by_windows_parental_controls", "parental_controls"), - 451: ("unavailable_for_legal_reasons", "legal_reasons"), - 499: ("client_closed_request",), - # Server Error. - 500: ("internal_server_error", "server_error", "/o\\", "✗"), - 501: ("not_implemented",), - 502: ("bad_gateway",), - 503: ("service_unavailable", "unavailable"), - 504: ("gateway_timeout",), - 505: ("http_version_not_supported", "http_version"), - 506: ("variant_also_negotiates",), - 507: ("insufficient_storage",), - 509: ("bandwidth_limit_exceeded", "bandwidth"), - 510: ("not_extended",), - 511: ("network_authentication_required", "network_auth", "network_authentication"), -} - -codes = LookupDict(name="status_codes") - - -def _init(): - for code, titles in _codes.items(): - for title in titles: - setattr(codes, title, code) - if not title.startswith(("\\", "/")): - setattr(codes, title.upper(), code) - - def doc(code): - names = ", ".join(f"``{n}``" for n in _codes[code]) - return "* %d: %s" % (code, names) - - global __doc__ - __doc__ = ( - __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes)) - if __doc__ is not None - else None - ) - - -_init() diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/structures.py b/.venv/Lib/site-packages/pip/_vendor/requests/structures.py deleted file mode 100644 index 188e13e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/structures.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -requests.structures -~~~~~~~~~~~~~~~~~~~ - -Data structures that power Requests. -""" - -from collections import OrderedDict - -from .compat import Mapping, MutableMapping - - -class CaseInsensitiveDict(MutableMapping): - """A case-insensitive ``dict``-like object. - - Implements all methods and operations of - ``MutableMapping`` as well as dict's ``copy``. Also - provides ``lower_items``. - - All keys are expected to be strings. The structure remembers the - case of the last key to be set, and ``iter(instance)``, - ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` - will contain case-sensitive keys. However, querying and contains - testing is case insensitive:: - - cid = CaseInsensitiveDict() - cid['Accept'] = 'application/json' - cid['aCCEPT'] == 'application/json' # True - list(cid) == ['Accept'] # True - - For example, ``headers['content-encoding']`` will return the - value of a ``'Content-Encoding'`` response header, regardless - of how the header name was originally stored. - - If the constructor, ``.update``, or equality comparison - operations are given keys that have equal ``.lower()``s, the - behavior is undefined. - """ - - def __init__(self, data=None, **kwargs): - self._store = OrderedDict() - if data is None: - data = {} - self.update(data, **kwargs) - - def __setitem__(self, key, value): - # Use the lowercased key for lookups, but store the actual - # key alongside the value. - self._store[key.lower()] = (key, value) - - def __getitem__(self, key): - return self._store[key.lower()][1] - - def __delitem__(self, key): - del self._store[key.lower()] - - def __iter__(self): - return (casedkey for casedkey, mappedvalue in self._store.values()) - - def __len__(self): - return len(self._store) - - def lower_items(self): - """Like iteritems(), but with all lowercase keys.""" - return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()) - - def __eq__(self, other): - if isinstance(other, Mapping): - other = CaseInsensitiveDict(other) - else: - return NotImplemented - # Compare insensitively - return dict(self.lower_items()) == dict(other.lower_items()) - - # Copy is required - def copy(self): - return CaseInsensitiveDict(self._store.values()) - - def __repr__(self): - return str(dict(self.items())) - - -class LookupDict(dict): - """Dictionary lookup object.""" - - def __init__(self, name=None): - self.name = name - super().__init__() - - def __repr__(self): - return f"" - - def __getitem__(self, key): - # We allow fall-through here, so values default to None - - return self.__dict__.get(key, None) - - def get(self, key, default=None): - return self.__dict__.get(key, default) diff --git a/.venv/Lib/site-packages/pip/_vendor/requests/utils.py b/.venv/Lib/site-packages/pip/_vendor/requests/utils.py deleted file mode 100644 index 33f394d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/requests/utils.py +++ /dev/null @@ -1,1086 +0,0 @@ -""" -requests.utils -~~~~~~~~~~~~~~ - -This module provides utility functions that are used within Requests -that are also useful for external consumption. -""" - -import codecs -import contextlib -import io -import os -import re -import socket -import struct -import sys -import tempfile -import warnings -import zipfile -from collections import OrderedDict - -from pip._vendor.urllib3.util import make_headers, parse_url - -from . import certs -from .__version__ import __version__ - -# to_native_string is unused here, but imported here for backwards compatibility -from ._internal_utils import HEADER_VALIDATORS, to_native_string # noqa: F401 -from .compat import ( - Mapping, - basestring, - bytes, - getproxies, - getproxies_environment, - integer_types, -) -from .compat import parse_http_list as _parse_list_header -from .compat import ( - proxy_bypass, - proxy_bypass_environment, - quote, - str, - unquote, - urlparse, - urlunparse, -) -from .cookies import cookiejar_from_dict -from .exceptions import ( - FileModeWarning, - InvalidHeader, - InvalidURL, - UnrewindableBodyError, -) -from .structures import CaseInsensitiveDict - -NETRC_FILES = (".netrc", "_netrc") - -DEFAULT_CA_BUNDLE_PATH = certs.where() - -DEFAULT_PORTS = {"http": 80, "https": 443} - -# Ensure that ', ' is used to preserve previous delimiter behavior. -DEFAULT_ACCEPT_ENCODING = ", ".join( - re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) -) - - -if sys.platform == "win32": - # provide a proxy_bypass version on Windows without DNS lookups - - def proxy_bypass_registry(host): - try: - import winreg - except ImportError: - return False - - try: - internetSettings = winreg.OpenKey( - winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", - ) - # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it - proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0]) - # ProxyOverride is almost always a string - proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0] - except (OSError, ValueError): - return False - if not proxyEnable or not proxyOverride: - return False - - # make a check value list from the registry entry: replace the - # '' string by the localhost entry and the corresponding - # canonical entry. - proxyOverride = proxyOverride.split(";") - # now check if we match one of the registry values. - for test in proxyOverride: - if test == "": - if "." not in host: - return True - test = test.replace(".", r"\.") # mask dots - test = test.replace("*", r".*") # change glob sequence - test = test.replace("?", r".") # change glob char - if re.match(test, host, re.I): - return True - return False - - def proxy_bypass(host): # noqa - """Return True, if the host should be bypassed. - - Checks proxy settings gathered from the environment, if specified, - or the registry. - """ - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_registry(host) - - -def dict_to_sequence(d): - """Returns an internal sequence dictionary update.""" - - if hasattr(d, "items"): - d = d.items() - - return d - - -def super_len(o): - total_length = None - current_position = 0 - - if hasattr(o, "__len__"): - total_length = len(o) - - elif hasattr(o, "len"): - total_length = o.len - - elif hasattr(o, "fileno"): - try: - fileno = o.fileno() - except (io.UnsupportedOperation, AttributeError): - # AttributeError is a surprising exception, seeing as how we've just checked - # that `hasattr(o, 'fileno')`. It happens for objects obtained via - # `Tarfile.extractfile()`, per issue 5229. - pass - else: - total_length = os.fstat(fileno).st_size - - # Having used fstat to determine the file length, we need to - # confirm that this file was opened up in binary mode. - if "b" not in o.mode: - warnings.warn( - ( - "Requests has determined the content-length for this " - "request using the binary size of the file: however, the " - "file has been opened in text mode (i.e. without the 'b' " - "flag in the mode). This may lead to an incorrect " - "content-length. In Requests 3.0, support will be removed " - "for files in text mode." - ), - FileModeWarning, - ) - - if hasattr(o, "tell"): - try: - current_position = o.tell() - except OSError: - # This can happen in some weird situations, such as when the file - # is actually a special file descriptor like stdin. In this - # instance, we don't know what the length is, so set it to zero and - # let requests chunk it instead. - if total_length is not None: - current_position = total_length - else: - if hasattr(o, "seek") and total_length is None: - # StringIO and BytesIO have seek but no usable fileno - try: - # seek to end of file - o.seek(0, 2) - total_length = o.tell() - - # seek back to current position to support - # partially read file-like objects - o.seek(current_position or 0) - except OSError: - total_length = 0 - - if total_length is None: - total_length = 0 - - return max(0, total_length - current_position) - - -def get_netrc_auth(url, raise_errors=False): - """Returns the Requests tuple auth for a given url from netrc.""" - - netrc_file = os.environ.get("NETRC") - if netrc_file is not None: - netrc_locations = (netrc_file,) - else: - netrc_locations = (f"~/{f}" for f in NETRC_FILES) - - try: - from netrc import NetrcParseError, netrc - - netrc_path = None - - for f in netrc_locations: - try: - loc = os.path.expanduser(f) - except KeyError: - # os.path.expanduser can fail when $HOME is undefined and - # getpwuid fails. See https://bugs.python.org/issue20164 & - # https://github.com/psf/requests/issues/1846 - return - - if os.path.exists(loc): - netrc_path = loc - break - - # Abort early if there isn't one. - if netrc_path is None: - return - - ri = urlparse(url) - - # Strip port numbers from netloc. This weird `if...encode`` dance is - # used for Python 3.2, which doesn't support unicode literals. - splitstr = b":" - if isinstance(url, str): - splitstr = splitstr.decode("ascii") - host = ri.netloc.split(splitstr)[0] - - try: - _netrc = netrc(netrc_path).authenticators(host) - if _netrc: - # Return with login / password - login_i = 0 if _netrc[0] else 1 - return (_netrc[login_i], _netrc[2]) - except (NetrcParseError, OSError): - # If there was a parsing error or a permissions issue reading the file, - # we'll just skip netrc auth unless explicitly asked to raise errors. - if raise_errors: - raise - - # App Engine hackiness. - except (ImportError, AttributeError): - pass - - -def guess_filename(obj): - """Tries to guess the filename of the given object.""" - name = getattr(obj, "name", None) - if name and isinstance(name, basestring) and name[0] != "<" and name[-1] != ">": - return os.path.basename(name) - - -def extract_zipped_paths(path): - """Replace nonexistent paths that look like they refer to a member of a zip - archive with the location of an extracted copy of the target, or else - just return the provided path unchanged. - """ - if os.path.exists(path): - # this is already a valid path, no need to do anything further - return path - - # find the first valid part of the provided path and treat that as a zip archive - # assume the rest of the path is the name of a member in the archive - archive, member = os.path.split(path) - while archive and not os.path.exists(archive): - archive, prefix = os.path.split(archive) - if not prefix: - # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split), - # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users - break - member = "/".join([prefix, member]) - - if not zipfile.is_zipfile(archive): - return path - - zip_file = zipfile.ZipFile(archive) - if member not in zip_file.namelist(): - return path - - # we have a valid zip archive and a valid member of that archive - tmp = tempfile.gettempdir() - extracted_path = os.path.join(tmp, member.split("/")[-1]) - if not os.path.exists(extracted_path): - # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition - with atomic_open(extracted_path) as file_handler: - file_handler.write(zip_file.read(member)) - return extracted_path - - -@contextlib.contextmanager -def atomic_open(filename): - """Write a file to the disk in an atomic fashion""" - tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) - try: - with os.fdopen(tmp_descriptor, "wb") as tmp_handler: - yield tmp_handler - os.replace(tmp_name, filename) - except BaseException: - os.remove(tmp_name) - raise - - -def from_key_val_list(value): - """Take an object and test to see if it can be represented as a - dictionary. Unless it can not be represented as such, return an - OrderedDict, e.g., - - :: - - >>> from_key_val_list([('key', 'val')]) - OrderedDict([('key', 'val')]) - >>> from_key_val_list('string') - Traceback (most recent call last): - ... - ValueError: cannot encode objects that are not 2-tuples - >>> from_key_val_list({'key': 'val'}) - OrderedDict([('key', 'val')]) - - :rtype: OrderedDict - """ - if value is None: - return None - - if isinstance(value, (str, bytes, bool, int)): - raise ValueError("cannot encode objects that are not 2-tuples") - - return OrderedDict(value) - - -def to_key_val_list(value): - """Take an object and test to see if it can be represented as a - dictionary. If it can be, return a list of tuples, e.g., - - :: - - >>> to_key_val_list([('key', 'val')]) - [('key', 'val')] - >>> to_key_val_list({'key': 'val'}) - [('key', 'val')] - >>> to_key_val_list('string') - Traceback (most recent call last): - ... - ValueError: cannot encode objects that are not 2-tuples - - :rtype: list - """ - if value is None: - return None - - if isinstance(value, (str, bytes, bool, int)): - raise ValueError("cannot encode objects that are not 2-tuples") - - if isinstance(value, Mapping): - value = value.items() - - return list(value) - - -# From mitsuhiko/werkzeug (used with permission). -def parse_list_header(value): - """Parse lists as described by RFC 2068 Section 2. - - In particular, parse comma-separated lists where the elements of - the list may include quoted-strings. A quoted-string could - contain a comma. A non-quoted string could have quotes in the - middle. Quotes are removed automatically after parsing. - - It basically works like :func:`parse_set_header` just that items - may appear multiple times and case sensitivity is preserved. - - The return value is a standard :class:`list`: - - >>> parse_list_header('token, "quoted value"') - ['token', 'quoted value'] - - To create a header from the :class:`list` again, use the - :func:`dump_header` function. - - :param value: a string with a list header. - :return: :class:`list` - :rtype: list - """ - result = [] - for item in _parse_list_header(value): - if item[:1] == item[-1:] == '"': - item = unquote_header_value(item[1:-1]) - result.append(item) - return result - - -# From mitsuhiko/werkzeug (used with permission). -def parse_dict_header(value): - """Parse lists of key, value pairs as described by RFC 2068 Section 2 and - convert them into a python dict: - - >>> d = parse_dict_header('foo="is a fish", bar="as well"') - >>> type(d) is dict - True - >>> sorted(d.items()) - [('bar', 'as well'), ('foo', 'is a fish')] - - If there is no value for a key it will be `None`: - - >>> parse_dict_header('key_without_value') - {'key_without_value': None} - - To create a header from the :class:`dict` again, use the - :func:`dump_header` function. - - :param value: a string with a dict header. - :return: :class:`dict` - :rtype: dict - """ - result = {} - for item in _parse_list_header(value): - if "=" not in item: - result[item] = None - continue - name, value = item.split("=", 1) - if value[:1] == value[-1:] == '"': - value = unquote_header_value(value[1:-1]) - result[name] = value - return result - - -# From mitsuhiko/werkzeug (used with permission). -def unquote_header_value(value, is_filename=False): - r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). - This does not use the real unquoting but what browsers are actually - using for quoting. - - :param value: the header value to unquote. - :rtype: str - """ - if value and value[0] == value[-1] == '"': - # this is not the real unquoting, but fixing this so that the - # RFC is met will result in bugs with internet explorer and - # probably some other browsers as well. IE for example is - # uploading files with "C:\foo\bar.txt" as filename - value = value[1:-1] - - # if this is a filename and the starting characters look like - # a UNC path, then just return the value without quotes. Using the - # replace sequence below on a UNC path has the effect of turning - # the leading double slash into a single slash and then - # _fix_ie_filename() doesn't work correctly. See #458. - if not is_filename or value[:2] != "\\\\": - return value.replace("\\\\", "\\").replace('\\"', '"') - return value - - -def dict_from_cookiejar(cj): - """Returns a key/value dictionary from a CookieJar. - - :param cj: CookieJar object to extract cookies from. - :rtype: dict - """ - - cookie_dict = {} - - for cookie in cj: - cookie_dict[cookie.name] = cookie.value - - return cookie_dict - - -def add_dict_to_cookiejar(cj, cookie_dict): - """Returns a CookieJar from a key/value dictionary. - - :param cj: CookieJar to insert cookies into. - :param cookie_dict: Dict of key/values to insert into CookieJar. - :rtype: CookieJar - """ - - return cookiejar_from_dict(cookie_dict, cj) - - -def get_encodings_from_content(content): - """Returns encodings from given content string. - - :param content: bytestring to extract encodings from. - """ - warnings.warn( - ( - "In requests 3.0, get_encodings_from_content will be removed. For " - "more information, please see the discussion on issue #2266. (This" - " warning should only appear once.)" - ), - DeprecationWarning, - ) - - charset_re = re.compile(r']', flags=re.I) - pragma_re = re.compile(r']', flags=re.I) - xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') - - return ( - charset_re.findall(content) - + pragma_re.findall(content) - + xml_re.findall(content) - ) - - -def _parse_content_type_header(header): - """Returns content type and parameters from given header - - :param header: string - :return: tuple containing content type and dictionary of - parameters - """ - - tokens = header.split(";") - content_type, params = tokens[0].strip(), tokens[1:] - params_dict = {} - items_to_strip = "\"' " - - for param in params: - param = param.strip() - if param: - key, value = param, True - index_of_equals = param.find("=") - if index_of_equals != -1: - key = param[:index_of_equals].strip(items_to_strip) - value = param[index_of_equals + 1 :].strip(items_to_strip) - params_dict[key.lower()] = value - return content_type, params_dict - - -def get_encoding_from_headers(headers): - """Returns encodings from given HTTP Header Dict. - - :param headers: dictionary to extract encoding from. - :rtype: str - """ - - content_type = headers.get("content-type") - - if not content_type: - return None - - content_type, params = _parse_content_type_header(content_type) - - if "charset" in params: - return params["charset"].strip("'\"") - - if "text" in content_type: - return "ISO-8859-1" - - if "application/json" in content_type: - # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset - return "utf-8" - - -def stream_decode_response_unicode(iterator, r): - """Stream decodes an iterator.""" - - if r.encoding is None: - yield from iterator - return - - decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace") - for chunk in iterator: - rv = decoder.decode(chunk) - if rv: - yield rv - rv = decoder.decode(b"", final=True) - if rv: - yield rv - - -def iter_slices(string, slice_length): - """Iterate over slices of a string.""" - pos = 0 - if slice_length is None or slice_length <= 0: - slice_length = len(string) - while pos < len(string): - yield string[pos : pos + slice_length] - pos += slice_length - - -def get_unicode_from_response(r): - """Returns the requested content back in unicode. - - :param r: Response object to get unicode content from. - - Tried: - - 1. charset from content-type - 2. fall back and replace all unicode characters - - :rtype: str - """ - warnings.warn( - ( - "In requests 3.0, get_unicode_from_response will be removed. For " - "more information, please see the discussion on issue #2266. (This" - " warning should only appear once.)" - ), - DeprecationWarning, - ) - - tried_encodings = [] - - # Try charset from content-type - encoding = get_encoding_from_headers(r.headers) - - if encoding: - try: - return str(r.content, encoding) - except UnicodeError: - tried_encodings.append(encoding) - - # Fall back: - try: - return str(r.content, encoding, errors="replace") - except TypeError: - return r.content - - -# The unreserved URI characters (RFC 3986) -UNRESERVED_SET = frozenset( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~" -) - - -def unquote_unreserved(uri): - """Un-escape any percent-escape sequences in a URI that are unreserved - characters. This leaves all reserved, illegal and non-ASCII bytes encoded. - - :rtype: str - """ - parts = uri.split("%") - for i in range(1, len(parts)): - h = parts[i][0:2] - if len(h) == 2 and h.isalnum(): - try: - c = chr(int(h, 16)) - except ValueError: - raise InvalidURL(f"Invalid percent-escape sequence: '{h}'") - - if c in UNRESERVED_SET: - parts[i] = c + parts[i][2:] - else: - parts[i] = f"%{parts[i]}" - else: - parts[i] = f"%{parts[i]}" - return "".join(parts) - - -def requote_uri(uri): - """Re-quote the given URI. - - This function passes the given URI through an unquote/quote cycle to - ensure that it is fully and consistently quoted. - - :rtype: str - """ - safe_with_percent = "!#$%&'()*+,/:;=?@[]~" - safe_without_percent = "!#$&'()*+,/:;=?@[]~" - try: - # Unquote only the unreserved characters - # Then quote only illegal characters (do not quote reserved, - # unreserved, or '%') - return quote(unquote_unreserved(uri), safe=safe_with_percent) - except InvalidURL: - # We couldn't unquote the given URI, so let's try quoting it, but - # there may be unquoted '%'s in the URI. We need to make sure they're - # properly quoted so they do not cause issues elsewhere. - return quote(uri, safe=safe_without_percent) - - -def address_in_network(ip, net): - """This function allows you to check if an IP belongs to a network subnet - - Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 - returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 - - :rtype: bool - """ - ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0] - netaddr, bits = net.split("/") - netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0] - network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask - return (ipaddr & netmask) == (network & netmask) - - -def dotted_netmask(mask): - """Converts mask from /xx format to xxx.xxx.xxx.xxx - - Example: if mask is 24 function returns 255.255.255.0 - - :rtype: str - """ - bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1 - return socket.inet_ntoa(struct.pack(">I", bits)) - - -def is_ipv4_address(string_ip): - """ - :rtype: bool - """ - try: - socket.inet_aton(string_ip) - except OSError: - return False - return True - - -def is_valid_cidr(string_network): - """ - Very simple check of the cidr format in no_proxy variable. - - :rtype: bool - """ - if string_network.count("/") == 1: - try: - mask = int(string_network.split("/")[1]) - except ValueError: - return False - - if mask < 1 or mask > 32: - return False - - try: - socket.inet_aton(string_network.split("/")[0]) - except OSError: - return False - else: - return False - return True - - -@contextlib.contextmanager -def set_environ(env_name, value): - """Set the environment variable 'env_name' to 'value' - - Save previous value, yield, and then restore the previous value stored in - the environment variable 'env_name'. - - If 'value' is None, do nothing""" - value_changed = value is not None - if value_changed: - old_value = os.environ.get(env_name) - os.environ[env_name] = value - try: - yield - finally: - if value_changed: - if old_value is None: - del os.environ[env_name] - else: - os.environ[env_name] = old_value - - -def should_bypass_proxies(url, no_proxy): - """ - Returns whether we should bypass proxies or not. - - :rtype: bool - """ - # Prioritize lowercase environment variables over uppercase - # to keep a consistent behaviour with other http projects (curl, wget). - def get_proxy(key): - return os.environ.get(key) or os.environ.get(key.upper()) - - # First check whether no_proxy is defined. If it is, check that the URL - # we're getting isn't in the no_proxy list. - no_proxy_arg = no_proxy - if no_proxy is None: - no_proxy = get_proxy("no_proxy") - parsed = urlparse(url) - - if parsed.hostname is None: - # URLs don't always have hostnames, e.g. file:/// urls. - return True - - if no_proxy: - # We need to check whether we match here. We need to see if we match - # the end of the hostname, both with and without the port. - no_proxy = (host for host in no_proxy.replace(" ", "").split(",") if host) - - if is_ipv4_address(parsed.hostname): - for proxy_ip in no_proxy: - if is_valid_cidr(proxy_ip): - if address_in_network(parsed.hostname, proxy_ip): - return True - elif parsed.hostname == proxy_ip: - # If no_proxy ip was defined in plain IP notation instead of cidr notation & - # matches the IP of the index - return True - else: - host_with_port = parsed.hostname - if parsed.port: - host_with_port += f":{parsed.port}" - - for host in no_proxy: - if parsed.hostname.endswith(host) or host_with_port.endswith(host): - # The URL does match something in no_proxy, so we don't want - # to apply the proxies on this URL. - return True - - with set_environ("no_proxy", no_proxy_arg): - # parsed.hostname can be `None` in cases such as a file URI. - try: - bypass = proxy_bypass(parsed.hostname) - except (TypeError, socket.gaierror): - bypass = False - - if bypass: - return True - - return False - - -def get_environ_proxies(url, no_proxy=None): - """ - Return a dict of environment proxies. - - :rtype: dict - """ - if should_bypass_proxies(url, no_proxy=no_proxy): - return {} - else: - return getproxies() - - -def select_proxy(url, proxies): - """Select a proxy for the url, if applicable. - - :param url: The url being for the request - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs - """ - proxies = proxies or {} - urlparts = urlparse(url) - if urlparts.hostname is None: - return proxies.get(urlparts.scheme, proxies.get("all")) - - proxy_keys = [ - urlparts.scheme + "://" + urlparts.hostname, - urlparts.scheme, - "all://" + urlparts.hostname, - "all", - ] - proxy = None - for proxy_key in proxy_keys: - if proxy_key in proxies: - proxy = proxies[proxy_key] - break - - return proxy - - -def resolve_proxies(request, proxies, trust_env=True): - """This method takes proxy information from a request and configuration - input to resolve a mapping of target proxies. This will consider settings - such a NO_PROXY to strip proxy configurations. - - :param request: Request or PreparedRequest - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs - :param trust_env: Boolean declaring whether to trust environment configs - - :rtype: dict - """ - proxies = proxies if proxies is not None else {} - url = request.url - scheme = urlparse(url).scheme - no_proxy = proxies.get("no_proxy") - new_proxies = proxies.copy() - - if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy): - environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) - - proxy = environ_proxies.get(scheme, environ_proxies.get("all")) - - if proxy: - new_proxies.setdefault(scheme, proxy) - return new_proxies - - -def default_user_agent(name="python-requests"): - """ - Return a string representing the default user agent. - - :rtype: str - """ - return f"{name}/{__version__}" - - -def default_headers(): - """ - :rtype: requests.structures.CaseInsensitiveDict - """ - return CaseInsensitiveDict( - { - "User-Agent": default_user_agent(), - "Accept-Encoding": DEFAULT_ACCEPT_ENCODING, - "Accept": "*/*", - "Connection": "keep-alive", - } - ) - - -def parse_header_links(value): - """Return a list of parsed link headers proxies. - - i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" - - :rtype: list - """ - - links = [] - - replace_chars = " '\"" - - value = value.strip(replace_chars) - if not value: - return links - - for val in re.split(", *<", value): - try: - url, params = val.split(";", 1) - except ValueError: - url, params = val, "" - - link = {"url": url.strip("<> '\"")} - - for param in params.split(";"): - try: - key, value = param.split("=") - except ValueError: - break - - link[key.strip(replace_chars)] = value.strip(replace_chars) - - links.append(link) - - return links - - -# Null bytes; no need to recreate these on each call to guess_json_utf -_null = "\x00".encode("ascii") # encoding to ASCII for Python 3 -_null2 = _null * 2 -_null3 = _null * 3 - - -def guess_json_utf(data): - """ - :rtype: str - """ - # JSON always starts with two ASCII characters, so detection is as - # easy as counting the nulls and from their location and count - # determine the encoding. Also detect a BOM, if present. - sample = data[:4] - if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): - return "utf-32" # BOM included - if sample[:3] == codecs.BOM_UTF8: - return "utf-8-sig" # BOM included, MS style (discouraged) - if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): - return "utf-16" # BOM included - nullcount = sample.count(_null) - if nullcount == 0: - return "utf-8" - if nullcount == 2: - if sample[::2] == _null2: # 1st and 3rd are null - return "utf-16-be" - if sample[1::2] == _null2: # 2nd and 4th are null - return "utf-16-le" - # Did not detect 2 valid UTF-16 ascii-range characters - if nullcount == 3: - if sample[:3] == _null3: - return "utf-32-be" - if sample[1:] == _null3: - return "utf-32-le" - # Did not detect a valid UTF-32 ascii-range character - return None - - -def prepend_scheme_if_needed(url, new_scheme): - """Given a URL that may or may not have a scheme, prepend the given scheme. - Does not replace a present scheme with the one provided as an argument. - - :rtype: str - """ - parsed = parse_url(url) - scheme, auth, host, port, path, query, fragment = parsed - - # A defect in urlparse determines that there isn't a netloc present in some - # urls. We previously assumed parsing was overly cautious, and swapped the - # netloc and path. Due to a lack of tests on the original defect, this is - # maintained with parse_url for backwards compatibility. - netloc = parsed.netloc - if not netloc: - netloc, path = path, netloc - - if auth: - # parse_url doesn't provide the netloc with auth - # so we'll add it ourselves. - netloc = "@".join([auth, netloc]) - if scheme is None: - scheme = new_scheme - if path is None: - path = "" - - return urlunparse((scheme, netloc, path, "", query, fragment)) - - -def get_auth_from_url(url): - """Given a url with authentication components, extract them into a tuple of - username,password. - - :rtype: (str,str) - """ - parsed = urlparse(url) - - try: - auth = (unquote(parsed.username), unquote(parsed.password)) - except (AttributeError, TypeError): - auth = ("", "") - - return auth - - -def check_header_validity(header): - """Verifies that header parts don't contain leading whitespace - reserved characters, or return characters. - - :param header: tuple, in the format (name, value). - """ - name, value = header - - for part in header: - if type(part) not in HEADER_VALIDATORS: - raise InvalidHeader( - f"Header part ({part!r}) from {{{name!r}: {value!r}}} must be " - f"of type str or bytes, not {type(part)}" - ) - - _validate_header_part(name, "name", HEADER_VALIDATORS[type(name)][0]) - _validate_header_part(value, "value", HEADER_VALIDATORS[type(value)][1]) - - -def _validate_header_part(header_part, header_kind, validator): - if not validator.match(header_part): - raise InvalidHeader( - f"Invalid leading whitespace, reserved character(s), or return" - f"character(s) in header {header_kind}: {header_part!r}" - ) - - -def urldefragauth(url): - """ - Given a url remove the fragment and the authentication part. - - :rtype: str - """ - scheme, netloc, path, params, query, fragment = urlparse(url) - - # see func:`prepend_scheme_if_needed` - if not netloc: - netloc, path = path, netloc - - netloc = netloc.rsplit("@", 1)[-1] - - return urlunparse((scheme, netloc, path, params, query, "")) - - -def rewind_body(prepared_request): - """Move file pointer back to its recorded starting position - so it can be read again on redirect. - """ - body_seek = getattr(prepared_request.body, "seek", None) - if body_seek is not None and isinstance( - prepared_request._body_position, integer_types - ): - try: - body_seek(prepared_request._body_position) - except OSError: - raise UnrewindableBodyError( - "An error occurred when rewinding request body for redirect." - ) - else: - raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py deleted file mode 100644 index ce05fd3..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -__all__ = [ - "__version__", - "AbstractProvider", - "AbstractResolver", - "BaseReporter", - "InconsistentCandidate", - "Resolver", - "RequirementsConflicted", - "ResolutionError", - "ResolutionImpossible", - "ResolutionTooDeep", -] - -__version__ = "0.8.1" - - -from .providers import AbstractProvider, AbstractResolver -from .reporters import BaseReporter -from .resolvers import ( - InconsistentCandidate, - RequirementsConflicted, - ResolutionError, - ResolutionImpossible, - ResolutionTooDeep, - Resolver, -) diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/compat/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py deleted file mode 100644 index 1becc50..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py +++ /dev/null @@ -1,6 +0,0 @@ -__all__ = ["Mapping", "Sequence"] - -try: - from collections.abc import Mapping, Sequence -except ImportError: - from collections import Mapping, Sequence diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py deleted file mode 100644 index 7d0a9c2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py +++ /dev/null @@ -1,133 +0,0 @@ -class AbstractProvider(object): - """Delegate class to provide requirement interface for the resolver.""" - - def identify(self, requirement_or_candidate): - """Given a requirement, return an identifier for it. - - This is used to identify a requirement, e.g. whether two requirements - should have their specifier parts merged. - """ - raise NotImplementedError - - def get_preference( - self, - identifier, - resolutions, - candidates, - information, - backtrack_causes, - ): - """Produce a sort key for given requirement based on preference. - - The preference is defined as "I think this requirement should be - resolved first". The lower the return value is, the more preferred - this group of arguments is. - - :param identifier: An identifier as returned by ``identify()``. This - identifies the dependency matches of which should be returned. - :param resolutions: Mapping of candidates currently pinned by the - resolver. Each key is an identifier, and the value a candidate. - The candidate may conflict with requirements from ``information``. - :param candidates: Mapping of each dependency's possible candidates. - Each value is an iterator of candidates. - :param information: Mapping of requirement information of each package. - Each value is an iterator of *requirement information*. - :param backtrack_causes: Sequence of requirement information that were - the requirements that caused the resolver to most recently backtrack. - - A *requirement information* instance is a named tuple with two members: - - * ``requirement`` specifies a requirement contributing to the current - list of candidates. - * ``parent`` specifies the candidate that provides (dependend on) the - requirement, or ``None`` to indicate a root requirement. - - The preference could depend on a various of issues, including (not - necessarily in this order): - - * Is this package pinned in the current resolution result? - * How relaxed is the requirement? Stricter ones should probably be - worked on first? (I don't know, actually.) - * How many possibilities are there to satisfy this requirement? Those - with few left should likely be worked on first, I guess? - * Are there any known conflicts for this requirement? We should - probably work on those with the most known conflicts. - - A sortable value should be returned (this will be used as the ``key`` - parameter of the built-in sorting function). The smaller the value is, - the more preferred this requirement is (i.e. the sorting function - is called with ``reverse=False``). - """ - raise NotImplementedError - - def find_matches(self, identifier, requirements, incompatibilities): - """Find all possible candidates that satisfy given constraints. - - :param identifier: An identifier as returned by ``identify()``. This - identifies the dependency matches of which should be returned. - :param requirements: A mapping of requirements that all returned - candidates must satisfy. Each key is an identifier, and the value - an iterator of requirements for that dependency. - :param incompatibilities: A mapping of known incompatibilities of - each dependency. Each key is an identifier, and the value an - iterator of incompatibilities known to the resolver. All - incompatibilities *must* be excluded from the return value. - - This should try to get candidates based on the requirements' types. - For VCS, local, and archive requirements, the one-and-only match is - returned, and for a "named" requirement, the index(es) should be - consulted to find concrete candidates for this requirement. - - The return value should produce candidates ordered by preference; the - most preferred candidate should come first. The return type may be one - of the following: - - * A callable that returns an iterator that yields candidates. - * An collection of candidates. - * An iterable of candidates. This will be consumed immediately into a - list of candidates. - """ - raise NotImplementedError - - def is_satisfied_by(self, requirement, candidate): - """Whether the given requirement can be satisfied by a candidate. - - The candidate is guarenteed to have been generated from the - requirement. - - A boolean should be returned to indicate whether ``candidate`` is a - viable solution to the requirement. - """ - raise NotImplementedError - - def get_dependencies(self, candidate): - """Get dependencies of a candidate. - - This should return a collection of requirements that `candidate` - specifies as its dependencies. - """ - raise NotImplementedError - - -class AbstractResolver(object): - """The thing that performs the actual resolution work.""" - - base_exception = Exception - - def __init__(self, provider, reporter): - self.provider = provider - self.reporter = reporter - - def resolve(self, requirements, **kwargs): - """Take a collection of constraints, spit out the resolution result. - - This returns a representation of the final resolution state, with one - guarenteed attribute ``mapping`` that contains resolved candidates as - values. The keys are their respective identifiers. - - :param requirements: A collection of constraints. - :param kwargs: Additional keyword arguments that subclasses may accept. - - :raises: ``self.base_exception`` or its subclass. - """ - raise NotImplementedError diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py deleted file mode 100644 index 6695480..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py +++ /dev/null @@ -1,43 +0,0 @@ -class BaseReporter(object): - """Delegate class to provider progress reporting for the resolver.""" - - def starting(self): - """Called before the resolution actually starts.""" - - def starting_round(self, index): - """Called before each round of resolution starts. - - The index is zero-based. - """ - - def ending_round(self, index, state): - """Called before each round of resolution ends. - - This is NOT called if the resolution ends at this round. Use `ending` - if you want to report finalization. The index is zero-based. - """ - - def ending(self, state): - """Called before the resolution ends successfully.""" - - def adding_requirement(self, requirement, parent): - """Called when adding a new requirement into the resolve criteria. - - :param requirement: The additional requirement to be applied to filter - the available candidaites. - :param parent: The candidate that requires ``requirement`` as a - dependency, or None if ``requirement`` is one of the root - requirements passed in from ``Resolver.resolve()``. - """ - - def resolving_conflicts(self, causes): - """Called when starting to attempt requirement conflict resolution. - - :param causes: The information on the collision that caused the backtracking. - """ - - def backtracking(self, candidate): - """Called when rejecting a candidate during backtracking.""" - - def pinning(self, candidate): - """Called when adding a candidate to the potential solution.""" diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py deleted file mode 100644 index 787681b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers.py +++ /dev/null @@ -1,482 +0,0 @@ -import collections -import operator - -from .providers import AbstractResolver -from .structs import DirectedGraph, IteratorMapping, build_iter_view - -RequirementInformation = collections.namedtuple( - "RequirementInformation", ["requirement", "parent"] -) - - -class ResolverException(Exception): - """A base class for all exceptions raised by this module. - - Exceptions derived by this class should all be handled in this module. Any - bubbling pass the resolver should be treated as a bug. - """ - - -class RequirementsConflicted(ResolverException): - def __init__(self, criterion): - super(RequirementsConflicted, self).__init__(criterion) - self.criterion = criterion - - def __str__(self): - return "Requirements conflict: {}".format( - ", ".join(repr(r) for r in self.criterion.iter_requirement()), - ) - - -class InconsistentCandidate(ResolverException): - def __init__(self, candidate, criterion): - super(InconsistentCandidate, self).__init__(candidate, criterion) - self.candidate = candidate - self.criterion = criterion - - def __str__(self): - return "Provided candidate {!r} does not satisfy {}".format( - self.candidate, - ", ".join(repr(r) for r in self.criterion.iter_requirement()), - ) - - -class Criterion(object): - """Representation of possible resolution results of a package. - - This holds three attributes: - - * `information` is a collection of `RequirementInformation` pairs. - Each pair is a requirement contributing to this criterion, and the - candidate that provides the requirement. - * `incompatibilities` is a collection of all known not-to-work candidates - to exclude from consideration. - * `candidates` is a collection containing all possible candidates deducted - from the union of contributing requirements and known incompatibilities. - It should never be empty, except when the criterion is an attribute of a - raised `RequirementsConflicted` (in which case it is always empty). - - .. note:: - This class is intended to be externally immutable. **Do not** mutate - any of its attribute containers. - """ - - def __init__(self, candidates, information, incompatibilities): - self.candidates = candidates - self.information = information - self.incompatibilities = incompatibilities - - def __repr__(self): - requirements = ", ".join( - "({!r}, via={!r})".format(req, parent) - for req, parent in self.information - ) - return "Criterion({})".format(requirements) - - def iter_requirement(self): - return (i.requirement for i in self.information) - - def iter_parent(self): - return (i.parent for i in self.information) - - -class ResolutionError(ResolverException): - pass - - -class ResolutionImpossible(ResolutionError): - def __init__(self, causes): - super(ResolutionImpossible, self).__init__(causes) - # causes is a list of RequirementInformation objects - self.causes = causes - - -class ResolutionTooDeep(ResolutionError): - def __init__(self, round_count): - super(ResolutionTooDeep, self).__init__(round_count) - self.round_count = round_count - - -# Resolution state in a round. -State = collections.namedtuple("State", "mapping criteria backtrack_causes") - - -class Resolution(object): - """Stateful resolution object. - - This is designed as a one-off object that holds information to kick start - the resolution process, and holds the results afterwards. - """ - - def __init__(self, provider, reporter): - self._p = provider - self._r = reporter - self._states = [] - - @property - def state(self): - try: - return self._states[-1] - except IndexError: - raise AttributeError("state") - - def _push_new_state(self): - """Push a new state into history. - - This new state will be used to hold resolution results of the next - coming round. - """ - base = self._states[-1] - state = State( - mapping=base.mapping.copy(), - criteria=base.criteria.copy(), - backtrack_causes=base.backtrack_causes[:], - ) - self._states.append(state) - - def _add_to_criteria(self, criteria, requirement, parent): - self._r.adding_requirement(requirement=requirement, parent=parent) - - identifier = self._p.identify(requirement_or_candidate=requirement) - criterion = criteria.get(identifier) - if criterion: - incompatibilities = list(criterion.incompatibilities) - else: - incompatibilities = [] - - matches = self._p.find_matches( - identifier=identifier, - requirements=IteratorMapping( - criteria, - operator.methodcaller("iter_requirement"), - {identifier: [requirement]}, - ), - incompatibilities=IteratorMapping( - criteria, - operator.attrgetter("incompatibilities"), - {identifier: incompatibilities}, - ), - ) - - if criterion: - information = list(criterion.information) - information.append(RequirementInformation(requirement, parent)) - else: - information = [RequirementInformation(requirement, parent)] - - criterion = Criterion( - candidates=build_iter_view(matches), - information=information, - incompatibilities=incompatibilities, - ) - if not criterion.candidates: - raise RequirementsConflicted(criterion) - criteria[identifier] = criterion - - def _get_preference(self, name): - return self._p.get_preference( - identifier=name, - resolutions=self.state.mapping, - candidates=IteratorMapping( - self.state.criteria, - operator.attrgetter("candidates"), - ), - information=IteratorMapping( - self.state.criteria, - operator.attrgetter("information"), - ), - backtrack_causes=self.state.backtrack_causes, - ) - - def _is_current_pin_satisfying(self, name, criterion): - try: - current_pin = self.state.mapping[name] - except KeyError: - return False - return all( - self._p.is_satisfied_by(requirement=r, candidate=current_pin) - for r in criterion.iter_requirement() - ) - - def _get_updated_criteria(self, candidate): - criteria = self.state.criteria.copy() - for requirement in self._p.get_dependencies(candidate=candidate): - self._add_to_criteria(criteria, requirement, parent=candidate) - return criteria - - def _attempt_to_pin_criterion(self, name): - criterion = self.state.criteria[name] - - causes = [] - for candidate in criterion.candidates: - try: - criteria = self._get_updated_criteria(candidate) - except RequirementsConflicted as e: - causes.append(e.criterion) - continue - - # Check the newly-pinned candidate actually works. This should - # always pass under normal circumstances, but in the case of a - # faulty provider, we will raise an error to notify the implementer - # to fix find_matches() and/or is_satisfied_by(). - satisfied = all( - self._p.is_satisfied_by(requirement=r, candidate=candidate) - for r in criterion.iter_requirement() - ) - if not satisfied: - raise InconsistentCandidate(candidate, criterion) - - self._r.pinning(candidate=candidate) - self.state.criteria.update(criteria) - - # Put newly-pinned candidate at the end. This is essential because - # backtracking looks at this mapping to get the last pin. - self.state.mapping.pop(name, None) - self.state.mapping[name] = candidate - - return [] - - # All candidates tried, nothing works. This criterion is a dead - # end, signal for backtracking. - return causes - - def _backtrack(self): - """Perform backtracking. - - When we enter here, the stack is like this:: - - [ state Z ] - [ state Y ] - [ state X ] - .... earlier states are irrelevant. - - 1. No pins worked for Z, so it does not have a pin. - 2. We want to reset state Y to unpinned, and pin another candidate. - 3. State X holds what state Y was before the pin, but does not - have the incompatibility information gathered in state Y. - - Each iteration of the loop will: - - 1. Discard Z. - 2. Discard Y but remember its incompatibility information gathered - previously, and the failure we're dealing with right now. - 3. Push a new state Y' based on X, and apply the incompatibility - information from Y to Y'. - 4a. If this causes Y' to conflict, we need to backtrack again. Make Y' - the new Z and go back to step 2. - 4b. If the incompatibilities apply cleanly, end backtracking. - """ - while len(self._states) >= 3: - # Remove the state that triggered backtracking. - del self._states[-1] - - # Retrieve the last candidate pin and known incompatibilities. - broken_state = self._states.pop() - name, candidate = broken_state.mapping.popitem() - incompatibilities_from_broken = [ - (k, list(v.incompatibilities)) - for k, v in broken_state.criteria.items() - ] - - # Also mark the newly known incompatibility. - incompatibilities_from_broken.append((name, [candidate])) - - self._r.backtracking(candidate=candidate) - - # Create a new state from the last known-to-work one, and apply - # the previously gathered incompatibility information. - def _patch_criteria(): - for k, incompatibilities in incompatibilities_from_broken: - if not incompatibilities: - continue - try: - criterion = self.state.criteria[k] - except KeyError: - continue - matches = self._p.find_matches( - identifier=k, - requirements=IteratorMapping( - self.state.criteria, - operator.methodcaller("iter_requirement"), - ), - incompatibilities=IteratorMapping( - self.state.criteria, - operator.attrgetter("incompatibilities"), - {k: incompatibilities}, - ), - ) - candidates = build_iter_view(matches) - if not candidates: - return False - incompatibilities.extend(criterion.incompatibilities) - self.state.criteria[k] = Criterion( - candidates=candidates, - information=list(criterion.information), - incompatibilities=incompatibilities, - ) - return True - - self._push_new_state() - success = _patch_criteria() - - # It works! Let's work on this new state. - if success: - return True - - # State does not work after applying known incompatibilities. - # Try the still previous state. - - # No way to backtrack anymore. - return False - - def resolve(self, requirements, max_rounds): - if self._states: - raise RuntimeError("already resolved") - - self._r.starting() - - # Initialize the root state. - self._states = [ - State( - mapping=collections.OrderedDict(), - criteria={}, - backtrack_causes=[], - ) - ] - for r in requirements: - try: - self._add_to_criteria(self.state.criteria, r, parent=None) - except RequirementsConflicted as e: - raise ResolutionImpossible(e.criterion.information) - - # The root state is saved as a sentinel so the first ever pin can have - # something to backtrack to if it fails. The root state is basically - # pinning the virtual "root" package in the graph. - self._push_new_state() - - for round_index in range(max_rounds): - self._r.starting_round(index=round_index) - - unsatisfied_names = [ - key - for key, criterion in self.state.criteria.items() - if not self._is_current_pin_satisfying(key, criterion) - ] - - # All criteria are accounted for. Nothing more to pin, we are done! - if not unsatisfied_names: - self._r.ending(state=self.state) - return self.state - - # Choose the most preferred unpinned criterion to try. - name = min(unsatisfied_names, key=self._get_preference) - failure_causes = self._attempt_to_pin_criterion(name) - - if failure_causes: - causes = [i for c in failure_causes for i in c.information] - # Backtrack if pinning fails. The backtrack process puts us in - # an unpinned state, so we can work on it in the next round. - self._r.resolving_conflicts(causes=causes) - success = self._backtrack() - self.state.backtrack_causes[:] = causes - - # Dead ends everywhere. Give up. - if not success: - raise ResolutionImpossible(self.state.backtrack_causes) - else: - # Pinning was successful. Push a new state to do another pin. - self._push_new_state() - - self._r.ending_round(index=round_index, state=self.state) - - raise ResolutionTooDeep(max_rounds) - - -def _has_route_to_root(criteria, key, all_keys, connected): - if key in connected: - return True - if key not in criteria: - return False - for p in criteria[key].iter_parent(): - try: - pkey = all_keys[id(p)] - except KeyError: - continue - if pkey in connected: - connected.add(key) - return True - if _has_route_to_root(criteria, pkey, all_keys, connected): - connected.add(key) - return True - return False - - -Result = collections.namedtuple("Result", "mapping graph criteria") - - -def _build_result(state): - mapping = state.mapping - all_keys = {id(v): k for k, v in mapping.items()} - all_keys[id(None)] = None - - graph = DirectedGraph() - graph.add(None) # Sentinel as root dependencies' parent. - - connected = {None} - for key, criterion in state.criteria.items(): - if not _has_route_to_root(state.criteria, key, all_keys, connected): - continue - if key not in graph: - graph.add(key) - for p in criterion.iter_parent(): - try: - pkey = all_keys[id(p)] - except KeyError: - continue - if pkey not in graph: - graph.add(pkey) - graph.connect(pkey, key) - - return Result( - mapping={k: v for k, v in mapping.items() if k in connected}, - graph=graph, - criteria=state.criteria, - ) - - -class Resolver(AbstractResolver): - """The thing that performs the actual resolution work.""" - - base_exception = ResolverException - - def resolve(self, requirements, max_rounds=100): - """Take a collection of constraints, spit out the resolution result. - - The return value is a representation to the final resolution result. It - is a tuple subclass with three public members: - - * `mapping`: A dict of resolved candidates. Each key is an identifier - of a requirement (as returned by the provider's `identify` method), - and the value is the resolved candidate. - * `graph`: A `DirectedGraph` instance representing the dependency tree. - The vertices are keys of `mapping`, and each edge represents *why* - a particular package is included. A special vertex `None` is - included to represent parents of user-supplied requirements. - * `criteria`: A dict of "criteria" that hold detailed information on - how edges in the graph are derived. Each key is an identifier of a - requirement, and the value is a `Criterion` instance. - - The following exceptions may be raised if a resolution cannot be found: - - * `ResolutionImpossible`: A resolution cannot be found for the given - combination of requirements. The `causes` attribute of the - exception is a list of (requirement, parent), giving the - requirements that could not be satisfied. - * `ResolutionTooDeep`: The dependency tree is too deeply nested and - the resolver gave up. This is usually caused by a circular - dependency, but you can try to resolve this by increasing the - `max_rounds` argument. - """ - resolution = Resolution(self.provider, self.reporter) - state = resolution.resolve(requirements, max_rounds=max_rounds) - return _build_result(state) diff --git a/.venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py b/.venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py deleted file mode 100644 index 93d1568..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py +++ /dev/null @@ -1,165 +0,0 @@ -import itertools - -from .compat import collections_abc - - -class DirectedGraph(object): - """A graph structure with directed edges.""" - - def __init__(self): - self._vertices = set() - self._forwards = {} # -> Set[] - self._backwards = {} # -> Set[] - - def __iter__(self): - return iter(self._vertices) - - def __len__(self): - return len(self._vertices) - - def __contains__(self, key): - return key in self._vertices - - def copy(self): - """Return a shallow copy of this graph.""" - other = DirectedGraph() - other._vertices = set(self._vertices) - other._forwards = {k: set(v) for k, v in self._forwards.items()} - other._backwards = {k: set(v) for k, v in self._backwards.items()} - return other - - def add(self, key): - """Add a new vertex to the graph.""" - if key in self._vertices: - raise ValueError("vertex exists") - self._vertices.add(key) - self._forwards[key] = set() - self._backwards[key] = set() - - def remove(self, key): - """Remove a vertex from the graph, disconnecting all edges from/to it.""" - self._vertices.remove(key) - for f in self._forwards.pop(key): - self._backwards[f].remove(key) - for t in self._backwards.pop(key): - self._forwards[t].remove(key) - - def connected(self, f, t): - return f in self._backwards[t] and t in self._forwards[f] - - def connect(self, f, t): - """Connect two existing vertices. - - Nothing happens if the vertices are already connected. - """ - if t not in self._vertices: - raise KeyError(t) - self._forwards[f].add(t) - self._backwards[t].add(f) - - def iter_edges(self): - for f, children in self._forwards.items(): - for t in children: - yield f, t - - def iter_children(self, key): - return iter(self._forwards[key]) - - def iter_parents(self, key): - return iter(self._backwards[key]) - - -class IteratorMapping(collections_abc.Mapping): - def __init__(self, mapping, accessor, appends=None): - self._mapping = mapping - self._accessor = accessor - self._appends = appends or {} - - def __repr__(self): - return "IteratorMapping({!r}, {!r}, {!r})".format( - self._mapping, - self._accessor, - self._appends, - ) - - def __bool__(self): - return bool(self._mapping or self._appends) - - __nonzero__ = __bool__ # XXX: Python 2. - - def __contains__(self, key): - return key in self._mapping or key in self._appends - - def __getitem__(self, k): - try: - v = self._mapping[k] - except KeyError: - return iter(self._appends[k]) - return itertools.chain(self._accessor(v), self._appends.get(k, ())) - - def __iter__(self): - more = (k for k in self._appends if k not in self._mapping) - return itertools.chain(self._mapping, more) - - def __len__(self): - more = sum(1 for k in self._appends if k not in self._mapping) - return len(self._mapping) + more - - -class _FactoryIterableView(object): - """Wrap an iterator factory returned by `find_matches()`. - - Calling `iter()` on this class would invoke the underlying iterator - factory, making it a "collection with ordering" that can be iterated - through multiple times, but lacks random access methods presented in - built-in Python sequence types. - """ - - def __init__(self, factory): - self._factory = factory - - def __repr__(self): - return "{}({})".format(type(self).__name__, list(self._factory())) - - def __bool__(self): - try: - next(self._factory()) - except StopIteration: - return False - return True - - __nonzero__ = __bool__ # XXX: Python 2. - - def __iter__(self): - return self._factory() - - -class _SequenceIterableView(object): - """Wrap an iterable returned by find_matches(). - - This is essentially just a proxy to the underlying sequence that provides - the same interface as `_FactoryIterableView`. - """ - - def __init__(self, sequence): - self._sequence = sequence - - def __repr__(self): - return "{}({})".format(type(self).__name__, self._sequence) - - def __bool__(self): - return bool(self._sequence) - - __nonzero__ = __bool__ # XXX: Python 2. - - def __iter__(self): - return iter(self._sequence) - - -def build_iter_view(matches): - """Build an iterable view from the value returned by `find_matches()`.""" - if callable(matches): - return _FactoryIterableView(matches) - if not isinstance(matches, collections_abc.Sequence): - matches = list(matches) - return _SequenceIterableView(matches) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/__init__.py b/.venv/Lib/site-packages/pip/_vendor/rich/__init__.py deleted file mode 100644 index d35875d..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/__init__.py +++ /dev/null @@ -1,176 +0,0 @@ -"""Rich text and beautiful formatting in the terminal.""" - -import os -from typing import IO, TYPE_CHECKING, Any, Callable, Optional, Union - -from ._extension import load_ipython_extension # noqa: F401 - -__all__ = ["get_console", "reconfigure", "print", "inspect"] - -if TYPE_CHECKING: - from .console import Console - -# Global console used by alternative print -_console: Optional["Console"] = None - -try: - _IMPORT_CWD = os.path.abspath(os.getcwd()) -except FileNotFoundError: - # Can happen if the cwd has been deleted - _IMPORT_CWD = "" - - -def get_console() -> "Console": - """Get a global :class:`~rich.console.Console` instance. This function is used when Rich requires a Console, - and hasn't been explicitly given one. - - Returns: - Console: A console instance. - """ - global _console - if _console is None: - from .console import Console - - _console = Console() - - return _console - - -def reconfigure(*args: Any, **kwargs: Any) -> None: - """Reconfigures the global console by replacing it with another. - - Args: - console (Console): Replacement console instance. - """ - from pip._vendor.rich.console import Console - - new_console = Console(*args, **kwargs) - _console = get_console() - _console.__dict__ = new_console.__dict__ - - -def print( - *objects: Any, - sep: str = " ", - end: str = "\n", - file: Optional[IO[str]] = None, - flush: bool = False, -) -> None: - r"""Print object(s) supplied via positional arguments. - This function has an identical signature to the built-in print. - For more advanced features, see the :class:`~rich.console.Console` class. - - Args: - sep (str, optional): Separator between printed objects. Defaults to " ". - end (str, optional): Character to write at end of output. Defaults to "\\n". - file (IO[str], optional): File to write to, or None for stdout. Defaults to None. - flush (bool, optional): Has no effect as Rich always flushes output. Defaults to False. - - """ - from .console import Console - - write_console = get_console() if file is None else Console(file=file) - return write_console.print(*objects, sep=sep, end=end) - - -def print_json( - json: Optional[str] = None, - *, - data: Any = None, - indent: Union[None, int, str] = 2, - highlight: bool = True, - skip_keys: bool = False, - ensure_ascii: bool = True, - check_circular: bool = True, - allow_nan: bool = True, - default: Optional[Callable[[Any], Any]] = None, - sort_keys: bool = False, -) -> None: - """Pretty prints JSON. Output will be valid JSON. - - Args: - json (str): A string containing JSON. - data (Any): If json is not supplied, then encode this data. - indent (int, optional): Number of spaces to indent. Defaults to 2. - highlight (bool, optional): Enable highlighting of output: Defaults to True. - skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False. - ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False. - check_circular (bool, optional): Check for circular references. Defaults to True. - allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True. - default (Callable, optional): A callable that converts values that can not be encoded - in to something that can be JSON encoded. Defaults to None. - sort_keys (bool, optional): Sort dictionary keys. Defaults to False. - """ - - get_console().print_json( - json, - data=data, - indent=indent, - highlight=highlight, - skip_keys=skip_keys, - ensure_ascii=ensure_ascii, - check_circular=check_circular, - allow_nan=allow_nan, - default=default, - sort_keys=sort_keys, - ) - - -def inspect( - obj: Any, - *, - console: Optional["Console"] = None, - title: Optional[str] = None, - help: bool = False, - methods: bool = False, - docs: bool = True, - private: bool = False, - dunder: bool = False, - sort: bool = True, - all: bool = False, - value: bool = True, -) -> None: - """Inspect any Python object. - - * inspect() to see summarized info. - * inspect(, methods=True) to see methods. - * inspect(, help=True) to see full (non-abbreviated) help. - * inspect(, private=True) to see private attributes (single underscore). - * inspect(, dunder=True) to see attributes beginning with double underscore. - * inspect(, all=True) to see all attributes. - - Args: - obj (Any): An object to inspect. - title (str, optional): Title to display over inspect result, or None use type. Defaults to None. - help (bool, optional): Show full help text rather than just first paragraph. Defaults to False. - methods (bool, optional): Enable inspection of callables. Defaults to False. - docs (bool, optional): Also render doc strings. Defaults to True. - private (bool, optional): Show private attributes (beginning with underscore). Defaults to False. - dunder (bool, optional): Show attributes starting with double underscore. Defaults to False. - sort (bool, optional): Sort attributes alphabetically. Defaults to True. - all (bool, optional): Show all attributes. Defaults to False. - value (bool, optional): Pretty print value. Defaults to True. - """ - _console = console or get_console() - from pip._vendor.rich._inspect import Inspect - - # Special case for inspect(inspect) - is_inspect = obj is inspect - - _inspect = Inspect( - obj, - title=title, - help=is_inspect or help, - methods=is_inspect or methods, - docs=is_inspect or docs, - private=private, - dunder=dunder, - sort=sort, - all=all, - value=value, - ) - _console.print(_inspect) - - -if __name__ == "__main__": # pragma: no cover - print("Hello, **World**") diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/__main__.py b/.venv/Lib/site-packages/pip/_vendor/rich/__main__.py deleted file mode 100644 index 54e6d5e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/__main__.py +++ /dev/null @@ -1,282 +0,0 @@ -import colorsys -import io -from time import process_time - -from pip._vendor.rich import box -from pip._vendor.rich.color import Color -from pip._vendor.rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult -from pip._vendor.rich.markdown import Markdown -from pip._vendor.rich.measure import Measurement -from pip._vendor.rich.pretty import Pretty -from pip._vendor.rich.segment import Segment -from pip._vendor.rich.style import Style -from pip._vendor.rich.syntax import Syntax -from pip._vendor.rich.table import Table -from pip._vendor.rich.text import Text - - -class ColorBox: - def __rich_console__( - self, console: Console, options: ConsoleOptions - ) -> RenderResult: - for y in range(0, 5): - for x in range(options.max_width): - h = x / options.max_width - l = 0.1 + ((y / 5) * 0.7) - r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0) - r2, g2, b2 = colorsys.hls_to_rgb(h, l + 0.7 / 10, 1.0) - bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255) - color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255) - yield Segment("▄", Style(color=color, bgcolor=bgcolor)) - yield Segment.line() - - def __rich_measure__( - self, console: "Console", options: ConsoleOptions - ) -> Measurement: - return Measurement(1, options.max_width) - - -def make_test_card() -> Table: - """Get a renderable that demonstrates a number of features.""" - table = Table.grid(padding=1, pad_edge=True) - table.title = "Rich features" - table.add_column("Feature", no_wrap=True, justify="center", style="bold red") - table.add_column("Demonstration") - - color_table = Table( - box=None, - expand=False, - show_header=False, - show_edge=False, - pad_edge=False, - ) - color_table.add_row( - ( - "✓ [bold green]4-bit color[/]\n" - "✓ [bold blue]8-bit color[/]\n" - "✓ [bold magenta]Truecolor (16.7 million)[/]\n" - "✓ [bold yellow]Dumb terminals[/]\n" - "✓ [bold cyan]Automatic color conversion" - ), - ColorBox(), - ) - - table.add_row("Colors", color_table) - - table.add_row( - "Styles", - "All ansi styles: [bold]bold[/], [dim]dim[/], [italic]italic[/italic], [underline]underline[/], [strike]strikethrough[/], [reverse]reverse[/], and even [blink]blink[/].", - ) - - lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in metus sed sapien ultricies pretium a at justo. Maecenas luctus velit et auctor maximus." - lorem_table = Table.grid(padding=1, collapse_padding=True) - lorem_table.pad_edge = False - lorem_table.add_row( - Text(lorem, justify="left", style="green"), - Text(lorem, justify="center", style="yellow"), - Text(lorem, justify="right", style="blue"), - Text(lorem, justify="full", style="red"), - ) - table.add_row( - "Text", - Group( - Text.from_markup( - """Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n""" - ), - lorem_table, - ), - ) - - def comparison(renderable1: RenderableType, renderable2: RenderableType) -> Table: - table = Table(show_header=False, pad_edge=False, box=None, expand=True) - table.add_column("1", ratio=1) - table.add_column("2", ratio=1) - table.add_row(renderable1, renderable2) - return table - - table.add_row( - "Asian\nlanguage\nsupport", - ":flag_for_china: 该库支持中文,日文和韩文文本!\n:flag_for_japan: ライブラリは中国語、日本語、韓国語のテキストをサポートしています\n:flag_for_south_korea: 이 라이브러리는 중국어, 일본어 및 한국어 텍스트를 지원합니다", - ) - - markup_example = ( - "[bold magenta]Rich[/] supports a simple [i]bbcode[/i]-like [b]markup[/b] for [yellow]color[/], [underline]style[/], and emoji! " - ":+1: :apple: :ant: :bear: :baguette_bread: :bus: " - ) - table.add_row("Markup", markup_example) - - example_table = Table( - show_edge=False, - show_header=True, - expand=False, - row_styles=["none", "dim"], - box=box.SIMPLE, - ) - example_table.add_column("[green]Date", style="green", no_wrap=True) - example_table.add_column("[blue]Title", style="blue") - example_table.add_column( - "[cyan]Production Budget", - style="cyan", - justify="right", - no_wrap=True, - ) - example_table.add_column( - "[magenta]Box Office", - style="magenta", - justify="right", - no_wrap=True, - ) - example_table.add_row( - "Dec 20, 2019", - "Star Wars: The Rise of Skywalker", - "$275,000,000", - "$375,126,118", - ) - example_table.add_row( - "May 25, 2018", - "[b]Solo[/]: A Star Wars Story", - "$275,000,000", - "$393,151,347", - ) - example_table.add_row( - "Dec 15, 2017", - "Star Wars Ep. VIII: The Last Jedi", - "$262,000,000", - "[bold]$1,332,539,889[/bold]", - ) - example_table.add_row( - "May 19, 1999", - "Star Wars Ep. [b]I[/b]: [i]The phantom Menace", - "$115,000,000", - "$1,027,044,677", - ) - - table.add_row("Tables", example_table) - - code = '''\ -def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: - """Iterate and generate a tuple with a flag for last value.""" - iter_values = iter(values) - try: - previous_value = next(iter_values) - except StopIteration: - return - for value in iter_values: - yield False, previous_value - previous_value = value - yield True, previous_value''' - - pretty_data = { - "foo": [ - 3.1427, - ( - "Paul Atreides", - "Vladimir Harkonnen", - "Thufir Hawat", - ), - ], - "atomic": (False, True, None), - } - table.add_row( - "Syntax\nhighlighting\n&\npretty\nprinting", - comparison( - Syntax(code, "python3", line_numbers=True, indent_guides=True), - Pretty(pretty_data, indent_guides=True), - ), - ) - - markdown_example = """\ -# Markdown - -Supports much of the *markdown* __syntax__! - -- Headers -- Basic formatting: **bold**, *italic*, `code` -- Block quotes -- Lists, and more... - """ - table.add_row( - "Markdown", comparison("[cyan]" + markdown_example, Markdown(markdown_example)) - ) - - table.add_row( - "+more!", - """Progress bars, columns, styled logging handler, tracebacks, etc...""", - ) - return table - - -if __name__ == "__main__": # pragma: no cover - - console = Console( - file=io.StringIO(), - force_terminal=True, - ) - test_card = make_test_card() - - # Print once to warm cache - start = process_time() - console.print(test_card) - pre_cache_taken = round((process_time() - start) * 1000.0, 1) - - console.file = io.StringIO() - - start = process_time() - console.print(test_card) - taken = round((process_time() - start) * 1000.0, 1) - - c = Console(record=True) - c.print(test_card) - # c.save_svg( - # path="/Users/darrenburns/Library/Application Support/JetBrains/PyCharm2021.3/scratches/svg_export.svg", - # title="Rich can export to SVG", - # ) - - print(f"rendered in {pre_cache_taken}ms (cold cache)") - print(f"rendered in {taken}ms (warm cache)") - - from pip._vendor.rich.panel import Panel - - console = Console() - - sponsor_message = Table.grid(padding=1) - sponsor_message.add_column(style="green", justify="right") - sponsor_message.add_column(no_wrap=True) - - sponsor_message.add_row( - "Textualize", - "[u blue link=https://github.com/textualize]https://github.com/textualize", - ) - sponsor_message.add_row( - "Buy devs a :coffee:", - "[u blue link=https://ko-fi.com/textualize]https://ko-fi.com/textualize", - ) - sponsor_message.add_row( - "Twitter", - "[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan", - ) - - intro_message = Text.from_markup( - """\ -We hope you enjoy using Rich! - -Rich is maintained with [red]:heart:[/] by [link=https://www.textualize.io]Textualize.io[/] - -- Will McGugan""" - ) - - message = Table.grid(padding=2) - message.add_column() - message.add_column(no_wrap=True) - message.add_row(intro_message, sponsor_message) - - console.print( - Panel.fit( - message, - box=box.ROUNDED, - padding=(1, 2), - title="[b red]Thanks for trying out Rich!", - border_style="bright_blue", - ), - justify="center", - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py b/.venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py deleted file mode 100644 index 36286df..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_cell_widths.py +++ /dev/null @@ -1,451 +0,0 @@ -# Auto generated by make_terminal_widths.py - -CELL_WIDTHS = [ - (0, 0, 0), - (1, 31, -1), - (127, 159, -1), - (768, 879, 0), - (1155, 1161, 0), - (1425, 1469, 0), - (1471, 1471, 0), - (1473, 1474, 0), - (1476, 1477, 0), - (1479, 1479, 0), - (1552, 1562, 0), - (1611, 1631, 0), - (1648, 1648, 0), - (1750, 1756, 0), - (1759, 1764, 0), - (1767, 1768, 0), - (1770, 1773, 0), - (1809, 1809, 0), - (1840, 1866, 0), - (1958, 1968, 0), - (2027, 2035, 0), - (2045, 2045, 0), - (2070, 2073, 0), - (2075, 2083, 0), - (2085, 2087, 0), - (2089, 2093, 0), - (2137, 2139, 0), - (2259, 2273, 0), - (2275, 2306, 0), - (2362, 2362, 0), - (2364, 2364, 0), - (2369, 2376, 0), - (2381, 2381, 0), - (2385, 2391, 0), - (2402, 2403, 0), - (2433, 2433, 0), - (2492, 2492, 0), - (2497, 2500, 0), - (2509, 2509, 0), - (2530, 2531, 0), - (2558, 2558, 0), - (2561, 2562, 0), - (2620, 2620, 0), - (2625, 2626, 0), - (2631, 2632, 0), - (2635, 2637, 0), - (2641, 2641, 0), - (2672, 2673, 0), - (2677, 2677, 0), - (2689, 2690, 0), - (2748, 2748, 0), - (2753, 2757, 0), - (2759, 2760, 0), - (2765, 2765, 0), - (2786, 2787, 0), - (2810, 2815, 0), - (2817, 2817, 0), - (2876, 2876, 0), - (2879, 2879, 0), - (2881, 2884, 0), - (2893, 2893, 0), - (2901, 2902, 0), - (2914, 2915, 0), - (2946, 2946, 0), - (3008, 3008, 0), - (3021, 3021, 0), - (3072, 3072, 0), - (3076, 3076, 0), - (3134, 3136, 0), - (3142, 3144, 0), - (3146, 3149, 0), - (3157, 3158, 0), - (3170, 3171, 0), - (3201, 3201, 0), - (3260, 3260, 0), - (3263, 3263, 0), - (3270, 3270, 0), - (3276, 3277, 0), - (3298, 3299, 0), - (3328, 3329, 0), - (3387, 3388, 0), - (3393, 3396, 0), - (3405, 3405, 0), - (3426, 3427, 0), - (3457, 3457, 0), - (3530, 3530, 0), - (3538, 3540, 0), - (3542, 3542, 0), - (3633, 3633, 0), - (3636, 3642, 0), - (3655, 3662, 0), - (3761, 3761, 0), - (3764, 3772, 0), - (3784, 3789, 0), - (3864, 3865, 0), - (3893, 3893, 0), - (3895, 3895, 0), - (3897, 3897, 0), - (3953, 3966, 0), - (3968, 3972, 0), - (3974, 3975, 0), - (3981, 3991, 0), - (3993, 4028, 0), - (4038, 4038, 0), - (4141, 4144, 0), - (4146, 4151, 0), - (4153, 4154, 0), - (4157, 4158, 0), - (4184, 4185, 0), - (4190, 4192, 0), - (4209, 4212, 0), - (4226, 4226, 0), - (4229, 4230, 0), - (4237, 4237, 0), - (4253, 4253, 0), - (4352, 4447, 2), - (4957, 4959, 0), - (5906, 5908, 0), - (5938, 5940, 0), - (5970, 5971, 0), - (6002, 6003, 0), - (6068, 6069, 0), - (6071, 6077, 0), - (6086, 6086, 0), - (6089, 6099, 0), - (6109, 6109, 0), - (6155, 6157, 0), - (6277, 6278, 0), - (6313, 6313, 0), - (6432, 6434, 0), - (6439, 6440, 0), - (6450, 6450, 0), - (6457, 6459, 0), - (6679, 6680, 0), - (6683, 6683, 0), - (6742, 6742, 0), - (6744, 6750, 0), - (6752, 6752, 0), - (6754, 6754, 0), - (6757, 6764, 0), - (6771, 6780, 0), - (6783, 6783, 0), - (6832, 6848, 0), - (6912, 6915, 0), - (6964, 6964, 0), - (6966, 6970, 0), - (6972, 6972, 0), - (6978, 6978, 0), - (7019, 7027, 0), - (7040, 7041, 0), - (7074, 7077, 0), - (7080, 7081, 0), - (7083, 7085, 0), - (7142, 7142, 0), - (7144, 7145, 0), - (7149, 7149, 0), - (7151, 7153, 0), - (7212, 7219, 0), - (7222, 7223, 0), - (7376, 7378, 0), - (7380, 7392, 0), - (7394, 7400, 0), - (7405, 7405, 0), - (7412, 7412, 0), - (7416, 7417, 0), - (7616, 7673, 0), - (7675, 7679, 0), - (8203, 8207, 0), - (8232, 8238, 0), - (8288, 8291, 0), - (8400, 8432, 0), - (8986, 8987, 2), - (9001, 9002, 2), - (9193, 9196, 2), - (9200, 9200, 2), - (9203, 9203, 2), - (9725, 9726, 2), - (9748, 9749, 2), - (9800, 9811, 2), - (9855, 9855, 2), - (9875, 9875, 2), - (9889, 9889, 2), - (9898, 9899, 2), - (9917, 9918, 2), - (9924, 9925, 2), - (9934, 9934, 2), - (9940, 9940, 2), - (9962, 9962, 2), - (9970, 9971, 2), - (9973, 9973, 2), - (9978, 9978, 2), - (9981, 9981, 2), - (9989, 9989, 2), - (9994, 9995, 2), - (10024, 10024, 2), - (10060, 10060, 2), - (10062, 10062, 2), - (10067, 10069, 2), - (10071, 10071, 2), - (10133, 10135, 2), - (10160, 10160, 2), - (10175, 10175, 2), - (11035, 11036, 2), - (11088, 11088, 2), - (11093, 11093, 2), - (11503, 11505, 0), - (11647, 11647, 0), - (11744, 11775, 0), - (11904, 11929, 2), - (11931, 12019, 2), - (12032, 12245, 2), - (12272, 12283, 2), - (12288, 12329, 2), - (12330, 12333, 0), - (12334, 12350, 2), - (12353, 12438, 2), - (12441, 12442, 0), - (12443, 12543, 2), - (12549, 12591, 2), - (12593, 12686, 2), - (12688, 12771, 2), - (12784, 12830, 2), - (12832, 12871, 2), - (12880, 19903, 2), - (19968, 42124, 2), - (42128, 42182, 2), - (42607, 42610, 0), - (42612, 42621, 0), - (42654, 42655, 0), - (42736, 42737, 0), - (43010, 43010, 0), - (43014, 43014, 0), - (43019, 43019, 0), - (43045, 43046, 0), - (43052, 43052, 0), - (43204, 43205, 0), - (43232, 43249, 0), - (43263, 43263, 0), - (43302, 43309, 0), - (43335, 43345, 0), - (43360, 43388, 2), - (43392, 43394, 0), - (43443, 43443, 0), - (43446, 43449, 0), - (43452, 43453, 0), - (43493, 43493, 0), - (43561, 43566, 0), - (43569, 43570, 0), - (43573, 43574, 0), - (43587, 43587, 0), - (43596, 43596, 0), - (43644, 43644, 0), - (43696, 43696, 0), - (43698, 43700, 0), - (43703, 43704, 0), - (43710, 43711, 0), - (43713, 43713, 0), - (43756, 43757, 0), - (43766, 43766, 0), - (44005, 44005, 0), - (44008, 44008, 0), - (44013, 44013, 0), - (44032, 55203, 2), - (63744, 64255, 2), - (64286, 64286, 0), - (65024, 65039, 0), - (65040, 65049, 2), - (65056, 65071, 0), - (65072, 65106, 2), - (65108, 65126, 2), - (65128, 65131, 2), - (65281, 65376, 2), - (65504, 65510, 2), - (66045, 66045, 0), - (66272, 66272, 0), - (66422, 66426, 0), - (68097, 68099, 0), - (68101, 68102, 0), - (68108, 68111, 0), - (68152, 68154, 0), - (68159, 68159, 0), - (68325, 68326, 0), - (68900, 68903, 0), - (69291, 69292, 0), - (69446, 69456, 0), - (69633, 69633, 0), - (69688, 69702, 0), - (69759, 69761, 0), - (69811, 69814, 0), - (69817, 69818, 0), - (69888, 69890, 0), - (69927, 69931, 0), - (69933, 69940, 0), - (70003, 70003, 0), - (70016, 70017, 0), - (70070, 70078, 0), - (70089, 70092, 0), - (70095, 70095, 0), - (70191, 70193, 0), - (70196, 70196, 0), - (70198, 70199, 0), - (70206, 70206, 0), - (70367, 70367, 0), - (70371, 70378, 0), - (70400, 70401, 0), - (70459, 70460, 0), - (70464, 70464, 0), - (70502, 70508, 0), - (70512, 70516, 0), - (70712, 70719, 0), - (70722, 70724, 0), - (70726, 70726, 0), - (70750, 70750, 0), - (70835, 70840, 0), - (70842, 70842, 0), - (70847, 70848, 0), - (70850, 70851, 0), - (71090, 71093, 0), - (71100, 71101, 0), - (71103, 71104, 0), - (71132, 71133, 0), - (71219, 71226, 0), - (71229, 71229, 0), - (71231, 71232, 0), - (71339, 71339, 0), - (71341, 71341, 0), - (71344, 71349, 0), - (71351, 71351, 0), - (71453, 71455, 0), - (71458, 71461, 0), - (71463, 71467, 0), - (71727, 71735, 0), - (71737, 71738, 0), - (71995, 71996, 0), - (71998, 71998, 0), - (72003, 72003, 0), - (72148, 72151, 0), - (72154, 72155, 0), - (72160, 72160, 0), - (72193, 72202, 0), - (72243, 72248, 0), - (72251, 72254, 0), - (72263, 72263, 0), - (72273, 72278, 0), - (72281, 72283, 0), - (72330, 72342, 0), - (72344, 72345, 0), - (72752, 72758, 0), - (72760, 72765, 0), - (72767, 72767, 0), - (72850, 72871, 0), - (72874, 72880, 0), - (72882, 72883, 0), - (72885, 72886, 0), - (73009, 73014, 0), - (73018, 73018, 0), - (73020, 73021, 0), - (73023, 73029, 0), - (73031, 73031, 0), - (73104, 73105, 0), - (73109, 73109, 0), - (73111, 73111, 0), - (73459, 73460, 0), - (92912, 92916, 0), - (92976, 92982, 0), - (94031, 94031, 0), - (94095, 94098, 0), - (94176, 94179, 2), - (94180, 94180, 0), - (94192, 94193, 2), - (94208, 100343, 2), - (100352, 101589, 2), - (101632, 101640, 2), - (110592, 110878, 2), - (110928, 110930, 2), - (110948, 110951, 2), - (110960, 111355, 2), - (113821, 113822, 0), - (119143, 119145, 0), - (119163, 119170, 0), - (119173, 119179, 0), - (119210, 119213, 0), - (119362, 119364, 0), - (121344, 121398, 0), - (121403, 121452, 0), - (121461, 121461, 0), - (121476, 121476, 0), - (121499, 121503, 0), - (121505, 121519, 0), - (122880, 122886, 0), - (122888, 122904, 0), - (122907, 122913, 0), - (122915, 122916, 0), - (122918, 122922, 0), - (123184, 123190, 0), - (123628, 123631, 0), - (125136, 125142, 0), - (125252, 125258, 0), - (126980, 126980, 2), - (127183, 127183, 2), - (127374, 127374, 2), - (127377, 127386, 2), - (127488, 127490, 2), - (127504, 127547, 2), - (127552, 127560, 2), - (127568, 127569, 2), - (127584, 127589, 2), - (127744, 127776, 2), - (127789, 127797, 2), - (127799, 127868, 2), - (127870, 127891, 2), - (127904, 127946, 2), - (127951, 127955, 2), - (127968, 127984, 2), - (127988, 127988, 2), - (127992, 128062, 2), - (128064, 128064, 2), - (128066, 128252, 2), - (128255, 128317, 2), - (128331, 128334, 2), - (128336, 128359, 2), - (128378, 128378, 2), - (128405, 128406, 2), - (128420, 128420, 2), - (128507, 128591, 2), - (128640, 128709, 2), - (128716, 128716, 2), - (128720, 128722, 2), - (128725, 128727, 2), - (128747, 128748, 2), - (128756, 128764, 2), - (128992, 129003, 2), - (129292, 129338, 2), - (129340, 129349, 2), - (129351, 129400, 2), - (129402, 129483, 2), - (129485, 129535, 2), - (129648, 129652, 2), - (129656, 129658, 2), - (129664, 129670, 2), - (129680, 129704, 2), - (129712, 129718, 2), - (129728, 129730, 2), - (129744, 129750, 2), - (131072, 196605, 2), - (196608, 262141, 2), - (917760, 917999, 0), -] diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py b/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py deleted file mode 100644 index 1f2877b..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_codes.py +++ /dev/null @@ -1,3610 +0,0 @@ -EMOJI = { - "1st_place_medal": "🥇", - "2nd_place_medal": "🥈", - "3rd_place_medal": "🥉", - "ab_button_(blood_type)": "🆎", - "atm_sign": "🏧", - "a_button_(blood_type)": "🅰", - "afghanistan": "🇦🇫", - "albania": "🇦🇱", - "algeria": "🇩🇿", - "american_samoa": "🇦🇸", - "andorra": "🇦🇩", - "angola": "🇦🇴", - "anguilla": "🇦🇮", - "antarctica": "🇦🇶", - "antigua_&_barbuda": "🇦🇬", - "aquarius": "♒", - "argentina": "🇦🇷", - "aries": "♈", - "armenia": "🇦🇲", - "aruba": "🇦🇼", - "ascension_island": "🇦🇨", - "australia": "🇦🇺", - "austria": "🇦🇹", - "azerbaijan": "🇦🇿", - "back_arrow": "🔙", - "b_button_(blood_type)": "🅱", - "bahamas": "🇧🇸", - "bahrain": "🇧🇭", - "bangladesh": "🇧🇩", - "barbados": "🇧🇧", - "belarus": "🇧🇾", - "belgium": "🇧🇪", - "belize": "🇧🇿", - "benin": "🇧🇯", - "bermuda": "🇧🇲", - "bhutan": "🇧🇹", - "bolivia": "🇧🇴", - "bosnia_&_herzegovina": "🇧🇦", - "botswana": "🇧🇼", - "bouvet_island": "🇧🇻", - "brazil": "🇧🇷", - "british_indian_ocean_territory": "🇮🇴", - "british_virgin_islands": "🇻🇬", - "brunei": "🇧🇳", - "bulgaria": "🇧🇬", - "burkina_faso": "🇧🇫", - "burundi": "🇧🇮", - "cl_button": "🆑", - "cool_button": "🆒", - "cambodia": "🇰🇭", - "cameroon": "🇨🇲", - "canada": "🇨🇦", - "canary_islands": "🇮🇨", - "cancer": "♋", - "cape_verde": "🇨🇻", - "capricorn": "♑", - "caribbean_netherlands": "🇧🇶", - "cayman_islands": "🇰🇾", - "central_african_republic": "🇨🇫", - "ceuta_&_melilla": "🇪🇦", - "chad": "🇹🇩", - "chile": "🇨🇱", - "china": "🇨🇳", - "christmas_island": "🇨🇽", - "christmas_tree": "🎄", - "clipperton_island": "🇨🇵", - "cocos_(keeling)_islands": "🇨🇨", - "colombia": "🇨🇴", - "comoros": "🇰🇲", - "congo_-_brazzaville": "🇨🇬", - "congo_-_kinshasa": "🇨🇩", - "cook_islands": "🇨🇰", - "costa_rica": "🇨🇷", - "croatia": "🇭🇷", - "cuba": "🇨🇺", - "curaçao": "🇨🇼", - "cyprus": "🇨🇾", - "czechia": "🇨🇿", - "côte_d’ivoire": "🇨🇮", - "denmark": "🇩🇰", - "diego_garcia": "🇩🇬", - "djibouti": "🇩🇯", - "dominica": "🇩🇲", - "dominican_republic": "🇩🇴", - "end_arrow": "🔚", - "ecuador": "🇪🇨", - "egypt": "🇪🇬", - "el_salvador": "🇸🇻", - "england": "🏴\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f", - "equatorial_guinea": "🇬🇶", - "eritrea": "🇪🇷", - "estonia": "🇪🇪", - "ethiopia": "🇪🇹", - "european_union": "🇪🇺", - "free_button": "🆓", - "falkland_islands": "🇫🇰", - "faroe_islands": "🇫🇴", - "fiji": "🇫🇯", - "finland": "🇫🇮", - "france": "🇫🇷", - "french_guiana": "🇬🇫", - "french_polynesia": "🇵🇫", - "french_southern_territories": "🇹🇫", - "gabon": "🇬🇦", - "gambia": "🇬🇲", - "gemini": "♊", - "georgia": "🇬🇪", - "germany": "🇩🇪", - "ghana": "🇬🇭", - "gibraltar": "🇬🇮", - "greece": "🇬🇷", - "greenland": "🇬🇱", - "grenada": "🇬🇩", - "guadeloupe": "🇬🇵", - "guam": "🇬🇺", - "guatemala": "🇬🇹", - "guernsey": "🇬🇬", - "guinea": "🇬🇳", - "guinea-bissau": "🇬🇼", - "guyana": "🇬🇾", - "haiti": "🇭🇹", - "heard_&_mcdonald_islands": "🇭🇲", - "honduras": "🇭🇳", - "hong_kong_sar_china": "🇭🇰", - "hungary": "🇭🇺", - "id_button": "🆔", - "iceland": "🇮🇸", - "india": "🇮🇳", - "indonesia": "🇮🇩", - "iran": "🇮🇷", - "iraq": "🇮🇶", - "ireland": "🇮🇪", - "isle_of_man": "🇮🇲", - "israel": "🇮🇱", - "italy": "🇮🇹", - "jamaica": "🇯🇲", - "japan": "🗾", - "japanese_acceptable_button": "🉑", - "japanese_application_button": "🈸", - "japanese_bargain_button": "🉐", - "japanese_castle": "🏯", - "japanese_congratulations_button": "㊗", - "japanese_discount_button": "🈹", - "japanese_dolls": "🎎", - "japanese_free_of_charge_button": "🈚", - "japanese_here_button": "🈁", - "japanese_monthly_amount_button": "🈷", - "japanese_no_vacancy_button": "🈵", - "japanese_not_free_of_charge_button": "🈶", - "japanese_open_for_business_button": "🈺", - "japanese_passing_grade_button": "🈴", - "japanese_post_office": "🏣", - "japanese_prohibited_button": "🈲", - "japanese_reserved_button": "🈯", - "japanese_secret_button": "㊙", - "japanese_service_charge_button": "🈂", - "japanese_symbol_for_beginner": "🔰", - "japanese_vacancy_button": "🈳", - "jersey": "🇯🇪", - "jordan": "🇯🇴", - "kazakhstan": "🇰🇿", - "kenya": "🇰🇪", - "kiribati": "🇰🇮", - "kosovo": "🇽🇰", - "kuwait": "🇰🇼", - "kyrgyzstan": "🇰🇬", - "laos": "🇱🇦", - "latvia": "🇱🇻", - "lebanon": "🇱🇧", - "leo": "♌", - "lesotho": "🇱🇸", - "liberia": "🇱🇷", - "libra": "♎", - "libya": "🇱🇾", - "liechtenstein": "🇱🇮", - "lithuania": "🇱🇹", - "luxembourg": "🇱🇺", - "macau_sar_china": "🇲🇴", - "macedonia": "🇲🇰", - "madagascar": "🇲🇬", - "malawi": "🇲🇼", - "malaysia": "🇲🇾", - "maldives": "🇲🇻", - "mali": "🇲🇱", - "malta": "🇲🇹", - "marshall_islands": "🇲🇭", - "martinique": "🇲🇶", - "mauritania": "🇲🇷", - "mauritius": "🇲🇺", - "mayotte": "🇾🇹", - "mexico": "🇲🇽", - "micronesia": "🇫🇲", - "moldova": "🇲🇩", - "monaco": "🇲🇨", - "mongolia": "🇲🇳", - "montenegro": "🇲🇪", - "montserrat": "🇲🇸", - "morocco": "🇲🇦", - "mozambique": "🇲🇿", - "mrs._claus": "🤶", - "mrs._claus_dark_skin_tone": "🤶🏿", - "mrs._claus_light_skin_tone": "🤶🏻", - "mrs._claus_medium-dark_skin_tone": "🤶🏾", - "mrs._claus_medium-light_skin_tone": "🤶🏼", - "mrs._claus_medium_skin_tone": "🤶🏽", - "myanmar_(burma)": "🇲🇲", - "new_button": "🆕", - "ng_button": "🆖", - "namibia": "🇳🇦", - "nauru": "🇳🇷", - "nepal": "🇳🇵", - "netherlands": "🇳🇱", - "new_caledonia": "🇳🇨", - "new_zealand": "🇳🇿", - "nicaragua": "🇳🇮", - "niger": "🇳🇪", - "nigeria": "🇳🇬", - "niue": "🇳🇺", - "norfolk_island": "🇳🇫", - "north_korea": "🇰🇵", - "northern_mariana_islands": "🇲🇵", - "norway": "🇳🇴", - "ok_button": "🆗", - "ok_hand": "👌", - "ok_hand_dark_skin_tone": "👌🏿", - "ok_hand_light_skin_tone": "👌🏻", - "ok_hand_medium-dark_skin_tone": "👌🏾", - "ok_hand_medium-light_skin_tone": "👌🏼", - "ok_hand_medium_skin_tone": "👌🏽", - "on!_arrow": "🔛", - "o_button_(blood_type)": "🅾", - "oman": "🇴🇲", - "ophiuchus": "⛎", - "p_button": "🅿", - "pakistan": "🇵🇰", - "palau": "🇵🇼", - "palestinian_territories": "🇵🇸", - "panama": "🇵🇦", - "papua_new_guinea": "🇵🇬", - "paraguay": "🇵🇾", - "peru": "🇵🇪", - "philippines": "🇵🇭", - "pisces": "♓", - "pitcairn_islands": "🇵🇳", - "poland": "🇵🇱", - "portugal": "🇵🇹", - "puerto_rico": "🇵🇷", - "qatar": "🇶🇦", - "romania": "🇷🇴", - "russia": "🇷🇺", - "rwanda": "🇷🇼", - "réunion": "🇷🇪", - "soon_arrow": "🔜", - "sos_button": "🆘", - "sagittarius": "♐", - "samoa": "🇼🇸", - "san_marino": "🇸🇲", - "santa_claus": "🎅", - "santa_claus_dark_skin_tone": "🎅🏿", - "santa_claus_light_skin_tone": "🎅🏻", - "santa_claus_medium-dark_skin_tone": "🎅🏾", - "santa_claus_medium-light_skin_tone": "🎅🏼", - "santa_claus_medium_skin_tone": "🎅🏽", - "saudi_arabia": "🇸🇦", - "scorpio": "♏", - "scotland": "🏴\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f", - "senegal": "🇸🇳", - "serbia": "🇷🇸", - "seychelles": "🇸🇨", - "sierra_leone": "🇸🇱", - "singapore": "🇸🇬", - "sint_maarten": "🇸🇽", - "slovakia": "🇸🇰", - "slovenia": "🇸🇮", - "solomon_islands": "🇸🇧", - "somalia": "🇸🇴", - "south_africa": "🇿🇦", - "south_georgia_&_south_sandwich_islands": "🇬🇸", - "south_korea": "🇰🇷", - "south_sudan": "🇸🇸", - "spain": "🇪🇸", - "sri_lanka": "🇱🇰", - "st._barthélemy": "🇧🇱", - "st._helena": "🇸🇭", - "st._kitts_&_nevis": "🇰🇳", - "st._lucia": "🇱🇨", - "st._martin": "🇲🇫", - "st._pierre_&_miquelon": "🇵🇲", - "st._vincent_&_grenadines": "🇻🇨", - "statue_of_liberty": "🗽", - "sudan": "🇸🇩", - "suriname": "🇸🇷", - "svalbard_&_jan_mayen": "🇸🇯", - "swaziland": "🇸🇿", - "sweden": "🇸🇪", - "switzerland": "🇨🇭", - "syria": "🇸🇾", - "são_tomé_&_príncipe": "🇸🇹", - "t-rex": "🦖", - "top_arrow": "🔝", - "taiwan": "🇹🇼", - "tajikistan": "🇹🇯", - "tanzania": "🇹🇿", - "taurus": "♉", - "thailand": "🇹🇭", - "timor-leste": "🇹🇱", - "togo": "🇹🇬", - "tokelau": "🇹🇰", - "tokyo_tower": "🗼", - "tonga": "🇹🇴", - "trinidad_&_tobago": "🇹🇹", - "tristan_da_cunha": "🇹🇦", - "tunisia": "🇹🇳", - "turkey": "🦃", - "turkmenistan": "🇹🇲", - "turks_&_caicos_islands": "🇹🇨", - "tuvalu": "🇹🇻", - "u.s._outlying_islands": "🇺🇲", - "u.s._virgin_islands": "🇻🇮", - "up!_button": "🆙", - "uganda": "🇺🇬", - "ukraine": "🇺🇦", - "united_arab_emirates": "🇦🇪", - "united_kingdom": "🇬🇧", - "united_nations": "🇺🇳", - "united_states": "🇺🇸", - "uruguay": "🇺🇾", - "uzbekistan": "🇺🇿", - "vs_button": "🆚", - "vanuatu": "🇻🇺", - "vatican_city": "🇻🇦", - "venezuela": "🇻🇪", - "vietnam": "🇻🇳", - "virgo": "♍", - "wales": "🏴\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f", - "wallis_&_futuna": "🇼🇫", - "western_sahara": "🇪🇭", - "yemen": "🇾🇪", - "zambia": "🇿🇲", - "zimbabwe": "🇿🇼", - "abacus": "🧮", - "adhesive_bandage": "🩹", - "admission_tickets": "🎟", - "adult": "🧑", - "adult_dark_skin_tone": "🧑🏿", - "adult_light_skin_tone": "🧑🏻", - "adult_medium-dark_skin_tone": "🧑🏾", - "adult_medium-light_skin_tone": "🧑🏼", - "adult_medium_skin_tone": "🧑🏽", - "aerial_tramway": "🚡", - "airplane": "✈", - "airplane_arrival": "🛬", - "airplane_departure": "🛫", - "alarm_clock": "⏰", - "alembic": "⚗", - "alien": "👽", - "alien_monster": "👾", - "ambulance": "🚑", - "american_football": "🏈", - "amphora": "🏺", - "anchor": "⚓", - "anger_symbol": "💢", - "angry_face": "😠", - "angry_face_with_horns": "👿", - "anguished_face": "😧", - "ant": "🐜", - "antenna_bars": "📶", - "anxious_face_with_sweat": "😰", - "articulated_lorry": "🚛", - "artist_palette": "🎨", - "astonished_face": "😲", - "atom_symbol": "⚛", - "auto_rickshaw": "🛺", - "automobile": "🚗", - "avocado": "🥑", - "axe": "🪓", - "baby": "👶", - "baby_angel": "👼", - "baby_angel_dark_skin_tone": "👼🏿", - "baby_angel_light_skin_tone": "👼🏻", - "baby_angel_medium-dark_skin_tone": "👼🏾", - "baby_angel_medium-light_skin_tone": "👼🏼", - "baby_angel_medium_skin_tone": "👼🏽", - "baby_bottle": "🍼", - "baby_chick": "🐤", - "baby_dark_skin_tone": "👶🏿", - "baby_light_skin_tone": "👶🏻", - "baby_medium-dark_skin_tone": "👶🏾", - "baby_medium-light_skin_tone": "👶🏼", - "baby_medium_skin_tone": "👶🏽", - "baby_symbol": "🚼", - "backhand_index_pointing_down": "👇", - "backhand_index_pointing_down_dark_skin_tone": "👇🏿", - "backhand_index_pointing_down_light_skin_tone": "👇🏻", - "backhand_index_pointing_down_medium-dark_skin_tone": "👇🏾", - "backhand_index_pointing_down_medium-light_skin_tone": "👇🏼", - "backhand_index_pointing_down_medium_skin_tone": "👇🏽", - "backhand_index_pointing_left": "👈", - "backhand_index_pointing_left_dark_skin_tone": "👈🏿", - "backhand_index_pointing_left_light_skin_tone": "👈🏻", - "backhand_index_pointing_left_medium-dark_skin_tone": "👈🏾", - "backhand_index_pointing_left_medium-light_skin_tone": "👈🏼", - "backhand_index_pointing_left_medium_skin_tone": "👈🏽", - "backhand_index_pointing_right": "👉", - "backhand_index_pointing_right_dark_skin_tone": "👉🏿", - "backhand_index_pointing_right_light_skin_tone": "👉🏻", - "backhand_index_pointing_right_medium-dark_skin_tone": "👉🏾", - "backhand_index_pointing_right_medium-light_skin_tone": "👉🏼", - "backhand_index_pointing_right_medium_skin_tone": "👉🏽", - "backhand_index_pointing_up": "👆", - "backhand_index_pointing_up_dark_skin_tone": "👆🏿", - "backhand_index_pointing_up_light_skin_tone": "👆🏻", - "backhand_index_pointing_up_medium-dark_skin_tone": "👆🏾", - "backhand_index_pointing_up_medium-light_skin_tone": "👆🏼", - "backhand_index_pointing_up_medium_skin_tone": "👆🏽", - "bacon": "🥓", - "badger": "🦡", - "badminton": "🏸", - "bagel": "🥯", - "baggage_claim": "🛄", - "baguette_bread": "🥖", - "balance_scale": "⚖", - "bald": "🦲", - "bald_man": "👨\u200d🦲", - "bald_woman": "👩\u200d🦲", - "ballet_shoes": "🩰", - "balloon": "🎈", - "ballot_box_with_ballot": "🗳", - "ballot_box_with_check": "☑", - "banana": "🍌", - "banjo": "🪕", - "bank": "🏦", - "bar_chart": "📊", - "barber_pole": "💈", - "baseball": "⚾", - "basket": "🧺", - "basketball": "🏀", - "bat": "🦇", - "bathtub": "🛁", - "battery": "🔋", - "beach_with_umbrella": "🏖", - "beaming_face_with_smiling_eyes": "😁", - "bear_face": "🐻", - "bearded_person": "🧔", - "bearded_person_dark_skin_tone": "🧔🏿", - "bearded_person_light_skin_tone": "🧔🏻", - "bearded_person_medium-dark_skin_tone": "🧔🏾", - "bearded_person_medium-light_skin_tone": "🧔🏼", - "bearded_person_medium_skin_tone": "🧔🏽", - "beating_heart": "💓", - "bed": "🛏", - "beer_mug": "🍺", - "bell": "🔔", - "bell_with_slash": "🔕", - "bellhop_bell": "🛎", - "bento_box": "🍱", - "beverage_box": "🧃", - "bicycle": "🚲", - "bikini": "👙", - "billed_cap": "🧢", - "biohazard": "☣", - "bird": "🐦", - "birthday_cake": "🎂", - "black_circle": "⚫", - "black_flag": "🏴", - "black_heart": "🖤", - "black_large_square": "⬛", - "black_medium-small_square": "◾", - "black_medium_square": "◼", - "black_nib": "✒", - "black_small_square": "▪", - "black_square_button": "🔲", - "blond-haired_man": "👱\u200d♂️", - "blond-haired_man_dark_skin_tone": "👱🏿\u200d♂️", - "blond-haired_man_light_skin_tone": "👱🏻\u200d♂️", - "blond-haired_man_medium-dark_skin_tone": "👱🏾\u200d♂️", - "blond-haired_man_medium-light_skin_tone": "👱🏼\u200d♂️", - "blond-haired_man_medium_skin_tone": "👱🏽\u200d♂️", - "blond-haired_person": "👱", - "blond-haired_person_dark_skin_tone": "👱🏿", - "blond-haired_person_light_skin_tone": "👱🏻", - "blond-haired_person_medium-dark_skin_tone": "👱🏾", - "blond-haired_person_medium-light_skin_tone": "👱🏼", - "blond-haired_person_medium_skin_tone": "👱🏽", - "blond-haired_woman": "👱\u200d♀️", - "blond-haired_woman_dark_skin_tone": "👱🏿\u200d♀️", - "blond-haired_woman_light_skin_tone": "👱🏻\u200d♀️", - "blond-haired_woman_medium-dark_skin_tone": "👱🏾\u200d♀️", - "blond-haired_woman_medium-light_skin_tone": "👱🏼\u200d♀️", - "blond-haired_woman_medium_skin_tone": "👱🏽\u200d♀️", - "blossom": "🌼", - "blowfish": "🐡", - "blue_book": "📘", - "blue_circle": "🔵", - "blue_heart": "💙", - "blue_square": "🟦", - "boar": "🐗", - "bomb": "💣", - "bone": "🦴", - "bookmark": "🔖", - "bookmark_tabs": "📑", - "books": "📚", - "bottle_with_popping_cork": "🍾", - "bouquet": "💐", - "bow_and_arrow": "🏹", - "bowl_with_spoon": "🥣", - "bowling": "🎳", - "boxing_glove": "🥊", - "boy": "👦", - "boy_dark_skin_tone": "👦🏿", - "boy_light_skin_tone": "👦🏻", - "boy_medium-dark_skin_tone": "👦🏾", - "boy_medium-light_skin_tone": "👦🏼", - "boy_medium_skin_tone": "👦🏽", - "brain": "🧠", - "bread": "🍞", - "breast-feeding": "🤱", - "breast-feeding_dark_skin_tone": "🤱🏿", - "breast-feeding_light_skin_tone": "🤱🏻", - "breast-feeding_medium-dark_skin_tone": "🤱🏾", - "breast-feeding_medium-light_skin_tone": "🤱🏼", - "breast-feeding_medium_skin_tone": "🤱🏽", - "brick": "🧱", - "bride_with_veil": "👰", - "bride_with_veil_dark_skin_tone": "👰🏿", - "bride_with_veil_light_skin_tone": "👰🏻", - "bride_with_veil_medium-dark_skin_tone": "👰🏾", - "bride_with_veil_medium-light_skin_tone": "👰🏼", - "bride_with_veil_medium_skin_tone": "👰🏽", - "bridge_at_night": "🌉", - "briefcase": "💼", - "briefs": "🩲", - "bright_button": "🔆", - "broccoli": "🥦", - "broken_heart": "💔", - "broom": "🧹", - "brown_circle": "🟤", - "brown_heart": "🤎", - "brown_square": "🟫", - "bug": "🐛", - "building_construction": "🏗", - "bullet_train": "🚅", - "burrito": "🌯", - "bus": "🚌", - "bus_stop": "🚏", - "bust_in_silhouette": "👤", - "busts_in_silhouette": "👥", - "butter": "🧈", - "butterfly": "🦋", - "cactus": "🌵", - "calendar": "📆", - "call_me_hand": "🤙", - "call_me_hand_dark_skin_tone": "🤙🏿", - "call_me_hand_light_skin_tone": "🤙🏻", - "call_me_hand_medium-dark_skin_tone": "🤙🏾", - "call_me_hand_medium-light_skin_tone": "🤙🏼", - "call_me_hand_medium_skin_tone": "🤙🏽", - "camel": "🐫", - "camera": "📷", - "camera_with_flash": "📸", - "camping": "🏕", - "candle": "🕯", - "candy": "🍬", - "canned_food": "🥫", - "canoe": "🛶", - "card_file_box": "🗃", - "card_index": "📇", - "card_index_dividers": "🗂", - "carousel_horse": "🎠", - "carp_streamer": "🎏", - "carrot": "🥕", - "castle": "🏰", - "cat": "🐱", - "cat_face": "🐱", - "cat_face_with_tears_of_joy": "😹", - "cat_face_with_wry_smile": "😼", - "chains": "⛓", - "chair": "🪑", - "chart_decreasing": "📉", - "chart_increasing": "📈", - "chart_increasing_with_yen": "💹", - "cheese_wedge": "🧀", - "chequered_flag": "🏁", - "cherries": "🍒", - "cherry_blossom": "🌸", - "chess_pawn": "♟", - "chestnut": "🌰", - "chicken": "🐔", - "child": "🧒", - "child_dark_skin_tone": "🧒🏿", - "child_light_skin_tone": "🧒🏻", - "child_medium-dark_skin_tone": "🧒🏾", - "child_medium-light_skin_tone": "🧒🏼", - "child_medium_skin_tone": "🧒🏽", - "children_crossing": "🚸", - "chipmunk": "🐿", - "chocolate_bar": "🍫", - "chopsticks": "🥢", - "church": "⛪", - "cigarette": "🚬", - "cinema": "🎦", - "circled_m": "Ⓜ", - "circus_tent": "🎪", - "cityscape": "🏙", - "cityscape_at_dusk": "🌆", - "clamp": "🗜", - "clapper_board": "🎬", - "clapping_hands": "👏", - "clapping_hands_dark_skin_tone": "👏🏿", - "clapping_hands_light_skin_tone": "👏🏻", - "clapping_hands_medium-dark_skin_tone": "👏🏾", - "clapping_hands_medium-light_skin_tone": "👏🏼", - "clapping_hands_medium_skin_tone": "👏🏽", - "classical_building": "🏛", - "clinking_beer_mugs": "🍻", - "clinking_glasses": "🥂", - "clipboard": "📋", - "clockwise_vertical_arrows": "🔃", - "closed_book": "📕", - "closed_mailbox_with_lowered_flag": "📪", - "closed_mailbox_with_raised_flag": "📫", - "closed_umbrella": "🌂", - "cloud": "☁", - "cloud_with_lightning": "🌩", - "cloud_with_lightning_and_rain": "⛈", - "cloud_with_rain": "🌧", - "cloud_with_snow": "🌨", - "clown_face": "🤡", - "club_suit": "♣", - "clutch_bag": "👝", - "coat": "🧥", - "cocktail_glass": "🍸", - "coconut": "🥥", - "coffin": "⚰", - "cold_face": "🥶", - "collision": "💥", - "comet": "☄", - "compass": "🧭", - "computer_disk": "💽", - "computer_mouse": "🖱", - "confetti_ball": "🎊", - "confounded_face": "😖", - "confused_face": "😕", - "construction": "🚧", - "construction_worker": "👷", - "construction_worker_dark_skin_tone": "👷🏿", - "construction_worker_light_skin_tone": "👷🏻", - "construction_worker_medium-dark_skin_tone": "👷🏾", - "construction_worker_medium-light_skin_tone": "👷🏼", - "construction_worker_medium_skin_tone": "👷🏽", - "control_knobs": "🎛", - "convenience_store": "🏪", - "cooked_rice": "🍚", - "cookie": "🍪", - "cooking": "🍳", - "copyright": "©", - "couch_and_lamp": "🛋", - "counterclockwise_arrows_button": "🔄", - "couple_with_heart": "💑", - "couple_with_heart_man_man": "👨\u200d❤️\u200d👨", - "couple_with_heart_woman_man": "👩\u200d❤️\u200d👨", - "couple_with_heart_woman_woman": "👩\u200d❤️\u200d👩", - "cow": "🐮", - "cow_face": "🐮", - "cowboy_hat_face": "🤠", - "crab": "🦀", - "crayon": "🖍", - "credit_card": "💳", - "crescent_moon": "🌙", - "cricket": "🦗", - "cricket_game": "🏏", - "crocodile": "🐊", - "croissant": "🥐", - "cross_mark": "❌", - "cross_mark_button": "❎", - "crossed_fingers": "🤞", - "crossed_fingers_dark_skin_tone": "🤞🏿", - "crossed_fingers_light_skin_tone": "🤞🏻", - "crossed_fingers_medium-dark_skin_tone": "🤞🏾", - "crossed_fingers_medium-light_skin_tone": "🤞🏼", - "crossed_fingers_medium_skin_tone": "🤞🏽", - "crossed_flags": "🎌", - "crossed_swords": "⚔", - "crown": "👑", - "crying_cat_face": "😿", - "crying_face": "😢", - "crystal_ball": "🔮", - "cucumber": "🥒", - "cupcake": "🧁", - "cup_with_straw": "🥤", - "curling_stone": "🥌", - "curly_hair": "🦱", - "curly-haired_man": "👨\u200d🦱", - "curly-haired_woman": "👩\u200d🦱", - "curly_loop": "➰", - "currency_exchange": "💱", - "curry_rice": "🍛", - "custard": "🍮", - "customs": "🛃", - "cut_of_meat": "🥩", - "cyclone": "🌀", - "dagger": "🗡", - "dango": "🍡", - "dashing_away": "💨", - "deaf_person": "🧏", - "deciduous_tree": "🌳", - "deer": "🦌", - "delivery_truck": "🚚", - "department_store": "🏬", - "derelict_house": "🏚", - "desert": "🏜", - "desert_island": "🏝", - "desktop_computer": "🖥", - "detective": "🕵", - "detective_dark_skin_tone": "🕵🏿", - "detective_light_skin_tone": "🕵🏻", - "detective_medium-dark_skin_tone": "🕵🏾", - "detective_medium-light_skin_tone": "🕵🏼", - "detective_medium_skin_tone": "🕵🏽", - "diamond_suit": "♦", - "diamond_with_a_dot": "💠", - "dim_button": "🔅", - "direct_hit": "🎯", - "disappointed_face": "😞", - "diving_mask": "🤿", - "diya_lamp": "🪔", - "dizzy": "💫", - "dizzy_face": "😵", - "dna": "🧬", - "dog": "🐶", - "dog_face": "🐶", - "dollar_banknote": "💵", - "dolphin": "🐬", - "door": "🚪", - "dotted_six-pointed_star": "🔯", - "double_curly_loop": "➿", - "double_exclamation_mark": "‼", - "doughnut": "🍩", - "dove": "🕊", - "down-left_arrow": "↙", - "down-right_arrow": "↘", - "down_arrow": "⬇", - "downcast_face_with_sweat": "😓", - "downwards_button": "🔽", - "dragon": "🐉", - "dragon_face": "🐲", - "dress": "👗", - "drooling_face": "🤤", - "drop_of_blood": "🩸", - "droplet": "💧", - "drum": "🥁", - "duck": "🦆", - "dumpling": "🥟", - "dvd": "📀", - "e-mail": "📧", - "eagle": "🦅", - "ear": "👂", - "ear_dark_skin_tone": "👂🏿", - "ear_light_skin_tone": "👂🏻", - "ear_medium-dark_skin_tone": "👂🏾", - "ear_medium-light_skin_tone": "👂🏼", - "ear_medium_skin_tone": "👂🏽", - "ear_of_corn": "🌽", - "ear_with_hearing_aid": "🦻", - "egg": "🍳", - "eggplant": "🍆", - "eight-pointed_star": "✴", - "eight-spoked_asterisk": "✳", - "eight-thirty": "🕣", - "eight_o’clock": "🕗", - "eject_button": "⏏", - "electric_plug": "🔌", - "elephant": "🐘", - "eleven-thirty": "🕦", - "eleven_o’clock": "🕚", - "elf": "🧝", - "elf_dark_skin_tone": "🧝🏿", - "elf_light_skin_tone": "🧝🏻", - "elf_medium-dark_skin_tone": "🧝🏾", - "elf_medium-light_skin_tone": "🧝🏼", - "elf_medium_skin_tone": "🧝🏽", - "envelope": "✉", - "envelope_with_arrow": "📩", - "euro_banknote": "💶", - "evergreen_tree": "🌲", - "ewe": "🐑", - "exclamation_mark": "❗", - "exclamation_question_mark": "⁉", - "exploding_head": "🤯", - "expressionless_face": "😑", - "eye": "👁", - "eye_in_speech_bubble": "👁️\u200d🗨️", - "eyes": "👀", - "face_blowing_a_kiss": "😘", - "face_savoring_food": "😋", - "face_screaming_in_fear": "😱", - "face_vomiting": "🤮", - "face_with_hand_over_mouth": "🤭", - "face_with_head-bandage": "🤕", - "face_with_medical_mask": "😷", - "face_with_monocle": "🧐", - "face_with_open_mouth": "😮", - "face_with_raised_eyebrow": "🤨", - "face_with_rolling_eyes": "🙄", - "face_with_steam_from_nose": "😤", - "face_with_symbols_on_mouth": "🤬", - "face_with_tears_of_joy": "😂", - "face_with_thermometer": "🤒", - "face_with_tongue": "😛", - "face_without_mouth": "😶", - "factory": "🏭", - "fairy": "🧚", - "fairy_dark_skin_tone": "🧚🏿", - "fairy_light_skin_tone": "🧚🏻", - "fairy_medium-dark_skin_tone": "🧚🏾", - "fairy_medium-light_skin_tone": "🧚🏼", - "fairy_medium_skin_tone": "🧚🏽", - "falafel": "🧆", - "fallen_leaf": "🍂", - "family": "👪", - "family_man_boy": "👨\u200d👦", - "family_man_boy_boy": "👨\u200d👦\u200d👦", - "family_man_girl": "👨\u200d👧", - "family_man_girl_boy": "👨\u200d👧\u200d👦", - "family_man_girl_girl": "👨\u200d👧\u200d👧", - "family_man_man_boy": "👨\u200d👨\u200d👦", - "family_man_man_boy_boy": "👨\u200d👨\u200d👦\u200d👦", - "family_man_man_girl": "👨\u200d👨\u200d👧", - "family_man_man_girl_boy": "👨\u200d👨\u200d👧\u200d👦", - "family_man_man_girl_girl": "👨\u200d👨\u200d👧\u200d👧", - "family_man_woman_boy": "👨\u200d👩\u200d👦", - "family_man_woman_boy_boy": "👨\u200d👩\u200d👦\u200d👦", - "family_man_woman_girl": "👨\u200d👩\u200d👧", - "family_man_woman_girl_boy": "👨\u200d👩\u200d👧\u200d👦", - "family_man_woman_girl_girl": "👨\u200d👩\u200d👧\u200d👧", - "family_woman_boy": "👩\u200d👦", - "family_woman_boy_boy": "👩\u200d👦\u200d👦", - "family_woman_girl": "👩\u200d👧", - "family_woman_girl_boy": "👩\u200d👧\u200d👦", - "family_woman_girl_girl": "👩\u200d👧\u200d👧", - "family_woman_woman_boy": "👩\u200d👩\u200d👦", - "family_woman_woman_boy_boy": "👩\u200d👩\u200d👦\u200d👦", - "family_woman_woman_girl": "👩\u200d👩\u200d👧", - "family_woman_woman_girl_boy": "👩\u200d👩\u200d👧\u200d👦", - "family_woman_woman_girl_girl": "👩\u200d👩\u200d👧\u200d👧", - "fast-forward_button": "⏩", - "fast_down_button": "⏬", - "fast_reverse_button": "⏪", - "fast_up_button": "⏫", - "fax_machine": "📠", - "fearful_face": "😨", - "female_sign": "♀", - "ferris_wheel": "🎡", - "ferry": "⛴", - "field_hockey": "🏑", - "file_cabinet": "🗄", - "file_folder": "📁", - "film_frames": "🎞", - "film_projector": "📽", - "fire": "🔥", - "fire_extinguisher": "🧯", - "firecracker": "🧨", - "fire_engine": "🚒", - "fireworks": "🎆", - "first_quarter_moon": "🌓", - "first_quarter_moon_face": "🌛", - "fish": "🐟", - "fish_cake_with_swirl": "🍥", - "fishing_pole": "🎣", - "five-thirty": "🕠", - "five_o’clock": "🕔", - "flag_in_hole": "⛳", - "flamingo": "🦩", - "flashlight": "🔦", - "flat_shoe": "🥿", - "fleur-de-lis": "⚜", - "flexed_biceps": "💪", - "flexed_biceps_dark_skin_tone": "💪🏿", - "flexed_biceps_light_skin_tone": "💪🏻", - "flexed_biceps_medium-dark_skin_tone": "💪🏾", - "flexed_biceps_medium-light_skin_tone": "💪🏼", - "flexed_biceps_medium_skin_tone": "💪🏽", - "floppy_disk": "💾", - "flower_playing_cards": "🎴", - "flushed_face": "😳", - "flying_disc": "🥏", - "flying_saucer": "🛸", - "fog": "🌫", - "foggy": "🌁", - "folded_hands": "🙏", - "folded_hands_dark_skin_tone": "🙏🏿", - "folded_hands_light_skin_tone": "🙏🏻", - "folded_hands_medium-dark_skin_tone": "🙏🏾", - "folded_hands_medium-light_skin_tone": "🙏🏼", - "folded_hands_medium_skin_tone": "🙏🏽", - "foot": "🦶", - "footprints": "👣", - "fork_and_knife": "🍴", - "fork_and_knife_with_plate": "🍽", - "fortune_cookie": "🥠", - "fountain": "⛲", - "fountain_pen": "🖋", - "four-thirty": "🕟", - "four_leaf_clover": "🍀", - "four_o’clock": "🕓", - "fox_face": "🦊", - "framed_picture": "🖼", - "french_fries": "🍟", - "fried_shrimp": "🍤", - "frog_face": "🐸", - "front-facing_baby_chick": "🐥", - "frowning_face": "☹", - "frowning_face_with_open_mouth": "😦", - "fuel_pump": "⛽", - "full_moon": "🌕", - "full_moon_face": "🌝", - "funeral_urn": "⚱", - "game_die": "🎲", - "garlic": "🧄", - "gear": "⚙", - "gem_stone": "💎", - "genie": "🧞", - "ghost": "👻", - "giraffe": "🦒", - "girl": "👧", - "girl_dark_skin_tone": "👧🏿", - "girl_light_skin_tone": "👧🏻", - "girl_medium-dark_skin_tone": "👧🏾", - "girl_medium-light_skin_tone": "👧🏼", - "girl_medium_skin_tone": "👧🏽", - "glass_of_milk": "🥛", - "glasses": "👓", - "globe_showing_americas": "🌎", - "globe_showing_asia-australia": "🌏", - "globe_showing_europe-africa": "🌍", - "globe_with_meridians": "🌐", - "gloves": "🧤", - "glowing_star": "🌟", - "goal_net": "🥅", - "goat": "🐐", - "goblin": "👺", - "goggles": "🥽", - "gorilla": "🦍", - "graduation_cap": "🎓", - "grapes": "🍇", - "green_apple": "🍏", - "green_book": "📗", - "green_circle": "🟢", - "green_heart": "💚", - "green_salad": "🥗", - "green_square": "🟩", - "grimacing_face": "😬", - "grinning_cat_face": "😺", - "grinning_cat_face_with_smiling_eyes": "😸", - "grinning_face": "😀", - "grinning_face_with_big_eyes": "😃", - "grinning_face_with_smiling_eyes": "😄", - "grinning_face_with_sweat": "😅", - "grinning_squinting_face": "😆", - "growing_heart": "💗", - "guard": "💂", - "guard_dark_skin_tone": "💂🏿", - "guard_light_skin_tone": "💂🏻", - "guard_medium-dark_skin_tone": "💂🏾", - "guard_medium-light_skin_tone": "💂🏼", - "guard_medium_skin_tone": "💂🏽", - "guide_dog": "🦮", - "guitar": "🎸", - "hamburger": "🍔", - "hammer": "🔨", - "hammer_and_pick": "⚒", - "hammer_and_wrench": "🛠", - "hamster_face": "🐹", - "hand_with_fingers_splayed": "🖐", - "hand_with_fingers_splayed_dark_skin_tone": "🖐🏿", - "hand_with_fingers_splayed_light_skin_tone": "🖐🏻", - "hand_with_fingers_splayed_medium-dark_skin_tone": "🖐🏾", - "hand_with_fingers_splayed_medium-light_skin_tone": "🖐🏼", - "hand_with_fingers_splayed_medium_skin_tone": "🖐🏽", - "handbag": "👜", - "handshake": "🤝", - "hatching_chick": "🐣", - "headphone": "🎧", - "hear-no-evil_monkey": "🙉", - "heart_decoration": "💟", - "heart_suit": "♥", - "heart_with_arrow": "💘", - "heart_with_ribbon": "💝", - "heavy_check_mark": "✔", - "heavy_division_sign": "➗", - "heavy_dollar_sign": "💲", - "heavy_heart_exclamation": "❣", - "heavy_large_circle": "⭕", - "heavy_minus_sign": "➖", - "heavy_multiplication_x": "✖", - "heavy_plus_sign": "➕", - "hedgehog": "🦔", - "helicopter": "🚁", - "herb": "🌿", - "hibiscus": "🌺", - "high-heeled_shoe": "👠", - "high-speed_train": "🚄", - "high_voltage": "⚡", - "hiking_boot": "🥾", - "hindu_temple": "🛕", - "hippopotamus": "🦛", - "hole": "🕳", - "honey_pot": "🍯", - "honeybee": "🐝", - "horizontal_traffic_light": "🚥", - "horse": "🐴", - "horse_face": "🐴", - "horse_racing": "🏇", - "horse_racing_dark_skin_tone": "🏇🏿", - "horse_racing_light_skin_tone": "🏇🏻", - "horse_racing_medium-dark_skin_tone": "🏇🏾", - "horse_racing_medium-light_skin_tone": "🏇🏼", - "horse_racing_medium_skin_tone": "🏇🏽", - "hospital": "🏥", - "hot_beverage": "☕", - "hot_dog": "🌭", - "hot_face": "🥵", - "hot_pepper": "🌶", - "hot_springs": "♨", - "hotel": "🏨", - "hourglass_done": "⌛", - "hourglass_not_done": "⏳", - "house": "🏠", - "house_with_garden": "🏡", - "houses": "🏘", - "hugging_face": "🤗", - "hundred_points": "💯", - "hushed_face": "😯", - "ice": "🧊", - "ice_cream": "🍨", - "ice_hockey": "🏒", - "ice_skate": "⛸", - "inbox_tray": "📥", - "incoming_envelope": "📨", - "index_pointing_up": "☝", - "index_pointing_up_dark_skin_tone": "☝🏿", - "index_pointing_up_light_skin_tone": "☝🏻", - "index_pointing_up_medium-dark_skin_tone": "☝🏾", - "index_pointing_up_medium-light_skin_tone": "☝🏼", - "index_pointing_up_medium_skin_tone": "☝🏽", - "infinity": "♾", - "information": "ℹ", - "input_latin_letters": "🔤", - "input_latin_lowercase": "🔡", - "input_latin_uppercase": "🔠", - "input_numbers": "🔢", - "input_symbols": "🔣", - "jack-o-lantern": "🎃", - "jeans": "👖", - "jigsaw": "🧩", - "joker": "🃏", - "joystick": "🕹", - "kaaba": "🕋", - "kangaroo": "🦘", - "key": "🔑", - "keyboard": "⌨", - "keycap_#": "#️⃣", - "keycap_*": "*️⃣", - "keycap_0": "0️⃣", - "keycap_1": "1️⃣", - "keycap_10": "🔟", - "keycap_2": "2️⃣", - "keycap_3": "3️⃣", - "keycap_4": "4️⃣", - "keycap_5": "5️⃣", - "keycap_6": "6️⃣", - "keycap_7": "7️⃣", - "keycap_8": "8️⃣", - "keycap_9": "9️⃣", - "kick_scooter": "🛴", - "kimono": "👘", - "kiss": "💋", - "kiss_man_man": "👨\u200d❤️\u200d💋\u200d👨", - "kiss_mark": "💋", - "kiss_woman_man": "👩\u200d❤️\u200d💋\u200d👨", - "kiss_woman_woman": "👩\u200d❤️\u200d💋\u200d👩", - "kissing_cat_face": "😽", - "kissing_face": "😗", - "kissing_face_with_closed_eyes": "😚", - "kissing_face_with_smiling_eyes": "😙", - "kitchen_knife": "🔪", - "kite": "🪁", - "kiwi_fruit": "🥝", - "koala": "🐨", - "lab_coat": "🥼", - "label": "🏷", - "lacrosse": "🥍", - "lady_beetle": "🐞", - "laptop_computer": "💻", - "large_blue_diamond": "🔷", - "large_orange_diamond": "🔶", - "last_quarter_moon": "🌗", - "last_quarter_moon_face": "🌜", - "last_track_button": "⏮", - "latin_cross": "✝", - "leaf_fluttering_in_wind": "🍃", - "leafy_green": "🥬", - "ledger": "📒", - "left-facing_fist": "🤛", - "left-facing_fist_dark_skin_tone": "🤛🏿", - "left-facing_fist_light_skin_tone": "🤛🏻", - "left-facing_fist_medium-dark_skin_tone": "🤛🏾", - "left-facing_fist_medium-light_skin_tone": "🤛🏼", - "left-facing_fist_medium_skin_tone": "🤛🏽", - "left-right_arrow": "↔", - "left_arrow": "⬅", - "left_arrow_curving_right": "↪", - "left_luggage": "🛅", - "left_speech_bubble": "🗨", - "leg": "🦵", - "lemon": "🍋", - "leopard": "🐆", - "level_slider": "🎚", - "light_bulb": "💡", - "light_rail": "🚈", - "link": "🔗", - "linked_paperclips": "🖇", - "lion_face": "🦁", - "lipstick": "💄", - "litter_in_bin_sign": "🚮", - "lizard": "🦎", - "llama": "🦙", - "lobster": "🦞", - "locked": "🔒", - "locked_with_key": "🔐", - "locked_with_pen": "🔏", - "locomotive": "🚂", - "lollipop": "🍭", - "lotion_bottle": "🧴", - "loudly_crying_face": "😭", - "loudspeaker": "📢", - "love-you_gesture": "🤟", - "love-you_gesture_dark_skin_tone": "🤟🏿", - "love-you_gesture_light_skin_tone": "🤟🏻", - "love-you_gesture_medium-dark_skin_tone": "🤟🏾", - "love-you_gesture_medium-light_skin_tone": "🤟🏼", - "love-you_gesture_medium_skin_tone": "🤟🏽", - "love_hotel": "🏩", - "love_letter": "💌", - "luggage": "🧳", - "lying_face": "🤥", - "mage": "🧙", - "mage_dark_skin_tone": "🧙🏿", - "mage_light_skin_tone": "🧙🏻", - "mage_medium-dark_skin_tone": "🧙🏾", - "mage_medium-light_skin_tone": "🧙🏼", - "mage_medium_skin_tone": "🧙🏽", - "magnet": "🧲", - "magnifying_glass_tilted_left": "🔍", - "magnifying_glass_tilted_right": "🔎", - "mahjong_red_dragon": "🀄", - "male_sign": "♂", - "man": "👨", - "man_and_woman_holding_hands": "👫", - "man_artist": "👨\u200d🎨", - "man_artist_dark_skin_tone": "👨🏿\u200d🎨", - "man_artist_light_skin_tone": "👨🏻\u200d🎨", - "man_artist_medium-dark_skin_tone": "👨🏾\u200d🎨", - "man_artist_medium-light_skin_tone": "👨🏼\u200d🎨", - "man_artist_medium_skin_tone": "👨🏽\u200d🎨", - "man_astronaut": "👨\u200d🚀", - "man_astronaut_dark_skin_tone": "👨🏿\u200d🚀", - "man_astronaut_light_skin_tone": "👨🏻\u200d🚀", - "man_astronaut_medium-dark_skin_tone": "👨🏾\u200d🚀", - "man_astronaut_medium-light_skin_tone": "👨🏼\u200d🚀", - "man_astronaut_medium_skin_tone": "👨🏽\u200d🚀", - "man_biking": "🚴\u200d♂️", - "man_biking_dark_skin_tone": "🚴🏿\u200d♂️", - "man_biking_light_skin_tone": "🚴🏻\u200d♂️", - "man_biking_medium-dark_skin_tone": "🚴🏾\u200d♂️", - "man_biking_medium-light_skin_tone": "🚴🏼\u200d♂️", - "man_biking_medium_skin_tone": "🚴🏽\u200d♂️", - "man_bouncing_ball": "⛹️\u200d♂️", - "man_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♂️", - "man_bouncing_ball_light_skin_tone": "⛹🏻\u200d♂️", - "man_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♂️", - "man_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♂️", - "man_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♂️", - "man_bowing": "🙇\u200d♂️", - "man_bowing_dark_skin_tone": "🙇🏿\u200d♂️", - "man_bowing_light_skin_tone": "🙇🏻\u200d♂️", - "man_bowing_medium-dark_skin_tone": "🙇🏾\u200d♂️", - "man_bowing_medium-light_skin_tone": "🙇🏼\u200d♂️", - "man_bowing_medium_skin_tone": "🙇🏽\u200d♂️", - "man_cartwheeling": "🤸\u200d♂️", - "man_cartwheeling_dark_skin_tone": "🤸🏿\u200d♂️", - "man_cartwheeling_light_skin_tone": "🤸🏻\u200d♂️", - "man_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♂️", - "man_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♂️", - "man_cartwheeling_medium_skin_tone": "🤸🏽\u200d♂️", - "man_climbing": "🧗\u200d♂️", - "man_climbing_dark_skin_tone": "🧗🏿\u200d♂️", - "man_climbing_light_skin_tone": "🧗🏻\u200d♂️", - "man_climbing_medium-dark_skin_tone": "🧗🏾\u200d♂️", - "man_climbing_medium-light_skin_tone": "🧗🏼\u200d♂️", - "man_climbing_medium_skin_tone": "🧗🏽\u200d♂️", - "man_construction_worker": "👷\u200d♂️", - "man_construction_worker_dark_skin_tone": "👷🏿\u200d♂️", - "man_construction_worker_light_skin_tone": "👷🏻\u200d♂️", - "man_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♂️", - "man_construction_worker_medium-light_skin_tone": "👷🏼\u200d♂️", - "man_construction_worker_medium_skin_tone": "👷🏽\u200d♂️", - "man_cook": "👨\u200d🍳", - "man_cook_dark_skin_tone": "👨🏿\u200d🍳", - "man_cook_light_skin_tone": "👨🏻\u200d🍳", - "man_cook_medium-dark_skin_tone": "👨🏾\u200d🍳", - "man_cook_medium-light_skin_tone": "👨🏼\u200d🍳", - "man_cook_medium_skin_tone": "👨🏽\u200d🍳", - "man_dancing": "🕺", - "man_dancing_dark_skin_tone": "🕺🏿", - "man_dancing_light_skin_tone": "🕺🏻", - "man_dancing_medium-dark_skin_tone": "🕺🏾", - "man_dancing_medium-light_skin_tone": "🕺🏼", - "man_dancing_medium_skin_tone": "🕺🏽", - "man_dark_skin_tone": "👨🏿", - "man_detective": "🕵️\u200d♂️", - "man_detective_dark_skin_tone": "🕵🏿\u200d♂️", - "man_detective_light_skin_tone": "🕵🏻\u200d♂️", - "man_detective_medium-dark_skin_tone": "🕵🏾\u200d♂️", - "man_detective_medium-light_skin_tone": "🕵🏼\u200d♂️", - "man_detective_medium_skin_tone": "🕵🏽\u200d♂️", - "man_elf": "🧝\u200d♂️", - "man_elf_dark_skin_tone": "🧝🏿\u200d♂️", - "man_elf_light_skin_tone": "🧝🏻\u200d♂️", - "man_elf_medium-dark_skin_tone": "🧝🏾\u200d♂️", - "man_elf_medium-light_skin_tone": "🧝🏼\u200d♂️", - "man_elf_medium_skin_tone": "🧝🏽\u200d♂️", - "man_facepalming": "🤦\u200d♂️", - "man_facepalming_dark_skin_tone": "🤦🏿\u200d♂️", - "man_facepalming_light_skin_tone": "🤦🏻\u200d♂️", - "man_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♂️", - "man_facepalming_medium-light_skin_tone": "🤦🏼\u200d♂️", - "man_facepalming_medium_skin_tone": "🤦🏽\u200d♂️", - "man_factory_worker": "👨\u200d🏭", - "man_factory_worker_dark_skin_tone": "👨🏿\u200d🏭", - "man_factory_worker_light_skin_tone": "👨🏻\u200d🏭", - "man_factory_worker_medium-dark_skin_tone": "👨🏾\u200d🏭", - "man_factory_worker_medium-light_skin_tone": "👨🏼\u200d🏭", - "man_factory_worker_medium_skin_tone": "👨🏽\u200d🏭", - "man_fairy": "🧚\u200d♂️", - "man_fairy_dark_skin_tone": "🧚🏿\u200d♂️", - "man_fairy_light_skin_tone": "🧚🏻\u200d♂️", - "man_fairy_medium-dark_skin_tone": "🧚🏾\u200d♂️", - "man_fairy_medium-light_skin_tone": "🧚🏼\u200d♂️", - "man_fairy_medium_skin_tone": "🧚🏽\u200d♂️", - "man_farmer": "👨\u200d🌾", - "man_farmer_dark_skin_tone": "👨🏿\u200d🌾", - "man_farmer_light_skin_tone": "👨🏻\u200d🌾", - "man_farmer_medium-dark_skin_tone": "👨🏾\u200d🌾", - "man_farmer_medium-light_skin_tone": "👨🏼\u200d🌾", - "man_farmer_medium_skin_tone": "👨🏽\u200d🌾", - "man_firefighter": "👨\u200d🚒", - "man_firefighter_dark_skin_tone": "👨🏿\u200d🚒", - "man_firefighter_light_skin_tone": "👨🏻\u200d🚒", - "man_firefighter_medium-dark_skin_tone": "👨🏾\u200d🚒", - "man_firefighter_medium-light_skin_tone": "👨🏼\u200d🚒", - "man_firefighter_medium_skin_tone": "👨🏽\u200d🚒", - "man_frowning": "🙍\u200d♂️", - "man_frowning_dark_skin_tone": "🙍🏿\u200d♂️", - "man_frowning_light_skin_tone": "🙍🏻\u200d♂️", - "man_frowning_medium-dark_skin_tone": "🙍🏾\u200d♂️", - "man_frowning_medium-light_skin_tone": "🙍🏼\u200d♂️", - "man_frowning_medium_skin_tone": "🙍🏽\u200d♂️", - "man_genie": "🧞\u200d♂️", - "man_gesturing_no": "🙅\u200d♂️", - "man_gesturing_no_dark_skin_tone": "🙅🏿\u200d♂️", - "man_gesturing_no_light_skin_tone": "🙅🏻\u200d♂️", - "man_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♂️", - "man_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♂️", - "man_gesturing_no_medium_skin_tone": "🙅🏽\u200d♂️", - "man_gesturing_ok": "🙆\u200d♂️", - "man_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♂️", - "man_gesturing_ok_light_skin_tone": "🙆🏻\u200d♂️", - "man_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♂️", - "man_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♂️", - "man_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♂️", - "man_getting_haircut": "💇\u200d♂️", - "man_getting_haircut_dark_skin_tone": "💇🏿\u200d♂️", - "man_getting_haircut_light_skin_tone": "💇🏻\u200d♂️", - "man_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♂️", - "man_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♂️", - "man_getting_haircut_medium_skin_tone": "💇🏽\u200d♂️", - "man_getting_massage": "💆\u200d♂️", - "man_getting_massage_dark_skin_tone": "💆🏿\u200d♂️", - "man_getting_massage_light_skin_tone": "💆🏻\u200d♂️", - "man_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♂️", - "man_getting_massage_medium-light_skin_tone": "💆🏼\u200d♂️", - "man_getting_massage_medium_skin_tone": "💆🏽\u200d♂️", - "man_golfing": "🏌️\u200d♂️", - "man_golfing_dark_skin_tone": "🏌🏿\u200d♂️", - "man_golfing_light_skin_tone": "🏌🏻\u200d♂️", - "man_golfing_medium-dark_skin_tone": "🏌🏾\u200d♂️", - "man_golfing_medium-light_skin_tone": "🏌🏼\u200d♂️", - "man_golfing_medium_skin_tone": "🏌🏽\u200d♂️", - "man_guard": "💂\u200d♂️", - "man_guard_dark_skin_tone": "💂🏿\u200d♂️", - "man_guard_light_skin_tone": "💂🏻\u200d♂️", - "man_guard_medium-dark_skin_tone": "💂🏾\u200d♂️", - "man_guard_medium-light_skin_tone": "💂🏼\u200d♂️", - "man_guard_medium_skin_tone": "💂🏽\u200d♂️", - "man_health_worker": "👨\u200d⚕️", - "man_health_worker_dark_skin_tone": "👨🏿\u200d⚕️", - "man_health_worker_light_skin_tone": "👨🏻\u200d⚕️", - "man_health_worker_medium-dark_skin_tone": "👨🏾\u200d⚕️", - "man_health_worker_medium-light_skin_tone": "👨🏼\u200d⚕️", - "man_health_worker_medium_skin_tone": "👨🏽\u200d⚕️", - "man_in_lotus_position": "🧘\u200d♂️", - "man_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♂️", - "man_in_lotus_position_light_skin_tone": "🧘🏻\u200d♂️", - "man_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♂️", - "man_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♂️", - "man_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♂️", - "man_in_manual_wheelchair": "👨\u200d🦽", - "man_in_motorized_wheelchair": "👨\u200d🦼", - "man_in_steamy_room": "🧖\u200d♂️", - "man_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♂️", - "man_in_steamy_room_light_skin_tone": "🧖🏻\u200d♂️", - "man_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♂️", - "man_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♂️", - "man_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♂️", - "man_in_suit_levitating": "🕴", - "man_in_suit_levitating_dark_skin_tone": "🕴🏿", - "man_in_suit_levitating_light_skin_tone": "🕴🏻", - "man_in_suit_levitating_medium-dark_skin_tone": "🕴🏾", - "man_in_suit_levitating_medium-light_skin_tone": "🕴🏼", - "man_in_suit_levitating_medium_skin_tone": "🕴🏽", - "man_in_tuxedo": "🤵", - "man_in_tuxedo_dark_skin_tone": "🤵🏿", - "man_in_tuxedo_light_skin_tone": "🤵🏻", - "man_in_tuxedo_medium-dark_skin_tone": "🤵🏾", - "man_in_tuxedo_medium-light_skin_tone": "🤵🏼", - "man_in_tuxedo_medium_skin_tone": "🤵🏽", - "man_judge": "👨\u200d⚖️", - "man_judge_dark_skin_tone": "👨🏿\u200d⚖️", - "man_judge_light_skin_tone": "👨🏻\u200d⚖️", - "man_judge_medium-dark_skin_tone": "👨🏾\u200d⚖️", - "man_judge_medium-light_skin_tone": "👨🏼\u200d⚖️", - "man_judge_medium_skin_tone": "👨🏽\u200d⚖️", - "man_juggling": "🤹\u200d♂️", - "man_juggling_dark_skin_tone": "🤹🏿\u200d♂️", - "man_juggling_light_skin_tone": "🤹🏻\u200d♂️", - "man_juggling_medium-dark_skin_tone": "🤹🏾\u200d♂️", - "man_juggling_medium-light_skin_tone": "🤹🏼\u200d♂️", - "man_juggling_medium_skin_tone": "🤹🏽\u200d♂️", - "man_lifting_weights": "🏋️\u200d♂️", - "man_lifting_weights_dark_skin_tone": "🏋🏿\u200d♂️", - "man_lifting_weights_light_skin_tone": "🏋🏻\u200d♂️", - "man_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♂️", - "man_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♂️", - "man_lifting_weights_medium_skin_tone": "🏋🏽\u200d♂️", - "man_light_skin_tone": "👨🏻", - "man_mage": "🧙\u200d♂️", - "man_mage_dark_skin_tone": "🧙🏿\u200d♂️", - "man_mage_light_skin_tone": "🧙🏻\u200d♂️", - "man_mage_medium-dark_skin_tone": "🧙🏾\u200d♂️", - "man_mage_medium-light_skin_tone": "🧙🏼\u200d♂️", - "man_mage_medium_skin_tone": "🧙🏽\u200d♂️", - "man_mechanic": "👨\u200d🔧", - "man_mechanic_dark_skin_tone": "👨🏿\u200d🔧", - "man_mechanic_light_skin_tone": "👨🏻\u200d🔧", - "man_mechanic_medium-dark_skin_tone": "👨🏾\u200d🔧", - "man_mechanic_medium-light_skin_tone": "👨🏼\u200d🔧", - "man_mechanic_medium_skin_tone": "👨🏽\u200d🔧", - "man_medium-dark_skin_tone": "👨🏾", - "man_medium-light_skin_tone": "👨🏼", - "man_medium_skin_tone": "👨🏽", - "man_mountain_biking": "🚵\u200d♂️", - "man_mountain_biking_dark_skin_tone": "🚵🏿\u200d♂️", - "man_mountain_biking_light_skin_tone": "🚵🏻\u200d♂️", - "man_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♂️", - "man_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♂️", - "man_mountain_biking_medium_skin_tone": "🚵🏽\u200d♂️", - "man_office_worker": "👨\u200d💼", - "man_office_worker_dark_skin_tone": "👨🏿\u200d💼", - "man_office_worker_light_skin_tone": "👨🏻\u200d💼", - "man_office_worker_medium-dark_skin_tone": "👨🏾\u200d💼", - "man_office_worker_medium-light_skin_tone": "👨🏼\u200d💼", - "man_office_worker_medium_skin_tone": "👨🏽\u200d💼", - "man_pilot": "👨\u200d✈️", - "man_pilot_dark_skin_tone": "👨🏿\u200d✈️", - "man_pilot_light_skin_tone": "👨🏻\u200d✈️", - "man_pilot_medium-dark_skin_tone": "👨🏾\u200d✈️", - "man_pilot_medium-light_skin_tone": "👨🏼\u200d✈️", - "man_pilot_medium_skin_tone": "👨🏽\u200d✈️", - "man_playing_handball": "🤾\u200d♂️", - "man_playing_handball_dark_skin_tone": "🤾🏿\u200d♂️", - "man_playing_handball_light_skin_tone": "🤾🏻\u200d♂️", - "man_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♂️", - "man_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♂️", - "man_playing_handball_medium_skin_tone": "🤾🏽\u200d♂️", - "man_playing_water_polo": "🤽\u200d♂️", - "man_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♂️", - "man_playing_water_polo_light_skin_tone": "🤽🏻\u200d♂️", - "man_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♂️", - "man_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♂️", - "man_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♂️", - "man_police_officer": "👮\u200d♂️", - "man_police_officer_dark_skin_tone": "👮🏿\u200d♂️", - "man_police_officer_light_skin_tone": "👮🏻\u200d♂️", - "man_police_officer_medium-dark_skin_tone": "👮🏾\u200d♂️", - "man_police_officer_medium-light_skin_tone": "👮🏼\u200d♂️", - "man_police_officer_medium_skin_tone": "👮🏽\u200d♂️", - "man_pouting": "🙎\u200d♂️", - "man_pouting_dark_skin_tone": "🙎🏿\u200d♂️", - "man_pouting_light_skin_tone": "🙎🏻\u200d♂️", - "man_pouting_medium-dark_skin_tone": "🙎🏾\u200d♂️", - "man_pouting_medium-light_skin_tone": "🙎🏼\u200d♂️", - "man_pouting_medium_skin_tone": "🙎🏽\u200d♂️", - "man_raising_hand": "🙋\u200d♂️", - "man_raising_hand_dark_skin_tone": "🙋🏿\u200d♂️", - "man_raising_hand_light_skin_tone": "🙋🏻\u200d♂️", - "man_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♂️", - "man_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♂️", - "man_raising_hand_medium_skin_tone": "🙋🏽\u200d♂️", - "man_rowing_boat": "🚣\u200d♂️", - "man_rowing_boat_dark_skin_tone": "🚣🏿\u200d♂️", - "man_rowing_boat_light_skin_tone": "🚣🏻\u200d♂️", - "man_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♂️", - "man_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♂️", - "man_rowing_boat_medium_skin_tone": "🚣🏽\u200d♂️", - "man_running": "🏃\u200d♂️", - "man_running_dark_skin_tone": "🏃🏿\u200d♂️", - "man_running_light_skin_tone": "🏃🏻\u200d♂️", - "man_running_medium-dark_skin_tone": "🏃🏾\u200d♂️", - "man_running_medium-light_skin_tone": "🏃🏼\u200d♂️", - "man_running_medium_skin_tone": "🏃🏽\u200d♂️", - "man_scientist": "👨\u200d🔬", - "man_scientist_dark_skin_tone": "👨🏿\u200d🔬", - "man_scientist_light_skin_tone": "👨🏻\u200d🔬", - "man_scientist_medium-dark_skin_tone": "👨🏾\u200d🔬", - "man_scientist_medium-light_skin_tone": "👨🏼\u200d🔬", - "man_scientist_medium_skin_tone": "👨🏽\u200d🔬", - "man_shrugging": "🤷\u200d♂️", - "man_shrugging_dark_skin_tone": "🤷🏿\u200d♂️", - "man_shrugging_light_skin_tone": "🤷🏻\u200d♂️", - "man_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♂️", - "man_shrugging_medium-light_skin_tone": "🤷🏼\u200d♂️", - "man_shrugging_medium_skin_tone": "🤷🏽\u200d♂️", - "man_singer": "👨\u200d🎤", - "man_singer_dark_skin_tone": "👨🏿\u200d🎤", - "man_singer_light_skin_tone": "👨🏻\u200d🎤", - "man_singer_medium-dark_skin_tone": "👨🏾\u200d🎤", - "man_singer_medium-light_skin_tone": "👨🏼\u200d🎤", - "man_singer_medium_skin_tone": "👨🏽\u200d🎤", - "man_student": "👨\u200d🎓", - "man_student_dark_skin_tone": "👨🏿\u200d🎓", - "man_student_light_skin_tone": "👨🏻\u200d🎓", - "man_student_medium-dark_skin_tone": "👨🏾\u200d🎓", - "man_student_medium-light_skin_tone": "👨🏼\u200d🎓", - "man_student_medium_skin_tone": "👨🏽\u200d🎓", - "man_surfing": "🏄\u200d♂️", - "man_surfing_dark_skin_tone": "🏄🏿\u200d♂️", - "man_surfing_light_skin_tone": "🏄🏻\u200d♂️", - "man_surfing_medium-dark_skin_tone": "🏄🏾\u200d♂️", - "man_surfing_medium-light_skin_tone": "🏄🏼\u200d♂️", - "man_surfing_medium_skin_tone": "🏄🏽\u200d♂️", - "man_swimming": "🏊\u200d♂️", - "man_swimming_dark_skin_tone": "🏊🏿\u200d♂️", - "man_swimming_light_skin_tone": "🏊🏻\u200d♂️", - "man_swimming_medium-dark_skin_tone": "🏊🏾\u200d♂️", - "man_swimming_medium-light_skin_tone": "🏊🏼\u200d♂️", - "man_swimming_medium_skin_tone": "🏊🏽\u200d♂️", - "man_teacher": "👨\u200d🏫", - "man_teacher_dark_skin_tone": "👨🏿\u200d🏫", - "man_teacher_light_skin_tone": "👨🏻\u200d🏫", - "man_teacher_medium-dark_skin_tone": "👨🏾\u200d🏫", - "man_teacher_medium-light_skin_tone": "👨🏼\u200d🏫", - "man_teacher_medium_skin_tone": "👨🏽\u200d🏫", - "man_technologist": "👨\u200d💻", - "man_technologist_dark_skin_tone": "👨🏿\u200d💻", - "man_technologist_light_skin_tone": "👨🏻\u200d💻", - "man_technologist_medium-dark_skin_tone": "👨🏾\u200d💻", - "man_technologist_medium-light_skin_tone": "👨🏼\u200d💻", - "man_technologist_medium_skin_tone": "👨🏽\u200d💻", - "man_tipping_hand": "💁\u200d♂️", - "man_tipping_hand_dark_skin_tone": "💁🏿\u200d♂️", - "man_tipping_hand_light_skin_tone": "💁🏻\u200d♂️", - "man_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♂️", - "man_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♂️", - "man_tipping_hand_medium_skin_tone": "💁🏽\u200d♂️", - "man_vampire": "🧛\u200d♂️", - "man_vampire_dark_skin_tone": "🧛🏿\u200d♂️", - "man_vampire_light_skin_tone": "🧛🏻\u200d♂️", - "man_vampire_medium-dark_skin_tone": "🧛🏾\u200d♂️", - "man_vampire_medium-light_skin_tone": "🧛🏼\u200d♂️", - "man_vampire_medium_skin_tone": "🧛🏽\u200d♂️", - "man_walking": "🚶\u200d♂️", - "man_walking_dark_skin_tone": "🚶🏿\u200d♂️", - "man_walking_light_skin_tone": "🚶🏻\u200d♂️", - "man_walking_medium-dark_skin_tone": "🚶🏾\u200d♂️", - "man_walking_medium-light_skin_tone": "🚶🏼\u200d♂️", - "man_walking_medium_skin_tone": "🚶🏽\u200d♂️", - "man_wearing_turban": "👳\u200d♂️", - "man_wearing_turban_dark_skin_tone": "👳🏿\u200d♂️", - "man_wearing_turban_light_skin_tone": "👳🏻\u200d♂️", - "man_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♂️", - "man_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♂️", - "man_wearing_turban_medium_skin_tone": "👳🏽\u200d♂️", - "man_with_probing_cane": "👨\u200d🦯", - "man_with_chinese_cap": "👲", - "man_with_chinese_cap_dark_skin_tone": "👲🏿", - "man_with_chinese_cap_light_skin_tone": "👲🏻", - "man_with_chinese_cap_medium-dark_skin_tone": "👲🏾", - "man_with_chinese_cap_medium-light_skin_tone": "👲🏼", - "man_with_chinese_cap_medium_skin_tone": "👲🏽", - "man_zombie": "🧟\u200d♂️", - "mango": "🥭", - "mantelpiece_clock": "🕰", - "manual_wheelchair": "🦽", - "man’s_shoe": "👞", - "map_of_japan": "🗾", - "maple_leaf": "🍁", - "martial_arts_uniform": "🥋", - "mate": "🧉", - "meat_on_bone": "🍖", - "mechanical_arm": "🦾", - "mechanical_leg": "🦿", - "medical_symbol": "⚕", - "megaphone": "📣", - "melon": "🍈", - "memo": "📝", - "men_with_bunny_ears": "👯\u200d♂️", - "men_wrestling": "🤼\u200d♂️", - "menorah": "🕎", - "men’s_room": "🚹", - "mermaid": "🧜\u200d♀️", - "mermaid_dark_skin_tone": "🧜🏿\u200d♀️", - "mermaid_light_skin_tone": "🧜🏻\u200d♀️", - "mermaid_medium-dark_skin_tone": "🧜🏾\u200d♀️", - "mermaid_medium-light_skin_tone": "🧜🏼\u200d♀️", - "mermaid_medium_skin_tone": "🧜🏽\u200d♀️", - "merman": "🧜\u200d♂️", - "merman_dark_skin_tone": "🧜🏿\u200d♂️", - "merman_light_skin_tone": "🧜🏻\u200d♂️", - "merman_medium-dark_skin_tone": "🧜🏾\u200d♂️", - "merman_medium-light_skin_tone": "🧜🏼\u200d♂️", - "merman_medium_skin_tone": "🧜🏽\u200d♂️", - "merperson": "🧜", - "merperson_dark_skin_tone": "🧜🏿", - "merperson_light_skin_tone": "🧜🏻", - "merperson_medium-dark_skin_tone": "🧜🏾", - "merperson_medium-light_skin_tone": "🧜🏼", - "merperson_medium_skin_tone": "🧜🏽", - "metro": "🚇", - "microbe": "🦠", - "microphone": "🎤", - "microscope": "🔬", - "middle_finger": "🖕", - "middle_finger_dark_skin_tone": "🖕🏿", - "middle_finger_light_skin_tone": "🖕🏻", - "middle_finger_medium-dark_skin_tone": "🖕🏾", - "middle_finger_medium-light_skin_tone": "🖕🏼", - "middle_finger_medium_skin_tone": "🖕🏽", - "military_medal": "🎖", - "milky_way": "🌌", - "minibus": "🚐", - "moai": "🗿", - "mobile_phone": "📱", - "mobile_phone_off": "📴", - "mobile_phone_with_arrow": "📲", - "money-mouth_face": "🤑", - "money_bag": "💰", - "money_with_wings": "💸", - "monkey": "🐒", - "monkey_face": "🐵", - "monorail": "🚝", - "moon_cake": "🥮", - "moon_viewing_ceremony": "🎑", - "mosque": "🕌", - "mosquito": "🦟", - "motor_boat": "🛥", - "motor_scooter": "🛵", - "motorcycle": "🏍", - "motorized_wheelchair": "🦼", - "motorway": "🛣", - "mount_fuji": "🗻", - "mountain": "⛰", - "mountain_cableway": "🚠", - "mountain_railway": "🚞", - "mouse": "🐭", - "mouse_face": "🐭", - "mouth": "👄", - "movie_camera": "🎥", - "mushroom": "🍄", - "musical_keyboard": "🎹", - "musical_note": "🎵", - "musical_notes": "🎶", - "musical_score": "🎼", - "muted_speaker": "🔇", - "nail_polish": "💅", - "nail_polish_dark_skin_tone": "💅🏿", - "nail_polish_light_skin_tone": "💅🏻", - "nail_polish_medium-dark_skin_tone": "💅🏾", - "nail_polish_medium-light_skin_tone": "💅🏼", - "nail_polish_medium_skin_tone": "💅🏽", - "name_badge": "📛", - "national_park": "🏞", - "nauseated_face": "🤢", - "nazar_amulet": "🧿", - "necktie": "👔", - "nerd_face": "🤓", - "neutral_face": "😐", - "new_moon": "🌑", - "new_moon_face": "🌚", - "newspaper": "📰", - "next_track_button": "⏭", - "night_with_stars": "🌃", - "nine-thirty": "🕤", - "nine_o’clock": "🕘", - "no_bicycles": "🚳", - "no_entry": "⛔", - "no_littering": "🚯", - "no_mobile_phones": "📵", - "no_one_under_eighteen": "🔞", - "no_pedestrians": "🚷", - "no_smoking": "🚭", - "non-potable_water": "🚱", - "nose": "👃", - "nose_dark_skin_tone": "👃🏿", - "nose_light_skin_tone": "👃🏻", - "nose_medium-dark_skin_tone": "👃🏾", - "nose_medium-light_skin_tone": "👃🏼", - "nose_medium_skin_tone": "👃🏽", - "notebook": "📓", - "notebook_with_decorative_cover": "📔", - "nut_and_bolt": "🔩", - "octopus": "🐙", - "oden": "🍢", - "office_building": "🏢", - "ogre": "👹", - "oil_drum": "🛢", - "old_key": "🗝", - "old_man": "👴", - "old_man_dark_skin_tone": "👴🏿", - "old_man_light_skin_tone": "👴🏻", - "old_man_medium-dark_skin_tone": "👴🏾", - "old_man_medium-light_skin_tone": "👴🏼", - "old_man_medium_skin_tone": "👴🏽", - "old_woman": "👵", - "old_woman_dark_skin_tone": "👵🏿", - "old_woman_light_skin_tone": "👵🏻", - "old_woman_medium-dark_skin_tone": "👵🏾", - "old_woman_medium-light_skin_tone": "👵🏼", - "old_woman_medium_skin_tone": "👵🏽", - "older_adult": "🧓", - "older_adult_dark_skin_tone": "🧓🏿", - "older_adult_light_skin_tone": "🧓🏻", - "older_adult_medium-dark_skin_tone": "🧓🏾", - "older_adult_medium-light_skin_tone": "🧓🏼", - "older_adult_medium_skin_tone": "🧓🏽", - "om": "🕉", - "oncoming_automobile": "🚘", - "oncoming_bus": "🚍", - "oncoming_fist": "👊", - "oncoming_fist_dark_skin_tone": "👊🏿", - "oncoming_fist_light_skin_tone": "👊🏻", - "oncoming_fist_medium-dark_skin_tone": "👊🏾", - "oncoming_fist_medium-light_skin_tone": "👊🏼", - "oncoming_fist_medium_skin_tone": "👊🏽", - "oncoming_police_car": "🚔", - "oncoming_taxi": "🚖", - "one-piece_swimsuit": "🩱", - "one-thirty": "🕜", - "one_o’clock": "🕐", - "onion": "🧅", - "open_book": "📖", - "open_file_folder": "📂", - "open_hands": "👐", - "open_hands_dark_skin_tone": "👐🏿", - "open_hands_light_skin_tone": "👐🏻", - "open_hands_medium-dark_skin_tone": "👐🏾", - "open_hands_medium-light_skin_tone": "👐🏼", - "open_hands_medium_skin_tone": "👐🏽", - "open_mailbox_with_lowered_flag": "📭", - "open_mailbox_with_raised_flag": "📬", - "optical_disk": "💿", - "orange_book": "📙", - "orange_circle": "🟠", - "orange_heart": "🧡", - "orange_square": "🟧", - "orangutan": "🦧", - "orthodox_cross": "☦", - "otter": "🦦", - "outbox_tray": "📤", - "owl": "🦉", - "ox": "🐂", - "oyster": "🦪", - "package": "📦", - "page_facing_up": "📄", - "page_with_curl": "📃", - "pager": "📟", - "paintbrush": "🖌", - "palm_tree": "🌴", - "palms_up_together": "🤲", - "palms_up_together_dark_skin_tone": "🤲🏿", - "palms_up_together_light_skin_tone": "🤲🏻", - "palms_up_together_medium-dark_skin_tone": "🤲🏾", - "palms_up_together_medium-light_skin_tone": "🤲🏼", - "palms_up_together_medium_skin_tone": "🤲🏽", - "pancakes": "🥞", - "panda_face": "🐼", - "paperclip": "📎", - "parrot": "🦜", - "part_alternation_mark": "〽", - "party_popper": "🎉", - "partying_face": "🥳", - "passenger_ship": "🛳", - "passport_control": "🛂", - "pause_button": "⏸", - "paw_prints": "🐾", - "peace_symbol": "☮", - "peach": "🍑", - "peacock": "🦚", - "peanuts": "🥜", - "pear": "🍐", - "pen": "🖊", - "pencil": "📝", - "penguin": "🐧", - "pensive_face": "😔", - "people_holding_hands": "🧑\u200d🤝\u200d🧑", - "people_with_bunny_ears": "👯", - "people_wrestling": "🤼", - "performing_arts": "🎭", - "persevering_face": "😣", - "person_biking": "🚴", - "person_biking_dark_skin_tone": "🚴🏿", - "person_biking_light_skin_tone": "🚴🏻", - "person_biking_medium-dark_skin_tone": "🚴🏾", - "person_biking_medium-light_skin_tone": "🚴🏼", - "person_biking_medium_skin_tone": "🚴🏽", - "person_bouncing_ball": "⛹", - "person_bouncing_ball_dark_skin_tone": "⛹🏿", - "person_bouncing_ball_light_skin_tone": "⛹🏻", - "person_bouncing_ball_medium-dark_skin_tone": "⛹🏾", - "person_bouncing_ball_medium-light_skin_tone": "⛹🏼", - "person_bouncing_ball_medium_skin_tone": "⛹🏽", - "person_bowing": "🙇", - "person_bowing_dark_skin_tone": "🙇🏿", - "person_bowing_light_skin_tone": "🙇🏻", - "person_bowing_medium-dark_skin_tone": "🙇🏾", - "person_bowing_medium-light_skin_tone": "🙇🏼", - "person_bowing_medium_skin_tone": "🙇🏽", - "person_cartwheeling": "🤸", - "person_cartwheeling_dark_skin_tone": "🤸🏿", - "person_cartwheeling_light_skin_tone": "🤸🏻", - "person_cartwheeling_medium-dark_skin_tone": "🤸🏾", - "person_cartwheeling_medium-light_skin_tone": "🤸🏼", - "person_cartwheeling_medium_skin_tone": "🤸🏽", - "person_climbing": "🧗", - "person_climbing_dark_skin_tone": "🧗🏿", - "person_climbing_light_skin_tone": "🧗🏻", - "person_climbing_medium-dark_skin_tone": "🧗🏾", - "person_climbing_medium-light_skin_tone": "🧗🏼", - "person_climbing_medium_skin_tone": "🧗🏽", - "person_facepalming": "🤦", - "person_facepalming_dark_skin_tone": "🤦🏿", - "person_facepalming_light_skin_tone": "🤦🏻", - "person_facepalming_medium-dark_skin_tone": "🤦🏾", - "person_facepalming_medium-light_skin_tone": "🤦🏼", - "person_facepalming_medium_skin_tone": "🤦🏽", - "person_fencing": "🤺", - "person_frowning": "🙍", - "person_frowning_dark_skin_tone": "🙍🏿", - "person_frowning_light_skin_tone": "🙍🏻", - "person_frowning_medium-dark_skin_tone": "🙍🏾", - "person_frowning_medium-light_skin_tone": "🙍🏼", - "person_frowning_medium_skin_tone": "🙍🏽", - "person_gesturing_no": "🙅", - "person_gesturing_no_dark_skin_tone": "🙅🏿", - "person_gesturing_no_light_skin_tone": "🙅🏻", - "person_gesturing_no_medium-dark_skin_tone": "🙅🏾", - "person_gesturing_no_medium-light_skin_tone": "🙅🏼", - "person_gesturing_no_medium_skin_tone": "🙅🏽", - "person_gesturing_ok": "🙆", - "person_gesturing_ok_dark_skin_tone": "🙆🏿", - "person_gesturing_ok_light_skin_tone": "🙆🏻", - "person_gesturing_ok_medium-dark_skin_tone": "🙆🏾", - "person_gesturing_ok_medium-light_skin_tone": "🙆🏼", - "person_gesturing_ok_medium_skin_tone": "🙆🏽", - "person_getting_haircut": "💇", - "person_getting_haircut_dark_skin_tone": "💇🏿", - "person_getting_haircut_light_skin_tone": "💇🏻", - "person_getting_haircut_medium-dark_skin_tone": "💇🏾", - "person_getting_haircut_medium-light_skin_tone": "💇🏼", - "person_getting_haircut_medium_skin_tone": "💇🏽", - "person_getting_massage": "💆", - "person_getting_massage_dark_skin_tone": "💆🏿", - "person_getting_massage_light_skin_tone": "💆🏻", - "person_getting_massage_medium-dark_skin_tone": "💆🏾", - "person_getting_massage_medium-light_skin_tone": "💆🏼", - "person_getting_massage_medium_skin_tone": "💆🏽", - "person_golfing": "🏌", - "person_golfing_dark_skin_tone": "🏌🏿", - "person_golfing_light_skin_tone": "🏌🏻", - "person_golfing_medium-dark_skin_tone": "🏌🏾", - "person_golfing_medium-light_skin_tone": "🏌🏼", - "person_golfing_medium_skin_tone": "🏌🏽", - "person_in_bed": "🛌", - "person_in_bed_dark_skin_tone": "🛌🏿", - "person_in_bed_light_skin_tone": "🛌🏻", - "person_in_bed_medium-dark_skin_tone": "🛌🏾", - "person_in_bed_medium-light_skin_tone": "🛌🏼", - "person_in_bed_medium_skin_tone": "🛌🏽", - "person_in_lotus_position": "🧘", - "person_in_lotus_position_dark_skin_tone": "🧘🏿", - "person_in_lotus_position_light_skin_tone": "🧘🏻", - "person_in_lotus_position_medium-dark_skin_tone": "🧘🏾", - "person_in_lotus_position_medium-light_skin_tone": "🧘🏼", - "person_in_lotus_position_medium_skin_tone": "🧘🏽", - "person_in_steamy_room": "🧖", - "person_in_steamy_room_dark_skin_tone": "🧖🏿", - "person_in_steamy_room_light_skin_tone": "🧖🏻", - "person_in_steamy_room_medium-dark_skin_tone": "🧖🏾", - "person_in_steamy_room_medium-light_skin_tone": "🧖🏼", - "person_in_steamy_room_medium_skin_tone": "🧖🏽", - "person_juggling": "🤹", - "person_juggling_dark_skin_tone": "🤹🏿", - "person_juggling_light_skin_tone": "🤹🏻", - "person_juggling_medium-dark_skin_tone": "🤹🏾", - "person_juggling_medium-light_skin_tone": "🤹🏼", - "person_juggling_medium_skin_tone": "🤹🏽", - "person_kneeling": "🧎", - "person_lifting_weights": "🏋", - "person_lifting_weights_dark_skin_tone": "🏋🏿", - "person_lifting_weights_light_skin_tone": "🏋🏻", - "person_lifting_weights_medium-dark_skin_tone": "🏋🏾", - "person_lifting_weights_medium-light_skin_tone": "🏋🏼", - "person_lifting_weights_medium_skin_tone": "🏋🏽", - "person_mountain_biking": "🚵", - "person_mountain_biking_dark_skin_tone": "🚵🏿", - "person_mountain_biking_light_skin_tone": "🚵🏻", - "person_mountain_biking_medium-dark_skin_tone": "🚵🏾", - "person_mountain_biking_medium-light_skin_tone": "🚵🏼", - "person_mountain_biking_medium_skin_tone": "🚵🏽", - "person_playing_handball": "🤾", - "person_playing_handball_dark_skin_tone": "🤾🏿", - "person_playing_handball_light_skin_tone": "🤾🏻", - "person_playing_handball_medium-dark_skin_tone": "🤾🏾", - "person_playing_handball_medium-light_skin_tone": "🤾🏼", - "person_playing_handball_medium_skin_tone": "🤾🏽", - "person_playing_water_polo": "🤽", - "person_playing_water_polo_dark_skin_tone": "🤽🏿", - "person_playing_water_polo_light_skin_tone": "🤽🏻", - "person_playing_water_polo_medium-dark_skin_tone": "🤽🏾", - "person_playing_water_polo_medium-light_skin_tone": "🤽🏼", - "person_playing_water_polo_medium_skin_tone": "🤽🏽", - "person_pouting": "🙎", - "person_pouting_dark_skin_tone": "🙎🏿", - "person_pouting_light_skin_tone": "🙎🏻", - "person_pouting_medium-dark_skin_tone": "🙎🏾", - "person_pouting_medium-light_skin_tone": "🙎🏼", - "person_pouting_medium_skin_tone": "🙎🏽", - "person_raising_hand": "🙋", - "person_raising_hand_dark_skin_tone": "🙋🏿", - "person_raising_hand_light_skin_tone": "🙋🏻", - "person_raising_hand_medium-dark_skin_tone": "🙋🏾", - "person_raising_hand_medium-light_skin_tone": "🙋🏼", - "person_raising_hand_medium_skin_tone": "🙋🏽", - "person_rowing_boat": "🚣", - "person_rowing_boat_dark_skin_tone": "🚣🏿", - "person_rowing_boat_light_skin_tone": "🚣🏻", - "person_rowing_boat_medium-dark_skin_tone": "🚣🏾", - "person_rowing_boat_medium-light_skin_tone": "🚣🏼", - "person_rowing_boat_medium_skin_tone": "🚣🏽", - "person_running": "🏃", - "person_running_dark_skin_tone": "🏃🏿", - "person_running_light_skin_tone": "🏃🏻", - "person_running_medium-dark_skin_tone": "🏃🏾", - "person_running_medium-light_skin_tone": "🏃🏼", - "person_running_medium_skin_tone": "🏃🏽", - "person_shrugging": "🤷", - "person_shrugging_dark_skin_tone": "🤷🏿", - "person_shrugging_light_skin_tone": "🤷🏻", - "person_shrugging_medium-dark_skin_tone": "🤷🏾", - "person_shrugging_medium-light_skin_tone": "🤷🏼", - "person_shrugging_medium_skin_tone": "🤷🏽", - "person_standing": "🧍", - "person_surfing": "🏄", - "person_surfing_dark_skin_tone": "🏄🏿", - "person_surfing_light_skin_tone": "🏄🏻", - "person_surfing_medium-dark_skin_tone": "🏄🏾", - "person_surfing_medium-light_skin_tone": "🏄🏼", - "person_surfing_medium_skin_tone": "🏄🏽", - "person_swimming": "🏊", - "person_swimming_dark_skin_tone": "🏊🏿", - "person_swimming_light_skin_tone": "🏊🏻", - "person_swimming_medium-dark_skin_tone": "🏊🏾", - "person_swimming_medium-light_skin_tone": "🏊🏼", - "person_swimming_medium_skin_tone": "🏊🏽", - "person_taking_bath": "🛀", - "person_taking_bath_dark_skin_tone": "🛀🏿", - "person_taking_bath_light_skin_tone": "🛀🏻", - "person_taking_bath_medium-dark_skin_tone": "🛀🏾", - "person_taking_bath_medium-light_skin_tone": "🛀🏼", - "person_taking_bath_medium_skin_tone": "🛀🏽", - "person_tipping_hand": "💁", - "person_tipping_hand_dark_skin_tone": "💁🏿", - "person_tipping_hand_light_skin_tone": "💁🏻", - "person_tipping_hand_medium-dark_skin_tone": "💁🏾", - "person_tipping_hand_medium-light_skin_tone": "💁🏼", - "person_tipping_hand_medium_skin_tone": "💁🏽", - "person_walking": "🚶", - "person_walking_dark_skin_tone": "🚶🏿", - "person_walking_light_skin_tone": "🚶🏻", - "person_walking_medium-dark_skin_tone": "🚶🏾", - "person_walking_medium-light_skin_tone": "🚶🏼", - "person_walking_medium_skin_tone": "🚶🏽", - "person_wearing_turban": "👳", - "person_wearing_turban_dark_skin_tone": "👳🏿", - "person_wearing_turban_light_skin_tone": "👳🏻", - "person_wearing_turban_medium-dark_skin_tone": "👳🏾", - "person_wearing_turban_medium-light_skin_tone": "👳🏼", - "person_wearing_turban_medium_skin_tone": "👳🏽", - "petri_dish": "🧫", - "pick": "⛏", - "pie": "🥧", - "pig": "🐷", - "pig_face": "🐷", - "pig_nose": "🐽", - "pile_of_poo": "💩", - "pill": "💊", - "pinching_hand": "🤏", - "pine_decoration": "🎍", - "pineapple": "🍍", - "ping_pong": "🏓", - "pirate_flag": "🏴\u200d☠️", - "pistol": "🔫", - "pizza": "🍕", - "place_of_worship": "🛐", - "play_button": "▶", - "play_or_pause_button": "⏯", - "pleading_face": "🥺", - "police_car": "🚓", - "police_car_light": "🚨", - "police_officer": "👮", - "police_officer_dark_skin_tone": "👮🏿", - "police_officer_light_skin_tone": "👮🏻", - "police_officer_medium-dark_skin_tone": "👮🏾", - "police_officer_medium-light_skin_tone": "👮🏼", - "police_officer_medium_skin_tone": "👮🏽", - "poodle": "🐩", - "pool_8_ball": "🎱", - "popcorn": "🍿", - "post_office": "🏣", - "postal_horn": "📯", - "postbox": "📮", - "pot_of_food": "🍲", - "potable_water": "🚰", - "potato": "🥔", - "poultry_leg": "🍗", - "pound_banknote": "💷", - "pouting_cat_face": "😾", - "pouting_face": "😡", - "prayer_beads": "📿", - "pregnant_woman": "🤰", - "pregnant_woman_dark_skin_tone": "🤰🏿", - "pregnant_woman_light_skin_tone": "🤰🏻", - "pregnant_woman_medium-dark_skin_tone": "🤰🏾", - "pregnant_woman_medium-light_skin_tone": "🤰🏼", - "pregnant_woman_medium_skin_tone": "🤰🏽", - "pretzel": "🥨", - "probing_cane": "🦯", - "prince": "🤴", - "prince_dark_skin_tone": "🤴🏿", - "prince_light_skin_tone": "🤴🏻", - "prince_medium-dark_skin_tone": "🤴🏾", - "prince_medium-light_skin_tone": "🤴🏼", - "prince_medium_skin_tone": "🤴🏽", - "princess": "👸", - "princess_dark_skin_tone": "👸🏿", - "princess_light_skin_tone": "👸🏻", - "princess_medium-dark_skin_tone": "👸🏾", - "princess_medium-light_skin_tone": "👸🏼", - "princess_medium_skin_tone": "👸🏽", - "printer": "🖨", - "prohibited": "🚫", - "purple_circle": "🟣", - "purple_heart": "💜", - "purple_square": "🟪", - "purse": "👛", - "pushpin": "📌", - "question_mark": "❓", - "rabbit": "🐰", - "rabbit_face": "🐰", - "raccoon": "🦝", - "racing_car": "🏎", - "radio": "📻", - "radio_button": "🔘", - "radioactive": "☢", - "railway_car": "🚃", - "railway_track": "🛤", - "rainbow": "🌈", - "rainbow_flag": "🏳️\u200d🌈", - "raised_back_of_hand": "🤚", - "raised_back_of_hand_dark_skin_tone": "🤚🏿", - "raised_back_of_hand_light_skin_tone": "🤚🏻", - "raised_back_of_hand_medium-dark_skin_tone": "🤚🏾", - "raised_back_of_hand_medium-light_skin_tone": "🤚🏼", - "raised_back_of_hand_medium_skin_tone": "🤚🏽", - "raised_fist": "✊", - "raised_fist_dark_skin_tone": "✊🏿", - "raised_fist_light_skin_tone": "✊🏻", - "raised_fist_medium-dark_skin_tone": "✊🏾", - "raised_fist_medium-light_skin_tone": "✊🏼", - "raised_fist_medium_skin_tone": "✊🏽", - "raised_hand": "✋", - "raised_hand_dark_skin_tone": "✋🏿", - "raised_hand_light_skin_tone": "✋🏻", - "raised_hand_medium-dark_skin_tone": "✋🏾", - "raised_hand_medium-light_skin_tone": "✋🏼", - "raised_hand_medium_skin_tone": "✋🏽", - "raising_hands": "🙌", - "raising_hands_dark_skin_tone": "🙌🏿", - "raising_hands_light_skin_tone": "🙌🏻", - "raising_hands_medium-dark_skin_tone": "🙌🏾", - "raising_hands_medium-light_skin_tone": "🙌🏼", - "raising_hands_medium_skin_tone": "🙌🏽", - "ram": "🐏", - "rat": "🐀", - "razor": "🪒", - "ringed_planet": "🪐", - "receipt": "🧾", - "record_button": "⏺", - "recycling_symbol": "♻", - "red_apple": "🍎", - "red_circle": "🔴", - "red_envelope": "🧧", - "red_hair": "🦰", - "red-haired_man": "👨\u200d🦰", - "red-haired_woman": "👩\u200d🦰", - "red_heart": "❤", - "red_paper_lantern": "🏮", - "red_square": "🟥", - "red_triangle_pointed_down": "🔻", - "red_triangle_pointed_up": "🔺", - "registered": "®", - "relieved_face": "😌", - "reminder_ribbon": "🎗", - "repeat_button": "🔁", - "repeat_single_button": "🔂", - "rescue_worker’s_helmet": "⛑", - "restroom": "🚻", - "reverse_button": "◀", - "revolving_hearts": "💞", - "rhinoceros": "🦏", - "ribbon": "🎀", - "rice_ball": "🍙", - "rice_cracker": "🍘", - "right-facing_fist": "🤜", - "right-facing_fist_dark_skin_tone": "🤜🏿", - "right-facing_fist_light_skin_tone": "🤜🏻", - "right-facing_fist_medium-dark_skin_tone": "🤜🏾", - "right-facing_fist_medium-light_skin_tone": "🤜🏼", - "right-facing_fist_medium_skin_tone": "🤜🏽", - "right_anger_bubble": "🗯", - "right_arrow": "➡", - "right_arrow_curving_down": "⤵", - "right_arrow_curving_left": "↩", - "right_arrow_curving_up": "⤴", - "ring": "💍", - "roasted_sweet_potato": "🍠", - "robot_face": "🤖", - "rocket": "🚀", - "roll_of_paper": "🧻", - "rolled-up_newspaper": "🗞", - "roller_coaster": "🎢", - "rolling_on_the_floor_laughing": "🤣", - "rooster": "🐓", - "rose": "🌹", - "rosette": "🏵", - "round_pushpin": "📍", - "rugby_football": "🏉", - "running_shirt": "🎽", - "running_shoe": "👟", - "sad_but_relieved_face": "😥", - "safety_pin": "🧷", - "safety_vest": "🦺", - "salt": "🧂", - "sailboat": "⛵", - "sake": "🍶", - "sandwich": "🥪", - "sari": "🥻", - "satellite": "📡", - "satellite_antenna": "📡", - "sauropod": "🦕", - "saxophone": "🎷", - "scarf": "🧣", - "school": "🏫", - "school_backpack": "🎒", - "scissors": "✂", - "scorpion": "🦂", - "scroll": "📜", - "seat": "💺", - "see-no-evil_monkey": "🙈", - "seedling": "🌱", - "selfie": "🤳", - "selfie_dark_skin_tone": "🤳🏿", - "selfie_light_skin_tone": "🤳🏻", - "selfie_medium-dark_skin_tone": "🤳🏾", - "selfie_medium-light_skin_tone": "🤳🏼", - "selfie_medium_skin_tone": "🤳🏽", - "service_dog": "🐕\u200d🦺", - "seven-thirty": "🕢", - "seven_o’clock": "🕖", - "shallow_pan_of_food": "🥘", - "shamrock": "☘", - "shark": "🦈", - "shaved_ice": "🍧", - "sheaf_of_rice": "🌾", - "shield": "🛡", - "shinto_shrine": "⛩", - "ship": "🚢", - "shooting_star": "🌠", - "shopping_bags": "🛍", - "shopping_cart": "🛒", - "shortcake": "🍰", - "shorts": "🩳", - "shower": "🚿", - "shrimp": "🦐", - "shuffle_tracks_button": "🔀", - "shushing_face": "🤫", - "sign_of_the_horns": "🤘", - "sign_of_the_horns_dark_skin_tone": "🤘🏿", - "sign_of_the_horns_light_skin_tone": "🤘🏻", - "sign_of_the_horns_medium-dark_skin_tone": "🤘🏾", - "sign_of_the_horns_medium-light_skin_tone": "🤘🏼", - "sign_of_the_horns_medium_skin_tone": "🤘🏽", - "six-thirty": "🕡", - "six_o’clock": "🕕", - "skateboard": "🛹", - "skier": "⛷", - "skis": "🎿", - "skull": "💀", - "skull_and_crossbones": "☠", - "skunk": "🦨", - "sled": "🛷", - "sleeping_face": "😴", - "sleepy_face": "😪", - "slightly_frowning_face": "🙁", - "slightly_smiling_face": "🙂", - "slot_machine": "🎰", - "sloth": "🦥", - "small_airplane": "🛩", - "small_blue_diamond": "🔹", - "small_orange_diamond": "🔸", - "smiling_cat_face_with_heart-eyes": "😻", - "smiling_face": "☺", - "smiling_face_with_halo": "😇", - "smiling_face_with_3_hearts": "🥰", - "smiling_face_with_heart-eyes": "😍", - "smiling_face_with_horns": "😈", - "smiling_face_with_smiling_eyes": "😊", - "smiling_face_with_sunglasses": "😎", - "smirking_face": "😏", - "snail": "🐌", - "snake": "🐍", - "sneezing_face": "🤧", - "snow-capped_mountain": "🏔", - "snowboarder": "🏂", - "snowboarder_dark_skin_tone": "🏂🏿", - "snowboarder_light_skin_tone": "🏂🏻", - "snowboarder_medium-dark_skin_tone": "🏂🏾", - "snowboarder_medium-light_skin_tone": "🏂🏼", - "snowboarder_medium_skin_tone": "🏂🏽", - "snowflake": "❄", - "snowman": "☃", - "snowman_without_snow": "⛄", - "soap": "🧼", - "soccer_ball": "⚽", - "socks": "🧦", - "softball": "🥎", - "soft_ice_cream": "🍦", - "spade_suit": "♠", - "spaghetti": "🍝", - "sparkle": "❇", - "sparkler": "🎇", - "sparkles": "✨", - "sparkling_heart": "💖", - "speak-no-evil_monkey": "🙊", - "speaker_high_volume": "🔊", - "speaker_low_volume": "🔈", - "speaker_medium_volume": "🔉", - "speaking_head": "🗣", - "speech_balloon": "💬", - "speedboat": "🚤", - "spider": "🕷", - "spider_web": "🕸", - "spiral_calendar": "🗓", - "spiral_notepad": "🗒", - "spiral_shell": "🐚", - "spoon": "🥄", - "sponge": "🧽", - "sport_utility_vehicle": "🚙", - "sports_medal": "🏅", - "spouting_whale": "🐳", - "squid": "🦑", - "squinting_face_with_tongue": "😝", - "stadium": "🏟", - "star-struck": "🤩", - "star_and_crescent": "☪", - "star_of_david": "✡", - "station": "🚉", - "steaming_bowl": "🍜", - "stethoscope": "🩺", - "stop_button": "⏹", - "stop_sign": "🛑", - "stopwatch": "⏱", - "straight_ruler": "📏", - "strawberry": "🍓", - "studio_microphone": "🎙", - "stuffed_flatbread": "🥙", - "sun": "☀", - "sun_behind_cloud": "⛅", - "sun_behind_large_cloud": "🌥", - "sun_behind_rain_cloud": "🌦", - "sun_behind_small_cloud": "🌤", - "sun_with_face": "🌞", - "sunflower": "🌻", - "sunglasses": "😎", - "sunrise": "🌅", - "sunrise_over_mountains": "🌄", - "sunset": "🌇", - "superhero": "🦸", - "supervillain": "🦹", - "sushi": "🍣", - "suspension_railway": "🚟", - "swan": "🦢", - "sweat_droplets": "💦", - "synagogue": "🕍", - "syringe": "💉", - "t-shirt": "👕", - "taco": "🌮", - "takeout_box": "🥡", - "tanabata_tree": "🎋", - "tangerine": "🍊", - "taxi": "🚕", - "teacup_without_handle": "🍵", - "tear-off_calendar": "📆", - "teddy_bear": "🧸", - "telephone": "☎", - "telephone_receiver": "📞", - "telescope": "🔭", - "television": "📺", - "ten-thirty": "🕥", - "ten_o’clock": "🕙", - "tennis": "🎾", - "tent": "⛺", - "test_tube": "🧪", - "thermometer": "🌡", - "thinking_face": "🤔", - "thought_balloon": "💭", - "thread": "🧵", - "three-thirty": "🕞", - "three_o’clock": "🕒", - "thumbs_down": "👎", - "thumbs_down_dark_skin_tone": "👎🏿", - "thumbs_down_light_skin_tone": "👎🏻", - "thumbs_down_medium-dark_skin_tone": "👎🏾", - "thumbs_down_medium-light_skin_tone": "👎🏼", - "thumbs_down_medium_skin_tone": "👎🏽", - "thumbs_up": "👍", - "thumbs_up_dark_skin_tone": "👍🏿", - "thumbs_up_light_skin_tone": "👍🏻", - "thumbs_up_medium-dark_skin_tone": "👍🏾", - "thumbs_up_medium-light_skin_tone": "👍🏼", - "thumbs_up_medium_skin_tone": "👍🏽", - "ticket": "🎫", - "tiger": "🐯", - "tiger_face": "🐯", - "timer_clock": "⏲", - "tired_face": "😫", - "toolbox": "🧰", - "toilet": "🚽", - "tomato": "🍅", - "tongue": "👅", - "tooth": "🦷", - "top_hat": "🎩", - "tornado": "🌪", - "trackball": "🖲", - "tractor": "🚜", - "trade_mark": "™", - "train": "🚋", - "tram": "🚊", - "tram_car": "🚋", - "triangular_flag": "🚩", - "triangular_ruler": "📐", - "trident_emblem": "🔱", - "trolleybus": "🚎", - "trophy": "🏆", - "tropical_drink": "🍹", - "tropical_fish": "🐠", - "trumpet": "🎺", - "tulip": "🌷", - "tumbler_glass": "🥃", - "turtle": "🐢", - "twelve-thirty": "🕧", - "twelve_o’clock": "🕛", - "two-hump_camel": "🐫", - "two-thirty": "🕝", - "two_hearts": "💕", - "two_men_holding_hands": "👬", - "two_o’clock": "🕑", - "two_women_holding_hands": "👭", - "umbrella": "☂", - "umbrella_on_ground": "⛱", - "umbrella_with_rain_drops": "☔", - "unamused_face": "😒", - "unicorn_face": "🦄", - "unlocked": "🔓", - "up-down_arrow": "↕", - "up-left_arrow": "↖", - "up-right_arrow": "↗", - "up_arrow": "⬆", - "upside-down_face": "🙃", - "upwards_button": "🔼", - "vampire": "🧛", - "vampire_dark_skin_tone": "🧛🏿", - "vampire_light_skin_tone": "🧛🏻", - "vampire_medium-dark_skin_tone": "🧛🏾", - "vampire_medium-light_skin_tone": "🧛🏼", - "vampire_medium_skin_tone": "🧛🏽", - "vertical_traffic_light": "🚦", - "vibration_mode": "📳", - "victory_hand": "✌", - "victory_hand_dark_skin_tone": "✌🏿", - "victory_hand_light_skin_tone": "✌🏻", - "victory_hand_medium-dark_skin_tone": "✌🏾", - "victory_hand_medium-light_skin_tone": "✌🏼", - "victory_hand_medium_skin_tone": "✌🏽", - "video_camera": "📹", - "video_game": "🎮", - "videocassette": "📼", - "violin": "🎻", - "volcano": "🌋", - "volleyball": "🏐", - "vulcan_salute": "🖖", - "vulcan_salute_dark_skin_tone": "🖖🏿", - "vulcan_salute_light_skin_tone": "🖖🏻", - "vulcan_salute_medium-dark_skin_tone": "🖖🏾", - "vulcan_salute_medium-light_skin_tone": "🖖🏼", - "vulcan_salute_medium_skin_tone": "🖖🏽", - "waffle": "🧇", - "waning_crescent_moon": "🌘", - "waning_gibbous_moon": "🌖", - "warning": "⚠", - "wastebasket": "🗑", - "watch": "⌚", - "water_buffalo": "🐃", - "water_closet": "🚾", - "water_wave": "🌊", - "watermelon": "🍉", - "waving_hand": "👋", - "waving_hand_dark_skin_tone": "👋🏿", - "waving_hand_light_skin_tone": "👋🏻", - "waving_hand_medium-dark_skin_tone": "👋🏾", - "waving_hand_medium-light_skin_tone": "👋🏼", - "waving_hand_medium_skin_tone": "👋🏽", - "wavy_dash": "〰", - "waxing_crescent_moon": "🌒", - "waxing_gibbous_moon": "🌔", - "weary_cat_face": "🙀", - "weary_face": "😩", - "wedding": "💒", - "whale": "🐳", - "wheel_of_dharma": "☸", - "wheelchair_symbol": "♿", - "white_circle": "⚪", - "white_exclamation_mark": "❕", - "white_flag": "🏳", - "white_flower": "💮", - "white_hair": "🦳", - "white-haired_man": "👨\u200d🦳", - "white-haired_woman": "👩\u200d🦳", - "white_heart": "🤍", - "white_heavy_check_mark": "✅", - "white_large_square": "⬜", - "white_medium-small_square": "◽", - "white_medium_square": "◻", - "white_medium_star": "⭐", - "white_question_mark": "❔", - "white_small_square": "▫", - "white_square_button": "🔳", - "wilted_flower": "🥀", - "wind_chime": "🎐", - "wind_face": "🌬", - "wine_glass": "🍷", - "winking_face": "😉", - "winking_face_with_tongue": "😜", - "wolf_face": "🐺", - "woman": "👩", - "woman_artist": "👩\u200d🎨", - "woman_artist_dark_skin_tone": "👩🏿\u200d🎨", - "woman_artist_light_skin_tone": "👩🏻\u200d🎨", - "woman_artist_medium-dark_skin_tone": "👩🏾\u200d🎨", - "woman_artist_medium-light_skin_tone": "👩🏼\u200d🎨", - "woman_artist_medium_skin_tone": "👩🏽\u200d🎨", - "woman_astronaut": "👩\u200d🚀", - "woman_astronaut_dark_skin_tone": "👩🏿\u200d🚀", - "woman_astronaut_light_skin_tone": "👩🏻\u200d🚀", - "woman_astronaut_medium-dark_skin_tone": "👩🏾\u200d🚀", - "woman_astronaut_medium-light_skin_tone": "👩🏼\u200d🚀", - "woman_astronaut_medium_skin_tone": "👩🏽\u200d🚀", - "woman_biking": "🚴\u200d♀️", - "woman_biking_dark_skin_tone": "🚴🏿\u200d♀️", - "woman_biking_light_skin_tone": "🚴🏻\u200d♀️", - "woman_biking_medium-dark_skin_tone": "🚴🏾\u200d♀️", - "woman_biking_medium-light_skin_tone": "🚴🏼\u200d♀️", - "woman_biking_medium_skin_tone": "🚴🏽\u200d♀️", - "woman_bouncing_ball": "⛹️\u200d♀️", - "woman_bouncing_ball_dark_skin_tone": "⛹🏿\u200d♀️", - "woman_bouncing_ball_light_skin_tone": "⛹🏻\u200d♀️", - "woman_bouncing_ball_medium-dark_skin_tone": "⛹🏾\u200d♀️", - "woman_bouncing_ball_medium-light_skin_tone": "⛹🏼\u200d♀️", - "woman_bouncing_ball_medium_skin_tone": "⛹🏽\u200d♀️", - "woman_bowing": "🙇\u200d♀️", - "woman_bowing_dark_skin_tone": "🙇🏿\u200d♀️", - "woman_bowing_light_skin_tone": "🙇🏻\u200d♀️", - "woman_bowing_medium-dark_skin_tone": "🙇🏾\u200d♀️", - "woman_bowing_medium-light_skin_tone": "🙇🏼\u200d♀️", - "woman_bowing_medium_skin_tone": "🙇🏽\u200d♀️", - "woman_cartwheeling": "🤸\u200d♀️", - "woman_cartwheeling_dark_skin_tone": "🤸🏿\u200d♀️", - "woman_cartwheeling_light_skin_tone": "🤸🏻\u200d♀️", - "woman_cartwheeling_medium-dark_skin_tone": "🤸🏾\u200d♀️", - "woman_cartwheeling_medium-light_skin_tone": "🤸🏼\u200d♀️", - "woman_cartwheeling_medium_skin_tone": "🤸🏽\u200d♀️", - "woman_climbing": "🧗\u200d♀️", - "woman_climbing_dark_skin_tone": "🧗🏿\u200d♀️", - "woman_climbing_light_skin_tone": "🧗🏻\u200d♀️", - "woman_climbing_medium-dark_skin_tone": "🧗🏾\u200d♀️", - "woman_climbing_medium-light_skin_tone": "🧗🏼\u200d♀️", - "woman_climbing_medium_skin_tone": "🧗🏽\u200d♀️", - "woman_construction_worker": "👷\u200d♀️", - "woman_construction_worker_dark_skin_tone": "👷🏿\u200d♀️", - "woman_construction_worker_light_skin_tone": "👷🏻\u200d♀️", - "woman_construction_worker_medium-dark_skin_tone": "👷🏾\u200d♀️", - "woman_construction_worker_medium-light_skin_tone": "👷🏼\u200d♀️", - "woman_construction_worker_medium_skin_tone": "👷🏽\u200d♀️", - "woman_cook": "👩\u200d🍳", - "woman_cook_dark_skin_tone": "👩🏿\u200d🍳", - "woman_cook_light_skin_tone": "👩🏻\u200d🍳", - "woman_cook_medium-dark_skin_tone": "👩🏾\u200d🍳", - "woman_cook_medium-light_skin_tone": "👩🏼\u200d🍳", - "woman_cook_medium_skin_tone": "👩🏽\u200d🍳", - "woman_dancing": "💃", - "woman_dancing_dark_skin_tone": "💃🏿", - "woman_dancing_light_skin_tone": "💃🏻", - "woman_dancing_medium-dark_skin_tone": "💃🏾", - "woman_dancing_medium-light_skin_tone": "💃🏼", - "woman_dancing_medium_skin_tone": "💃🏽", - "woman_dark_skin_tone": "👩🏿", - "woman_detective": "🕵️\u200d♀️", - "woman_detective_dark_skin_tone": "🕵🏿\u200d♀️", - "woman_detective_light_skin_tone": "🕵🏻\u200d♀️", - "woman_detective_medium-dark_skin_tone": "🕵🏾\u200d♀️", - "woman_detective_medium-light_skin_tone": "🕵🏼\u200d♀️", - "woman_detective_medium_skin_tone": "🕵🏽\u200d♀️", - "woman_elf": "🧝\u200d♀️", - "woman_elf_dark_skin_tone": "🧝🏿\u200d♀️", - "woman_elf_light_skin_tone": "🧝🏻\u200d♀️", - "woman_elf_medium-dark_skin_tone": "🧝🏾\u200d♀️", - "woman_elf_medium-light_skin_tone": "🧝🏼\u200d♀️", - "woman_elf_medium_skin_tone": "🧝🏽\u200d♀️", - "woman_facepalming": "🤦\u200d♀️", - "woman_facepalming_dark_skin_tone": "🤦🏿\u200d♀️", - "woman_facepalming_light_skin_tone": "🤦🏻\u200d♀️", - "woman_facepalming_medium-dark_skin_tone": "🤦🏾\u200d♀️", - "woman_facepalming_medium-light_skin_tone": "🤦🏼\u200d♀️", - "woman_facepalming_medium_skin_tone": "🤦🏽\u200d♀️", - "woman_factory_worker": "👩\u200d🏭", - "woman_factory_worker_dark_skin_tone": "👩🏿\u200d🏭", - "woman_factory_worker_light_skin_tone": "👩🏻\u200d🏭", - "woman_factory_worker_medium-dark_skin_tone": "👩🏾\u200d🏭", - "woman_factory_worker_medium-light_skin_tone": "👩🏼\u200d🏭", - "woman_factory_worker_medium_skin_tone": "👩🏽\u200d🏭", - "woman_fairy": "🧚\u200d♀️", - "woman_fairy_dark_skin_tone": "🧚🏿\u200d♀️", - "woman_fairy_light_skin_tone": "🧚🏻\u200d♀️", - "woman_fairy_medium-dark_skin_tone": "🧚🏾\u200d♀️", - "woman_fairy_medium-light_skin_tone": "🧚🏼\u200d♀️", - "woman_fairy_medium_skin_tone": "🧚🏽\u200d♀️", - "woman_farmer": "👩\u200d🌾", - "woman_farmer_dark_skin_tone": "👩🏿\u200d🌾", - "woman_farmer_light_skin_tone": "👩🏻\u200d🌾", - "woman_farmer_medium-dark_skin_tone": "👩🏾\u200d🌾", - "woman_farmer_medium-light_skin_tone": "👩🏼\u200d🌾", - "woman_farmer_medium_skin_tone": "👩🏽\u200d🌾", - "woman_firefighter": "👩\u200d🚒", - "woman_firefighter_dark_skin_tone": "👩🏿\u200d🚒", - "woman_firefighter_light_skin_tone": "👩🏻\u200d🚒", - "woman_firefighter_medium-dark_skin_tone": "👩🏾\u200d🚒", - "woman_firefighter_medium-light_skin_tone": "👩🏼\u200d🚒", - "woman_firefighter_medium_skin_tone": "👩🏽\u200d🚒", - "woman_frowning": "🙍\u200d♀️", - "woman_frowning_dark_skin_tone": "🙍🏿\u200d♀️", - "woman_frowning_light_skin_tone": "🙍🏻\u200d♀️", - "woman_frowning_medium-dark_skin_tone": "🙍🏾\u200d♀️", - "woman_frowning_medium-light_skin_tone": "🙍🏼\u200d♀️", - "woman_frowning_medium_skin_tone": "🙍🏽\u200d♀️", - "woman_genie": "🧞\u200d♀️", - "woman_gesturing_no": "🙅\u200d♀️", - "woman_gesturing_no_dark_skin_tone": "🙅🏿\u200d♀️", - "woman_gesturing_no_light_skin_tone": "🙅🏻\u200d♀️", - "woman_gesturing_no_medium-dark_skin_tone": "🙅🏾\u200d♀️", - "woman_gesturing_no_medium-light_skin_tone": "🙅🏼\u200d♀️", - "woman_gesturing_no_medium_skin_tone": "🙅🏽\u200d♀️", - "woman_gesturing_ok": "🙆\u200d♀️", - "woman_gesturing_ok_dark_skin_tone": "🙆🏿\u200d♀️", - "woman_gesturing_ok_light_skin_tone": "🙆🏻\u200d♀️", - "woman_gesturing_ok_medium-dark_skin_tone": "🙆🏾\u200d♀️", - "woman_gesturing_ok_medium-light_skin_tone": "🙆🏼\u200d♀️", - "woman_gesturing_ok_medium_skin_tone": "🙆🏽\u200d♀️", - "woman_getting_haircut": "💇\u200d♀️", - "woman_getting_haircut_dark_skin_tone": "💇🏿\u200d♀️", - "woman_getting_haircut_light_skin_tone": "💇🏻\u200d♀️", - "woman_getting_haircut_medium-dark_skin_tone": "💇🏾\u200d♀️", - "woman_getting_haircut_medium-light_skin_tone": "💇🏼\u200d♀️", - "woman_getting_haircut_medium_skin_tone": "💇🏽\u200d♀️", - "woman_getting_massage": "💆\u200d♀️", - "woman_getting_massage_dark_skin_tone": "💆🏿\u200d♀️", - "woman_getting_massage_light_skin_tone": "💆🏻\u200d♀️", - "woman_getting_massage_medium-dark_skin_tone": "💆🏾\u200d♀️", - "woman_getting_massage_medium-light_skin_tone": "💆🏼\u200d♀️", - "woman_getting_massage_medium_skin_tone": "💆🏽\u200d♀️", - "woman_golfing": "🏌️\u200d♀️", - "woman_golfing_dark_skin_tone": "🏌🏿\u200d♀️", - "woman_golfing_light_skin_tone": "🏌🏻\u200d♀️", - "woman_golfing_medium-dark_skin_tone": "🏌🏾\u200d♀️", - "woman_golfing_medium-light_skin_tone": "🏌🏼\u200d♀️", - "woman_golfing_medium_skin_tone": "🏌🏽\u200d♀️", - "woman_guard": "💂\u200d♀️", - "woman_guard_dark_skin_tone": "💂🏿\u200d♀️", - "woman_guard_light_skin_tone": "💂🏻\u200d♀️", - "woman_guard_medium-dark_skin_tone": "💂🏾\u200d♀️", - "woman_guard_medium-light_skin_tone": "💂🏼\u200d♀️", - "woman_guard_medium_skin_tone": "💂🏽\u200d♀️", - "woman_health_worker": "👩\u200d⚕️", - "woman_health_worker_dark_skin_tone": "👩🏿\u200d⚕️", - "woman_health_worker_light_skin_tone": "👩🏻\u200d⚕️", - "woman_health_worker_medium-dark_skin_tone": "👩🏾\u200d⚕️", - "woman_health_worker_medium-light_skin_tone": "👩🏼\u200d⚕️", - "woman_health_worker_medium_skin_tone": "👩🏽\u200d⚕️", - "woman_in_lotus_position": "🧘\u200d♀️", - "woman_in_lotus_position_dark_skin_tone": "🧘🏿\u200d♀️", - "woman_in_lotus_position_light_skin_tone": "🧘🏻\u200d♀️", - "woman_in_lotus_position_medium-dark_skin_tone": "🧘🏾\u200d♀️", - "woman_in_lotus_position_medium-light_skin_tone": "🧘🏼\u200d♀️", - "woman_in_lotus_position_medium_skin_tone": "🧘🏽\u200d♀️", - "woman_in_manual_wheelchair": "👩\u200d🦽", - "woman_in_motorized_wheelchair": "👩\u200d🦼", - "woman_in_steamy_room": "🧖\u200d♀️", - "woman_in_steamy_room_dark_skin_tone": "🧖🏿\u200d♀️", - "woman_in_steamy_room_light_skin_tone": "🧖🏻\u200d♀️", - "woman_in_steamy_room_medium-dark_skin_tone": "🧖🏾\u200d♀️", - "woman_in_steamy_room_medium-light_skin_tone": "🧖🏼\u200d♀️", - "woman_in_steamy_room_medium_skin_tone": "🧖🏽\u200d♀️", - "woman_judge": "👩\u200d⚖️", - "woman_judge_dark_skin_tone": "👩🏿\u200d⚖️", - "woman_judge_light_skin_tone": "👩🏻\u200d⚖️", - "woman_judge_medium-dark_skin_tone": "👩🏾\u200d⚖️", - "woman_judge_medium-light_skin_tone": "👩🏼\u200d⚖️", - "woman_judge_medium_skin_tone": "👩🏽\u200d⚖️", - "woman_juggling": "🤹\u200d♀️", - "woman_juggling_dark_skin_tone": "🤹🏿\u200d♀️", - "woman_juggling_light_skin_tone": "🤹🏻\u200d♀️", - "woman_juggling_medium-dark_skin_tone": "🤹🏾\u200d♀️", - "woman_juggling_medium-light_skin_tone": "🤹🏼\u200d♀️", - "woman_juggling_medium_skin_tone": "🤹🏽\u200d♀️", - "woman_lifting_weights": "🏋️\u200d♀️", - "woman_lifting_weights_dark_skin_tone": "🏋🏿\u200d♀️", - "woman_lifting_weights_light_skin_tone": "🏋🏻\u200d♀️", - "woman_lifting_weights_medium-dark_skin_tone": "🏋🏾\u200d♀️", - "woman_lifting_weights_medium-light_skin_tone": "🏋🏼\u200d♀️", - "woman_lifting_weights_medium_skin_tone": "🏋🏽\u200d♀️", - "woman_light_skin_tone": "👩🏻", - "woman_mage": "🧙\u200d♀️", - "woman_mage_dark_skin_tone": "🧙🏿\u200d♀️", - "woman_mage_light_skin_tone": "🧙🏻\u200d♀️", - "woman_mage_medium-dark_skin_tone": "🧙🏾\u200d♀️", - "woman_mage_medium-light_skin_tone": "🧙🏼\u200d♀️", - "woman_mage_medium_skin_tone": "🧙🏽\u200d♀️", - "woman_mechanic": "👩\u200d🔧", - "woman_mechanic_dark_skin_tone": "👩🏿\u200d🔧", - "woman_mechanic_light_skin_tone": "👩🏻\u200d🔧", - "woman_mechanic_medium-dark_skin_tone": "👩🏾\u200d🔧", - "woman_mechanic_medium-light_skin_tone": "👩🏼\u200d🔧", - "woman_mechanic_medium_skin_tone": "👩🏽\u200d🔧", - "woman_medium-dark_skin_tone": "👩🏾", - "woman_medium-light_skin_tone": "👩🏼", - "woman_medium_skin_tone": "👩🏽", - "woman_mountain_biking": "🚵\u200d♀️", - "woman_mountain_biking_dark_skin_tone": "🚵🏿\u200d♀️", - "woman_mountain_biking_light_skin_tone": "🚵🏻\u200d♀️", - "woman_mountain_biking_medium-dark_skin_tone": "🚵🏾\u200d♀️", - "woman_mountain_biking_medium-light_skin_tone": "🚵🏼\u200d♀️", - "woman_mountain_biking_medium_skin_tone": "🚵🏽\u200d♀️", - "woman_office_worker": "👩\u200d💼", - "woman_office_worker_dark_skin_tone": "👩🏿\u200d💼", - "woman_office_worker_light_skin_tone": "👩🏻\u200d💼", - "woman_office_worker_medium-dark_skin_tone": "👩🏾\u200d💼", - "woman_office_worker_medium-light_skin_tone": "👩🏼\u200d💼", - "woman_office_worker_medium_skin_tone": "👩🏽\u200d💼", - "woman_pilot": "👩\u200d✈️", - "woman_pilot_dark_skin_tone": "👩🏿\u200d✈️", - "woman_pilot_light_skin_tone": "👩🏻\u200d✈️", - "woman_pilot_medium-dark_skin_tone": "👩🏾\u200d✈️", - "woman_pilot_medium-light_skin_tone": "👩🏼\u200d✈️", - "woman_pilot_medium_skin_tone": "👩🏽\u200d✈️", - "woman_playing_handball": "🤾\u200d♀️", - "woman_playing_handball_dark_skin_tone": "🤾🏿\u200d♀️", - "woman_playing_handball_light_skin_tone": "🤾🏻\u200d♀️", - "woman_playing_handball_medium-dark_skin_tone": "🤾🏾\u200d♀️", - "woman_playing_handball_medium-light_skin_tone": "🤾🏼\u200d♀️", - "woman_playing_handball_medium_skin_tone": "🤾🏽\u200d♀️", - "woman_playing_water_polo": "🤽\u200d♀️", - "woman_playing_water_polo_dark_skin_tone": "🤽🏿\u200d♀️", - "woman_playing_water_polo_light_skin_tone": "🤽🏻\u200d♀️", - "woman_playing_water_polo_medium-dark_skin_tone": "🤽🏾\u200d♀️", - "woman_playing_water_polo_medium-light_skin_tone": "🤽🏼\u200d♀️", - "woman_playing_water_polo_medium_skin_tone": "🤽🏽\u200d♀️", - "woman_police_officer": "👮\u200d♀️", - "woman_police_officer_dark_skin_tone": "👮🏿\u200d♀️", - "woman_police_officer_light_skin_tone": "👮🏻\u200d♀️", - "woman_police_officer_medium-dark_skin_tone": "👮🏾\u200d♀️", - "woman_police_officer_medium-light_skin_tone": "👮🏼\u200d♀️", - "woman_police_officer_medium_skin_tone": "👮🏽\u200d♀️", - "woman_pouting": "🙎\u200d♀️", - "woman_pouting_dark_skin_tone": "🙎🏿\u200d♀️", - "woman_pouting_light_skin_tone": "🙎🏻\u200d♀️", - "woman_pouting_medium-dark_skin_tone": "🙎🏾\u200d♀️", - "woman_pouting_medium-light_skin_tone": "🙎🏼\u200d♀️", - "woman_pouting_medium_skin_tone": "🙎🏽\u200d♀️", - "woman_raising_hand": "🙋\u200d♀️", - "woman_raising_hand_dark_skin_tone": "🙋🏿\u200d♀️", - "woman_raising_hand_light_skin_tone": "🙋🏻\u200d♀️", - "woman_raising_hand_medium-dark_skin_tone": "🙋🏾\u200d♀️", - "woman_raising_hand_medium-light_skin_tone": "🙋🏼\u200d♀️", - "woman_raising_hand_medium_skin_tone": "🙋🏽\u200d♀️", - "woman_rowing_boat": "🚣\u200d♀️", - "woman_rowing_boat_dark_skin_tone": "🚣🏿\u200d♀️", - "woman_rowing_boat_light_skin_tone": "🚣🏻\u200d♀️", - "woman_rowing_boat_medium-dark_skin_tone": "🚣🏾\u200d♀️", - "woman_rowing_boat_medium-light_skin_tone": "🚣🏼\u200d♀️", - "woman_rowing_boat_medium_skin_tone": "🚣🏽\u200d♀️", - "woman_running": "🏃\u200d♀️", - "woman_running_dark_skin_tone": "🏃🏿\u200d♀️", - "woman_running_light_skin_tone": "🏃🏻\u200d♀️", - "woman_running_medium-dark_skin_tone": "🏃🏾\u200d♀️", - "woman_running_medium-light_skin_tone": "🏃🏼\u200d♀️", - "woman_running_medium_skin_tone": "🏃🏽\u200d♀️", - "woman_scientist": "👩\u200d🔬", - "woman_scientist_dark_skin_tone": "👩🏿\u200d🔬", - "woman_scientist_light_skin_tone": "👩🏻\u200d🔬", - "woman_scientist_medium-dark_skin_tone": "👩🏾\u200d🔬", - "woman_scientist_medium-light_skin_tone": "👩🏼\u200d🔬", - "woman_scientist_medium_skin_tone": "👩🏽\u200d🔬", - "woman_shrugging": "🤷\u200d♀️", - "woman_shrugging_dark_skin_tone": "🤷🏿\u200d♀️", - "woman_shrugging_light_skin_tone": "🤷🏻\u200d♀️", - "woman_shrugging_medium-dark_skin_tone": "🤷🏾\u200d♀️", - "woman_shrugging_medium-light_skin_tone": "🤷🏼\u200d♀️", - "woman_shrugging_medium_skin_tone": "🤷🏽\u200d♀️", - "woman_singer": "👩\u200d🎤", - "woman_singer_dark_skin_tone": "👩🏿\u200d🎤", - "woman_singer_light_skin_tone": "👩🏻\u200d🎤", - "woman_singer_medium-dark_skin_tone": "👩🏾\u200d🎤", - "woman_singer_medium-light_skin_tone": "👩🏼\u200d🎤", - "woman_singer_medium_skin_tone": "👩🏽\u200d🎤", - "woman_student": "👩\u200d🎓", - "woman_student_dark_skin_tone": "👩🏿\u200d🎓", - "woman_student_light_skin_tone": "👩🏻\u200d🎓", - "woman_student_medium-dark_skin_tone": "👩🏾\u200d🎓", - "woman_student_medium-light_skin_tone": "👩🏼\u200d🎓", - "woman_student_medium_skin_tone": "👩🏽\u200d🎓", - "woman_surfing": "🏄\u200d♀️", - "woman_surfing_dark_skin_tone": "🏄🏿\u200d♀️", - "woman_surfing_light_skin_tone": "🏄🏻\u200d♀️", - "woman_surfing_medium-dark_skin_tone": "🏄🏾\u200d♀️", - "woman_surfing_medium-light_skin_tone": "🏄🏼\u200d♀️", - "woman_surfing_medium_skin_tone": "🏄🏽\u200d♀️", - "woman_swimming": "🏊\u200d♀️", - "woman_swimming_dark_skin_tone": "🏊🏿\u200d♀️", - "woman_swimming_light_skin_tone": "🏊🏻\u200d♀️", - "woman_swimming_medium-dark_skin_tone": "🏊🏾\u200d♀️", - "woman_swimming_medium-light_skin_tone": "🏊🏼\u200d♀️", - "woman_swimming_medium_skin_tone": "🏊🏽\u200d♀️", - "woman_teacher": "👩\u200d🏫", - "woman_teacher_dark_skin_tone": "👩🏿\u200d🏫", - "woman_teacher_light_skin_tone": "👩🏻\u200d🏫", - "woman_teacher_medium-dark_skin_tone": "👩🏾\u200d🏫", - "woman_teacher_medium-light_skin_tone": "👩🏼\u200d🏫", - "woman_teacher_medium_skin_tone": "👩🏽\u200d🏫", - "woman_technologist": "👩\u200d💻", - "woman_technologist_dark_skin_tone": "👩🏿\u200d💻", - "woman_technologist_light_skin_tone": "👩🏻\u200d💻", - "woman_technologist_medium-dark_skin_tone": "👩🏾\u200d💻", - "woman_technologist_medium-light_skin_tone": "👩🏼\u200d💻", - "woman_technologist_medium_skin_tone": "👩🏽\u200d💻", - "woman_tipping_hand": "💁\u200d♀️", - "woman_tipping_hand_dark_skin_tone": "💁🏿\u200d♀️", - "woman_tipping_hand_light_skin_tone": "💁🏻\u200d♀️", - "woman_tipping_hand_medium-dark_skin_tone": "💁🏾\u200d♀️", - "woman_tipping_hand_medium-light_skin_tone": "💁🏼\u200d♀️", - "woman_tipping_hand_medium_skin_tone": "💁🏽\u200d♀️", - "woman_vampire": "🧛\u200d♀️", - "woman_vampire_dark_skin_tone": "🧛🏿\u200d♀️", - "woman_vampire_light_skin_tone": "🧛🏻\u200d♀️", - "woman_vampire_medium-dark_skin_tone": "🧛🏾\u200d♀️", - "woman_vampire_medium-light_skin_tone": "🧛🏼\u200d♀️", - "woman_vampire_medium_skin_tone": "🧛🏽\u200d♀️", - "woman_walking": "🚶\u200d♀️", - "woman_walking_dark_skin_tone": "🚶🏿\u200d♀️", - "woman_walking_light_skin_tone": "🚶🏻\u200d♀️", - "woman_walking_medium-dark_skin_tone": "🚶🏾\u200d♀️", - "woman_walking_medium-light_skin_tone": "🚶🏼\u200d♀️", - "woman_walking_medium_skin_tone": "🚶🏽\u200d♀️", - "woman_wearing_turban": "👳\u200d♀️", - "woman_wearing_turban_dark_skin_tone": "👳🏿\u200d♀️", - "woman_wearing_turban_light_skin_tone": "👳🏻\u200d♀️", - "woman_wearing_turban_medium-dark_skin_tone": "👳🏾\u200d♀️", - "woman_wearing_turban_medium-light_skin_tone": "👳🏼\u200d♀️", - "woman_wearing_turban_medium_skin_tone": "👳🏽\u200d♀️", - "woman_with_headscarf": "🧕", - "woman_with_headscarf_dark_skin_tone": "🧕🏿", - "woman_with_headscarf_light_skin_tone": "🧕🏻", - "woman_with_headscarf_medium-dark_skin_tone": "🧕🏾", - "woman_with_headscarf_medium-light_skin_tone": "🧕🏼", - "woman_with_headscarf_medium_skin_tone": "🧕🏽", - "woman_with_probing_cane": "👩\u200d🦯", - "woman_zombie": "🧟\u200d♀️", - "woman’s_boot": "👢", - "woman’s_clothes": "👚", - "woman’s_hat": "👒", - "woman’s_sandal": "👡", - "women_with_bunny_ears": "👯\u200d♀️", - "women_wrestling": "🤼\u200d♀️", - "women’s_room": "🚺", - "woozy_face": "🥴", - "world_map": "🗺", - "worried_face": "😟", - "wrapped_gift": "🎁", - "wrench": "🔧", - "writing_hand": "✍", - "writing_hand_dark_skin_tone": "✍🏿", - "writing_hand_light_skin_tone": "✍🏻", - "writing_hand_medium-dark_skin_tone": "✍🏾", - "writing_hand_medium-light_skin_tone": "✍🏼", - "writing_hand_medium_skin_tone": "✍🏽", - "yarn": "🧶", - "yawning_face": "🥱", - "yellow_circle": "🟡", - "yellow_heart": "💛", - "yellow_square": "🟨", - "yen_banknote": "💴", - "yo-yo": "🪀", - "yin_yang": "☯", - "zany_face": "🤪", - "zebra": "🦓", - "zipper-mouth_face": "🤐", - "zombie": "🧟", - "zzz": "💤", - "åland_islands": "🇦🇽", - "keycap_asterisk": "*⃣", - "keycap_digit_eight": "8⃣", - "keycap_digit_five": "5⃣", - "keycap_digit_four": "4⃣", - "keycap_digit_nine": "9⃣", - "keycap_digit_one": "1⃣", - "keycap_digit_seven": "7⃣", - "keycap_digit_six": "6⃣", - "keycap_digit_three": "3⃣", - "keycap_digit_two": "2⃣", - "keycap_digit_zero": "0⃣", - "keycap_number_sign": "#⃣", - "light_skin_tone": "🏻", - "medium_light_skin_tone": "🏼", - "medium_skin_tone": "🏽", - "medium_dark_skin_tone": "🏾", - "dark_skin_tone": "🏿", - "regional_indicator_symbol_letter_a": "🇦", - "regional_indicator_symbol_letter_b": "🇧", - "regional_indicator_symbol_letter_c": "🇨", - "regional_indicator_symbol_letter_d": "🇩", - "regional_indicator_symbol_letter_e": "🇪", - "regional_indicator_symbol_letter_f": "🇫", - "regional_indicator_symbol_letter_g": "🇬", - "regional_indicator_symbol_letter_h": "🇭", - "regional_indicator_symbol_letter_i": "🇮", - "regional_indicator_symbol_letter_j": "🇯", - "regional_indicator_symbol_letter_k": "🇰", - "regional_indicator_symbol_letter_l": "🇱", - "regional_indicator_symbol_letter_m": "🇲", - "regional_indicator_symbol_letter_n": "🇳", - "regional_indicator_symbol_letter_o": "🇴", - "regional_indicator_symbol_letter_p": "🇵", - "regional_indicator_symbol_letter_q": "🇶", - "regional_indicator_symbol_letter_r": "🇷", - "regional_indicator_symbol_letter_s": "🇸", - "regional_indicator_symbol_letter_t": "🇹", - "regional_indicator_symbol_letter_u": "🇺", - "regional_indicator_symbol_letter_v": "🇻", - "regional_indicator_symbol_letter_w": "🇼", - "regional_indicator_symbol_letter_x": "🇽", - "regional_indicator_symbol_letter_y": "🇾", - "regional_indicator_symbol_letter_z": "🇿", - "airplane_arriving": "🛬", - "space_invader": "👾", - "football": "🏈", - "anger": "💢", - "angry": "😠", - "anguished": "😧", - "signal_strength": "📶", - "arrows_counterclockwise": "🔄", - "arrow_heading_down": "⤵", - "arrow_heading_up": "⤴", - "art": "🎨", - "astonished": "😲", - "athletic_shoe": "👟", - "atm": "🏧", - "car": "🚗", - "red_car": "🚗", - "angel": "👼", - "back": "🔙", - "badminton_racquet_and_shuttlecock": "🏸", - "dollar": "💵", - "euro": "💶", - "pound": "💷", - "yen": "💴", - "barber": "💈", - "bath": "🛀", - "bear": "🐻", - "heartbeat": "💓", - "beer": "🍺", - "no_bell": "🔕", - "bento": "🍱", - "bike": "🚲", - "bicyclist": "🚴", - "8ball": "🎱", - "biohazard_sign": "☣", - "birthday": "🎂", - "black_circle_for_record": "⏺", - "clubs": "♣", - "diamonds": "♦", - "arrow_double_down": "⏬", - "hearts": "♥", - "rewind": "⏪", - "black_left__pointing_double_triangle_with_vertical_bar": "⏮", - "arrow_backward": "◀", - "black_medium_small_square": "◾", - "question": "❓", - "fast_forward": "⏩", - "black_right__pointing_double_triangle_with_vertical_bar": "⏭", - "arrow_forward": "▶", - "black_right__pointing_triangle_with_double_vertical_bar": "⏯", - "arrow_right": "➡", - "spades": "♠", - "black_square_for_stop": "⏹", - "sunny": "☀", - "phone": "☎", - "recycle": "♻", - "arrow_double_up": "⏫", - "busstop": "🚏", - "date": "📅", - "flags": "🎏", - "cat2": "🐈", - "joy_cat": "😹", - "smirk_cat": "😼", - "chart_with_downwards_trend": "📉", - "chart_with_upwards_trend": "📈", - "chart": "💹", - "mega": "📣", - "checkered_flag": "🏁", - "accept": "🉑", - "ideograph_advantage": "🉐", - "congratulations": "㊗", - "secret": "㊙", - "m": "Ⓜ", - "city_sunset": "🌆", - "clapper": "🎬", - "clap": "👏", - "beers": "🍻", - "clock830": "🕣", - "clock8": "🕗", - "clock1130": "🕦", - "clock11": "🕚", - "clock530": "🕠", - "clock5": "🕔", - "clock430": "🕟", - "clock4": "🕓", - "clock930": "🕤", - "clock9": "🕘", - "clock130": "🕜", - "clock1": "🕐", - "clock730": "🕢", - "clock7": "🕖", - "clock630": "🕡", - "clock6": "🕕", - "clock1030": "🕥", - "clock10": "🕙", - "clock330": "🕞", - "clock3": "🕒", - "clock1230": "🕧", - "clock12": "🕛", - "clock230": "🕝", - "clock2": "🕑", - "arrows_clockwise": "🔃", - "repeat": "🔁", - "repeat_one": "🔂", - "closed_lock_with_key": "🔐", - "mailbox_closed": "📪", - "mailbox": "📫", - "cloud_with_tornado": "🌪", - "cocktail": "🍸", - "boom": "💥", - "compression": "🗜", - "confounded": "😖", - "confused": "😕", - "rice": "🍚", - "cow2": "🐄", - "cricket_bat_and_ball": "🏏", - "x": "❌", - "cry": "😢", - "curry": "🍛", - "dagger_knife": "🗡", - "dancer": "💃", - "dark_sunglasses": "🕶", - "dash": "💨", - "truck": "🚚", - "derelict_house_building": "🏚", - "diamond_shape_with_a_dot_inside": "💠", - "dart": "🎯", - "disappointed_relieved": "😥", - "disappointed": "😞", - "do_not_litter": "🚯", - "dog2": "🐕", - "flipper": "🐬", - "loop": "➿", - "bangbang": "‼", - "double_vertical_bar": "⏸", - "dove_of_peace": "🕊", - "small_red_triangle_down": "🔻", - "arrow_down_small": "🔽", - "arrow_down": "⬇", - "dromedary_camel": "🐪", - "e__mail": "📧", - "corn": "🌽", - "ear_of_rice": "🌾", - "earth_americas": "🌎", - "earth_asia": "🌏", - "earth_africa": "🌍", - "eight_pointed_black_star": "✴", - "eight_spoked_asterisk": "✳", - "eject_symbol": "⏏", - "bulb": "💡", - "emoji_modifier_fitzpatrick_type__1__2": "🏻", - "emoji_modifier_fitzpatrick_type__3": "🏼", - "emoji_modifier_fitzpatrick_type__4": "🏽", - "emoji_modifier_fitzpatrick_type__5": "🏾", - "emoji_modifier_fitzpatrick_type__6": "🏿", - "end": "🔚", - "email": "✉", - "european_castle": "🏰", - "european_post_office": "🏤", - "interrobang": "⁉", - "expressionless": "😑", - "eyeglasses": "👓", - "massage": "💆", - "yum": "😋", - "scream": "😱", - "kissing_heart": "😘", - "sweat": "😓", - "face_with_head__bandage": "🤕", - "triumph": "😤", - "mask": "😷", - "no_good": "🙅", - "ok_woman": "🙆", - "open_mouth": "😮", - "cold_sweat": "😰", - "stuck_out_tongue": "😛", - "stuck_out_tongue_closed_eyes": "😝", - "stuck_out_tongue_winking_eye": "😜", - "joy": "😂", - "no_mouth": "😶", - "santa": "🎅", - "fax": "📠", - "fearful": "😨", - "field_hockey_stick_and_ball": "🏑", - "first_quarter_moon_with_face": "🌛", - "fish_cake": "🍥", - "fishing_pole_and_fish": "🎣", - "facepunch": "👊", - "punch": "👊", - "flag_for_afghanistan": "🇦🇫", - "flag_for_albania": "🇦🇱", - "flag_for_algeria": "🇩🇿", - "flag_for_american_samoa": "🇦🇸", - "flag_for_andorra": "🇦🇩", - "flag_for_angola": "🇦🇴", - "flag_for_anguilla": "🇦🇮", - "flag_for_antarctica": "🇦🇶", - "flag_for_antigua_&_barbuda": "🇦🇬", - "flag_for_argentina": "🇦🇷", - "flag_for_armenia": "🇦🇲", - "flag_for_aruba": "🇦🇼", - "flag_for_ascension_island": "🇦🇨", - "flag_for_australia": "🇦🇺", - "flag_for_austria": "🇦🇹", - "flag_for_azerbaijan": "🇦🇿", - "flag_for_bahamas": "🇧🇸", - "flag_for_bahrain": "🇧🇭", - "flag_for_bangladesh": "🇧🇩", - "flag_for_barbados": "🇧🇧", - "flag_for_belarus": "🇧🇾", - "flag_for_belgium": "🇧🇪", - "flag_for_belize": "🇧🇿", - "flag_for_benin": "🇧🇯", - "flag_for_bermuda": "🇧🇲", - "flag_for_bhutan": "🇧🇹", - "flag_for_bolivia": "🇧🇴", - "flag_for_bosnia_&_herzegovina": "🇧🇦", - "flag_for_botswana": "🇧🇼", - "flag_for_bouvet_island": "🇧🇻", - "flag_for_brazil": "🇧🇷", - "flag_for_british_indian_ocean_territory": "🇮🇴", - "flag_for_british_virgin_islands": "🇻🇬", - "flag_for_brunei": "🇧🇳", - "flag_for_bulgaria": "🇧🇬", - "flag_for_burkina_faso": "🇧🇫", - "flag_for_burundi": "🇧🇮", - "flag_for_cambodia": "🇰🇭", - "flag_for_cameroon": "🇨🇲", - "flag_for_canada": "🇨🇦", - "flag_for_canary_islands": "🇮🇨", - "flag_for_cape_verde": "🇨🇻", - "flag_for_caribbean_netherlands": "🇧🇶", - "flag_for_cayman_islands": "🇰🇾", - "flag_for_central_african_republic": "🇨🇫", - "flag_for_ceuta_&_melilla": "🇪🇦", - "flag_for_chad": "🇹🇩", - "flag_for_chile": "🇨🇱", - "flag_for_china": "🇨🇳", - "flag_for_christmas_island": "🇨🇽", - "flag_for_clipperton_island": "🇨🇵", - "flag_for_cocos__islands": "🇨🇨", - "flag_for_colombia": "🇨🇴", - "flag_for_comoros": "🇰🇲", - "flag_for_congo____brazzaville": "🇨🇬", - "flag_for_congo____kinshasa": "🇨🇩", - "flag_for_cook_islands": "🇨🇰", - "flag_for_costa_rica": "🇨🇷", - "flag_for_croatia": "🇭🇷", - "flag_for_cuba": "🇨🇺", - "flag_for_curaçao": "🇨🇼", - "flag_for_cyprus": "🇨🇾", - "flag_for_czech_republic": "🇨🇿", - "flag_for_côte_d’ivoire": "🇨🇮", - "flag_for_denmark": "🇩🇰", - "flag_for_diego_garcia": "🇩🇬", - "flag_for_djibouti": "🇩🇯", - "flag_for_dominica": "🇩🇲", - "flag_for_dominican_republic": "🇩🇴", - "flag_for_ecuador": "🇪🇨", - "flag_for_egypt": "🇪🇬", - "flag_for_el_salvador": "🇸🇻", - "flag_for_equatorial_guinea": "🇬🇶", - "flag_for_eritrea": "🇪🇷", - "flag_for_estonia": "🇪🇪", - "flag_for_ethiopia": "🇪🇹", - "flag_for_european_union": "🇪🇺", - "flag_for_falkland_islands": "🇫🇰", - "flag_for_faroe_islands": "🇫🇴", - "flag_for_fiji": "🇫🇯", - "flag_for_finland": "🇫🇮", - "flag_for_france": "🇫🇷", - "flag_for_french_guiana": "🇬🇫", - "flag_for_french_polynesia": "🇵🇫", - "flag_for_french_southern_territories": "🇹🇫", - "flag_for_gabon": "🇬🇦", - "flag_for_gambia": "🇬🇲", - "flag_for_georgia": "🇬🇪", - "flag_for_germany": "🇩🇪", - "flag_for_ghana": "🇬🇭", - "flag_for_gibraltar": "🇬🇮", - "flag_for_greece": "🇬🇷", - "flag_for_greenland": "🇬🇱", - "flag_for_grenada": "🇬🇩", - "flag_for_guadeloupe": "🇬🇵", - "flag_for_guam": "🇬🇺", - "flag_for_guatemala": "🇬🇹", - "flag_for_guernsey": "🇬🇬", - "flag_for_guinea": "🇬🇳", - "flag_for_guinea__bissau": "🇬🇼", - "flag_for_guyana": "🇬🇾", - "flag_for_haiti": "🇭🇹", - "flag_for_heard_&_mcdonald_islands": "🇭🇲", - "flag_for_honduras": "🇭🇳", - "flag_for_hong_kong": "🇭🇰", - "flag_for_hungary": "🇭🇺", - "flag_for_iceland": "🇮🇸", - "flag_for_india": "🇮🇳", - "flag_for_indonesia": "🇮🇩", - "flag_for_iran": "🇮🇷", - "flag_for_iraq": "🇮🇶", - "flag_for_ireland": "🇮🇪", - "flag_for_isle_of_man": "🇮🇲", - "flag_for_israel": "🇮🇱", - "flag_for_italy": "🇮🇹", - "flag_for_jamaica": "🇯🇲", - "flag_for_japan": "🇯🇵", - "flag_for_jersey": "🇯🇪", - "flag_for_jordan": "🇯🇴", - "flag_for_kazakhstan": "🇰🇿", - "flag_for_kenya": "🇰🇪", - "flag_for_kiribati": "🇰🇮", - "flag_for_kosovo": "🇽🇰", - "flag_for_kuwait": "🇰🇼", - "flag_for_kyrgyzstan": "🇰🇬", - "flag_for_laos": "🇱🇦", - "flag_for_latvia": "🇱🇻", - "flag_for_lebanon": "🇱🇧", - "flag_for_lesotho": "🇱🇸", - "flag_for_liberia": "🇱🇷", - "flag_for_libya": "🇱🇾", - "flag_for_liechtenstein": "🇱🇮", - "flag_for_lithuania": "🇱🇹", - "flag_for_luxembourg": "🇱🇺", - "flag_for_macau": "🇲🇴", - "flag_for_macedonia": "🇲🇰", - "flag_for_madagascar": "🇲🇬", - "flag_for_malawi": "🇲🇼", - "flag_for_malaysia": "🇲🇾", - "flag_for_maldives": "🇲🇻", - "flag_for_mali": "🇲🇱", - "flag_for_malta": "🇲🇹", - "flag_for_marshall_islands": "🇲🇭", - "flag_for_martinique": "🇲🇶", - "flag_for_mauritania": "🇲🇷", - "flag_for_mauritius": "🇲🇺", - "flag_for_mayotte": "🇾🇹", - "flag_for_mexico": "🇲🇽", - "flag_for_micronesia": "🇫🇲", - "flag_for_moldova": "🇲🇩", - "flag_for_monaco": "🇲🇨", - "flag_for_mongolia": "🇲🇳", - "flag_for_montenegro": "🇲🇪", - "flag_for_montserrat": "🇲🇸", - "flag_for_morocco": "🇲🇦", - "flag_for_mozambique": "🇲🇿", - "flag_for_myanmar": "🇲🇲", - "flag_for_namibia": "🇳🇦", - "flag_for_nauru": "🇳🇷", - "flag_for_nepal": "🇳🇵", - "flag_for_netherlands": "🇳🇱", - "flag_for_new_caledonia": "🇳🇨", - "flag_for_new_zealand": "🇳🇿", - "flag_for_nicaragua": "🇳🇮", - "flag_for_niger": "🇳🇪", - "flag_for_nigeria": "🇳🇬", - "flag_for_niue": "🇳🇺", - "flag_for_norfolk_island": "🇳🇫", - "flag_for_north_korea": "🇰🇵", - "flag_for_northern_mariana_islands": "🇲🇵", - "flag_for_norway": "🇳🇴", - "flag_for_oman": "🇴🇲", - "flag_for_pakistan": "🇵🇰", - "flag_for_palau": "🇵🇼", - "flag_for_palestinian_territories": "🇵🇸", - "flag_for_panama": "🇵🇦", - "flag_for_papua_new_guinea": "🇵🇬", - "flag_for_paraguay": "🇵🇾", - "flag_for_peru": "🇵🇪", - "flag_for_philippines": "🇵🇭", - "flag_for_pitcairn_islands": "🇵🇳", - "flag_for_poland": "🇵🇱", - "flag_for_portugal": "🇵🇹", - "flag_for_puerto_rico": "🇵🇷", - "flag_for_qatar": "🇶🇦", - "flag_for_romania": "🇷🇴", - "flag_for_russia": "🇷🇺", - "flag_for_rwanda": "🇷🇼", - "flag_for_réunion": "🇷🇪", - "flag_for_samoa": "🇼🇸", - "flag_for_san_marino": "🇸🇲", - "flag_for_saudi_arabia": "🇸🇦", - "flag_for_senegal": "🇸🇳", - "flag_for_serbia": "🇷🇸", - "flag_for_seychelles": "🇸🇨", - "flag_for_sierra_leone": "🇸🇱", - "flag_for_singapore": "🇸🇬", - "flag_for_sint_maarten": "🇸🇽", - "flag_for_slovakia": "🇸🇰", - "flag_for_slovenia": "🇸🇮", - "flag_for_solomon_islands": "🇸🇧", - "flag_for_somalia": "🇸🇴", - "flag_for_south_africa": "🇿🇦", - "flag_for_south_georgia_&_south_sandwich_islands": "🇬🇸", - "flag_for_south_korea": "🇰🇷", - "flag_for_south_sudan": "🇸🇸", - "flag_for_spain": "🇪🇸", - "flag_for_sri_lanka": "🇱🇰", - "flag_for_st._barthélemy": "🇧🇱", - "flag_for_st._helena": "🇸🇭", - "flag_for_st._kitts_&_nevis": "🇰🇳", - "flag_for_st._lucia": "🇱🇨", - "flag_for_st._martin": "🇲🇫", - "flag_for_st._pierre_&_miquelon": "🇵🇲", - "flag_for_st._vincent_&_grenadines": "🇻🇨", - "flag_for_sudan": "🇸🇩", - "flag_for_suriname": "🇸🇷", - "flag_for_svalbard_&_jan_mayen": "🇸🇯", - "flag_for_swaziland": "🇸🇿", - "flag_for_sweden": "🇸🇪", - "flag_for_switzerland": "🇨🇭", - "flag_for_syria": "🇸🇾", - "flag_for_são_tomé_&_príncipe": "🇸🇹", - "flag_for_taiwan": "🇹🇼", - "flag_for_tajikistan": "🇹🇯", - "flag_for_tanzania": "🇹🇿", - "flag_for_thailand": "🇹🇭", - "flag_for_timor__leste": "🇹🇱", - "flag_for_togo": "🇹🇬", - "flag_for_tokelau": "🇹🇰", - "flag_for_tonga": "🇹🇴", - "flag_for_trinidad_&_tobago": "🇹🇹", - "flag_for_tristan_da_cunha": "🇹🇦", - "flag_for_tunisia": "🇹🇳", - "flag_for_turkey": "🇹🇷", - "flag_for_turkmenistan": "🇹🇲", - "flag_for_turks_&_caicos_islands": "🇹🇨", - "flag_for_tuvalu": "🇹🇻", - "flag_for_u.s._outlying_islands": "🇺🇲", - "flag_for_u.s._virgin_islands": "🇻🇮", - "flag_for_uganda": "🇺🇬", - "flag_for_ukraine": "🇺🇦", - "flag_for_united_arab_emirates": "🇦🇪", - "flag_for_united_kingdom": "🇬🇧", - "flag_for_united_states": "🇺🇸", - "flag_for_uruguay": "🇺🇾", - "flag_for_uzbekistan": "🇺🇿", - "flag_for_vanuatu": "🇻🇺", - "flag_for_vatican_city": "🇻🇦", - "flag_for_venezuela": "🇻🇪", - "flag_for_vietnam": "🇻🇳", - "flag_for_wallis_&_futuna": "🇼🇫", - "flag_for_western_sahara": "🇪🇭", - "flag_for_yemen": "🇾🇪", - "flag_for_zambia": "🇿🇲", - "flag_for_zimbabwe": "🇿🇼", - "flag_for_åland_islands": "🇦🇽", - "golf": "⛳", - "fleur__de__lis": "⚜", - "muscle": "💪", - "flushed": "😳", - "frame_with_picture": "🖼", - "fries": "🍟", - "frog": "🐸", - "hatched_chick": "🐥", - "frowning": "😦", - "fuelpump": "⛽", - "full_moon_with_face": "🌝", - "gem": "💎", - "star2": "🌟", - "golfer": "🏌", - "mortar_board": "🎓", - "grimacing": "😬", - "smile_cat": "😸", - "grinning": "😀", - "grin": "😁", - "heartpulse": "💗", - "guardsman": "💂", - "haircut": "💇", - "hamster": "🐹", - "raising_hand": "🙋", - "headphones": "🎧", - "hear_no_evil": "🙉", - "cupid": "💘", - "gift_heart": "💝", - "heart": "❤", - "exclamation": "❗", - "heavy_exclamation_mark": "❗", - "heavy_heart_exclamation_mark_ornament": "❣", - "o": "⭕", - "helm_symbol": "⎈", - "helmet_with_white_cross": "⛑", - "high_heel": "👠", - "bullettrain_side": "🚄", - "bullettrain_front": "🚅", - "high_brightness": "🔆", - "zap": "⚡", - "hocho": "🔪", - "knife": "🔪", - "bee": "🐝", - "traffic_light": "🚥", - "racehorse": "🐎", - "coffee": "☕", - "hotsprings": "♨", - "hourglass": "⌛", - "hourglass_flowing_sand": "⏳", - "house_buildings": "🏘", - "100": "💯", - "hushed": "😯", - "ice_hockey_stick_and_puck": "🏒", - "imp": "👿", - "information_desk_person": "💁", - "information_source": "ℹ", - "capital_abcd": "🔠", - "abc": "🔤", - "abcd": "🔡", - "1234": "🔢", - "symbols": "🔣", - "izakaya_lantern": "🏮", - "lantern": "🏮", - "jack_o_lantern": "🎃", - "dolls": "🎎", - "japanese_goblin": "👺", - "japanese_ogre": "👹", - "beginner": "🔰", - "zero": "0️⃣", - "one": "1️⃣", - "ten": "🔟", - "two": "2️⃣", - "three": "3️⃣", - "four": "4️⃣", - "five": "5️⃣", - "six": "6️⃣", - "seven": "7️⃣", - "eight": "8️⃣", - "nine": "9️⃣", - "couplekiss": "💏", - "kissing_cat": "😽", - "kissing": "😗", - "kissing_closed_eyes": "😚", - "kissing_smiling_eyes": "😙", - "beetle": "🐞", - "large_blue_circle": "🔵", - "last_quarter_moon_with_face": "🌜", - "leaves": "🍃", - "mag": "🔍", - "left_right_arrow": "↔", - "leftwards_arrow_with_hook": "↩", - "arrow_left": "⬅", - "lock": "🔒", - "lock_with_ink_pen": "🔏", - "sob": "😭", - "low_brightness": "🔅", - "lower_left_ballpoint_pen": "🖊", - "lower_left_crayon": "🖍", - "lower_left_fountain_pen": "🖋", - "lower_left_paintbrush": "🖌", - "mahjong": "🀄", - "couple": "👫", - "man_in_business_suit_levitating": "🕴", - "man_with_gua_pi_mao": "👲", - "man_with_turban": "👳", - "mans_shoe": "👞", - "shoe": "👞", - "menorah_with_nine_branches": "🕎", - "mens": "🚹", - "minidisc": "💽", - "iphone": "📱", - "calling": "📲", - "money__mouth_face": "🤑", - "moneybag": "💰", - "rice_scene": "🎑", - "mountain_bicyclist": "🚵", - "mouse2": "🐁", - "lips": "👄", - "moyai": "🗿", - "notes": "🎶", - "nail_care": "💅", - "ab": "🆎", - "negative_squared_cross_mark": "❎", - "a": "🅰", - "b": "🅱", - "o2": "🅾", - "parking": "🅿", - "new_moon_with_face": "🌚", - "no_entry_sign": "🚫", - "underage": "🔞", - "non__potable_water": "🚱", - "arrow_upper_right": "↗", - "arrow_upper_left": "↖", - "office": "🏢", - "older_man": "👴", - "older_woman": "👵", - "om_symbol": "🕉", - "on": "🔛", - "book": "📖", - "unlock": "🔓", - "mailbox_with_no_mail": "📭", - "mailbox_with_mail": "📬", - "cd": "💿", - "tada": "🎉", - "feet": "🐾", - "walking": "🚶", - "pencil2": "✏", - "pensive": "😔", - "persevere": "😣", - "bow": "🙇", - "raised_hands": "🙌", - "person_with_ball": "⛹", - "person_with_blond_hair": "👱", - "pray": "🙏", - "person_with_pouting_face": "🙎", - "computer": "💻", - "pig2": "🐖", - "hankey": "💩", - "poop": "💩", - "shit": "💩", - "bamboo": "🎍", - "gun": "🔫", - "black_joker": "🃏", - "rotating_light": "🚨", - "cop": "👮", - "stew": "🍲", - "pouch": "👝", - "pouting_cat": "😾", - "rage": "😡", - "put_litter_in_its_place": "🚮", - "rabbit2": "🐇", - "racing_motorcycle": "🏍", - "radioactive_sign": "☢", - "fist": "✊", - "hand": "✋", - "raised_hand_with_fingers_splayed": "🖐", - "raised_hand_with_part_between_middle_and_ring_fingers": "🖖", - "blue_car": "🚙", - "apple": "🍎", - "relieved": "😌", - "reversed_hand_with_middle_finger_extended": "🖕", - "mag_right": "🔎", - "arrow_right_hook": "↪", - "sweet_potato": "🍠", - "robot": "🤖", - "rolled__up_newspaper": "🗞", - "rowboat": "🚣", - "runner": "🏃", - "running": "🏃", - "running_shirt_with_sash": "🎽", - "boat": "⛵", - "scales": "⚖", - "school_satchel": "🎒", - "scorpius": "♏", - "see_no_evil": "🙈", - "sheep": "🐑", - "stars": "🌠", - "cake": "🍰", - "six_pointed_star": "🔯", - "ski": "🎿", - "sleeping_accommodation": "🛌", - "sleeping": "😴", - "sleepy": "😪", - "sleuth_or_spy": "🕵", - "heart_eyes_cat": "😻", - "smiley_cat": "😺", - "innocent": "😇", - "heart_eyes": "😍", - "smiling_imp": "😈", - "smiley": "😃", - "sweat_smile": "😅", - "smile": "😄", - "laughing": "😆", - "satisfied": "😆", - "blush": "😊", - "smirk": "😏", - "smoking": "🚬", - "snow_capped_mountain": "🏔", - "soccer": "⚽", - "icecream": "🍦", - "soon": "🔜", - "arrow_lower_right": "↘", - "arrow_lower_left": "↙", - "speak_no_evil": "🙊", - "speaker": "🔈", - "mute": "🔇", - "sound": "🔉", - "loud_sound": "🔊", - "speaking_head_in_silhouette": "🗣", - "spiral_calendar_pad": "🗓", - "spiral_note_pad": "🗒", - "shell": "🐚", - "sweat_drops": "💦", - "u5272": "🈹", - "u5408": "🈴", - "u55b6": "🈺", - "u6307": "🈯", - "u6708": "🈷", - "u6709": "🈶", - "u6e80": "🈵", - "u7121": "🈚", - "u7533": "🈸", - "u7981": "🈲", - "u7a7a": "🈳", - "cl": "🆑", - "cool": "🆒", - "free": "🆓", - "id": "🆔", - "koko": "🈁", - "sa": "🈂", - "new": "🆕", - "ng": "🆖", - "ok": "🆗", - "sos": "🆘", - "up": "🆙", - "vs": "🆚", - "steam_locomotive": "🚂", - "ramen": "🍜", - "partly_sunny": "⛅", - "city_sunrise": "🌇", - "surfer": "🏄", - "swimmer": "🏊", - "shirt": "👕", - "tshirt": "👕", - "table_tennis_paddle_and_ball": "🏓", - "tea": "🍵", - "tv": "📺", - "three_button_mouse": "🖱", - "+1": "👍", - "thumbsup": "👍", - "__1": "👎", - "-1": "👎", - "thumbsdown": "👎", - "thunder_cloud_and_rain": "⛈", - "tiger2": "🐅", - "tophat": "🎩", - "top": "🔝", - "tm": "™", - "train2": "🚆", - "triangular_flag_on_post": "🚩", - "trident": "🔱", - "twisted_rightwards_arrows": "🔀", - "unamused": "😒", - "small_red_triangle": "🔺", - "arrow_up_small": "🔼", - "arrow_up_down": "↕", - "upside__down_face": "🙃", - "arrow_up": "⬆", - "v": "✌", - "vhs": "📼", - "wc": "🚾", - "ocean": "🌊", - "waving_black_flag": "🏴", - "wave": "👋", - "waving_white_flag": "🏳", - "moon": "🌔", - "scream_cat": "🙀", - "weary": "😩", - "weight_lifter": "🏋", - "whale2": "🐋", - "wheelchair": "♿", - "point_down": "👇", - "grey_exclamation": "❕", - "white_frowning_face": "☹", - "white_check_mark": "✅", - "point_left": "👈", - "white_medium_small_square": "◽", - "star": "⭐", - "grey_question": "❔", - "point_right": "👉", - "relaxed": "☺", - "white_sun_behind_cloud": "🌥", - "white_sun_behind_cloud_with_rain": "🌦", - "white_sun_with_small_cloud": "🌤", - "point_up_2": "👆", - "point_up": "☝", - "wind_blowing_face": "🌬", - "wink": "😉", - "wolf": "🐺", - "dancers": "👯", - "boot": "👢", - "womans_clothes": "👚", - "womans_hat": "👒", - "sandal": "👡", - "womens": "🚺", - "worried": "😟", - "gift": "🎁", - "zipper__mouth_face": "🤐", - "regional_indicator_a": "🇦", - "regional_indicator_b": "🇧", - "regional_indicator_c": "🇨", - "regional_indicator_d": "🇩", - "regional_indicator_e": "🇪", - "regional_indicator_f": "🇫", - "regional_indicator_g": "🇬", - "regional_indicator_h": "🇭", - "regional_indicator_i": "🇮", - "regional_indicator_j": "🇯", - "regional_indicator_k": "🇰", - "regional_indicator_l": "🇱", - "regional_indicator_m": "🇲", - "regional_indicator_n": "🇳", - "regional_indicator_o": "🇴", - "regional_indicator_p": "🇵", - "regional_indicator_q": "🇶", - "regional_indicator_r": "🇷", - "regional_indicator_s": "🇸", - "regional_indicator_t": "🇹", - "regional_indicator_u": "🇺", - "regional_indicator_v": "🇻", - "regional_indicator_w": "🇼", - "regional_indicator_x": "🇽", - "regional_indicator_y": "🇾", - "regional_indicator_z": "🇿", -} diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py b/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py deleted file mode 100644 index bb2cafa..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_emoji_replace.py +++ /dev/null @@ -1,32 +0,0 @@ -from typing import Callable, Match, Optional -import re - -from ._emoji_codes import EMOJI - - -_ReStringMatch = Match[str] # regex match object -_ReSubCallable = Callable[[_ReStringMatch], str] # Callable invoked by re.sub -_EmojiSubMethod = Callable[[_ReSubCallable, str], str] # Sub method of a compiled re - - -def _emoji_replace( - text: str, - default_variant: Optional[str] = None, - _emoji_sub: _EmojiSubMethod = re.compile(r"(:(\S*?)(?:(?:\-)(emoji|text))?:)").sub, -) -> str: - """Replace emoji code in text.""" - get_emoji = EMOJI.__getitem__ - variants = {"text": "\uFE0E", "emoji": "\uFE0F"} - get_variant = variants.get - default_variant_code = variants.get(default_variant, "") if default_variant else "" - - def do_replace(match: Match[str]) -> str: - emoji_code, emoji_name, variant = match.groups() - try: - return get_emoji(emoji_name.lower()) + get_variant( - variant, default_variant_code - ) - except KeyError: - return emoji_code - - return _emoji_sub(do_replace, text) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_export_format.py b/.venv/Lib/site-packages/pip/_vendor/rich/_export_format.py deleted file mode 100644 index b79c130..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_export_format.py +++ /dev/null @@ -1,78 +0,0 @@ -CONSOLE_HTML_FORMAT = """\ - - - - - - - - -
{code}
-
- - -""" - -CONSOLE_SVG_FORMAT = """\ - - - - - - - - - {lines} - - - {chrome} - - {backgrounds} - - {matrix} - - - -""" - -_SVG_FONT_FAMILY = "Rich Fira Code" -_SVG_CLASSES_PREFIX = "rich-svg" diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_extension.py b/.venv/Lib/site-packages/pip/_vendor/rich/_extension.py deleted file mode 100644 index cbd6da9..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_extension.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Any - - -def load_ipython_extension(ip: Any) -> None: # pragma: no cover - # prevent circular import - from pip._vendor.rich.pretty import install - from pip._vendor.rich.traceback import install as tr_install - - install() - tr_install() diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_inspect.py b/.venv/Lib/site-packages/pip/_vendor/rich/_inspect.py deleted file mode 100644 index 30446ce..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_inspect.py +++ /dev/null @@ -1,270 +0,0 @@ -from __future__ import absolute_import - -import inspect -from inspect import cleandoc, getdoc, getfile, isclass, ismodule, signature -from typing import Any, Collection, Iterable, Optional, Tuple, Type, Union - -from .console import Group, RenderableType -from .control import escape_control_codes -from .highlighter import ReprHighlighter -from .jupyter import JupyterMixin -from .panel import Panel -from .pretty import Pretty -from .table import Table -from .text import Text, TextType - - -def _first_paragraph(doc: str) -> str: - """Get the first paragraph from a docstring.""" - paragraph, _, _ = doc.partition("\n\n") - return paragraph - - -class Inspect(JupyterMixin): - """A renderable to inspect any Python Object. - - Args: - obj (Any): An object to inspect. - title (str, optional): Title to display over inspect result, or None use type. Defaults to None. - help (bool, optional): Show full help text rather than just first paragraph. Defaults to False. - methods (bool, optional): Enable inspection of callables. Defaults to False. - docs (bool, optional): Also render doc strings. Defaults to True. - private (bool, optional): Show private attributes (beginning with underscore). Defaults to False. - dunder (bool, optional): Show attributes starting with double underscore. Defaults to False. - sort (bool, optional): Sort attributes alphabetically. Defaults to True. - all (bool, optional): Show all attributes. Defaults to False. - value (bool, optional): Pretty print value of object. Defaults to True. - """ - - def __init__( - self, - obj: Any, - *, - title: Optional[TextType] = None, - help: bool = False, - methods: bool = False, - docs: bool = True, - private: bool = False, - dunder: bool = False, - sort: bool = True, - all: bool = True, - value: bool = True, - ) -> None: - self.highlighter = ReprHighlighter() - self.obj = obj - self.title = title or self._make_title(obj) - if all: - methods = private = dunder = True - self.help = help - self.methods = methods - self.docs = docs or help - self.private = private or dunder - self.dunder = dunder - self.sort = sort - self.value = value - - def _make_title(self, obj: Any) -> Text: - """Make a default title.""" - title_str = ( - str(obj) - if (isclass(obj) or callable(obj) or ismodule(obj)) - else str(type(obj)) - ) - title_text = self.highlighter(title_str) - return title_text - - def __rich__(self) -> Panel: - return Panel.fit( - Group(*self._render()), - title=self.title, - border_style="scope.border", - padding=(0, 1), - ) - - def _get_signature(self, name: str, obj: Any) -> Optional[Text]: - """Get a signature for a callable.""" - try: - _signature = str(signature(obj)) + ":" - except ValueError: - _signature = "(...)" - except TypeError: - return None - - source_filename: Optional[str] = None - try: - source_filename = getfile(obj) - except (OSError, TypeError): - # OSError is raised if obj has no source file, e.g. when defined in REPL. - pass - - callable_name = Text(name, style="inspect.callable") - if source_filename: - callable_name.stylize(f"link file://{source_filename}") - signature_text = self.highlighter(_signature) - - qualname = name or getattr(obj, "__qualname__", name) - - # If obj is a module, there may be classes (which are callable) to display - if inspect.isclass(obj): - prefix = "class" - elif inspect.iscoroutinefunction(obj): - prefix = "async def" - else: - prefix = "def" - - qual_signature = Text.assemble( - (f"{prefix} ", f"inspect.{prefix.replace(' ', '_')}"), - (qualname, "inspect.callable"), - signature_text, - ) - - return qual_signature - - def _render(self) -> Iterable[RenderableType]: - """Render object.""" - - def sort_items(item: Tuple[str, Any]) -> Tuple[bool, str]: - key, (_error, value) = item - return (callable(value), key.strip("_").lower()) - - def safe_getattr(attr_name: str) -> Tuple[Any, Any]: - """Get attribute or any exception.""" - try: - return (None, getattr(obj, attr_name)) - except Exception as error: - return (error, None) - - obj = self.obj - keys = dir(obj) - total_items = len(keys) - if not self.dunder: - keys = [key for key in keys if not key.startswith("__")] - if not self.private: - keys = [key for key in keys if not key.startswith("_")] - not_shown_count = total_items - len(keys) - items = [(key, safe_getattr(key)) for key in keys] - if self.sort: - items.sort(key=sort_items) - - items_table = Table.grid(padding=(0, 1), expand=False) - items_table.add_column(justify="right") - add_row = items_table.add_row - highlighter = self.highlighter - - if callable(obj): - signature = self._get_signature("", obj) - if signature is not None: - yield signature - yield "" - - if self.docs: - _doc = self._get_formatted_doc(obj) - if _doc is not None: - doc_text = Text(_doc, style="inspect.help") - doc_text = highlighter(doc_text) - yield doc_text - yield "" - - if self.value and not (isclass(obj) or callable(obj) or ismodule(obj)): - yield Panel( - Pretty(obj, indent_guides=True, max_length=10, max_string=60), - border_style="inspect.value.border", - ) - yield "" - - for key, (error, value) in items: - key_text = Text.assemble( - ( - key, - "inspect.attr.dunder" if key.startswith("__") else "inspect.attr", - ), - (" =", "inspect.equals"), - ) - if error is not None: - warning = key_text.copy() - warning.stylize("inspect.error") - add_row(warning, highlighter(repr(error))) - continue - - if callable(value): - if not self.methods: - continue - - _signature_text = self._get_signature(key, value) - if _signature_text is None: - add_row(key_text, Pretty(value, highlighter=highlighter)) - else: - if self.docs: - docs = self._get_formatted_doc(value) - if docs is not None: - _signature_text.append("\n" if "\n" in docs else " ") - doc = highlighter(docs) - doc.stylize("inspect.doc") - _signature_text.append(doc) - - add_row(key_text, _signature_text) - else: - add_row(key_text, Pretty(value, highlighter=highlighter)) - if items_table.row_count: - yield items_table - elif not_shown_count: - yield Text.from_markup( - f"[b cyan]{not_shown_count}[/][i] attribute(s) not shown.[/i] " - f"Run [b][magenta]inspect[/]([not b]inspect[/])[/b] for options." - ) - - def _get_formatted_doc(self, object_: Any) -> Optional[str]: - """ - Extract the docstring of an object, process it and returns it. - The processing consists in cleaning up the doctring's indentation, - taking only its 1st paragraph if `self.help` is not True, - and escape its control codes. - - Args: - object_ (Any): the object to get the docstring from. - - Returns: - Optional[str]: the processed docstring, or None if no docstring was found. - """ - docs = getdoc(object_) - if docs is None: - return None - docs = cleandoc(docs).strip() - if not self.help: - docs = _first_paragraph(docs) - return escape_control_codes(docs) - - -def get_object_types_mro(obj: Union[object, Type[Any]]) -> Tuple[type, ...]: - """Returns the MRO of an object's class, or of the object itself if it's a class.""" - if not hasattr(obj, "__mro__"): - # N.B. we cannot use `if type(obj) is type` here because it doesn't work with - # some types of classes, such as the ones that use abc.ABCMeta. - obj = type(obj) - return getattr(obj, "__mro__", ()) - - -def get_object_types_mro_as_strings(obj: object) -> Collection[str]: - """ - Returns the MRO of an object's class as full qualified names, or of the object itself if it's a class. - - Examples: - `object_types_mro_as_strings(JSONDecoder)` will return `['json.decoder.JSONDecoder', 'builtins.object']` - """ - return [ - f'{getattr(type_, "__module__", "")}.{getattr(type_, "__qualname__", "")}' - for type_ in get_object_types_mro(obj) - ] - - -def is_object_one_of_types( - obj: object, fully_qualified_types_names: Collection[str] -) -> bool: - """ - Returns `True` if the given object's class (or the object itself, if it's a class) has one of the - fully qualified names in its MRO. - """ - for type_name in get_object_types_mro_as_strings(obj): - if type_name in fully_qualified_types_names: - return True - return False diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_log_render.py b/.venv/Lib/site-packages/pip/_vendor/rich/_log_render.py deleted file mode 100644 index fc16c84..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_log_render.py +++ /dev/null @@ -1,94 +0,0 @@ -from datetime import datetime -from typing import Iterable, List, Optional, TYPE_CHECKING, Union, Callable - - -from .text import Text, TextType - -if TYPE_CHECKING: - from .console import Console, ConsoleRenderable, RenderableType - from .table import Table - -FormatTimeCallable = Callable[[datetime], Text] - - -class LogRender: - def __init__( - self, - show_time: bool = True, - show_level: bool = False, - show_path: bool = True, - time_format: Union[str, FormatTimeCallable] = "[%x %X]", - omit_repeated_times: bool = True, - level_width: Optional[int] = 8, - ) -> None: - self.show_time = show_time - self.show_level = show_level - self.show_path = show_path - self.time_format = time_format - self.omit_repeated_times = omit_repeated_times - self.level_width = level_width - self._last_time: Optional[Text] = None - - def __call__( - self, - console: "Console", - renderables: Iterable["ConsoleRenderable"], - log_time: Optional[datetime] = None, - time_format: Optional[Union[str, FormatTimeCallable]] = None, - level: TextType = "", - path: Optional[str] = None, - line_no: Optional[int] = None, - link_path: Optional[str] = None, - ) -> "Table": - from .containers import Renderables - from .table import Table - - output = Table.grid(padding=(0, 1)) - output.expand = True - if self.show_time: - output.add_column(style="log.time") - if self.show_level: - output.add_column(style="log.level", width=self.level_width) - output.add_column(ratio=1, style="log.message", overflow="fold") - if self.show_path and path: - output.add_column(style="log.path") - row: List["RenderableType"] = [] - if self.show_time: - log_time = log_time or console.get_datetime() - time_format = time_format or self.time_format - if callable(time_format): - log_time_display = time_format(log_time) - else: - log_time_display = Text(log_time.strftime(time_format)) - if log_time_display == self._last_time and self.omit_repeated_times: - row.append(Text(" " * len(log_time_display))) - else: - row.append(log_time_display) - self._last_time = log_time_display - if self.show_level: - row.append(level) - - row.append(Renderables(renderables)) - if self.show_path and path: - path_text = Text() - path_text.append( - path, style=f"link file://{link_path}" if link_path else "" - ) - if line_no: - path_text.append(":") - path_text.append( - f"{line_no}", - style=f"link file://{link_path}#{line_no}" if link_path else "", - ) - row.append(path_text) - - output.add_row(*row) - return output - - -if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.console import Console - - c = Console() - c.print("[on blue]Hello", justify="right") - c.log("[on blue]hello", justify="right") diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_loop.py b/.venv/Lib/site-packages/pip/_vendor/rich/_loop.py deleted file mode 100644 index 01c6caf..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_loop.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import Iterable, Tuple, TypeVar - -T = TypeVar("T") - - -def loop_first(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: - """Iterate and generate a tuple with a flag for first value.""" - iter_values = iter(values) - try: - value = next(iter_values) - except StopIteration: - return - yield True, value - for value in iter_values: - yield False, value - - -def loop_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: - """Iterate and generate a tuple with a flag for last value.""" - iter_values = iter(values) - try: - previous_value = next(iter_values) - except StopIteration: - return - for value in iter_values: - yield False, previous_value - previous_value = value - yield True, previous_value - - -def loop_first_last(values: Iterable[T]) -> Iterable[Tuple[bool, bool, T]]: - """Iterate and generate a tuple with a flag for first and last value.""" - iter_values = iter(values) - try: - previous_value = next(iter_values) - except StopIteration: - return - first = True - for value in iter_values: - yield first, False, previous_value - first = False - previous_value = value - yield first, True, previous_value diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_palettes.py b/.venv/Lib/site-packages/pip/_vendor/rich/_palettes.py deleted file mode 100644 index 3c748d3..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_palettes.py +++ /dev/null @@ -1,309 +0,0 @@ -from .palette import Palette - - -# Taken from https://en.wikipedia.org/wiki/ANSI_escape_code (Windows 10 column) -WINDOWS_PALETTE = Palette( - [ - (12, 12, 12), - (197, 15, 31), - (19, 161, 14), - (193, 156, 0), - (0, 55, 218), - (136, 23, 152), - (58, 150, 221), - (204, 204, 204), - (118, 118, 118), - (231, 72, 86), - (22, 198, 12), - (249, 241, 165), - (59, 120, 255), - (180, 0, 158), - (97, 214, 214), - (242, 242, 242), - ] -) - -# # The standard ansi colors (including bright variants) -STANDARD_PALETTE = Palette( - [ - (0, 0, 0), - (170, 0, 0), - (0, 170, 0), - (170, 85, 0), - (0, 0, 170), - (170, 0, 170), - (0, 170, 170), - (170, 170, 170), - (85, 85, 85), - (255, 85, 85), - (85, 255, 85), - (255, 255, 85), - (85, 85, 255), - (255, 85, 255), - (85, 255, 255), - (255, 255, 255), - ] -) - - -# The 256 color palette -EIGHT_BIT_PALETTE = Palette( - [ - (0, 0, 0), - (128, 0, 0), - (0, 128, 0), - (128, 128, 0), - (0, 0, 128), - (128, 0, 128), - (0, 128, 128), - (192, 192, 192), - (128, 128, 128), - (255, 0, 0), - (0, 255, 0), - (255, 255, 0), - (0, 0, 255), - (255, 0, 255), - (0, 255, 255), - (255, 255, 255), - (0, 0, 0), - (0, 0, 95), - (0, 0, 135), - (0, 0, 175), - (0, 0, 215), - (0, 0, 255), - (0, 95, 0), - (0, 95, 95), - (0, 95, 135), - (0, 95, 175), - (0, 95, 215), - (0, 95, 255), - (0, 135, 0), - (0, 135, 95), - (0, 135, 135), - (0, 135, 175), - (0, 135, 215), - (0, 135, 255), - (0, 175, 0), - (0, 175, 95), - (0, 175, 135), - (0, 175, 175), - (0, 175, 215), - (0, 175, 255), - (0, 215, 0), - (0, 215, 95), - (0, 215, 135), - (0, 215, 175), - (0, 215, 215), - (0, 215, 255), - (0, 255, 0), - (0, 255, 95), - (0, 255, 135), - (0, 255, 175), - (0, 255, 215), - (0, 255, 255), - (95, 0, 0), - (95, 0, 95), - (95, 0, 135), - (95, 0, 175), - (95, 0, 215), - (95, 0, 255), - (95, 95, 0), - (95, 95, 95), - (95, 95, 135), - (95, 95, 175), - (95, 95, 215), - (95, 95, 255), - (95, 135, 0), - (95, 135, 95), - (95, 135, 135), - (95, 135, 175), - (95, 135, 215), - (95, 135, 255), - (95, 175, 0), - (95, 175, 95), - (95, 175, 135), - (95, 175, 175), - (95, 175, 215), - (95, 175, 255), - (95, 215, 0), - (95, 215, 95), - (95, 215, 135), - (95, 215, 175), - (95, 215, 215), - (95, 215, 255), - (95, 255, 0), - (95, 255, 95), - (95, 255, 135), - (95, 255, 175), - (95, 255, 215), - (95, 255, 255), - (135, 0, 0), - (135, 0, 95), - (135, 0, 135), - (135, 0, 175), - (135, 0, 215), - (135, 0, 255), - (135, 95, 0), - (135, 95, 95), - (135, 95, 135), - (135, 95, 175), - (135, 95, 215), - (135, 95, 255), - (135, 135, 0), - (135, 135, 95), - (135, 135, 135), - (135, 135, 175), - (135, 135, 215), - (135, 135, 255), - (135, 175, 0), - (135, 175, 95), - (135, 175, 135), - (135, 175, 175), - (135, 175, 215), - (135, 175, 255), - (135, 215, 0), - (135, 215, 95), - (135, 215, 135), - (135, 215, 175), - (135, 215, 215), - (135, 215, 255), - (135, 255, 0), - (135, 255, 95), - (135, 255, 135), - (135, 255, 175), - (135, 255, 215), - (135, 255, 255), - (175, 0, 0), - (175, 0, 95), - (175, 0, 135), - (175, 0, 175), - (175, 0, 215), - (175, 0, 255), - (175, 95, 0), - (175, 95, 95), - (175, 95, 135), - (175, 95, 175), - (175, 95, 215), - (175, 95, 255), - (175, 135, 0), - (175, 135, 95), - (175, 135, 135), - (175, 135, 175), - (175, 135, 215), - (175, 135, 255), - (175, 175, 0), - (175, 175, 95), - (175, 175, 135), - (175, 175, 175), - (175, 175, 215), - (175, 175, 255), - (175, 215, 0), - (175, 215, 95), - (175, 215, 135), - (175, 215, 175), - (175, 215, 215), - (175, 215, 255), - (175, 255, 0), - (175, 255, 95), - (175, 255, 135), - (175, 255, 175), - (175, 255, 215), - (175, 255, 255), - (215, 0, 0), - (215, 0, 95), - (215, 0, 135), - (215, 0, 175), - (215, 0, 215), - (215, 0, 255), - (215, 95, 0), - (215, 95, 95), - (215, 95, 135), - (215, 95, 175), - (215, 95, 215), - (215, 95, 255), - (215, 135, 0), - (215, 135, 95), - (215, 135, 135), - (215, 135, 175), - (215, 135, 215), - (215, 135, 255), - (215, 175, 0), - (215, 175, 95), - (215, 175, 135), - (215, 175, 175), - (215, 175, 215), - (215, 175, 255), - (215, 215, 0), - (215, 215, 95), - (215, 215, 135), - (215, 215, 175), - (215, 215, 215), - (215, 215, 255), - (215, 255, 0), - (215, 255, 95), - (215, 255, 135), - (215, 255, 175), - (215, 255, 215), - (215, 255, 255), - (255, 0, 0), - (255, 0, 95), - (255, 0, 135), - (255, 0, 175), - (255, 0, 215), - (255, 0, 255), - (255, 95, 0), - (255, 95, 95), - (255, 95, 135), - (255, 95, 175), - (255, 95, 215), - (255, 95, 255), - (255, 135, 0), - (255, 135, 95), - (255, 135, 135), - (255, 135, 175), - (255, 135, 215), - (255, 135, 255), - (255, 175, 0), - (255, 175, 95), - (255, 175, 135), - (255, 175, 175), - (255, 175, 215), - (255, 175, 255), - (255, 215, 0), - (255, 215, 95), - (255, 215, 135), - (255, 215, 175), - (255, 215, 215), - (255, 215, 255), - (255, 255, 0), - (255, 255, 95), - (255, 255, 135), - (255, 255, 175), - (255, 255, 215), - (255, 255, 255), - (8, 8, 8), - (18, 18, 18), - (28, 28, 28), - (38, 38, 38), - (48, 48, 48), - (58, 58, 58), - (68, 68, 68), - (78, 78, 78), - (88, 88, 88), - (98, 98, 98), - (108, 108, 108), - (118, 118, 118), - (128, 128, 128), - (138, 138, 138), - (148, 148, 148), - (158, 158, 158), - (168, 168, 168), - (178, 178, 178), - (188, 188, 188), - (198, 198, 198), - (208, 208, 208), - (218, 218, 218), - (228, 228, 228), - (238, 238, 238), - ] -) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_pick.py b/.venv/Lib/site-packages/pip/_vendor/rich/_pick.py deleted file mode 100644 index 4f6d8b2..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_pick.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Optional - - -def pick_bool(*values: Optional[bool]) -> bool: - """Pick the first non-none bool or return the last value. - - Args: - *values (bool): Any number of boolean or None values. - - Returns: - bool: First non-none boolean. - """ - assert values, "1 or more values required" - for value in values: - if value is not None: - return value - return bool(value) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_ratio.py b/.venv/Lib/site-packages/pip/_vendor/rich/_ratio.py deleted file mode 100644 index e8a3a67..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_ratio.py +++ /dev/null @@ -1,160 +0,0 @@ -import sys -from fractions import Fraction -from math import ceil -from typing import cast, List, Optional, Sequence - -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from pip._vendor.typing_extensions import Protocol # pragma: no cover - - -class Edge(Protocol): - """Any object that defines an edge (such as Layout).""" - - size: Optional[int] = None - ratio: int = 1 - minimum_size: int = 1 - - -def ratio_resolve(total: int, edges: Sequence[Edge]) -> List[int]: - """Divide total space to satisfy size, ratio, and minimum_size, constraints. - - The returned list of integers should add up to total in most cases, unless it is - impossible to satisfy all the constraints. For instance, if there are two edges - with a minimum size of 20 each and `total` is 30 then the returned list will be - greater than total. In practice, this would mean that a Layout object would - clip the rows that would overflow the screen height. - - Args: - total (int): Total number of characters. - edges (List[Edge]): Edges within total space. - - Returns: - List[int]: Number of characters for each edge. - """ - # Size of edge or None for yet to be determined - sizes = [(edge.size or None) for edge in edges] - - _Fraction = Fraction - - # While any edges haven't been calculated - while None in sizes: - # Get flexible edges and index to map these back on to sizes list - flexible_edges = [ - (index, edge) - for index, (size, edge) in enumerate(zip(sizes, edges)) - if size is None - ] - # Remaining space in total - remaining = total - sum(size or 0 for size in sizes) - if remaining <= 0: - # No room for flexible edges - return [ - ((edge.minimum_size or 1) if size is None else size) - for size, edge in zip(sizes, edges) - ] - # Calculate number of characters in a ratio portion - portion = _Fraction( - remaining, sum((edge.ratio or 1) for _, edge in flexible_edges) - ) - - # If any edges will be less than their minimum, replace size with the minimum - for index, edge in flexible_edges: - if portion * edge.ratio <= edge.minimum_size: - sizes[index] = edge.minimum_size - # New fixed size will invalidate calculations, so we need to repeat the process - break - else: - # Distribute flexible space and compensate for rounding error - # Since edge sizes can only be integers we need to add the remainder - # to the following line - remainder = _Fraction(0) - for index, edge in flexible_edges: - size, remainder = divmod(portion * edge.ratio + remainder, 1) - sizes[index] = size - break - # Sizes now contains integers only - return cast(List[int], sizes) - - -def ratio_reduce( - total: int, ratios: List[int], maximums: List[int], values: List[int] -) -> List[int]: - """Divide an integer total in to parts based on ratios. - - Args: - total (int): The total to divide. - ratios (List[int]): A list of integer ratios. - maximums (List[int]): List of maximums values for each slot. - values (List[int]): List of values - - Returns: - List[int]: A list of integers guaranteed to sum to total. - """ - ratios = [ratio if _max else 0 for ratio, _max in zip(ratios, maximums)] - total_ratio = sum(ratios) - if not total_ratio: - return values[:] - total_remaining = total - result: List[int] = [] - append = result.append - for ratio, maximum, value in zip(ratios, maximums, values): - if ratio and total_ratio > 0: - distributed = min(maximum, round(ratio * total_remaining / total_ratio)) - append(value - distributed) - total_remaining -= distributed - total_ratio -= ratio - else: - append(value) - return result - - -def ratio_distribute( - total: int, ratios: List[int], minimums: Optional[List[int]] = None -) -> List[int]: - """Distribute an integer total in to parts based on ratios. - - Args: - total (int): The total to divide. - ratios (List[int]): A list of integer ratios. - minimums (List[int]): List of minimum values for each slot. - - Returns: - List[int]: A list of integers guaranteed to sum to total. - """ - if minimums: - ratios = [ratio if _min else 0 for ratio, _min in zip(ratios, minimums)] - total_ratio = sum(ratios) - assert total_ratio > 0, "Sum of ratios must be > 0" - - total_remaining = total - distributed_total: List[int] = [] - append = distributed_total.append - if minimums is None: - _minimums = [0] * len(ratios) - else: - _minimums = minimums - for ratio, minimum in zip(ratios, _minimums): - if total_ratio > 0: - distributed = max(minimum, ceil(ratio * total_remaining / total_ratio)) - else: - distributed = total_remaining - append(distributed) - total_ratio -= ratio - total_remaining -= distributed - return distributed_total - - -if __name__ == "__main__": - from dataclasses import dataclass - - @dataclass - class E: - - size: Optional[int] = None - ratio: int = 1 - minimum_size: int = 1 - - resolved = ratio_resolve(110, [E(None, 1, 1), E(None, 1, 1), E(None, 1, 1)]) - print(sum(resolved)) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_spinners.py b/.venv/Lib/site-packages/pip/_vendor/rich/_spinners.py deleted file mode 100644 index d0bb1fe..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_spinners.py +++ /dev/null @@ -1,482 +0,0 @@ -""" -Spinners are from: -* cli-spinners: - MIT License - Copyright (c) Sindre Sorhus (sindresorhus.com) - 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. -""" - -SPINNERS = { - "dots": { - "interval": 80, - "frames": "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏", - }, - "dots2": {"interval": 80, "frames": "⣾⣽⣻⢿⡿⣟⣯⣷"}, - "dots3": { - "interval": 80, - "frames": "⠋⠙⠚⠞⠖⠦⠴⠲⠳⠓", - }, - "dots4": { - "interval": 80, - "frames": "⠄⠆⠇⠋⠙⠸⠰⠠⠰⠸⠙⠋⠇⠆", - }, - "dots5": { - "interval": 80, - "frames": "⠋⠙⠚⠒⠂⠂⠒⠲⠴⠦⠖⠒⠐⠐⠒⠓⠋", - }, - "dots6": { - "interval": 80, - "frames": "⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠴⠲⠒⠂⠂⠒⠚⠙⠉⠁", - }, - "dots7": { - "interval": 80, - "frames": "⠈⠉⠋⠓⠒⠐⠐⠒⠖⠦⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈", - }, - "dots8": { - "interval": 80, - "frames": "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈", - }, - "dots9": {"interval": 80, "frames": "⢹⢺⢼⣸⣇⡧⡗⡏"}, - "dots10": {"interval": 80, "frames": "⢄⢂⢁⡁⡈⡐⡠"}, - "dots11": {"interval": 100, "frames": "⠁⠂⠄⡀⢀⠠⠐⠈"}, - "dots12": { - "interval": 80, - "frames": [ - "⢀⠀", - "⡀⠀", - "⠄⠀", - "⢂⠀", - "⡂⠀", - "⠅⠀", - "⢃⠀", - "⡃⠀", - "⠍⠀", - "⢋⠀", - "⡋⠀", - "⠍⠁", - "⢋⠁", - "⡋⠁", - "⠍⠉", - "⠋⠉", - "⠋⠉", - "⠉⠙", - "⠉⠙", - "⠉⠩", - "⠈⢙", - "⠈⡙", - "⢈⠩", - "⡀⢙", - "⠄⡙", - "⢂⠩", - "⡂⢘", - "⠅⡘", - "⢃⠨", - "⡃⢐", - "⠍⡐", - "⢋⠠", - "⡋⢀", - "⠍⡁", - "⢋⠁", - "⡋⠁", - "⠍⠉", - "⠋⠉", - "⠋⠉", - "⠉⠙", - "⠉⠙", - "⠉⠩", - "⠈⢙", - "⠈⡙", - "⠈⠩", - "⠀⢙", - "⠀⡙", - "⠀⠩", - "⠀⢘", - "⠀⡘", - "⠀⠨", - "⠀⢐", - "⠀⡐", - "⠀⠠", - "⠀⢀", - "⠀⡀", - ], - }, - "dots8Bit": { - "interval": 80, - "frames": "⠀⠁⠂⠃⠄⠅⠆⠇⡀⡁⡂⡃⡄⡅⡆⡇⠈⠉⠊⠋⠌⠍⠎⠏⡈⡉⡊⡋⡌⡍⡎⡏⠐⠑⠒⠓⠔⠕⠖⠗⡐⡑⡒⡓⡔⡕⡖⡗⠘⠙⠚⠛⠜⠝⠞⠟⡘⡙" - "⡚⡛⡜⡝⡞⡟⠠⠡⠢⠣⠤⠥⠦⠧⡠⡡⡢⡣⡤⡥⡦⡧⠨⠩⠪⠫⠬⠭⠮⠯⡨⡩⡪⡫⡬⡭⡮⡯⠰⠱⠲⠳⠴⠵⠶⠷⡰⡱⡲⡳⡴⡵⡶⡷⠸⠹⠺⠻" - "⠼⠽⠾⠿⡸⡹⡺⡻⡼⡽⡾⡿⢀⢁⢂⢃⢄⢅⢆⢇⣀⣁⣂⣃⣄⣅⣆⣇⢈⢉⢊⢋⢌⢍⢎⢏⣈⣉⣊⣋⣌⣍⣎⣏⢐⢑⢒⢓⢔⢕⢖⢗⣐⣑⣒⣓⣔⣕" - "⣖⣗⢘⢙⢚⢛⢜⢝⢞⢟⣘⣙⣚⣛⣜⣝⣞⣟⢠⢡⢢⢣⢤⢥⢦⢧⣠⣡⣢⣣⣤⣥⣦⣧⢨⢩⢪⢫⢬⢭⢮⢯⣨⣩⣪⣫⣬⣭⣮⣯⢰⢱⢲⢳⢴⢵⢶⢷" - "⣰⣱⣲⣳⣴⣵⣶⣷⢸⢹⢺⢻⢼⢽⢾⢿⣸⣹⣺⣻⣼⣽⣾⣿", - }, - "line": {"interval": 130, "frames": ["-", "\\", "|", "/"]}, - "line2": {"interval": 100, "frames": "⠂-–—–-"}, - "pipe": {"interval": 100, "frames": "┤┘┴└├┌┬┐"}, - "simpleDots": {"interval": 400, "frames": [". ", ".. ", "...", " "]}, - "simpleDotsScrolling": { - "interval": 200, - "frames": [". ", ".. ", "...", " ..", " .", " "], - }, - "star": {"interval": 70, "frames": "✶✸✹✺✹✷"}, - "star2": {"interval": 80, "frames": "+x*"}, - "flip": { - "interval": 70, - "frames": "___-``'´-___", - }, - "hamburger": {"interval": 100, "frames": "☱☲☴"}, - "growVertical": { - "interval": 120, - "frames": "▁▃▄▅▆▇▆▅▄▃", - }, - "growHorizontal": { - "interval": 120, - "frames": "▏▎▍▌▋▊▉▊▋▌▍▎", - }, - "balloon": {"interval": 140, "frames": " .oO@* "}, - "balloon2": {"interval": 120, "frames": ".oO°Oo."}, - "noise": {"interval": 100, "frames": "▓▒░"}, - "bounce": {"interval": 120, "frames": "⠁⠂⠄⠂"}, - "boxBounce": {"interval": 120, "frames": "▖▘▝▗"}, - "boxBounce2": {"interval": 100, "frames": "▌▀▐▄"}, - "triangle": {"interval": 50, "frames": "◢◣◤◥"}, - "arc": {"interval": 100, "frames": "◜◠◝◞◡◟"}, - "circle": {"interval": 120, "frames": "◡⊙◠"}, - "squareCorners": {"interval": 180, "frames": "◰◳◲◱"}, - "circleQuarters": {"interval": 120, "frames": "◴◷◶◵"}, - "circleHalves": {"interval": 50, "frames": "◐◓◑◒"}, - "squish": {"interval": 100, "frames": "╫╪"}, - "toggle": {"interval": 250, "frames": "⊶⊷"}, - "toggle2": {"interval": 80, "frames": "▫▪"}, - "toggle3": {"interval": 120, "frames": "□■"}, - "toggle4": {"interval": 100, "frames": "■□▪▫"}, - "toggle5": {"interval": 100, "frames": "▮▯"}, - "toggle6": {"interval": 300, "frames": "ဝ၀"}, - "toggle7": {"interval": 80, "frames": "⦾⦿"}, - "toggle8": {"interval": 100, "frames": "◍◌"}, - "toggle9": {"interval": 100, "frames": "◉◎"}, - "toggle10": {"interval": 100, "frames": "㊂㊀㊁"}, - "toggle11": {"interval": 50, "frames": "⧇⧆"}, - "toggle12": {"interval": 120, "frames": "☗☖"}, - "toggle13": {"interval": 80, "frames": "=*-"}, - "arrow": {"interval": 100, "frames": "←↖↑↗→↘↓↙"}, - "arrow2": { - "interval": 80, - "frames": ["⬆️ ", "↗️ ", "➡️ ", "↘️ ", "⬇️ ", "↙️ ", "⬅️ ", "↖️ "], - }, - "arrow3": { - "interval": 120, - "frames": ["▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸"], - }, - "bouncingBar": { - "interval": 80, - "frames": [ - "[ ]", - "[= ]", - "[== ]", - "[=== ]", - "[ ===]", - "[ ==]", - "[ =]", - "[ ]", - "[ =]", - "[ ==]", - "[ ===]", - "[====]", - "[=== ]", - "[== ]", - "[= ]", - ], - }, - "bouncingBall": { - "interval": 80, - "frames": [ - "( ● )", - "( ● )", - "( ● )", - "( ● )", - "( ●)", - "( ● )", - "( ● )", - "( ● )", - "( ● )", - "(● )", - ], - }, - "smiley": {"interval": 200, "frames": ["😄 ", "😝 "]}, - "monkey": {"interval": 300, "frames": ["🙈 ", "🙈 ", "🙉 ", "🙊 "]}, - "hearts": {"interval": 100, "frames": ["💛 ", "💙 ", "💜 ", "💚 ", "❤️ "]}, - "clock": { - "interval": 100, - "frames": [ - "🕛 ", - "🕐 ", - "🕑 ", - "🕒 ", - "🕓 ", - "🕔 ", - "🕕 ", - "🕖 ", - "🕗 ", - "🕘 ", - "🕙 ", - "🕚 ", - ], - }, - "earth": {"interval": 180, "frames": ["🌍 ", "🌎 ", "🌏 "]}, - "material": { - "interval": 17, - "frames": [ - "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "███████▁▁▁▁▁▁▁▁▁▁▁▁▁", - "████████▁▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "██████████▁▁▁▁▁▁▁▁▁▁", - "███████████▁▁▁▁▁▁▁▁▁", - "█████████████▁▁▁▁▁▁▁", - "██████████████▁▁▁▁▁▁", - "██████████████▁▁▁▁▁▁", - "▁██████████████▁▁▁▁▁", - "▁██████████████▁▁▁▁▁", - "▁██████████████▁▁▁▁▁", - "▁▁██████████████▁▁▁▁", - "▁▁▁██████████████▁▁▁", - "▁▁▁▁█████████████▁▁▁", - "▁▁▁▁██████████████▁▁", - "▁▁▁▁██████████████▁▁", - "▁▁▁▁▁██████████████▁", - "▁▁▁▁▁██████████████▁", - "▁▁▁▁▁██████████████▁", - "▁▁▁▁▁▁██████████████", - "▁▁▁▁▁▁██████████████", - "▁▁▁▁▁▁▁█████████████", - "▁▁▁▁▁▁▁█████████████", - "▁▁▁▁▁▁▁▁████████████", - "▁▁▁▁▁▁▁▁████████████", - "▁▁▁▁▁▁▁▁▁███████████", - "▁▁▁▁▁▁▁▁▁███████████", - "▁▁▁▁▁▁▁▁▁▁██████████", - "▁▁▁▁▁▁▁▁▁▁██████████", - "▁▁▁▁▁▁▁▁▁▁▁▁████████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", - "█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", - "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", - "██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", - "███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", - "████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", - "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "██████▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "████████▁▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "█████████▁▁▁▁▁▁▁▁▁▁▁", - "███████████▁▁▁▁▁▁▁▁▁", - "████████████▁▁▁▁▁▁▁▁", - "████████████▁▁▁▁▁▁▁▁", - "██████████████▁▁▁▁▁▁", - "██████████████▁▁▁▁▁▁", - "▁██████████████▁▁▁▁▁", - "▁██████████████▁▁▁▁▁", - "▁▁▁█████████████▁▁▁▁", - "▁▁▁▁▁████████████▁▁▁", - "▁▁▁▁▁████████████▁▁▁", - "▁▁▁▁▁▁███████████▁▁▁", - "▁▁▁▁▁▁▁▁█████████▁▁▁", - "▁▁▁▁▁▁▁▁█████████▁▁▁", - "▁▁▁▁▁▁▁▁▁█████████▁▁", - "▁▁▁▁▁▁▁▁▁█████████▁▁", - "▁▁▁▁▁▁▁▁▁▁█████████▁", - "▁▁▁▁▁▁▁▁▁▁▁████████▁", - "▁▁▁▁▁▁▁▁▁▁▁████████▁", - "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", - "▁▁▁▁▁▁▁▁▁▁▁▁███████▁", - "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁███████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", - ], - }, - "moon": { - "interval": 80, - "frames": ["🌑 ", "🌒 ", "🌓 ", "🌔 ", "🌕 ", "🌖 ", "🌗 ", "🌘 "], - }, - "runner": {"interval": 140, "frames": ["🚶 ", "🏃 "]}, - "pong": { - "interval": 80, - "frames": [ - "▐⠂ ▌", - "▐⠈ ▌", - "▐ ⠂ ▌", - "▐ ⠠ ▌", - "▐ ⡀ ▌", - "▐ ⠠ ▌", - "▐ ⠂ ▌", - "▐ ⠈ ▌", - "▐ ⠂ ▌", - "▐ ⠠ ▌", - "▐ ⡀ ▌", - "▐ ⠠ ▌", - "▐ ⠂ ▌", - "▐ ⠈ ▌", - "▐ ⠂▌", - "▐ ⠠▌", - "▐ ⡀▌", - "▐ ⠠ ▌", - "▐ ⠂ ▌", - "▐ ⠈ ▌", - "▐ ⠂ ▌", - "▐ ⠠ ▌", - "▐ ⡀ ▌", - "▐ ⠠ ▌", - "▐ ⠂ ▌", - "▐ ⠈ ▌", - "▐ ⠂ ▌", - "▐ ⠠ ▌", - "▐ ⡀ ▌", - "▐⠠ ▌", - ], - }, - "shark": { - "interval": 120, - "frames": [ - "▐|\\____________▌", - "▐_|\\___________▌", - "▐__|\\__________▌", - "▐___|\\_________▌", - "▐____|\\________▌", - "▐_____|\\_______▌", - "▐______|\\______▌", - "▐_______|\\_____▌", - "▐________|\\____▌", - "▐_________|\\___▌", - "▐__________|\\__▌", - "▐___________|\\_▌", - "▐____________|\\▌", - "▐____________/|▌", - "▐___________/|_▌", - "▐__________/|__▌", - "▐_________/|___▌", - "▐________/|____▌", - "▐_______/|_____▌", - "▐______/|______▌", - "▐_____/|_______▌", - "▐____/|________▌", - "▐___/|_________▌", - "▐__/|__________▌", - "▐_/|___________▌", - "▐/|____________▌", - ], - }, - "dqpb": {"interval": 100, "frames": "dqpb"}, - "weather": { - "interval": 100, - "frames": [ - "☀️ ", - "☀️ ", - "☀️ ", - "🌤 ", - "⛅️ ", - "🌥 ", - "☁️ ", - "🌧 ", - "🌨 ", - "🌧 ", - "🌨 ", - "🌧 ", - "🌨 ", - "⛈ ", - "🌨 ", - "🌧 ", - "🌨 ", - "☁️ ", - "🌥 ", - "⛅️ ", - "🌤 ", - "☀️ ", - "☀️ ", - ], - }, - "christmas": {"interval": 400, "frames": "🌲🎄"}, - "grenade": { - "interval": 80, - "frames": [ - "، ", - "′ ", - " ´ ", - " ‾ ", - " ⸌", - " ⸊", - " |", - " ⁎", - " ⁕", - " ෴ ", - " ⁓", - " ", - " ", - " ", - ], - }, - "point": {"interval": 125, "frames": ["∙∙∙", "●∙∙", "∙●∙", "∙∙●", "∙∙∙"]}, - "layer": {"interval": 150, "frames": "-=≡"}, - "betaWave": { - "interval": 80, - "frames": [ - "ρββββββ", - "βρβββββ", - "ββρββββ", - "βββρβββ", - "ββββρββ", - "βββββρβ", - "ββββββρ", - ], - }, - "aesthetic": { - "interval": 80, - "frames": [ - "▰▱▱▱▱▱▱", - "▰▰▱▱▱▱▱", - "▰▰▰▱▱▱▱", - "▰▰▰▰▱▱▱", - "▰▰▰▰▰▱▱", - "▰▰▰▰▰▰▱", - "▰▰▰▰▰▰▰", - "▰▱▱▱▱▱▱", - ], - }, -} diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_stack.py b/.venv/Lib/site-packages/pip/_vendor/rich/_stack.py deleted file mode 100644 index 194564e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_stack.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import List, TypeVar - -T = TypeVar("T") - - -class Stack(List[T]): - """A small shim over builtin list.""" - - @property - def top(self) -> T: - """Get top of stack.""" - return self[-1] - - def push(self, item: T) -> None: - """Push an item on to the stack (append in stack nomenclature).""" - self.append(item) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_timer.py b/.venv/Lib/site-packages/pip/_vendor/rich/_timer.py deleted file mode 100644 index a2ca6be..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_timer.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Timer context manager, only used in debug. - -""" - -from time import time - -import contextlib -from typing import Generator - - -@contextlib.contextmanager -def timer(subject: str = "time") -> Generator[None, None, None]: - """print the elapsed time. (only used in debugging)""" - start = time() - yield - elapsed = time() - start - elapsed_ms = elapsed * 1000 - print(f"{subject} elapsed {elapsed_ms:.1f}ms") diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py b/.venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py deleted file mode 100644 index 81b1082..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_win32_console.py +++ /dev/null @@ -1,662 +0,0 @@ -"""Light wrapper around the Win32 Console API - this module should only be imported on Windows - -The API that this module wraps is documented at https://docs.microsoft.com/en-us/windows/console/console-functions -""" -import ctypes -import sys -from typing import Any - -windll: Any = None -if sys.platform == "win32": - windll = ctypes.LibraryLoader(ctypes.WinDLL) -else: - raise ImportError(f"{__name__} can only be imported on Windows") - -import time -from ctypes import Structure, byref, wintypes -from typing import IO, NamedTuple, Type, cast - -from pip._vendor.rich.color import ColorSystem -from pip._vendor.rich.style import Style - -STDOUT = -11 -ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 - -COORD = wintypes._COORD - - -class LegacyWindowsError(Exception): - pass - - -class WindowsCoordinates(NamedTuple): - """Coordinates in the Windows Console API are (y, x), not (x, y). - This class is intended to prevent that confusion. - Rows and columns are indexed from 0. - This class can be used in place of wintypes._COORD in arguments and argtypes. - """ - - row: int - col: int - - @classmethod - def from_param(cls, value: "WindowsCoordinates") -> COORD: - """Converts a WindowsCoordinates into a wintypes _COORD structure. - This classmethod is internally called by ctypes to perform the conversion. - - Args: - value (WindowsCoordinates): The input coordinates to convert. - - Returns: - wintypes._COORD: The converted coordinates struct. - """ - return COORD(value.col, value.row) - - -class CONSOLE_SCREEN_BUFFER_INFO(Structure): - _fields_ = [ - ("dwSize", COORD), - ("dwCursorPosition", COORD), - ("wAttributes", wintypes.WORD), - ("srWindow", wintypes.SMALL_RECT), - ("dwMaximumWindowSize", COORD), - ] - - -class CONSOLE_CURSOR_INFO(ctypes.Structure): - _fields_ = [("dwSize", wintypes.DWORD), ("bVisible", wintypes.BOOL)] - - -_GetStdHandle = windll.kernel32.GetStdHandle -_GetStdHandle.argtypes = [ - wintypes.DWORD, -] -_GetStdHandle.restype = wintypes.HANDLE - - -def GetStdHandle(handle: int = STDOUT) -> wintypes.HANDLE: - """Retrieves a handle to the specified standard device (standard input, standard output, or standard error). - - Args: - handle (int): Integer identifier for the handle. Defaults to -11 (stdout). - - Returns: - wintypes.HANDLE: The handle - """ - return cast(wintypes.HANDLE, _GetStdHandle(handle)) - - -_GetConsoleMode = windll.kernel32.GetConsoleMode -_GetConsoleMode.argtypes = [wintypes.HANDLE, wintypes.LPDWORD] -_GetConsoleMode.restype = wintypes.BOOL - - -def GetConsoleMode(std_handle: wintypes.HANDLE) -> int: - """Retrieves the current input mode of a console's input buffer - or the current output mode of a console screen buffer. - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - - Raises: - LegacyWindowsError: If any error occurs while calling the Windows console API. - - Returns: - int: Value representing the current console mode as documented at - https://docs.microsoft.com/en-us/windows/console/getconsolemode#parameters - """ - - console_mode = wintypes.DWORD() - success = bool(_GetConsoleMode(std_handle, console_mode)) - if not success: - raise LegacyWindowsError("Unable to get legacy Windows Console Mode") - return console_mode.value - - -_FillConsoleOutputCharacterW = windll.kernel32.FillConsoleOutputCharacterW -_FillConsoleOutputCharacterW.argtypes = [ - wintypes.HANDLE, - ctypes.c_char, - wintypes.DWORD, - cast(Type[COORD], WindowsCoordinates), - ctypes.POINTER(wintypes.DWORD), -] -_FillConsoleOutputCharacterW.restype = wintypes.BOOL - - -def FillConsoleOutputCharacter( - std_handle: wintypes.HANDLE, - char: str, - length: int, - start: WindowsCoordinates, -) -> int: - """Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates. - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - char (str): The character to write. Must be a string of length 1. - length (int): The number of times to write the character. - start (WindowsCoordinates): The coordinates to start writing at. - - Returns: - int: The number of characters written. - """ - character = ctypes.c_char(char.encode()) - num_characters = wintypes.DWORD(length) - num_written = wintypes.DWORD(0) - _FillConsoleOutputCharacterW( - std_handle, - character, - num_characters, - start, - byref(num_written), - ) - return num_written.value - - -_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute -_FillConsoleOutputAttribute.argtypes = [ - wintypes.HANDLE, - wintypes.WORD, - wintypes.DWORD, - cast(Type[COORD], WindowsCoordinates), - ctypes.POINTER(wintypes.DWORD), -] -_FillConsoleOutputAttribute.restype = wintypes.BOOL - - -def FillConsoleOutputAttribute( - std_handle: wintypes.HANDLE, - attributes: int, - length: int, - start: WindowsCoordinates, -) -> int: - """Sets the character attributes for a specified number of character cells, - beginning at the specified coordinates in a screen buffer. - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - attributes (int): Integer value representing the foreground and background colours of the cells. - length (int): The number of cells to set the output attribute of. - start (WindowsCoordinates): The coordinates of the first cell whose attributes are to be set. - - Returns: - int: The number of cells whose attributes were actually set. - """ - num_cells = wintypes.DWORD(length) - style_attrs = wintypes.WORD(attributes) - num_written = wintypes.DWORD(0) - _FillConsoleOutputAttribute( - std_handle, style_attrs, num_cells, start, byref(num_written) - ) - return num_written.value - - -_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute -_SetConsoleTextAttribute.argtypes = [ - wintypes.HANDLE, - wintypes.WORD, -] -_SetConsoleTextAttribute.restype = wintypes.BOOL - - -def SetConsoleTextAttribute( - std_handle: wintypes.HANDLE, attributes: wintypes.WORD -) -> bool: - """Set the colour attributes for all text written after this function is called. - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - attributes (int): Integer value representing the foreground and background colours. - - - Returns: - bool: True if the attribute was set successfully, otherwise False. - """ - return bool(_SetConsoleTextAttribute(std_handle, attributes)) - - -_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo -_GetConsoleScreenBufferInfo.argtypes = [ - wintypes.HANDLE, - ctypes.POINTER(CONSOLE_SCREEN_BUFFER_INFO), -] -_GetConsoleScreenBufferInfo.restype = wintypes.BOOL - - -def GetConsoleScreenBufferInfo( - std_handle: wintypes.HANDLE, -) -> CONSOLE_SCREEN_BUFFER_INFO: - """Retrieves information about the specified console screen buffer. - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - - Returns: - CONSOLE_SCREEN_BUFFER_INFO: A CONSOLE_SCREEN_BUFFER_INFO ctype struct contain information about - screen size, cursor position, colour attributes, and more.""" - console_screen_buffer_info = CONSOLE_SCREEN_BUFFER_INFO() - _GetConsoleScreenBufferInfo(std_handle, byref(console_screen_buffer_info)) - return console_screen_buffer_info - - -_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition -_SetConsoleCursorPosition.argtypes = [ - wintypes.HANDLE, - cast(Type[COORD], WindowsCoordinates), -] -_SetConsoleCursorPosition.restype = wintypes.BOOL - - -def SetConsoleCursorPosition( - std_handle: wintypes.HANDLE, coords: WindowsCoordinates -) -> bool: - """Set the position of the cursor in the console screen - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - coords (WindowsCoordinates): The coordinates to move the cursor to. - - Returns: - bool: True if the function succeeds, otherwise False. - """ - return bool(_SetConsoleCursorPosition(std_handle, coords)) - - -_GetConsoleCursorInfo = windll.kernel32.GetConsoleCursorInfo -_GetConsoleCursorInfo.argtypes = [ - wintypes.HANDLE, - ctypes.POINTER(CONSOLE_CURSOR_INFO), -] -_GetConsoleCursorInfo.restype = wintypes.BOOL - - -def GetConsoleCursorInfo( - std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO -) -> bool: - """Get the cursor info - used to get cursor visibility and width - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct that receives information - about the console's cursor. - - Returns: - bool: True if the function succeeds, otherwise False. - """ - return bool(_GetConsoleCursorInfo(std_handle, byref(cursor_info))) - - -_SetConsoleCursorInfo = windll.kernel32.SetConsoleCursorInfo -_SetConsoleCursorInfo.argtypes = [ - wintypes.HANDLE, - ctypes.POINTER(CONSOLE_CURSOR_INFO), -] -_SetConsoleCursorInfo.restype = wintypes.BOOL - - -def SetConsoleCursorInfo( - std_handle: wintypes.HANDLE, cursor_info: CONSOLE_CURSOR_INFO -) -> bool: - """Set the cursor info - used for adjusting cursor visibility and width - - Args: - std_handle (wintypes.HANDLE): A handle to the console input buffer or the console screen buffer. - cursor_info (CONSOLE_CURSOR_INFO): CONSOLE_CURSOR_INFO ctype struct containing the new cursor info. - - Returns: - bool: True if the function succeeds, otherwise False. - """ - return bool(_SetConsoleCursorInfo(std_handle, byref(cursor_info))) - - -_SetConsoleTitle = windll.kernel32.SetConsoleTitleW -_SetConsoleTitle.argtypes = [wintypes.LPCWSTR] -_SetConsoleTitle.restype = wintypes.BOOL - - -def SetConsoleTitle(title: str) -> bool: - """Sets the title of the current console window - - Args: - title (str): The new title of the console window. - - Returns: - bool: True if the function succeeds, otherwise False. - """ - return bool(_SetConsoleTitle(title)) - - -class LegacyWindowsTerm: - """This class allows interaction with the legacy Windows Console API. It should only be used in the context - of environments where virtual terminal processing is not available. However, if it is used in a Windows environment, - the entire API should work. - - Args: - file (IO[str]): The file which the Windows Console API HANDLE is retrieved from, defaults to sys.stdout. - """ - - BRIGHT_BIT = 8 - - # Indices are ANSI color numbers, values are the corresponding Windows Console API color numbers - ANSI_TO_WINDOWS = [ - 0, # black The Windows colours are defined in wincon.h as follows: - 4, # red define FOREGROUND_BLUE 0x0001 -- 0000 0001 - 2, # green define FOREGROUND_GREEN 0x0002 -- 0000 0010 - 6, # yellow define FOREGROUND_RED 0x0004 -- 0000 0100 - 1, # blue define FOREGROUND_INTENSITY 0x0008 -- 0000 1000 - 5, # magenta define BACKGROUND_BLUE 0x0010 -- 0001 0000 - 3, # cyan define BACKGROUND_GREEN 0x0020 -- 0010 0000 - 7, # white define BACKGROUND_RED 0x0040 -- 0100 0000 - 8, # bright black (grey) define BACKGROUND_INTENSITY 0x0080 -- 1000 0000 - 12, # bright red - 10, # bright green - 14, # bright yellow - 9, # bright blue - 13, # bright magenta - 11, # bright cyan - 15, # bright white - ] - - def __init__(self, file: "IO[str]") -> None: - handle = GetStdHandle(STDOUT) - self._handle = handle - default_text = GetConsoleScreenBufferInfo(handle).wAttributes - self._default_text = default_text - - self._default_fore = default_text & 7 - self._default_back = (default_text >> 4) & 7 - self._default_attrs = self._default_fore | (self._default_back << 4) - - self._file = file - self.write = file.write - self.flush = file.flush - - @property - def cursor_position(self) -> WindowsCoordinates: - """Returns the current position of the cursor (0-based) - - Returns: - WindowsCoordinates: The current cursor position. - """ - coord: COORD = GetConsoleScreenBufferInfo(self._handle).dwCursorPosition - return WindowsCoordinates(row=cast(int, coord.Y), col=cast(int, coord.X)) - - @property - def screen_size(self) -> WindowsCoordinates: - """Returns the current size of the console screen buffer, in character columns and rows - - Returns: - WindowsCoordinates: The width and height of the screen as WindowsCoordinates. - """ - screen_size: COORD = GetConsoleScreenBufferInfo(self._handle).dwSize - return WindowsCoordinates( - row=cast(int, screen_size.Y), col=cast(int, screen_size.X) - ) - - def write_text(self, text: str) -> None: - """Write text directly to the terminal without any modification of styles - - Args: - text (str): The text to write to the console - """ - self.write(text) - self.flush() - - def write_styled(self, text: str, style: Style) -> None: - """Write styled text to the terminal. - - Args: - text (str): The text to write - style (Style): The style of the text - """ - color = style.color - bgcolor = style.bgcolor - if style.reverse: - color, bgcolor = bgcolor, color - - if color: - fore = color.downgrade(ColorSystem.WINDOWS).number - fore = fore if fore is not None else 7 # Default to ANSI 7: White - if style.bold: - fore = fore | self.BRIGHT_BIT - if style.dim: - fore = fore & ~self.BRIGHT_BIT - fore = self.ANSI_TO_WINDOWS[fore] - else: - fore = self._default_fore - - if bgcolor: - back = bgcolor.downgrade(ColorSystem.WINDOWS).number - back = back if back is not None else 0 # Default to ANSI 0: Black - back = self.ANSI_TO_WINDOWS[back] - else: - back = self._default_back - - assert fore is not None - assert back is not None - - SetConsoleTextAttribute( - self._handle, attributes=ctypes.c_ushort(fore | (back << 4)) - ) - self.write_text(text) - SetConsoleTextAttribute(self._handle, attributes=self._default_text) - - def move_cursor_to(self, new_position: WindowsCoordinates) -> None: - """Set the position of the cursor - - Args: - new_position (WindowsCoordinates): The WindowsCoordinates representing the new position of the cursor. - """ - if new_position.col < 0 or new_position.row < 0: - return - SetConsoleCursorPosition(self._handle, coords=new_position) - - def erase_line(self) -> None: - """Erase all content on the line the cursor is currently located at""" - screen_size = self.screen_size - cursor_position = self.cursor_position - cells_to_erase = screen_size.col - start_coordinates = WindowsCoordinates(row=cursor_position.row, col=0) - FillConsoleOutputCharacter( - self._handle, " ", length=cells_to_erase, start=start_coordinates - ) - FillConsoleOutputAttribute( - self._handle, - self._default_attrs, - length=cells_to_erase, - start=start_coordinates, - ) - - def erase_end_of_line(self) -> None: - """Erase all content from the cursor position to the end of that line""" - cursor_position = self.cursor_position - cells_to_erase = self.screen_size.col - cursor_position.col - FillConsoleOutputCharacter( - self._handle, " ", length=cells_to_erase, start=cursor_position - ) - FillConsoleOutputAttribute( - self._handle, - self._default_attrs, - length=cells_to_erase, - start=cursor_position, - ) - - def erase_start_of_line(self) -> None: - """Erase all content from the cursor position to the start of that line""" - row, col = self.cursor_position - start = WindowsCoordinates(row, 0) - FillConsoleOutputCharacter(self._handle, " ", length=col, start=start) - FillConsoleOutputAttribute( - self._handle, self._default_attrs, length=col, start=start - ) - - def move_cursor_up(self) -> None: - """Move the cursor up a single cell""" - cursor_position = self.cursor_position - SetConsoleCursorPosition( - self._handle, - coords=WindowsCoordinates( - row=cursor_position.row - 1, col=cursor_position.col - ), - ) - - def move_cursor_down(self) -> None: - """Move the cursor down a single cell""" - cursor_position = self.cursor_position - SetConsoleCursorPosition( - self._handle, - coords=WindowsCoordinates( - row=cursor_position.row + 1, - col=cursor_position.col, - ), - ) - - def move_cursor_forward(self) -> None: - """Move the cursor forward a single cell. Wrap to the next line if required.""" - row, col = self.cursor_position - if col == self.screen_size.col - 1: - row += 1 - col = 0 - else: - col += 1 - SetConsoleCursorPosition( - self._handle, coords=WindowsCoordinates(row=row, col=col) - ) - - def move_cursor_to_column(self, column: int) -> None: - """Move cursor to the column specified by the zero-based column index, staying on the same row - - Args: - column (int): The zero-based column index to move the cursor to. - """ - row, _ = self.cursor_position - SetConsoleCursorPosition(self._handle, coords=WindowsCoordinates(row, column)) - - def move_cursor_backward(self) -> None: - """Move the cursor backward a single cell. Wrap to the previous line if required.""" - row, col = self.cursor_position - if col == 0: - row -= 1 - col = self.screen_size.col - 1 - else: - col -= 1 - SetConsoleCursorPosition( - self._handle, coords=WindowsCoordinates(row=row, col=col) - ) - - def hide_cursor(self) -> None: - """Hide the cursor""" - current_cursor_size = self._get_cursor_size() - invisible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=0) - SetConsoleCursorInfo(self._handle, cursor_info=invisible_cursor) - - def show_cursor(self) -> None: - """Show the cursor""" - current_cursor_size = self._get_cursor_size() - visible_cursor = CONSOLE_CURSOR_INFO(dwSize=current_cursor_size, bVisible=1) - SetConsoleCursorInfo(self._handle, cursor_info=visible_cursor) - - def set_title(self, title: str) -> None: - """Set the title of the terminal window - - Args: - title (str): The new title of the console window - """ - assert len(title) < 255, "Console title must be less than 255 characters" - SetConsoleTitle(title) - - def _get_cursor_size(self) -> int: - """Get the percentage of the character cell that is filled by the cursor""" - cursor_info = CONSOLE_CURSOR_INFO() - GetConsoleCursorInfo(self._handle, cursor_info=cursor_info) - return int(cursor_info.dwSize) - - -if __name__ == "__main__": - handle = GetStdHandle() - - from pip._vendor.rich.console import Console - - console = Console() - - term = LegacyWindowsTerm(sys.stdout) - term.set_title("Win32 Console Examples") - - style = Style(color="black", bgcolor="red") - - heading = Style.parse("black on green") - - # Check colour output - console.rule("Checking colour output") - console.print("[on red]on red!") - console.print("[blue]blue!") - console.print("[yellow]yellow!") - console.print("[bold yellow]bold yellow!") - console.print("[bright_yellow]bright_yellow!") - console.print("[dim bright_yellow]dim bright_yellow!") - console.print("[italic cyan]italic cyan!") - console.print("[bold white on blue]bold white on blue!") - console.print("[reverse bold white on blue]reverse bold white on blue!") - console.print("[bold black on cyan]bold black on cyan!") - console.print("[black on green]black on green!") - console.print("[blue on green]blue on green!") - console.print("[white on black]white on black!") - console.print("[black on white]black on white!") - console.print("[#1BB152 on #DA812D]#1BB152 on #DA812D!") - - # Check cursor movement - console.rule("Checking cursor movement") - console.print() - term.move_cursor_backward() - term.move_cursor_backward() - term.write_text("went back and wrapped to prev line") - time.sleep(1) - term.move_cursor_up() - term.write_text("we go up") - time.sleep(1) - term.move_cursor_down() - term.write_text("and down") - time.sleep(1) - term.move_cursor_up() - term.move_cursor_backward() - term.move_cursor_backward() - term.write_text("we went up and back 2") - time.sleep(1) - term.move_cursor_down() - term.move_cursor_backward() - term.move_cursor_backward() - term.write_text("we went down and back 2") - time.sleep(1) - - # Check erasing of lines - term.hide_cursor() - console.print() - console.rule("Checking line erasing") - console.print("\n...Deleting to the start of the line...") - term.write_text("The red arrow shows the cursor location, and direction of erase") - time.sleep(1) - term.move_cursor_to_column(16) - term.write_styled("<", Style.parse("black on red")) - term.move_cursor_backward() - time.sleep(1) - term.erase_start_of_line() - time.sleep(1) - - console.print("\n\n...And to the end of the line...") - term.write_text("The red arrow shows the cursor location, and direction of erase") - time.sleep(1) - - term.move_cursor_to_column(16) - term.write_styled(">", Style.parse("black on red")) - time.sleep(1) - term.erase_end_of_line() - time.sleep(1) - - console.print("\n\n...Now the whole line will be erased...") - term.write_styled("I'm going to disappear!", style=Style.parse("black on cyan")) - time.sleep(1) - term.erase_line() - - term.show_cursor() - print("\n") diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_windows.py b/.venv/Lib/site-packages/pip/_vendor/rich/_windows.py deleted file mode 100644 index 10fc0d7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_windows.py +++ /dev/null @@ -1,72 +0,0 @@ -import sys -from dataclasses import dataclass - - -@dataclass -class WindowsConsoleFeatures: - """Windows features available.""" - - vt: bool = False - """The console supports VT codes.""" - truecolor: bool = False - """The console supports truecolor.""" - - -try: - import ctypes - from ctypes import LibraryLoader - - if sys.platform == "win32": - windll = LibraryLoader(ctypes.WinDLL) - else: - windll = None - raise ImportError("Not windows") - - from pip._vendor.rich._win32_console import ( - ENABLE_VIRTUAL_TERMINAL_PROCESSING, - GetConsoleMode, - GetStdHandle, - LegacyWindowsError, - ) - -except (AttributeError, ImportError, ValueError): - - # Fallback if we can't load the Windows DLL - def get_windows_console_features() -> WindowsConsoleFeatures: - features = WindowsConsoleFeatures() - return features - -else: - - def get_windows_console_features() -> WindowsConsoleFeatures: - """Get windows console features. - - Returns: - WindowsConsoleFeatures: An instance of WindowsConsoleFeatures. - """ - handle = GetStdHandle() - try: - console_mode = GetConsoleMode(handle) - success = True - except LegacyWindowsError: - console_mode = 0 - success = False - vt = bool(success and console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) - truecolor = False - if vt: - win_version = sys.getwindowsversion() - truecolor = win_version.major > 10 or ( - win_version.major == 10 and win_version.build >= 15063 - ) - features = WindowsConsoleFeatures(vt=vt, truecolor=truecolor) - return features - - -if __name__ == "__main__": - import platform - - features = get_windows_console_features() - from pip._vendor.rich import print - - print(f'platform="{platform.system()}"') - print(repr(features)) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py b/.venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py deleted file mode 100644 index 5ece056..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_windows_renderer.py +++ /dev/null @@ -1,56 +0,0 @@ -from typing import Iterable, Sequence, Tuple, cast - -from pip._vendor.rich._win32_console import LegacyWindowsTerm, WindowsCoordinates -from pip._vendor.rich.segment import ControlCode, ControlType, Segment - - -def legacy_windows_render(buffer: Iterable[Segment], term: LegacyWindowsTerm) -> None: - """Makes appropriate Windows Console API calls based on the segments in the buffer. - - Args: - buffer (Iterable[Segment]): Iterable of Segments to convert to Win32 API calls. - term (LegacyWindowsTerm): Used to call the Windows Console API. - """ - for text, style, control in buffer: - if not control: - if style: - term.write_styled(text, style) - else: - term.write_text(text) - else: - control_codes: Sequence[ControlCode] = control - for control_code in control_codes: - control_type = control_code[0] - if control_type == ControlType.CURSOR_MOVE_TO: - _, x, y = cast(Tuple[ControlType, int, int], control_code) - term.move_cursor_to(WindowsCoordinates(row=y - 1, col=x - 1)) - elif control_type == ControlType.CARRIAGE_RETURN: - term.write_text("\r") - elif control_type == ControlType.HOME: - term.move_cursor_to(WindowsCoordinates(0, 0)) - elif control_type == ControlType.CURSOR_UP: - term.move_cursor_up() - elif control_type == ControlType.CURSOR_DOWN: - term.move_cursor_down() - elif control_type == ControlType.CURSOR_FORWARD: - term.move_cursor_forward() - elif control_type == ControlType.CURSOR_BACKWARD: - term.move_cursor_backward() - elif control_type == ControlType.CURSOR_MOVE_TO_COLUMN: - _, column = cast(Tuple[ControlType, int], control_code) - term.move_cursor_to_column(column - 1) - elif control_type == ControlType.HIDE_CURSOR: - term.hide_cursor() - elif control_type == ControlType.SHOW_CURSOR: - term.show_cursor() - elif control_type == ControlType.ERASE_IN_LINE: - _, mode = cast(Tuple[ControlType, int], control_code) - if mode == 0: - term.erase_end_of_line() - elif mode == 1: - term.erase_start_of_line() - elif mode == 2: - term.erase_line() - elif control_type == ControlType.SET_WINDOW_TITLE: - _, title = cast(Tuple[ControlType, str], control_code) - term.set_title(title) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/_wrap.py b/.venv/Lib/site-packages/pip/_vendor/rich/_wrap.py deleted file mode 100644 index c45f193..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/_wrap.py +++ /dev/null @@ -1,56 +0,0 @@ -import re -from typing import Iterable, List, Tuple - -from ._loop import loop_last -from .cells import cell_len, chop_cells - -re_word = re.compile(r"\s*\S+\s*") - - -def words(text: str) -> Iterable[Tuple[int, int, str]]: - position = 0 - word_match = re_word.match(text, position) - while word_match is not None: - start, end = word_match.span() - word = word_match.group(0) - yield start, end, word - word_match = re_word.match(text, end) - - -def divide_line(text: str, width: int, fold: bool = True) -> List[int]: - divides: List[int] = [] - append = divides.append - line_position = 0 - _cell_len = cell_len - for start, _end, word in words(text): - word_length = _cell_len(word.rstrip()) - if line_position + word_length > width: - if word_length > width: - if fold: - chopped_words = chop_cells(word, max_size=width, position=0) - for last, line in loop_last(chopped_words): - if start: - append(start) - - if last: - line_position = _cell_len(line) - else: - start += len(line) - else: - if start: - append(start) - line_position = _cell_len(word) - elif line_position and start: - append(start) - line_position = _cell_len(word) - else: - line_position += _cell_len(word) - return divides - - -if __name__ == "__main__": # pragma: no cover - from .console import Console - - console = Console(width=10) - console.print("12345 abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ 12345") - print(chop_cells("abcdefghijklmnopqrstuvwxyz", 10, position=2)) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/abc.py b/.venv/Lib/site-packages/pip/_vendor/rich/abc.py deleted file mode 100644 index e6e498e..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/abc.py +++ /dev/null @@ -1,33 +0,0 @@ -from abc import ABC - - -class RichRenderable(ABC): - """An abstract base class for Rich renderables. - - Note that there is no need to extend this class, the intended use is to check if an - object supports the Rich renderable protocol. For example:: - - if isinstance(my_object, RichRenderable): - console.print(my_object) - - """ - - @classmethod - def __subclasshook__(cls, other: type) -> bool: - """Check if this class supports the rich render protocol.""" - return hasattr(other, "__rich_console__") or hasattr(other, "__rich__") - - -if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.text import Text - - t = Text() - print(isinstance(Text, RichRenderable)) - print(isinstance(t, RichRenderable)) - - class Foo: - pass - - f = Foo() - print(isinstance(f, RichRenderable)) - print(isinstance("", RichRenderable)) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/align.py b/.venv/Lib/site-packages/pip/_vendor/rich/align.py deleted file mode 100644 index d5abb59..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/align.py +++ /dev/null @@ -1,311 +0,0 @@ -import sys -from itertools import chain -from typing import TYPE_CHECKING, Iterable, Optional - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover - -from .constrain import Constrain -from .jupyter import JupyterMixin -from .measure import Measurement -from .segment import Segment -from .style import StyleType - -if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderableType, RenderResult - -AlignMethod = Literal["left", "center", "right"] -VerticalAlignMethod = Literal["top", "middle", "bottom"] - - -class Align(JupyterMixin): - """Align a renderable by adding spaces if necessary. - - Args: - renderable (RenderableType): A console renderable. - align (AlignMethod): One of "left", "center", or "right"" - style (StyleType, optional): An optional style to apply to the background. - vertical (Optional[VerticalAlginMethod], optional): Optional vertical align, one of "top", "middle", or "bottom". Defaults to None. - pad (bool, optional): Pad the right with spaces. Defaults to True. - width (int, optional): Restrict contents to given width, or None to use default width. Defaults to None. - height (int, optional): Set height of align renderable, or None to fit to contents. Defaults to None. - - Raises: - ValueError: if ``align`` is not one of the expected values. - """ - - def __init__( - self, - renderable: "RenderableType", - align: AlignMethod = "left", - style: Optional[StyleType] = None, - *, - vertical: Optional[VerticalAlignMethod] = None, - pad: bool = True, - width: Optional[int] = None, - height: Optional[int] = None, - ) -> None: - if align not in ("left", "center", "right"): - raise ValueError( - f'invalid value for align, expected "left", "center", or "right" (not {align!r})' - ) - if vertical is not None and vertical not in ("top", "middle", "bottom"): - raise ValueError( - f'invalid value for vertical, expected "top", "middle", or "bottom" (not {vertical!r})' - ) - self.renderable = renderable - self.align = align - self.style = style - self.vertical = vertical - self.pad = pad - self.width = width - self.height = height - - def __repr__(self) -> str: - return f"Align({self.renderable!r}, {self.align!r})" - - @classmethod - def left( - cls, - renderable: "RenderableType", - style: Optional[StyleType] = None, - *, - vertical: Optional[VerticalAlignMethod] = None, - pad: bool = True, - width: Optional[int] = None, - height: Optional[int] = None, - ) -> "Align": - """Align a renderable to the left.""" - return cls( - renderable, - "left", - style=style, - vertical=vertical, - pad=pad, - width=width, - height=height, - ) - - @classmethod - def center( - cls, - renderable: "RenderableType", - style: Optional[StyleType] = None, - *, - vertical: Optional[VerticalAlignMethod] = None, - pad: bool = True, - width: Optional[int] = None, - height: Optional[int] = None, - ) -> "Align": - """Align a renderable to the center.""" - return cls( - renderable, - "center", - style=style, - vertical=vertical, - pad=pad, - width=width, - height=height, - ) - - @classmethod - def right( - cls, - renderable: "RenderableType", - style: Optional[StyleType] = None, - *, - vertical: Optional[VerticalAlignMethod] = None, - pad: bool = True, - width: Optional[int] = None, - height: Optional[int] = None, - ) -> "Align": - """Align a renderable to the right.""" - return cls( - renderable, - "right", - style=style, - vertical=vertical, - pad=pad, - width=width, - height=height, - ) - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - align = self.align - width = console.measure(self.renderable, options=options).maximum - rendered = console.render( - Constrain( - self.renderable, width if self.width is None else min(width, self.width) - ), - options.update(height=None), - ) - lines = list(Segment.split_lines(rendered)) - width, height = Segment.get_shape(lines) - lines = Segment.set_shape(lines, width, height) - new_line = Segment.line() - excess_space = options.max_width - width - style = console.get_style(self.style) if self.style is not None else None - - def generate_segments() -> Iterable[Segment]: - if excess_space <= 0: - # Exact fit - for line in lines: - yield from line - yield new_line - - elif align == "left": - # Pad on the right - pad = Segment(" " * excess_space, style) if self.pad else None - for line in lines: - yield from line - if pad: - yield pad - yield new_line - - elif align == "center": - # Pad left and right - left = excess_space // 2 - pad = Segment(" " * left, style) - pad_right = ( - Segment(" " * (excess_space - left), style) if self.pad else None - ) - for line in lines: - if left: - yield pad - yield from line - if pad_right: - yield pad_right - yield new_line - - elif align == "right": - # Padding on left - pad = Segment(" " * excess_space, style) - for line in lines: - yield pad - yield from line - yield new_line - - blank_line = ( - Segment(f"{' ' * (self.width or options.max_width)}\n", style) - if self.pad - else Segment("\n") - ) - - def blank_lines(count: int) -> Iterable[Segment]: - if count > 0: - for _ in range(count): - yield blank_line - - vertical_height = self.height or options.height - iter_segments: Iterable[Segment] - if self.vertical and vertical_height is not None: - if self.vertical == "top": - bottom_space = vertical_height - height - iter_segments = chain(generate_segments(), blank_lines(bottom_space)) - elif self.vertical == "middle": - top_space = (vertical_height - height) // 2 - bottom_space = vertical_height - top_space - height - iter_segments = chain( - blank_lines(top_space), - generate_segments(), - blank_lines(bottom_space), - ) - else: # self.vertical == "bottom": - top_space = vertical_height - height - iter_segments = chain(blank_lines(top_space), generate_segments()) - else: - iter_segments = generate_segments() - if self.style: - style = console.get_style(self.style) - iter_segments = Segment.apply_style(iter_segments, style) - yield from iter_segments - - def __rich_measure__( - self, console: "Console", options: "ConsoleOptions" - ) -> Measurement: - measurement = Measurement.get(console, options, self.renderable) - return measurement - - -class VerticalCenter(JupyterMixin): - """Vertically aligns a renderable. - - Warn: - This class is deprecated and may be removed in a future version. Use Align class with - `vertical="middle"`. - - Args: - renderable (RenderableType): A renderable object. - """ - - def __init__( - self, - renderable: "RenderableType", - style: Optional[StyleType] = None, - ) -> None: - self.renderable = renderable - self.style = style - - def __repr__(self) -> str: - return f"VerticalCenter({self.renderable!r})" - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - style = console.get_style(self.style) if self.style is not None else None - lines = console.render_lines( - self.renderable, options.update(height=None), pad=False - ) - width, _height = Segment.get_shape(lines) - new_line = Segment.line() - height = options.height or options.size.height - top_space = (height - len(lines)) // 2 - bottom_space = height - top_space - len(lines) - blank_line = Segment(f"{' ' * width}", style) - - def blank_lines(count: int) -> Iterable[Segment]: - for _ in range(count): - yield blank_line - yield new_line - - if top_space > 0: - yield from blank_lines(top_space) - for line in lines: - yield from line - yield new_line - if bottom_space > 0: - yield from blank_lines(bottom_space) - - def __rich_measure__( - self, console: "Console", options: "ConsoleOptions" - ) -> Measurement: - measurement = Measurement.get(console, options, self.renderable) - return measurement - - -if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.console import Console, Group - from pip._vendor.rich.highlighter import ReprHighlighter - from pip._vendor.rich.panel import Panel - - highlighter = ReprHighlighter() - console = Console() - - panel = Panel( - Group( - Align.left(highlighter("align='left'")), - Align.center(highlighter("align='center'")), - Align.right(highlighter("align='right'")), - ), - width=60, - style="on dark_blue", - title="Algin", - ) - - console.print( - Align.center(panel, vertical="middle", style="on red", height=console.height) - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/ansi.py b/.venv/Lib/site-packages/pip/_vendor/rich/ansi.py deleted file mode 100644 index d4c32ce..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/ansi.py +++ /dev/null @@ -1,237 +0,0 @@ -import re -import sys -from contextlib import suppress -from typing import Iterable, NamedTuple, Optional - -from .color import Color -from .style import Style -from .text import Text - -re_ansi = re.compile( - r""" -(?:\x1b\](.*?)\x1b\\)| -(?:\x1b([(@-Z\\-_]|\[[0-?]*[ -/]*[@-~])) -""", - re.VERBOSE, -) - - -class _AnsiToken(NamedTuple): - """Result of ansi tokenized string.""" - - plain: str = "" - sgr: Optional[str] = "" - osc: Optional[str] = "" - - -def _ansi_tokenize(ansi_text: str) -> Iterable[_AnsiToken]: - """Tokenize a string in to plain text and ANSI codes. - - Args: - ansi_text (str): A String containing ANSI codes. - - Yields: - AnsiToken: A named tuple of (plain, sgr, osc) - """ - - position = 0 - sgr: Optional[str] - osc: Optional[str] - for match in re_ansi.finditer(ansi_text): - start, end = match.span(0) - osc, sgr = match.groups() - if start > position: - yield _AnsiToken(ansi_text[position:start]) - if sgr: - if sgr.endswith("m"): - yield _AnsiToken("", sgr[1:-1], osc) - else: - yield _AnsiToken("", sgr, osc) - position = end - if position < len(ansi_text): - yield _AnsiToken(ansi_text[position:]) - - -SGR_STYLE_MAP = { - 1: "bold", - 2: "dim", - 3: "italic", - 4: "underline", - 5: "blink", - 6: "blink2", - 7: "reverse", - 8: "conceal", - 9: "strike", - 21: "underline2", - 22: "not dim not bold", - 23: "not italic", - 24: "not underline", - 25: "not blink", - 26: "not blink2", - 27: "not reverse", - 28: "not conceal", - 29: "not strike", - 30: "color(0)", - 31: "color(1)", - 32: "color(2)", - 33: "color(3)", - 34: "color(4)", - 35: "color(5)", - 36: "color(6)", - 37: "color(7)", - 39: "default", - 40: "on color(0)", - 41: "on color(1)", - 42: "on color(2)", - 43: "on color(3)", - 44: "on color(4)", - 45: "on color(5)", - 46: "on color(6)", - 47: "on color(7)", - 49: "on default", - 51: "frame", - 52: "encircle", - 53: "overline", - 54: "not frame not encircle", - 55: "not overline", - 90: "color(8)", - 91: "color(9)", - 92: "color(10)", - 93: "color(11)", - 94: "color(12)", - 95: "color(13)", - 96: "color(14)", - 97: "color(15)", - 100: "on color(8)", - 101: "on color(9)", - 102: "on color(10)", - 103: "on color(11)", - 104: "on color(12)", - 105: "on color(13)", - 106: "on color(14)", - 107: "on color(15)", -} - - -class AnsiDecoder: - """Translate ANSI code in to styled Text.""" - - def __init__(self) -> None: - self.style = Style.null() - - def decode(self, terminal_text: str) -> Iterable[Text]: - """Decode ANSI codes in an interable of lines. - - Args: - lines (Iterable[str]): An iterable of lines of terminal output. - - Yields: - Text: Marked up Text. - """ - for line in terminal_text.splitlines(): - yield self.decode_line(line) - - def decode_line(self, line: str) -> Text: - """Decode a line containing ansi codes. - - Args: - line (str): A line of terminal output. - - Returns: - Text: A Text instance marked up according to ansi codes. - """ - from_ansi = Color.from_ansi - from_rgb = Color.from_rgb - _Style = Style - text = Text() - append = text.append - line = line.rsplit("\r", 1)[-1] - for plain_text, sgr, osc in _ansi_tokenize(line): - if plain_text: - append(plain_text, self.style or None) - elif osc is not None: - if osc.startswith("8;"): - _params, semicolon, link = osc[2:].partition(";") - if semicolon: - self.style = self.style.update_link(link or None) - elif sgr is not None: - # Translate in to semi-colon separated codes - # Ignore invalid codes, because we want to be lenient - codes = [ - min(255, int(_code) if _code else 0) - for _code in sgr.split(";") - if _code.isdigit() or _code == "" - ] - iter_codes = iter(codes) - for code in iter_codes: - if code == 0: - # reset - self.style = _Style.null() - elif code in SGR_STYLE_MAP: - # styles - self.style += _Style.parse(SGR_STYLE_MAP[code]) - elif code == 38: - #  Foreground - with suppress(StopIteration): - color_type = next(iter_codes) - if color_type == 5: - self.style += _Style.from_color( - from_ansi(next(iter_codes)) - ) - elif color_type == 2: - self.style += _Style.from_color( - from_rgb( - next(iter_codes), - next(iter_codes), - next(iter_codes), - ) - ) - elif code == 48: - # Background - with suppress(StopIteration): - color_type = next(iter_codes) - if color_type == 5: - self.style += _Style.from_color( - None, from_ansi(next(iter_codes)) - ) - elif color_type == 2: - self.style += _Style.from_color( - None, - from_rgb( - next(iter_codes), - next(iter_codes), - next(iter_codes), - ), - ) - - return text - - -if sys.platform != "win32" and __name__ == "__main__": # pragma: no cover - import io - import os - import pty - import sys - - decoder = AnsiDecoder() - - stdout = io.BytesIO() - - def read(fd: int) -> bytes: - data = os.read(fd, 1024) - stdout.write(data) - return data - - pty.spawn(sys.argv[1:], read) - - from .console import Console - - console = Console(record=True) - - stdout_result = stdout.getvalue().decode("utf-8") - print(stdout_result) - - for line in decoder.decode(stdout_result): - console.print(line) - - console.save_html("stdout.html") diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/bar.py b/.venv/Lib/site-packages/pip/_vendor/rich/bar.py deleted file mode 100644 index ed86a55..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/bar.py +++ /dev/null @@ -1,94 +0,0 @@ -from typing import Optional, Union - -from .color import Color -from .console import Console, ConsoleOptions, RenderResult -from .jupyter import JupyterMixin -from .measure import Measurement -from .segment import Segment -from .style import Style - -# There are left-aligned characters for 1/8 to 7/8, but -# the right-aligned characters exist only for 1/8 and 4/8. -BEGIN_BLOCK_ELEMENTS = ["█", "█", "█", "▐", "▐", "▐", "▕", "▕"] -END_BLOCK_ELEMENTS = [" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉"] -FULL_BLOCK = "█" - - -class Bar(JupyterMixin): - """Renders a solid block bar. - - Args: - size (float): Value for the end of the bar. - begin (float): Begin point (between 0 and size, inclusive). - end (float): End point (between 0 and size, inclusive). - width (int, optional): Width of the bar, or ``None`` for maximum width. Defaults to None. - color (Union[Color, str], optional): Color of the bar. Defaults to "default". - bgcolor (Union[Color, str], optional): Color of bar background. Defaults to "default". - """ - - def __init__( - self, - size: float, - begin: float, - end: float, - *, - width: Optional[int] = None, - color: Union[Color, str] = "default", - bgcolor: Union[Color, str] = "default", - ): - self.size = size - self.begin = max(begin, 0) - self.end = min(end, size) - self.width = width - self.style = Style(color=color, bgcolor=bgcolor) - - def __repr__(self) -> str: - return f"Bar({self.size}, {self.begin}, {self.end})" - - def __rich_console__( - self, console: Console, options: ConsoleOptions - ) -> RenderResult: - - width = min( - self.width if self.width is not None else options.max_width, - options.max_width, - ) - - if self.begin >= self.end: - yield Segment(" " * width, self.style) - yield Segment.line() - return - - prefix_complete_eights = int(width * 8 * self.begin / self.size) - prefix_bar_count = prefix_complete_eights // 8 - prefix_eights_count = prefix_complete_eights % 8 - - body_complete_eights = int(width * 8 * self.end / self.size) - body_bar_count = body_complete_eights // 8 - body_eights_count = body_complete_eights % 8 - - # When start and end fall into the same cell, we ideally should render - # a symbol that's "center-aligned", but there is no good symbol in Unicode. - # In this case, we fall back to right-aligned block symbol for simplicity. - - prefix = " " * prefix_bar_count - if prefix_eights_count: - prefix += BEGIN_BLOCK_ELEMENTS[prefix_eights_count] - - body = FULL_BLOCK * body_bar_count - if body_eights_count: - body += END_BLOCK_ELEMENTS[body_eights_count] - - suffix = " " * (width - len(body)) - - yield Segment(prefix + body[len(prefix) :] + suffix, self.style) - yield Segment.line() - - def __rich_measure__( - self, console: Console, options: ConsoleOptions - ) -> Measurement: - return ( - Measurement(self.width, self.width) - if self.width is not None - else Measurement(4, options.max_width) - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/box.py b/.venv/Lib/site-packages/pip/_vendor/rich/box.py deleted file mode 100644 index d0b07cf..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/box.py +++ /dev/null @@ -1,517 +0,0 @@ -import sys -from typing import TYPE_CHECKING, Iterable, List - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover - - -from ._loop import loop_last - -if TYPE_CHECKING: - from pip._vendor.rich.console import ConsoleOptions - - -class Box: - """Defines characters to render boxes. - - ┌─┬┐ top - │ ││ head - ├─┼┤ head_row - │ ││ mid - ├─┼┤ row - ├─┼┤ foot_row - │ ││ foot - └─┴┘ bottom - - Args: - box (str): Characters making up box. - ascii (bool, optional): True if this box uses ascii characters only. Default is False. - """ - - def __init__(self, box: str, *, ascii: bool = False) -> None: - self._box = box - self.ascii = ascii - line1, line2, line3, line4, line5, line6, line7, line8 = box.splitlines() - # top - self.top_left, self.top, self.top_divider, self.top_right = iter(line1) - # head - self.head_left, _, self.head_vertical, self.head_right = iter(line2) - # head_row - ( - self.head_row_left, - self.head_row_horizontal, - self.head_row_cross, - self.head_row_right, - ) = iter(line3) - - # mid - self.mid_left, _, self.mid_vertical, self.mid_right = iter(line4) - # row - self.row_left, self.row_horizontal, self.row_cross, self.row_right = iter(line5) - # foot_row - ( - self.foot_row_left, - self.foot_row_horizontal, - self.foot_row_cross, - self.foot_row_right, - ) = iter(line6) - # foot - self.foot_left, _, self.foot_vertical, self.foot_right = iter(line7) - # bottom - self.bottom_left, self.bottom, self.bottom_divider, self.bottom_right = iter( - line8 - ) - - def __repr__(self) -> str: - return "Box(...)" - - def __str__(self) -> str: - return self._box - - def substitute(self, options: "ConsoleOptions", safe: bool = True) -> "Box": - """Substitute this box for another if it won't render due to platform issues. - - Args: - options (ConsoleOptions): Console options used in rendering. - safe (bool, optional): Substitute this for another Box if there are known problems - displaying on the platform (currently only relevant on Windows). Default is True. - - Returns: - Box: A different Box or the same Box. - """ - box = self - if options.legacy_windows and safe: - box = LEGACY_WINDOWS_SUBSTITUTIONS.get(box, box) - if options.ascii_only and not box.ascii: - box = ASCII - return box - - def get_plain_headed_box(self) -> "Box": - """If this box uses special characters for the borders of the header, then - return the equivalent box that does not. - - Returns: - Box: The most similar Box that doesn't use header-specific box characters. - If the current Box already satisfies this criterion, then it's returned. - """ - return PLAIN_HEADED_SUBSTITUTIONS.get(self, self) - - def get_top(self, widths: Iterable[int]) -> str: - """Get the top of a simple box. - - Args: - widths (List[int]): Widths of columns. - - Returns: - str: A string of box characters. - """ - - parts: List[str] = [] - append = parts.append - append(self.top_left) - for last, width in loop_last(widths): - append(self.top * width) - if not last: - append(self.top_divider) - append(self.top_right) - return "".join(parts) - - def get_row( - self, - widths: Iterable[int], - level: Literal["head", "row", "foot", "mid"] = "row", - edge: bool = True, - ) -> str: - """Get the top of a simple box. - - Args: - width (List[int]): Widths of columns. - - Returns: - str: A string of box characters. - """ - if level == "head": - left = self.head_row_left - horizontal = self.head_row_horizontal - cross = self.head_row_cross - right = self.head_row_right - elif level == "row": - left = self.row_left - horizontal = self.row_horizontal - cross = self.row_cross - right = self.row_right - elif level == "mid": - left = self.mid_left - horizontal = " " - cross = self.mid_vertical - right = self.mid_right - elif level == "foot": - left = self.foot_row_left - horizontal = self.foot_row_horizontal - cross = self.foot_row_cross - right = self.foot_row_right - else: - raise ValueError("level must be 'head', 'row' or 'foot'") - - parts: List[str] = [] - append = parts.append - if edge: - append(left) - for last, width in loop_last(widths): - append(horizontal * width) - if not last: - append(cross) - if edge: - append(right) - return "".join(parts) - - def get_bottom(self, widths: Iterable[int]) -> str: - """Get the bottom of a simple box. - - Args: - widths (List[int]): Widths of columns. - - Returns: - str: A string of box characters. - """ - - parts: List[str] = [] - append = parts.append - append(self.bottom_left) - for last, width in loop_last(widths): - append(self.bottom * width) - if not last: - append(self.bottom_divider) - append(self.bottom_right) - return "".join(parts) - - -ASCII: Box = Box( - """\ -+--+ -| || -|-+| -| || -|-+| -|-+| -| || -+--+ -""", - ascii=True, -) - -ASCII2: Box = Box( - """\ -+-++ -| || -+-++ -| || -+-++ -+-++ -| || -+-++ -""", - ascii=True, -) - -ASCII_DOUBLE_HEAD: Box = Box( - """\ -+-++ -| || -+=++ -| || -+-++ -+-++ -| || -+-++ -""", - ascii=True, -) - -SQUARE: Box = Box( - """\ -┌─┬┐ -│ ││ -├─┼┤ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" -) - -SQUARE_DOUBLE_HEAD: Box = Box( - """\ -┌─┬┐ -│ ││ -╞═╪╡ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" -) - -MINIMAL: Box = Box( - """\ - ╷ - │ -╶─┼╴ - │ -╶─┼╴ -╶─┼╴ - │ - ╵ -""" -) - - -MINIMAL_HEAVY_HEAD: Box = Box( - """\ - ╷ - │ -╺━┿╸ - │ -╶─┼╴ -╶─┼╴ - │ - ╵ -""" -) - -MINIMAL_DOUBLE_HEAD: Box = Box( - """\ - ╷ - │ - ═╪ - │ - ─┼ - ─┼ - │ - ╵ -""" -) - - -SIMPLE: Box = Box( - """\ - - - ── - - - ── - - -""" -) - -SIMPLE_HEAD: Box = Box( - """\ - - - ── - - - - - -""" -) - - -SIMPLE_HEAVY: Box = Box( - """\ - - - ━━ - - - ━━ - - -""" -) - - -HORIZONTALS: Box = Box( - """\ - ── - - ── - - ── - ── - - ── -""" -) - -ROUNDED: Box = Box( - """\ -╭─┬╮ -│ ││ -├─┼┤ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -╰─┴╯ -""" -) - -HEAVY: Box = Box( - """\ -┏━┳┓ -┃ ┃┃ -┣━╋┫ -┃ ┃┃ -┣━╋┫ -┣━╋┫ -┃ ┃┃ -┗━┻┛ -""" -) - -HEAVY_EDGE: Box = Box( - """\ -┏━┯┓ -┃ │┃ -┠─┼┨ -┃ │┃ -┠─┼┨ -┠─┼┨ -┃ │┃ -┗━┷┛ -""" -) - -HEAVY_HEAD: Box = Box( - """\ -┏━┳┓ -┃ ┃┃ -┡━╇┩ -│ ││ -├─┼┤ -├─┼┤ -│ ││ -└─┴┘ -""" -) - -DOUBLE: Box = Box( - """\ -╔═╦╗ -║ ║║ -╠═╬╣ -║ ║║ -╠═╬╣ -╠═╬╣ -║ ║║ -╚═╩╝ -""" -) - -DOUBLE_EDGE: Box = Box( - """\ -╔═╤╗ -║ │║ -╟─┼╢ -║ │║ -╟─┼╢ -╟─┼╢ -║ │║ -╚═╧╝ -""" -) - -MARKDOWN: Box = Box( - """\ - -| || -|-|| -| || -|-|| -|-|| -| || - -""", - ascii=True, -) - -# Map Boxes that don't render with raster fonts on to equivalent that do -LEGACY_WINDOWS_SUBSTITUTIONS = { - ROUNDED: SQUARE, - MINIMAL_HEAVY_HEAD: MINIMAL, - SIMPLE_HEAVY: SIMPLE, - HEAVY: SQUARE, - HEAVY_EDGE: SQUARE, - HEAVY_HEAD: SQUARE, -} - -# Map headed boxes to their headerless equivalents -PLAIN_HEADED_SUBSTITUTIONS = { - HEAVY_HEAD: SQUARE, - SQUARE_DOUBLE_HEAD: SQUARE, - MINIMAL_DOUBLE_HEAD: MINIMAL, - MINIMAL_HEAVY_HEAD: MINIMAL, - ASCII_DOUBLE_HEAD: ASCII2, -} - - -if __name__ == "__main__": # pragma: no cover - - from pip._vendor.rich.columns import Columns - from pip._vendor.rich.panel import Panel - - from . import box as box - from .console import Console - from .table import Table - from .text import Text - - console = Console(record=True) - - BOXES = [ - "ASCII", - "ASCII2", - "ASCII_DOUBLE_HEAD", - "SQUARE", - "SQUARE_DOUBLE_HEAD", - "MINIMAL", - "MINIMAL_HEAVY_HEAD", - "MINIMAL_DOUBLE_HEAD", - "SIMPLE", - "SIMPLE_HEAD", - "SIMPLE_HEAVY", - "HORIZONTALS", - "ROUNDED", - "HEAVY", - "HEAVY_EDGE", - "HEAVY_HEAD", - "DOUBLE", - "DOUBLE_EDGE", - "MARKDOWN", - ] - - console.print(Panel("[bold green]Box Constants", style="green"), justify="center") - console.print() - - columns = Columns(expand=True, padding=2) - for box_name in sorted(BOXES): - table = Table( - show_footer=True, style="dim", border_style="not dim", expand=True - ) - table.add_column("Header 1", "Footer 1") - table.add_column("Header 2", "Footer 2") - table.add_row("Cell", "Cell") - table.add_row("Cell", "Cell") - table.box = getattr(box, box_name) - table.title = Text(f"box.{box_name}", style="magenta") - columns.add_renderable(table) - console.print(columns) - - # console.save_html("box.html", inline_styles=True) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/cells.py b/.venv/Lib/site-packages/pip/_vendor/rich/cells.py deleted file mode 100644 index 139b949..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/cells.py +++ /dev/null @@ -1,154 +0,0 @@ -import re -from functools import lru_cache -from typing import Callable, List - -from ._cell_widths import CELL_WIDTHS - -# Regex to match sequence of the most common character ranges -_is_single_cell_widths = re.compile("^[\u0020-\u006f\u00a0\u02ff\u0370-\u0482]*$").match - - -@lru_cache(4096) -def cached_cell_len(text: str) -> int: - """Get the number of cells required to display text. - - This method always caches, which may use up a lot of memory. It is recommended to use - `cell_len` over this method. - - Args: - text (str): Text to display. - - Returns: - int: Get the number of cells required to display text. - """ - _get_size = get_character_cell_size - total_size = sum(_get_size(character) for character in text) - return total_size - - -def cell_len(text: str, _cell_len: Callable[[str], int] = cached_cell_len) -> int: - """Get the number of cells required to display text. - - Args: - text (str): Text to display. - - Returns: - int: Get the number of cells required to display text. - """ - if len(text) < 512: - return _cell_len(text) - _get_size = get_character_cell_size - total_size = sum(_get_size(character) for character in text) - return total_size - - -@lru_cache(maxsize=4096) -def get_character_cell_size(character: str) -> int: - """Get the cell size of a character. - - Args: - character (str): A single character. - - Returns: - int: Number of cells (0, 1 or 2) occupied by that character. - """ - return _get_codepoint_cell_size(ord(character)) - - -@lru_cache(maxsize=4096) -def _get_codepoint_cell_size(codepoint: int) -> int: - """Get the cell size of a character. - - Args: - character (str): A single character. - - Returns: - int: Number of cells (0, 1 or 2) occupied by that character. - """ - - _table = CELL_WIDTHS - lower_bound = 0 - upper_bound = len(_table) - 1 - index = (lower_bound + upper_bound) // 2 - while True: - start, end, width = _table[index] - if codepoint < start: - upper_bound = index - 1 - elif codepoint > end: - lower_bound = index + 1 - else: - return 0 if width == -1 else width - if upper_bound < lower_bound: - break - index = (lower_bound + upper_bound) // 2 - return 1 - - -def set_cell_size(text: str, total: int) -> str: - """Set the length of a string to fit within given number of cells.""" - - if _is_single_cell_widths(text): - size = len(text) - if size < total: - return text + " " * (total - size) - return text[:total] - - if total <= 0: - return "" - cell_size = cell_len(text) - if cell_size == total: - return text - if cell_size < total: - return text + " " * (total - cell_size) - - start = 0 - end = len(text) - - # Binary search until we find the right size - while True: - pos = (start + end) // 2 - before = text[: pos + 1] - before_len = cell_len(before) - if before_len == total + 1 and cell_len(before[-1]) == 2: - return before[:-1] + " " - if before_len == total: - return before - if before_len > total: - end = pos - else: - start = pos - - -# TODO: This is inefficient -# TODO: This might not work with CWJ type characters -def chop_cells(text: str, max_size: int, position: int = 0) -> List[str]: - """Break text in to equal (cell) length strings, returning the characters in reverse - order""" - _get_character_cell_size = get_character_cell_size - characters = [ - (character, _get_character_cell_size(character)) for character in text - ] - total_size = position - lines: List[List[str]] = [[]] - append = lines[-1].append - - for character, size in reversed(characters): - if total_size + size > max_size: - lines.append([character]) - append = lines[-1].append - total_size = size - else: - total_size += size - append(character) - - return ["".join(line) for line in lines] - - -if __name__ == "__main__": # pragma: no cover - - print(get_character_cell_size("😽")) - for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8): - print(line) - for n in range(80, 1, -1): - print(set_cell_size("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", n) + "|") - print("x" * n) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/color.py b/.venv/Lib/site-packages/pip/_vendor/rich/color.py deleted file mode 100644 index 6bca2da..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/color.py +++ /dev/null @@ -1,615 +0,0 @@ -import platform -import re -from colorsys import rgb_to_hls -from enum import IntEnum -from functools import lru_cache -from typing import TYPE_CHECKING, NamedTuple, Optional, Tuple - -from ._palettes import EIGHT_BIT_PALETTE, STANDARD_PALETTE, WINDOWS_PALETTE -from .color_triplet import ColorTriplet -from .repr import Result, rich_repr -from .terminal_theme import DEFAULT_TERMINAL_THEME - -if TYPE_CHECKING: # pragma: no cover - from .terminal_theme import TerminalTheme - from .text import Text - - -WINDOWS = platform.system() == "Windows" - - -class ColorSystem(IntEnum): - """One of the 3 color system supported by terminals.""" - - STANDARD = 1 - EIGHT_BIT = 2 - TRUECOLOR = 3 - WINDOWS = 4 - - def __repr__(self) -> str: - return f"ColorSystem.{self.name}" - - -class ColorType(IntEnum): - """Type of color stored in Color class.""" - - DEFAULT = 0 - STANDARD = 1 - EIGHT_BIT = 2 - TRUECOLOR = 3 - WINDOWS = 4 - - def __repr__(self) -> str: - return f"ColorType.{self.name}" - - -ANSI_COLOR_NAMES = { - "black": 0, - "red": 1, - "green": 2, - "yellow": 3, - "blue": 4, - "magenta": 5, - "cyan": 6, - "white": 7, - "bright_black": 8, - "bright_red": 9, - "bright_green": 10, - "bright_yellow": 11, - "bright_blue": 12, - "bright_magenta": 13, - "bright_cyan": 14, - "bright_white": 15, - "grey0": 16, - "gray0": 16, - "navy_blue": 17, - "dark_blue": 18, - "blue3": 20, - "blue1": 21, - "dark_green": 22, - "deep_sky_blue4": 25, - "dodger_blue3": 26, - "dodger_blue2": 27, - "green4": 28, - "spring_green4": 29, - "turquoise4": 30, - "deep_sky_blue3": 32, - "dodger_blue1": 33, - "green3": 40, - "spring_green3": 41, - "dark_cyan": 36, - "light_sea_green": 37, - "deep_sky_blue2": 38, - "deep_sky_blue1": 39, - "spring_green2": 47, - "cyan3": 43, - "dark_turquoise": 44, - "turquoise2": 45, - "green1": 46, - "spring_green1": 48, - "medium_spring_green": 49, - "cyan2": 50, - "cyan1": 51, - "dark_red": 88, - "deep_pink4": 125, - "purple4": 55, - "purple3": 56, - "blue_violet": 57, - "orange4": 94, - "grey37": 59, - "gray37": 59, - "medium_purple4": 60, - "slate_blue3": 62, - "royal_blue1": 63, - "chartreuse4": 64, - "dark_sea_green4": 71, - "pale_turquoise4": 66, - "steel_blue": 67, - "steel_blue3": 68, - "cornflower_blue": 69, - "chartreuse3": 76, - "cadet_blue": 73, - "sky_blue3": 74, - "steel_blue1": 81, - "pale_green3": 114, - "sea_green3": 78, - "aquamarine3": 79, - "medium_turquoise": 80, - "chartreuse2": 112, - "sea_green2": 83, - "sea_green1": 85, - "aquamarine1": 122, - "dark_slate_gray2": 87, - "dark_magenta": 91, - "dark_violet": 128, - "purple": 129, - "light_pink4": 95, - "plum4": 96, - "medium_purple3": 98, - "slate_blue1": 99, - "yellow4": 106, - "wheat4": 101, - "grey53": 102, - "gray53": 102, - "light_slate_grey": 103, - "light_slate_gray": 103, - "medium_purple": 104, - "light_slate_blue": 105, - "dark_olive_green3": 149, - "dark_sea_green": 108, - "light_sky_blue3": 110, - "sky_blue2": 111, - "dark_sea_green3": 150, - "dark_slate_gray3": 116, - "sky_blue1": 117, - "chartreuse1": 118, - "light_green": 120, - "pale_green1": 156, - "dark_slate_gray1": 123, - "red3": 160, - "medium_violet_red": 126, - "magenta3": 164, - "dark_orange3": 166, - "indian_red": 167, - "hot_pink3": 168, - "medium_orchid3": 133, - "medium_orchid": 134, - "medium_purple2": 140, - "dark_goldenrod": 136, - "light_salmon3": 173, - "rosy_brown": 138, - "grey63": 139, - "gray63": 139, - "medium_purple1": 141, - "gold3": 178, - "dark_khaki": 143, - "navajo_white3": 144, - "grey69": 145, - "gray69": 145, - "light_steel_blue3": 146, - "light_steel_blue": 147, - "yellow3": 184, - "dark_sea_green2": 157, - "light_cyan3": 152, - "light_sky_blue1": 153, - "green_yellow": 154, - "dark_olive_green2": 155, - "dark_sea_green1": 193, - "pale_turquoise1": 159, - "deep_pink3": 162, - "magenta2": 200, - "hot_pink2": 169, - "orchid": 170, - "medium_orchid1": 207, - "orange3": 172, - "light_pink3": 174, - "pink3": 175, - "plum3": 176, - "violet": 177, - "light_goldenrod3": 179, - "tan": 180, - "misty_rose3": 181, - "thistle3": 182, - "plum2": 183, - "khaki3": 185, - "light_goldenrod2": 222, - "light_yellow3": 187, - "grey84": 188, - "gray84": 188, - "light_steel_blue1": 189, - "yellow2": 190, - "dark_olive_green1": 192, - "honeydew2": 194, - "light_cyan1": 195, - "red1": 196, - "deep_pink2": 197, - "deep_pink1": 199, - "magenta1": 201, - "orange_red1": 202, - "indian_red1": 204, - "hot_pink": 206, - "dark_orange": 208, - "salmon1": 209, - "light_coral": 210, - "pale_violet_red1": 211, - "orchid2": 212, - "orchid1": 213, - "orange1": 214, - "sandy_brown": 215, - "light_salmon1": 216, - "light_pink1": 217, - "pink1": 218, - "plum1": 219, - "gold1": 220, - "navajo_white1": 223, - "misty_rose1": 224, - "thistle1": 225, - "yellow1": 226, - "light_goldenrod1": 227, - "khaki1": 228, - "wheat1": 229, - "cornsilk1": 230, - "grey100": 231, - "gray100": 231, - "grey3": 232, - "gray3": 232, - "grey7": 233, - "gray7": 233, - "grey11": 234, - "gray11": 234, - "grey15": 235, - "gray15": 235, - "grey19": 236, - "gray19": 236, - "grey23": 237, - "gray23": 237, - "grey27": 238, - "gray27": 238, - "grey30": 239, - "gray30": 239, - "grey35": 240, - "gray35": 240, - "grey39": 241, - "gray39": 241, - "grey42": 242, - "gray42": 242, - "grey46": 243, - "gray46": 243, - "grey50": 244, - "gray50": 244, - "grey54": 245, - "gray54": 245, - "grey58": 246, - "gray58": 246, - "grey62": 247, - "gray62": 247, - "grey66": 248, - "gray66": 248, - "grey70": 249, - "gray70": 249, - "grey74": 250, - "gray74": 250, - "grey78": 251, - "gray78": 251, - "grey82": 252, - "gray82": 252, - "grey85": 253, - "gray85": 253, - "grey89": 254, - "gray89": 254, - "grey93": 255, - "gray93": 255, -} - - -class ColorParseError(Exception): - """The color could not be parsed.""" - - -RE_COLOR = re.compile( - r"""^ -\#([0-9a-f]{6})$| -color\(([0-9]{1,3})\)$| -rgb\(([\d\s,]+)\)$ -""", - re.VERBOSE, -) - - -@rich_repr -class Color(NamedTuple): - """Terminal color definition.""" - - name: str - """The name of the color (typically the input to Color.parse).""" - type: ColorType - """The type of the color.""" - number: Optional[int] = None - """The color number, if a standard color, or None.""" - triplet: Optional[ColorTriplet] = None - """A triplet of color components, if an RGB color.""" - - def __rich__(self) -> "Text": - """Dispays the actual color if Rich printed.""" - from .style import Style - from .text import Text - - return Text.assemble( - f"", - ) - - def __rich_repr__(self) -> Result: - yield self.name - yield self.type - yield "number", self.number, None - yield "triplet", self.triplet, None - - @property - def system(self) -> ColorSystem: - """Get the native color system for this color.""" - if self.type == ColorType.DEFAULT: - return ColorSystem.STANDARD - return ColorSystem(int(self.type)) - - @property - def is_system_defined(self) -> bool: - """Check if the color is ultimately defined by the system.""" - return self.system not in (ColorSystem.EIGHT_BIT, ColorSystem.TRUECOLOR) - - @property - def is_default(self) -> bool: - """Check if the color is a default color.""" - return self.type == ColorType.DEFAULT - - def get_truecolor( - self, theme: Optional["TerminalTheme"] = None, foreground: bool = True - ) -> ColorTriplet: - """Get an equivalent color triplet for this color. - - Args: - theme (TerminalTheme, optional): Optional terminal theme, or None to use default. Defaults to None. - foreground (bool, optional): True for a foreground color, or False for background. Defaults to True. - - Returns: - ColorTriplet: A color triplet containing RGB components. - """ - - if theme is None: - theme = DEFAULT_TERMINAL_THEME - if self.type == ColorType.TRUECOLOR: - assert self.triplet is not None - return self.triplet - elif self.type == ColorType.EIGHT_BIT: - assert self.number is not None - return EIGHT_BIT_PALETTE[self.number] - elif self.type == ColorType.STANDARD: - assert self.number is not None - return theme.ansi_colors[self.number] - elif self.type == ColorType.WINDOWS: - assert self.number is not None - return WINDOWS_PALETTE[self.number] - else: # self.type == ColorType.DEFAULT: - assert self.number is None - return theme.foreground_color if foreground else theme.background_color - - @classmethod - def from_ansi(cls, number: int) -> "Color": - """Create a Color number from it's 8-bit ansi number. - - Args: - number (int): A number between 0-255 inclusive. - - Returns: - Color: A new Color instance. - """ - return cls( - name=f"color({number})", - type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT), - number=number, - ) - - @classmethod - def from_triplet(cls, triplet: "ColorTriplet") -> "Color": - """Create a truecolor RGB color from a triplet of values. - - Args: - triplet (ColorTriplet): A color triplet containing red, green and blue components. - - Returns: - Color: A new color object. - """ - return cls(name=triplet.hex, type=ColorType.TRUECOLOR, triplet=triplet) - - @classmethod - def from_rgb(cls, red: float, green: float, blue: float) -> "Color": - """Create a truecolor from three color components in the range(0->255). - - Args: - red (float): Red component in range 0-255. - green (float): Green component in range 0-255. - blue (float): Blue component in range 0-255. - - Returns: - Color: A new color object. - """ - return cls.from_triplet(ColorTriplet(int(red), int(green), int(blue))) - - @classmethod - def default(cls) -> "Color": - """Get a Color instance representing the default color. - - Returns: - Color: Default color. - """ - return cls(name="default", type=ColorType.DEFAULT) - - @classmethod - @lru_cache(maxsize=1024) - def parse(cls, color: str) -> "Color": - """Parse a color definition.""" - original_color = color - color = color.lower().strip() - - if color == "default": - return cls(color, type=ColorType.DEFAULT) - - color_number = ANSI_COLOR_NAMES.get(color) - if color_number is not None: - return cls( - color, - type=(ColorType.STANDARD if color_number < 16 else ColorType.EIGHT_BIT), - number=color_number, - ) - - color_match = RE_COLOR.match(color) - if color_match is None: - raise ColorParseError(f"{original_color!r} is not a valid color") - - color_24, color_8, color_rgb = color_match.groups() - if color_24: - triplet = ColorTriplet( - int(color_24[0:2], 16), int(color_24[2:4], 16), int(color_24[4:6], 16) - ) - return cls(color, ColorType.TRUECOLOR, triplet=triplet) - - elif color_8: - number = int(color_8) - if number > 255: - raise ColorParseError(f"color number must be <= 255 in {color!r}") - return cls( - color, - type=(ColorType.STANDARD if number < 16 else ColorType.EIGHT_BIT), - number=number, - ) - - else: # color_rgb: - components = color_rgb.split(",") - if len(components) != 3: - raise ColorParseError( - f"expected three components in {original_color!r}" - ) - red, green, blue = components - triplet = ColorTriplet(int(red), int(green), int(blue)) - if not all(component <= 255 for component in triplet): - raise ColorParseError( - f"color components must be <= 255 in {original_color!r}" - ) - return cls(color, ColorType.TRUECOLOR, triplet=triplet) - - @lru_cache(maxsize=1024) - def get_ansi_codes(self, foreground: bool = True) -> Tuple[str, ...]: - """Get the ANSI escape codes for this color.""" - _type = self.type - if _type == ColorType.DEFAULT: - return ("39" if foreground else "49",) - - elif _type == ColorType.WINDOWS: - number = self.number - assert number is not None - fore, back = (30, 40) if number < 8 else (82, 92) - return (str(fore + number if foreground else back + number),) - - elif _type == ColorType.STANDARD: - number = self.number - assert number is not None - fore, back = (30, 40) if number < 8 else (82, 92) - return (str(fore + number if foreground else back + number),) - - elif _type == ColorType.EIGHT_BIT: - assert self.number is not None - return ("38" if foreground else "48", "5", str(self.number)) - - else: # self.standard == ColorStandard.TRUECOLOR: - assert self.triplet is not None - red, green, blue = self.triplet - return ("38" if foreground else "48", "2", str(red), str(green), str(blue)) - - @lru_cache(maxsize=1024) - def downgrade(self, system: ColorSystem) -> "Color": - """Downgrade a color system to a system with fewer colors.""" - - if self.type in [ColorType.DEFAULT, system]: - return self - # Convert to 8-bit color from truecolor color - if system == ColorSystem.EIGHT_BIT and self.system == ColorSystem.TRUECOLOR: - assert self.triplet is not None - red, green, blue = self.triplet.normalized - _h, l, s = rgb_to_hls(red, green, blue) - # If saturation is under 10% assume it is grayscale - if s < 0.1: - gray = round(l * 25.0) - if gray == 0: - color_number = 16 - elif gray == 25: - color_number = 231 - else: - color_number = 231 + gray - return Color(self.name, ColorType.EIGHT_BIT, number=color_number) - - color_number = ( - 16 + 36 * round(red * 5.0) + 6 * round(green * 5.0) + round(blue * 5.0) - ) - return Color(self.name, ColorType.EIGHT_BIT, number=color_number) - - # Convert to standard from truecolor or 8-bit - elif system == ColorSystem.STANDARD: - if self.system == ColorSystem.TRUECOLOR: - assert self.triplet is not None - triplet = self.triplet - else: # self.system == ColorSystem.EIGHT_BIT - assert self.number is not None - triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number]) - - color_number = STANDARD_PALETTE.match(triplet) - return Color(self.name, ColorType.STANDARD, number=color_number) - - elif system == ColorSystem.WINDOWS: - if self.system == ColorSystem.TRUECOLOR: - assert self.triplet is not None - triplet = self.triplet - else: # self.system == ColorSystem.EIGHT_BIT - assert self.number is not None - if self.number < 16: - return Color(self.name, ColorType.WINDOWS, number=self.number) - triplet = ColorTriplet(*EIGHT_BIT_PALETTE[self.number]) - - color_number = WINDOWS_PALETTE.match(triplet) - return Color(self.name, ColorType.WINDOWS, number=color_number) - - return self - - -def parse_rgb_hex(hex_color: str) -> ColorTriplet: - """Parse six hex characters in to RGB triplet.""" - assert len(hex_color) == 6, "must be 6 characters" - color = ColorTriplet( - int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16) - ) - return color - - -def blend_rgb( - color1: ColorTriplet, color2: ColorTriplet, cross_fade: float = 0.5 -) -> ColorTriplet: - """Blend one RGB color in to another.""" - r1, g1, b1 = color1 - r2, g2, b2 = color2 - new_color = ColorTriplet( - int(r1 + (r2 - r1) * cross_fade), - int(g1 + (g2 - g1) * cross_fade), - int(b1 + (b2 - b1) * cross_fade), - ) - return new_color - - -if __name__ == "__main__": # pragma: no cover - - from .console import Console - from .table import Table - from .text import Text - - console = Console() - - table = Table(show_footer=False, show_edge=True) - table.add_column("Color", width=10, overflow="ellipsis") - table.add_column("Number", justify="right", style="yellow") - table.add_column("Name", style="green") - table.add_column("Hex", style="blue") - table.add_column("RGB", style="magenta") - - colors = sorted((v, k) for k, v in ANSI_COLOR_NAMES.items()) - for color_number, name in colors: - if "grey" in name: - continue - color_cell = Text(" " * 10, style=f"on {name}") - if color_number < 16: - table.add_row(color_cell, f"{color_number}", Text(f'"{name}"')) - else: - color = EIGHT_BIT_PALETTE[color_number] # type: ignore[has-type] - table.add_row( - color_cell, str(color_number), Text(f'"{name}"'), color.hex, color.rgb - ) - - console.print(table) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py b/.venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py deleted file mode 100644 index 02cab32..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/color_triplet.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import NamedTuple, Tuple - - -class ColorTriplet(NamedTuple): - """The red, green, and blue components of a color.""" - - red: int - """Red component in 0 to 255 range.""" - green: int - """Green component in 0 to 255 range.""" - blue: int - """Blue component in 0 to 255 range.""" - - @property - def hex(self) -> str: - """get the color triplet in CSS style.""" - red, green, blue = self - return f"#{red:02x}{green:02x}{blue:02x}" - - @property - def rgb(self) -> str: - """The color in RGB format. - - Returns: - str: An rgb color, e.g. ``"rgb(100,23,255)"``. - """ - red, green, blue = self - return f"rgb({red},{green},{blue})" - - @property - def normalized(self) -> Tuple[float, float, float]: - """Convert components into floats between 0 and 1. - - Returns: - Tuple[float, float, float]: A tuple of three normalized colour components. - """ - red, green, blue = self - return red / 255.0, green / 255.0, blue / 255.0 diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/columns.py b/.venv/Lib/site-packages/pip/_vendor/rich/columns.py deleted file mode 100644 index 669a3a7..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/columns.py +++ /dev/null @@ -1,187 +0,0 @@ -from collections import defaultdict -from itertools import chain -from operator import itemgetter -from typing import Dict, Iterable, List, Optional, Tuple - -from .align import Align, AlignMethod -from .console import Console, ConsoleOptions, RenderableType, RenderResult -from .constrain import Constrain -from .measure import Measurement -from .padding import Padding, PaddingDimensions -from .table import Table -from .text import TextType -from .jupyter import JupyterMixin - - -class Columns(JupyterMixin): - """Display renderables in neat columns. - - Args: - renderables (Iterable[RenderableType]): Any number of Rich renderables (including str). - width (int, optional): The desired width of the columns, or None to auto detect. Defaults to None. - padding (PaddingDimensions, optional): Optional padding around cells. Defaults to (0, 1). - expand (bool, optional): Expand columns to full width. Defaults to False. - equal (bool, optional): Arrange in to equal sized columns. Defaults to False. - column_first (bool, optional): Align items from top to bottom (rather than left to right). Defaults to False. - right_to_left (bool, optional): Start column from right hand side. Defaults to False. - align (str, optional): Align value ("left", "right", or "center") or None for default. Defaults to None. - title (TextType, optional): Optional title for Columns. - """ - - def __init__( - self, - renderables: Optional[Iterable[RenderableType]] = None, - padding: PaddingDimensions = (0, 1), - *, - width: Optional[int] = None, - expand: bool = False, - equal: bool = False, - column_first: bool = False, - right_to_left: bool = False, - align: Optional[AlignMethod] = None, - title: Optional[TextType] = None, - ) -> None: - self.renderables = list(renderables or []) - self.width = width - self.padding = padding - self.expand = expand - self.equal = equal - self.column_first = column_first - self.right_to_left = right_to_left - self.align: Optional[AlignMethod] = align - self.title = title - - def add_renderable(self, renderable: RenderableType) -> None: - """Add a renderable to the columns. - - Args: - renderable (RenderableType): Any renderable object. - """ - self.renderables.append(renderable) - - def __rich_console__( - self, console: Console, options: ConsoleOptions - ) -> RenderResult: - render_str = console.render_str - renderables = [ - render_str(renderable) if isinstance(renderable, str) else renderable - for renderable in self.renderables - ] - if not renderables: - return - _top, right, _bottom, left = Padding.unpack(self.padding) - width_padding = max(left, right) - max_width = options.max_width - widths: Dict[int, int] = defaultdict(int) - column_count = len(renderables) - - get_measurement = Measurement.get - renderable_widths = [ - get_measurement(console, options, renderable).maximum - for renderable in renderables - ] - if self.equal: - renderable_widths = [max(renderable_widths)] * len(renderable_widths) - - def iter_renderables( - column_count: int, - ) -> Iterable[Tuple[int, Optional[RenderableType]]]: - item_count = len(renderables) - if self.column_first: - width_renderables = list(zip(renderable_widths, renderables)) - - column_lengths: List[int] = [item_count // column_count] * column_count - for col_no in range(item_count % column_count): - column_lengths[col_no] += 1 - - row_count = (item_count + column_count - 1) // column_count - cells = [[-1] * column_count for _ in range(row_count)] - row = col = 0 - for index in range(item_count): - cells[row][col] = index - column_lengths[col] -= 1 - if column_lengths[col]: - row += 1 - else: - col += 1 - row = 0 - for index in chain.from_iterable(cells): - if index == -1: - break - yield width_renderables[index] - else: - yield from zip(renderable_widths, renderables) - # Pad odd elements with spaces - if item_count % column_count: - for _ in range(column_count - (item_count % column_count)): - yield 0, None - - table = Table.grid(padding=self.padding, collapse_padding=True, pad_edge=False) - table.expand = self.expand - table.title = self.title - - if self.width is not None: - column_count = (max_width) // (self.width + width_padding) - for _ in range(column_count): - table.add_column(width=self.width) - else: - while column_count > 1: - widths.clear() - column_no = 0 - for renderable_width, _ in iter_renderables(column_count): - widths[column_no] = max(widths[column_no], renderable_width) - total_width = sum(widths.values()) + width_padding * ( - len(widths) - 1 - ) - if total_width > max_width: - column_count = len(widths) - 1 - break - else: - column_no = (column_no + 1) % column_count - else: - break - - get_renderable = itemgetter(1) - _renderables = [ - get_renderable(_renderable) - for _renderable in iter_renderables(column_count) - ] - if self.equal: - _renderables = [ - None - if renderable is None - else Constrain(renderable, renderable_widths[0]) - for renderable in _renderables - ] - if self.align: - align = self.align - _Align = Align - _renderables = [ - None if renderable is None else _Align(renderable, align) - for renderable in _renderables - ] - - right_to_left = self.right_to_left - add_row = table.add_row - for start in range(0, len(_renderables), column_count): - row = _renderables[start : start + column_count] - if right_to_left: - row = row[::-1] - add_row(*row) - yield table - - -if __name__ == "__main__": # pragma: no cover - import os - - console = Console() - - files = [f"{i} {s}" for i, s in enumerate(sorted(os.listdir()))] - columns = Columns(files, padding=(0, 1), expand=False, equal=False) - console.print(columns) - console.rule() - columns.column_first = True - console.print(columns) - columns.right_to_left = True - console.rule() - console.print(columns) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/console.py b/.venv/Lib/site-packages/pip/_vendor/rich/console.py deleted file mode 100644 index 93a10b0..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/console.py +++ /dev/null @@ -1,2572 +0,0 @@ -import inspect -import io -import os -import platform -import sys -import threading -import zlib -from abc import ABC, abstractmethod -from dataclasses import dataclass, field -from datetime import datetime -from functools import wraps -from getpass import getpass -from html import escape -from inspect import isclass -from itertools import islice -from math import ceil -from time import monotonic -from types import FrameType, ModuleType, TracebackType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Callable, - Dict, - Iterable, - List, - Mapping, - NamedTuple, - Optional, - TextIO, - Tuple, - Type, - Union, - cast, -) - -if sys.version_info >= (3, 8): - from typing import Literal, Protocol, runtime_checkable -else: - from pip._vendor.typing_extensions import ( - Literal, - Protocol, - runtime_checkable, - ) # pragma: no cover - -from . import errors, themes -from ._emoji_replace import _emoji_replace -from ._export_format import CONSOLE_HTML_FORMAT, CONSOLE_SVG_FORMAT -from ._log_render import FormatTimeCallable, LogRender -from .align import Align, AlignMethod -from .color import ColorSystem, blend_rgb -from .control import Control -from .emoji import EmojiVariant -from .highlighter import NullHighlighter, ReprHighlighter -from .markup import render as render_markup -from .measure import Measurement, measure_renderables -from .pager import Pager, SystemPager -from .pretty import Pretty, is_expandable -from .protocol import rich_cast -from .region import Region -from .scope import render_scope -from .screen import Screen -from .segment import Segment -from .style import Style, StyleType -from .styled import Styled -from .terminal_theme import DEFAULT_TERMINAL_THEME, SVG_EXPORT_THEME, TerminalTheme -from .text import Text, TextType -from .theme import Theme, ThemeStack - -if TYPE_CHECKING: - from ._windows import WindowsConsoleFeatures - from .live import Live - from .status import Status - -JUPYTER_DEFAULT_COLUMNS = 115 -JUPYTER_DEFAULT_LINES = 100 -WINDOWS = platform.system() == "Windows" - -HighlighterType = Callable[[Union[str, "Text"]], "Text"] -JustifyMethod = Literal["default", "left", "center", "right", "full"] -OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"] - - -class NoChange: - pass - - -NO_CHANGE = NoChange() - -try: - _STDIN_FILENO = sys.__stdin__.fileno() -except Exception: - _STDIN_FILENO = 0 -try: - _STDOUT_FILENO = sys.__stdout__.fileno() -except Exception: - _STDOUT_FILENO = 1 -try: - _STDERR_FILENO = sys.__stderr__.fileno() -except Exception: - _STDERR_FILENO = 2 - -_STD_STREAMS = (_STDIN_FILENO, _STDOUT_FILENO, _STDERR_FILENO) -_STD_STREAMS_OUTPUT = (_STDOUT_FILENO, _STDERR_FILENO) - - -_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD} - - -class ConsoleDimensions(NamedTuple): - """Size of the terminal.""" - - width: int - """The width of the console in 'cells'.""" - height: int - """The height of the console in lines.""" - - -@dataclass -class ConsoleOptions: - """Options for __rich_console__ method.""" - - size: ConsoleDimensions - """Size of console.""" - legacy_windows: bool - """legacy_windows: flag for legacy windows.""" - min_width: int - """Minimum width of renderable.""" - max_width: int - """Maximum width of renderable.""" - is_terminal: bool - """True if the target is a terminal, otherwise False.""" - encoding: str - """Encoding of terminal.""" - max_height: int - """Height of container (starts as terminal)""" - justify: Optional[JustifyMethod] = None - """Justify value override for renderable.""" - overflow: Optional[OverflowMethod] = None - """Overflow value override for renderable.""" - no_wrap: Optional[bool] = False - """Disable wrapping for text.""" - highlight: Optional[bool] = None - """Highlight override for render_str.""" - markup: Optional[bool] = None - """Enable markup when rendering strings.""" - height: Optional[int] = None - - @property - def ascii_only(self) -> bool: - """Check if renderables should use ascii only.""" - return not self.encoding.startswith("utf") - - def copy(self) -> "ConsoleOptions": - """Return a copy of the options. - - Returns: - ConsoleOptions: a copy of self. - """ - options: ConsoleOptions = ConsoleOptions.__new__(ConsoleOptions) - options.__dict__ = self.__dict__.copy() - return options - - def update( - self, - *, - width: Union[int, NoChange] = NO_CHANGE, - min_width: Union[int, NoChange] = NO_CHANGE, - max_width: Union[int, NoChange] = NO_CHANGE, - justify: Union[Optional[JustifyMethod], NoChange] = NO_CHANGE, - overflow: Union[Optional[OverflowMethod], NoChange] = NO_CHANGE, - no_wrap: Union[Optional[bool], NoChange] = NO_CHANGE, - highlight: Union[Optional[bool], NoChange] = NO_CHANGE, - markup: Union[Optional[bool], NoChange] = NO_CHANGE, - height: Union[Optional[int], NoChange] = NO_CHANGE, - ) -> "ConsoleOptions": - """Update values, return a copy.""" - options = self.copy() - if not isinstance(width, NoChange): - options.min_width = options.max_width = max(0, width) - if not isinstance(min_width, NoChange): - options.min_width = min_width - if not isinstance(max_width, NoChange): - options.max_width = max_width - if not isinstance(justify, NoChange): - options.justify = justify - if not isinstance(overflow, NoChange): - options.overflow = overflow - if not isinstance(no_wrap, NoChange): - options.no_wrap = no_wrap - if not isinstance(highlight, NoChange): - options.highlight = highlight - if not isinstance(markup, NoChange): - options.markup = markup - if not isinstance(height, NoChange): - if height is not None: - options.max_height = height - options.height = None if height is None else max(0, height) - return options - - def update_width(self, width: int) -> "ConsoleOptions": - """Update just the width, return a copy. - - Args: - width (int): New width (sets both min_width and max_width) - - Returns: - ~ConsoleOptions: New console options instance. - """ - options = self.copy() - options.min_width = options.max_width = max(0, width) - return options - - def update_height(self, height: int) -> "ConsoleOptions": - """Update the height, and return a copy. - - Args: - height (int): New height - - Returns: - ~ConsoleOptions: New Console options instance. - """ - options = self.copy() - options.max_height = options.height = height - return options - - def reset_height(self) -> "ConsoleOptions": - """Return a copy of the options with height set to ``None``. - - Returns: - ~ConsoleOptions: New console options instance. - """ - options = self.copy() - options.height = None - return options - - def update_dimensions(self, width: int, height: int) -> "ConsoleOptions": - """Update the width and height, and return a copy. - - Args: - width (int): New width (sets both min_width and max_width). - height (int): New height. - - Returns: - ~ConsoleOptions: New console options instance. - """ - options = self.copy() - options.min_width = options.max_width = max(0, width) - options.height = options.max_height = height - return options - - -@runtime_checkable -class RichCast(Protocol): - """An object that may be 'cast' to a console renderable.""" - - def __rich__( - self, - ) -> Union["ConsoleRenderable", "RichCast", str]: # pragma: no cover - ... - - -@runtime_checkable -class ConsoleRenderable(Protocol): - """An object that supports the console protocol.""" - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": # pragma: no cover - ... - - -# A type that may be rendered by Console. -RenderableType = Union[ConsoleRenderable, RichCast, str] - -# The result of calling a __rich_console__ method. -RenderResult = Iterable[Union[RenderableType, Segment]] - -_null_highlighter = NullHighlighter() - - -class CaptureError(Exception): - """An error in the Capture context manager.""" - - -class NewLine: - """A renderable to generate new line(s)""" - - def __init__(self, count: int = 1) -> None: - self.count = count - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> Iterable[Segment]: - yield Segment("\n" * self.count) - - -class ScreenUpdate: - """Render a list of lines at a given offset.""" - - def __init__(self, lines: List[List[Segment]], x: int, y: int) -> None: - self._lines = lines - self.x = x - self.y = y - - def __rich_console__( - self, console: "Console", options: ConsoleOptions - ) -> RenderResult: - x = self.x - move_to = Control.move_to - for offset, line in enumerate(self._lines, self.y): - yield move_to(x, offset) - yield from line - - -class Capture: - """Context manager to capture the result of printing to the console. - See :meth:`~rich.console.Console.capture` for how to use. - - Args: - console (Console): A console instance to capture output. - """ - - def __init__(self, console: "Console") -> None: - self._console = console - self._result: Optional[str] = None - - def __enter__(self) -> "Capture": - self._console.begin_capture() - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> None: - self._result = self._console.end_capture() - - def get(self) -> str: - """Get the result of the capture.""" - if self._result is None: - raise CaptureError( - "Capture result is not available until context manager exits." - ) - return self._result - - -class ThemeContext: - """A context manager to use a temporary theme. See :meth:`~rich.console.Console.use_theme` for usage.""" - - def __init__(self, console: "Console", theme: Theme, inherit: bool = True) -> None: - self.console = console - self.theme = theme - self.inherit = inherit - - def __enter__(self) -> "ThemeContext": - self.console.push_theme(self.theme) - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> None: - self.console.pop_theme() - - -class PagerContext: - """A context manager that 'pages' content. See :meth:`~rich.console.Console.pager` for usage.""" - - def __init__( - self, - console: "Console", - pager: Optional[Pager] = None, - styles: bool = False, - links: bool = False, - ) -> None: - self._console = console - self.pager = SystemPager() if pager is None else pager - self.styles = styles - self.links = links - - def __enter__(self) -> "PagerContext": - self._console._enter_buffer() - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> None: - if exc_type is None: - with self._console._lock: - buffer: List[Segment] = self._console._buffer[:] - del self._console._buffer[:] - segments: Iterable[Segment] = buffer - if not self.styles: - segments = Segment.strip_styles(segments) - elif not self.links: - segments = Segment.strip_links(segments) - content = self._console._render_buffer(segments) - self.pager.show(content) - self._console._exit_buffer() - - -class ScreenContext: - """A context manager that enables an alternative screen. See :meth:`~rich.console.Console.screen` for usage.""" - - def __init__( - self, console: "Console", hide_cursor: bool, style: StyleType = "" - ) -> None: - self.console = console - self.hide_cursor = hide_cursor - self.screen = Screen(style=style) - self._changed = False - - def update( - self, *renderables: RenderableType, style: Optional[StyleType] = None - ) -> None: - """Update the screen. - - Args: - renderable (RenderableType, optional): Optional renderable to replace current renderable, - or None for no change. Defaults to None. - style: (Style, optional): Replacement style, or None for no change. Defaults to None. - """ - if renderables: - self.screen.renderable = ( - Group(*renderables) if len(renderables) > 1 else renderables[0] - ) - if style is not None: - self.screen.style = style - self.console.print(self.screen, end="") - - def __enter__(self) -> "ScreenContext": - self._changed = self.console.set_alt_screen(True) - if self._changed and self.hide_cursor: - self.console.show_cursor(False) - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> None: - if self._changed: - self.console.set_alt_screen(False) - if self.hide_cursor: - self.console.show_cursor(True) - - -class Group: - """Takes a group of renderables and returns a renderable object that renders the group. - - Args: - renderables (Iterable[RenderableType]): An iterable of renderable objects. - fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True. - """ - - def __init__(self, *renderables: "RenderableType", fit: bool = True) -> None: - self._renderables = renderables - self.fit = fit - self._render: Optional[List[RenderableType]] = None - - @property - def renderables(self) -> List["RenderableType"]: - if self._render is None: - self._render = list(self._renderables) - return self._render - - def __rich_measure__( - self, console: "Console", options: "ConsoleOptions" - ) -> "Measurement": - if self.fit: - return measure_renderables(console, options, self.renderables) - else: - return Measurement(options.max_width, options.max_width) - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> RenderResult: - yield from self.renderables - - -def group(fit: bool = True) -> Callable[..., Callable[..., Group]]: - """A decorator that turns an iterable of renderables in to a group. - - Args: - fit (bool, optional): Fit dimension of group to contents, or fill available space. Defaults to True. - """ - - def decorator( - method: Callable[..., Iterable[RenderableType]] - ) -> Callable[..., Group]: - """Convert a method that returns an iterable of renderables in to a Group.""" - - @wraps(method) - def _replace(*args: Any, **kwargs: Any) -> Group: - renderables = method(*args, **kwargs) - return Group(*renderables, fit=fit) - - return _replace - - return decorator - - -def _is_jupyter() -> bool: # pragma: no cover - """Check if we're running in a Jupyter notebook.""" - try: - get_ipython # type: ignore[name-defined] - except NameError: - return False - ipython = get_ipython() # type: ignore[name-defined] - shell = ipython.__class__.__name__ - if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell": - return True # Jupyter notebook or qtconsole - elif shell == "TerminalInteractiveShell": - return False # Terminal running IPython - else: - return False # Other type (?) - - -COLOR_SYSTEMS = { - "standard": ColorSystem.STANDARD, - "256": ColorSystem.EIGHT_BIT, - "truecolor": ColorSystem.TRUECOLOR, - "windows": ColorSystem.WINDOWS, -} - -_COLOR_SYSTEMS_NAMES = {system: name for name, system in COLOR_SYSTEMS.items()} - - -@dataclass -class ConsoleThreadLocals(threading.local): - """Thread local values for Console context.""" - - theme_stack: ThemeStack - buffer: List[Segment] = field(default_factory=list) - buffer_index: int = 0 - - -class RenderHook(ABC): - """Provides hooks in to the render process.""" - - @abstractmethod - def process_renderables( - self, renderables: List[ConsoleRenderable] - ) -> List[ConsoleRenderable]: - """Called with a list of objects to render. - - This method can return a new list of renderables, or modify and return the same list. - - Args: - renderables (List[ConsoleRenderable]): A number of renderable objects. - - Returns: - List[ConsoleRenderable]: A replacement list of renderables. - """ - - -_windows_console_features: Optional["WindowsConsoleFeatures"] = None - - -def get_windows_console_features() -> "WindowsConsoleFeatures": # pragma: no cover - global _windows_console_features - if _windows_console_features is not None: - return _windows_console_features - from ._windows import get_windows_console_features - - _windows_console_features = get_windows_console_features() - return _windows_console_features - - -def detect_legacy_windows() -> bool: - """Detect legacy Windows.""" - return WINDOWS and not get_windows_console_features().vt - - -class Console: - """A high level console interface. - - Args: - color_system (str, optional): The color system supported by your terminal, - either ``"standard"``, ``"256"`` or ``"truecolor"``. Leave as ``"auto"`` to autodetect. - force_terminal (Optional[bool], optional): Enable/disable terminal control codes, or None to auto-detect terminal. Defaults to None. - force_jupyter (Optional[bool], optional): Enable/disable Jupyter rendering, or None to auto-detect Jupyter. Defaults to None. - force_interactive (Optional[bool], optional): Enable/disable interactive mode, or None to auto detect. Defaults to None. - soft_wrap (Optional[bool], optional): Set soft wrap default on print method. Defaults to False. - theme (Theme, optional): An optional style theme object, or ``None`` for default theme. - stderr (bool, optional): Use stderr rather than stdout if ``file`` is not specified. Defaults to False. - file (IO, optional): A file object where the console should write to. Defaults to stdout. - quiet (bool, Optional): Boolean to suppress all output. Defaults to False. - width (int, optional): The width of the terminal. Leave as default to auto-detect width. - height (int, optional): The height of the terminal. Leave as default to auto-detect height. - style (StyleType, optional): Style to apply to all output, or None for no style. Defaults to None. - no_color (Optional[bool], optional): Enabled no color mode, or None to auto detect. Defaults to None. - tab_size (int, optional): Number of spaces used to replace a tab character. Defaults to 8. - record (bool, optional): Boolean to enable recording of terminal output, - required to call :meth:`export_html`, :meth:`export_svg`, and :meth:`export_text`. Defaults to False. - markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True. - emoji (bool, optional): Enable emoji code. Defaults to True. - emoji_variant (str, optional): Optional emoji variant, either "text" or "emoji". Defaults to None. - highlight (bool, optional): Enable automatic highlighting. Defaults to True. - log_time (bool, optional): Boolean to enable logging of time by :meth:`log` methods. Defaults to True. - log_path (bool, optional): Boolean to enable the logging of the caller by :meth:`log`. Defaults to True. - log_time_format (Union[str, TimeFormatterCallable], optional): If ``log_time`` is enabled, either string for strftime or callable that formats the time. Defaults to "[%X] ". - highlighter (HighlighterType, optional): Default highlighter. - legacy_windows (bool, optional): Enable legacy Windows mode, or ``None`` to auto detect. Defaults to ``None``. - safe_box (bool, optional): Restrict box options that don't render on legacy Windows. - get_datetime (Callable[[], datetime], optional): Callable that gets the current time as a datetime.datetime object (used by Console.log), - or None for datetime.now. - get_time (Callable[[], time], optional): Callable that gets the current time in seconds, default uses time.monotonic. - """ - - _environ: Mapping[str, str] = os.environ - - def __init__( - self, - *, - color_system: Optional[ - Literal["auto", "standard", "256", "truecolor", "windows"] - ] = "auto", - force_terminal: Optional[bool] = None, - force_jupyter: Optional[bool] = None, - force_interactive: Optional[bool] = None, - soft_wrap: bool = False, - theme: Optional[Theme] = None, - stderr: bool = False, - file: Optional[IO[str]] = None, - quiet: bool = False, - width: Optional[int] = None, - height: Optional[int] = None, - style: Optional[StyleType] = None, - no_color: Optional[bool] = None, - tab_size: int = 8, - record: bool = False, - markup: bool = True, - emoji: bool = True, - emoji_variant: Optional[EmojiVariant] = None, - highlight: bool = True, - log_time: bool = True, - log_path: bool = True, - log_time_format: Union[str, FormatTimeCallable] = "[%X]", - highlighter: Optional["HighlighterType"] = ReprHighlighter(), - legacy_windows: Optional[bool] = None, - safe_box: bool = True, - get_datetime: Optional[Callable[[], datetime]] = None, - get_time: Optional[Callable[[], float]] = None, - _environ: Optional[Mapping[str, str]] = None, - ): - # Copy of os.environ allows us to replace it for testing - if _environ is not None: - self._environ = _environ - - self.is_jupyter = _is_jupyter() if force_jupyter is None else force_jupyter - if self.is_jupyter: - if width is None: - jupyter_columns = self._environ.get("JUPYTER_COLUMNS") - if jupyter_columns is not None and jupyter_columns.isdigit(): - width = int(jupyter_columns) - else: - width = JUPYTER_DEFAULT_COLUMNS - if height is None: - jupyter_lines = self._environ.get("JUPYTER_LINES") - if jupyter_lines is not None and jupyter_lines.isdigit(): - height = int(jupyter_lines) - else: - height = JUPYTER_DEFAULT_LINES - - self.tab_size = tab_size - self.record = record - self._markup = markup - self._emoji = emoji - self._emoji_variant: Optional[EmojiVariant] = emoji_variant - self._highlight = highlight - self.legacy_windows: bool = ( - (detect_legacy_windows() and not self.is_jupyter) - if legacy_windows is None - else legacy_windows - ) - - if width is None: - columns = self._environ.get("COLUMNS") - if columns is not None and columns.isdigit(): - width = int(columns) - self.legacy_windows - if height is None: - lines = self._environ.get("LINES") - if lines is not None and lines.isdigit(): - height = int(lines) - - self.soft_wrap = soft_wrap - self._width = width - self._height = height - - self._color_system: Optional[ColorSystem] - self._force_terminal = force_terminal - self._file = file - self.quiet = quiet - self.stderr = stderr - - if color_system is None: - self._color_system = None - elif color_system == "auto": - self._color_system = self._detect_color_system() - else: - self._color_system = COLOR_SYSTEMS[color_system] - - self._lock = threading.RLock() - self._log_render = LogRender( - show_time=log_time, - show_path=log_path, - time_format=log_time_format, - ) - self.highlighter: HighlighterType = highlighter or _null_highlighter - self.safe_box = safe_box - self.get_datetime = get_datetime or datetime.now - self.get_time = get_time or monotonic - self.style = style - self.no_color = ( - no_color if no_color is not None else "NO_COLOR" in self._environ - ) - self.is_interactive = ( - (self.is_terminal and not self.is_dumb_terminal) - if force_interactive is None - else force_interactive - ) - - self._record_buffer_lock = threading.RLock() - self._thread_locals = ConsoleThreadLocals( - theme_stack=ThemeStack(themes.DEFAULT if theme is None else theme) - ) - self._record_buffer: List[Segment] = [] - self._render_hooks: List[RenderHook] = [] - self._live: Optional["Live"] = None - self._is_alt_screen = False - - def __repr__(self) -> str: - return f"" - - @property - def file(self) -> IO[str]: - """Get the file object to write to.""" - file = self._file or (sys.stderr if self.stderr else sys.stdout) - file = getattr(file, "rich_proxied_file", file) - return file - - @file.setter - def file(self, new_file: IO[str]) -> None: - """Set a new file object.""" - self._file = new_file - - @property - def _buffer(self) -> List[Segment]: - """Get a thread local buffer.""" - return self._thread_locals.buffer - - @property - def _buffer_index(self) -> int: - """Get a thread local buffer.""" - return self._thread_locals.buffer_index - - @_buffer_index.setter - def _buffer_index(self, value: int) -> None: - self._thread_locals.buffer_index = value - - @property - def _theme_stack(self) -> ThemeStack: - """Get the thread local theme stack.""" - return self._thread_locals.theme_stack - - def _detect_color_system(self) -> Optional[ColorSystem]: - """Detect color system from env vars.""" - if self.is_jupyter: - return ColorSystem.TRUECOLOR - if not self.is_terminal or self.is_dumb_terminal: - return None - if WINDOWS: # pragma: no cover - if self.legacy_windows: # pragma: no cover - return ColorSystem.WINDOWS - windows_console_features = get_windows_console_features() - return ( - ColorSystem.TRUECOLOR - if windows_console_features.truecolor - else ColorSystem.EIGHT_BIT - ) - else: - color_term = self._environ.get("COLORTERM", "").strip().lower() - if color_term in ("truecolor", "24bit"): - return ColorSystem.TRUECOLOR - term = self._environ.get("TERM", "").strip().lower() - _term_name, _hyphen, colors = term.rpartition("-") - color_system = _TERM_COLORS.get(colors, ColorSystem.STANDARD) - return color_system - - def _enter_buffer(self) -> None: - """Enter in to a buffer context, and buffer all output.""" - self._buffer_index += 1 - - def _exit_buffer(self) -> None: - """Leave buffer context, and render content if required.""" - self._buffer_index -= 1 - self._check_buffer() - - def set_live(self, live: "Live") -> None: - """Set Live instance. Used by Live context manager. - - Args: - live (Live): Live instance using this Console. - - Raises: - errors.LiveError: If this Console has a Live context currently active. - """ - with self._lock: - if self._live is not None: - raise errors.LiveError("Only one live display may be active at once") - self._live = live - - def clear_live(self) -> None: - """Clear the Live instance.""" - with self._lock: - self._live = None - - def push_render_hook(self, hook: RenderHook) -> None: - """Add a new render hook to the stack. - - Args: - hook (RenderHook): Render hook instance. - """ - with self._lock: - self._render_hooks.append(hook) - - def pop_render_hook(self) -> None: - """Pop the last renderhook from the stack.""" - with self._lock: - self._render_hooks.pop() - - def __enter__(self) -> "Console": - """Own context manager to enter buffer context.""" - self._enter_buffer() - return self - - def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: - """Exit buffer context.""" - self._exit_buffer() - - def begin_capture(self) -> None: - """Begin capturing console output. Call :meth:`end_capture` to exit capture mode and return output.""" - self._enter_buffer() - - def end_capture(self) -> str: - """End capture mode and return captured string. - - Returns: - str: Console output. - """ - render_result = self._render_buffer(self._buffer) - del self._buffer[:] - self._exit_buffer() - return render_result - - def push_theme(self, theme: Theme, *, inherit: bool = True) -> None: - """Push a new theme on to the top of the stack, replacing the styles from the previous theme. - Generally speaking, you should call :meth:`~rich.console.Console.use_theme` to get a context manager, rather - than calling this method directly. - - Args: - theme (Theme): A theme instance. - inherit (bool, optional): Inherit existing styles. Defaults to True. - """ - self._theme_stack.push_theme(theme, inherit=inherit) - - def pop_theme(self) -> None: - """Remove theme from top of stack, restoring previous theme.""" - self._theme_stack.pop_theme() - - def use_theme(self, theme: Theme, *, inherit: bool = True) -> ThemeContext: - """Use a different theme for the duration of the context manager. - - Args: - theme (Theme): Theme instance to user. - inherit (bool, optional): Inherit existing console styles. Defaults to True. - - Returns: - ThemeContext: [description] - """ - return ThemeContext(self, theme, inherit) - - @property - def color_system(self) -> Optional[str]: - """Get color system string. - - Returns: - Optional[str]: "standard", "256" or "truecolor". - """ - - if self._color_system is not None: - return _COLOR_SYSTEMS_NAMES[self._color_system] - else: - return None - - @property - def encoding(self) -> str: - """Get the encoding of the console file, e.g. ``"utf-8"``. - - Returns: - str: A standard encoding string. - """ - return (getattr(self.file, "encoding", "utf-8") or "utf-8").lower() - - @property - def is_terminal(self) -> bool: - """Check if the console is writing to a terminal. - - Returns: - bool: True if the console writing to a device capable of - understanding terminal codes, otherwise False. - """ - if self._force_terminal is not None: - return self._force_terminal - - if hasattr(sys.stdin, "__module__") and sys.stdin.__module__.startswith( - "idlelib" - ): - # Return False for Idle which claims to be a tty but can't handle ansi codes - return False - - isatty: Optional[Callable[[], bool]] = getattr(self.file, "isatty", None) - try: - return False if isatty is None else isatty() - except ValueError: - # in some situation (at the end of a pytest run for example) isatty() can raise - # ValueError: I/O operation on closed file - # return False because we aren't in a terminal anymore - return False - - @property - def is_dumb_terminal(self) -> bool: - """Detect dumb terminal. - - Returns: - bool: True if writing to a dumb terminal, otherwise False. - - """ - _term = self._environ.get("TERM", "") - is_dumb = _term.lower() in ("dumb", "unknown") - return self.is_terminal and is_dumb - - @property - def options(self) -> ConsoleOptions: - """Get default console options.""" - return ConsoleOptions( - max_height=self.size.height, - size=self.size, - legacy_windows=self.legacy_windows, - min_width=1, - max_width=self.width, - encoding=self.encoding, - is_terminal=self.is_terminal, - ) - - @property - def size(self) -> ConsoleDimensions: - """Get the size of the console. - - Returns: - ConsoleDimensions: A named tuple containing the dimensions. - """ - - if self._width is not None and self._height is not None: - return ConsoleDimensions(self._width - self.legacy_windows, self._height) - - if self.is_dumb_terminal: - return ConsoleDimensions(80, 25) - - width: Optional[int] = None - height: Optional[int] = None - - if WINDOWS: # pragma: no cover - try: - width, height = os.get_terminal_size() - except (AttributeError, ValueError, OSError): # Probably not a terminal - pass - else: - for file_descriptor in _STD_STREAMS: - try: - width, height = os.get_terminal_size(file_descriptor) - except (AttributeError, ValueError, OSError): - pass - else: - break - - columns = self._environ.get("COLUMNS") - if columns is not None and columns.isdigit(): - width = int(columns) - lines = self._environ.get("LINES") - if lines is not None and lines.isdigit(): - height = int(lines) - - # get_terminal_size can report 0, 0 if run from pseudo-terminal - width = width or 80 - height = height or 25 - return ConsoleDimensions( - width - self.legacy_windows if self._width is None else self._width, - height if self._height is None else self._height, - ) - - @size.setter - def size(self, new_size: Tuple[int, int]) -> None: - """Set a new size for the terminal. - - Args: - new_size (Tuple[int, int]): New width and height. - """ - width, height = new_size - self._width = width - self._height = height - - @property - def width(self) -> int: - """Get the width of the console. - - Returns: - int: The width (in characters) of the console. - """ - return self.size.width - - @width.setter - def width(self, width: int) -> None: - """Set width. - - Args: - width (int): New width. - """ - self._width = width - - @property - def height(self) -> int: - """Get the height of the console. - - Returns: - int: The height (in lines) of the console. - """ - return self.size.height - - @height.setter - def height(self, height: int) -> None: - """Set height. - - Args: - height (int): new height. - """ - self._height = height - - def bell(self) -> None: - """Play a 'bell' sound (if supported by the terminal).""" - self.control(Control.bell()) - - def capture(self) -> Capture: - """A context manager to *capture* the result of print() or log() in a string, - rather than writing it to the console. - - Example: - >>> from rich.console import Console - >>> console = Console() - >>> with console.capture() as capture: - ... console.print("[bold magenta]Hello World[/]") - >>> print(capture.get()) - - Returns: - Capture: Context manager with disables writing to the terminal. - """ - capture = Capture(self) - return capture - - def pager( - self, pager: Optional[Pager] = None, styles: bool = False, links: bool = False - ) -> PagerContext: - """A context manager to display anything printed within a "pager". The pager application - is defined by the system and will typically support at least pressing a key to scroll. - - Args: - pager (Pager, optional): A pager object, or None to use :class:`~rich.pager.SystemPager`. Defaults to None. - styles (bool, optional): Show styles in pager. Defaults to False. - links (bool, optional): Show links in pager. Defaults to False. - - Example: - >>> from rich.console import Console - >>> from rich.__main__ import make_test_card - >>> console = Console() - >>> with console.pager(): - console.print(make_test_card()) - - Returns: - PagerContext: A context manager. - """ - return PagerContext(self, pager=pager, styles=styles, links=links) - - def line(self, count: int = 1) -> None: - """Write new line(s). - - Args: - count (int, optional): Number of new lines. Defaults to 1. - """ - - assert count >= 0, "count must be >= 0" - self.print(NewLine(count)) - - def clear(self, home: bool = True) -> None: - """Clear the screen. - - Args: - home (bool, optional): Also move the cursor to 'home' position. Defaults to True. - """ - if home: - self.control(Control.clear(), Control.home()) - else: - self.control(Control.clear()) - - def status( - self, - status: RenderableType, - *, - spinner: str = "dots", - spinner_style: str = "status.spinner", - speed: float = 1.0, - refresh_per_second: float = 12.5, - ) -> "Status": - """Display a status and spinner. - - Args: - status (RenderableType): A status renderable (str or Text typically). - spinner (str, optional): Name of spinner animation (see python -m rich.spinner). Defaults to "dots". - spinner_style (StyleType, optional): Style of spinner. Defaults to "status.spinner". - speed (float, optional): Speed factor for spinner animation. Defaults to 1.0. - refresh_per_second (float, optional): Number of refreshes per second. Defaults to 12.5. - - Returns: - Status: A Status object that may be used as a context manager. - """ - from .status import Status - - status_renderable = Status( - status, - console=self, - spinner=spinner, - spinner_style=spinner_style, - speed=speed, - refresh_per_second=refresh_per_second, - ) - return status_renderable - - def show_cursor(self, show: bool = True) -> bool: - """Show or hide the cursor. - - Args: - show (bool, optional): Set visibility of the cursor. - """ - if self.is_terminal: - self.control(Control.show_cursor(show)) - return True - return False - - def set_alt_screen(self, enable: bool = True) -> bool: - """Enables alternative screen mode. - - Note, if you enable this mode, you should ensure that is disabled before - the application exits. See :meth:`~rich.Console.screen` for a context manager - that handles this for you. - - Args: - enable (bool, optional): Enable (True) or disable (False) alternate screen. Defaults to True. - - Returns: - bool: True if the control codes were written. - - """ - changed = False - if self.is_terminal and not self.legacy_windows: - self.control(Control.alt_screen(enable)) - changed = True - self._is_alt_screen = enable - return changed - - @property - def is_alt_screen(self) -> bool: - """Check if the alt screen was enabled. - - Returns: - bool: True if the alt screen was enabled, otherwise False. - """ - return self._is_alt_screen - - def set_window_title(self, title: str) -> bool: - """Set the title of the console terminal window. - - Warning: There is no means within Rich of "resetting" the window title to its - previous value, meaning the title you set will persist even after your application - exits. - - ``fish`` shell resets the window title before and after each command by default, - negating this issue. Windows Terminal and command prompt will also reset the title for you. - Most other shells and terminals, however, do not do this. - - Some terminals may require configuration changes before you can set the title. - Some terminals may not support setting the title at all. - - Other software (including the terminal itself, the shell, custom prompts, plugins, etc.) - may also set the terminal window title. This could result in whatever value you write - using this method being overwritten. - - Args: - title (str): The new title of the terminal window. - - Returns: - bool: True if the control code to change the terminal title was - written, otherwise False. Note that a return value of True - does not guarantee that the window title has actually changed, - since the feature may be unsupported/disabled in some terminals. - """ - if self.is_terminal: - self.control(Control.title(title)) - return True - return False - - def screen( - self, hide_cursor: bool = True, style: Optional[StyleType] = None - ) -> "ScreenContext": - """Context manager to enable and disable 'alternative screen' mode. - - Args: - hide_cursor (bool, optional): Also hide the cursor. Defaults to False. - style (Style, optional): Optional style for screen. Defaults to None. - - Returns: - ~ScreenContext: Context which enables alternate screen on enter, and disables it on exit. - """ - return ScreenContext(self, hide_cursor=hide_cursor, style=style or "") - - def measure( - self, renderable: RenderableType, *, options: Optional[ConsoleOptions] = None - ) -> Measurement: - """Measure a renderable. Returns a :class:`~rich.measure.Measurement` object which contains - information regarding the number of characters required to print the renderable. - - Args: - renderable (RenderableType): Any renderable or string. - options (Optional[ConsoleOptions], optional): Options to use when measuring, or None - to use default options. Defaults to None. - - Returns: - Measurement: A measurement of the renderable. - """ - measurement = Measurement.get(self, options or self.options, renderable) - return measurement - - def render( - self, renderable: RenderableType, options: Optional[ConsoleOptions] = None - ) -> Iterable[Segment]: - """Render an object in to an iterable of `Segment` instances. - - This method contains the logic for rendering objects with the console protocol. - You are unlikely to need to use it directly, unless you are extending the library. - - Args: - renderable (RenderableType): An object supporting the console protocol, or - an object that may be converted to a string. - options (ConsoleOptions, optional): An options object, or None to use self.options. Defaults to None. - - Returns: - Iterable[Segment]: An iterable of segments that may be rendered. - """ - - _options = options or self.options - if _options.max_width < 1: - # No space to render anything. This prevents potential recursion errors. - return - render_iterable: RenderResult - - renderable = rich_cast(renderable) - if hasattr(renderable, "__rich_console__") and not isclass(renderable): - render_iterable = renderable.__rich_console__(self, _options) # type: ignore[union-attr] - elif isinstance(renderable, str): - text_renderable = self.render_str( - renderable, highlight=_options.highlight, markup=_options.markup - ) - render_iterable = text_renderable.__rich_console__(self, _options) - else: - raise errors.NotRenderableError( - f"Unable to render {renderable!r}; " - "A str, Segment or object with __rich_console__ method is required" - ) - - try: - iter_render = iter(render_iterable) - except TypeError: - raise errors.NotRenderableError( - f"object {render_iterable!r} is not renderable" - ) - _Segment = Segment - _options = _options.reset_height() - for render_output in iter_render: - if isinstance(render_output, _Segment): - yield render_output - else: - yield from self.render(render_output, _options) - - def render_lines( - self, - renderable: RenderableType, - options: Optional[ConsoleOptions] = None, - *, - style: Optional[Style] = None, - pad: bool = True, - new_lines: bool = False, - ) -> List[List[Segment]]: - """Render objects in to a list of lines. - - The output of render_lines is useful when further formatting of rendered console text - is required, such as the Panel class which draws a border around any renderable object. - - Args: - renderable (RenderableType): Any object renderable in the console. - options (Optional[ConsoleOptions], optional): Console options, or None to use self.options. Default to ``None``. - style (Style, optional): Optional style to apply to renderables. Defaults to ``None``. - pad (bool, optional): Pad lines shorter than render width. Defaults to ``True``. - new_lines (bool, optional): Include "\n" characters at end of lines. - - Returns: - List[List[Segment]]: A list of lines, where a line is a list of Segment objects. - """ - with self._lock: - render_options = options or self.options - _rendered = self.render(renderable, render_options) - if style: - _rendered = Segment.apply_style(_rendered, style) - - render_height = render_options.height - if render_height is not None: - render_height = max(0, render_height) - - lines = list( - islice( - Segment.split_and_crop_lines( - _rendered, - render_options.max_width, - include_new_lines=new_lines, - pad=pad, - style=style, - ), - None, - render_height, - ) - ) - if render_options.height is not None: - extra_lines = render_options.height - len(lines) - if extra_lines > 0: - pad_line = [ - [Segment(" " * render_options.max_width, style), Segment("\n")] - if new_lines - else [Segment(" " * render_options.max_width, style)] - ] - lines.extend(pad_line * extra_lines) - - return lines - - def render_str( - self, - text: str, - *, - style: Union[str, Style] = "", - justify: Optional[JustifyMethod] = None, - overflow: Optional[OverflowMethod] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - highlighter: Optional[HighlighterType] = None, - ) -> "Text": - """Convert a string to a Text instance. This is called automatically if - you print or log a string. - - Args: - text (str): Text to render. - style (Union[str, Style], optional): Style to apply to rendered text. - justify (str, optional): Justify method: "default", "left", "center", "full", or "right". Defaults to ``None``. - overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to ``None``. - emoji (Optional[bool], optional): Enable emoji, or ``None`` to use Console default. - markup (Optional[bool], optional): Enable markup, or ``None`` to use Console default. - highlight (Optional[bool], optional): Enable highlighting, or ``None`` to use Console default. - highlighter (HighlighterType, optional): Optional highlighter to apply. - Returns: - ConsoleRenderable: Renderable object. - - """ - emoji_enabled = emoji or (emoji is None and self._emoji) - markup_enabled = markup or (markup is None and self._markup) - highlight_enabled = highlight or (highlight is None and self._highlight) - - if markup_enabled: - rich_text = render_markup( - text, - style=style, - emoji=emoji_enabled, - emoji_variant=self._emoji_variant, - ) - rich_text.justify = justify - rich_text.overflow = overflow - else: - rich_text = Text( - _emoji_replace(text, default_variant=self._emoji_variant) - if emoji_enabled - else text, - justify=justify, - overflow=overflow, - style=style, - ) - - _highlighter = (highlighter or self.highlighter) if highlight_enabled else None - if _highlighter is not None: - highlight_text = _highlighter(str(rich_text)) - highlight_text.copy_styles(rich_text) - return highlight_text - - return rich_text - - def get_style( - self, name: Union[str, Style], *, default: Optional[Union[Style, str]] = None - ) -> Style: - """Get a Style instance by its theme name or parse a definition. - - Args: - name (str): The name of a style or a style definition. - - Returns: - Style: A Style object. - - Raises: - MissingStyle: If no style could be parsed from name. - - """ - if isinstance(name, Style): - return name - - try: - style = self._theme_stack.get(name) - if style is None: - style = Style.parse(name) - return style.copy() if style.link else style - except errors.StyleSyntaxError as error: - if default is not None: - return self.get_style(default) - raise errors.MissingStyle( - f"Failed to get style {name!r}; {error}" - ) from None - - def _collect_renderables( - self, - objects: Iterable[Any], - sep: str, - end: str, - *, - justify: Optional[JustifyMethod] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - ) -> List[ConsoleRenderable]: - """Combine a number of renderables and text into one renderable. - - Args: - objects (Iterable[Any]): Anything that Rich can render. - sep (str): String to write between print data. - end (str): String to write at end of print data. - justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. - emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. - markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. - highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. - - Returns: - List[ConsoleRenderable]: A list of things to render. - """ - renderables: List[ConsoleRenderable] = [] - _append = renderables.append - text: List[Text] = [] - append_text = text.append - - append = _append - if justify in ("left", "center", "right"): - - def align_append(renderable: RenderableType) -> None: - _append(Align(renderable, cast(AlignMethod, justify))) - - append = align_append - - _highlighter: HighlighterType = _null_highlighter - if highlight or (highlight is None and self._highlight): - _highlighter = self.highlighter - - def check_text() -> None: - if text: - sep_text = Text(sep, justify=justify, end=end) - append(sep_text.join(text)) - del text[:] - - for renderable in objects: - renderable = rich_cast(renderable) - if isinstance(renderable, str): - append_text( - self.render_str( - renderable, emoji=emoji, markup=markup, highlighter=_highlighter - ) - ) - elif isinstance(renderable, Text): - append_text(renderable) - elif isinstance(renderable, ConsoleRenderable): - check_text() - append(renderable) - elif is_expandable(renderable): - check_text() - append(Pretty(renderable, highlighter=_highlighter)) - else: - append_text(_highlighter(str(renderable))) - - check_text() - - if self.style is not None: - style = self.get_style(self.style) - renderables = [Styled(renderable, style) for renderable in renderables] - - return renderables - - def rule( - self, - title: TextType = "", - *, - characters: str = "─", - style: Union[str, Style] = "rule.line", - align: AlignMethod = "center", - ) -> None: - """Draw a line with optional centered title. - - Args: - title (str, optional): Text to render over the rule. Defaults to "". - characters (str, optional): Character(s) to form the line. Defaults to "─". - style (str, optional): Style of line. Defaults to "rule.line". - align (str, optional): How to align the title, one of "left", "center", or "right". Defaults to "center". - """ - from .rule import Rule - - rule = Rule(title=title, characters=characters, style=style, align=align) - self.print(rule) - - def control(self, *control: Control) -> None: - """Insert non-printing control codes. - - Args: - control_codes (str): Control codes, such as those that may move the cursor. - """ - if not self.is_dumb_terminal: - with self: - self._buffer.extend(_control.segment for _control in control) - - def out( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[Union[str, Style]] = None, - highlight: Optional[bool] = None, - ) -> None: - """Output to the terminal. This is a low-level way of writing to the terminal which unlike - :meth:`~rich.console.Console.print` won't pretty print, wrap text, or apply markup, but will - optionally apply highlighting and a basic style. - - Args: - sep (str, optional): String to write between print data. Defaults to " ". - end (str, optional): String to write at end of print data. Defaults to "\\\\n". - style (Union[str, Style], optional): A style to apply to output. Defaults to None. - highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use - console default. Defaults to ``None``. - """ - raw_output: str = sep.join(str(_object) for _object in objects) - self.print( - raw_output, - style=style, - highlight=highlight, - emoji=False, - markup=False, - no_wrap=True, - overflow="ignore", - crop=False, - end=end, - ) - - def print( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[Union[str, Style]] = None, - justify: Optional[JustifyMethod] = None, - overflow: Optional[OverflowMethod] = None, - no_wrap: Optional[bool] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - width: Optional[int] = None, - height: Optional[int] = None, - crop: bool = True, - soft_wrap: Optional[bool] = None, - new_line_start: bool = False, - ) -> None: - """Print to the console. - - Args: - objects (positional args): Objects to log to the terminal. - sep (str, optional): String to write between print data. Defaults to " ". - end (str, optional): String to write at end of print data. Defaults to "\\\\n". - style (Union[str, Style], optional): A style to apply to output. Defaults to None. - justify (str, optional): Justify method: "default", "left", "right", "center", or "full". Defaults to ``None``. - overflow (str, optional): Overflow method: "ignore", "crop", "fold", or "ellipsis". Defaults to None. - no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to None. - emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to ``None``. - markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to ``None``. - highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to ``None``. - width (Optional[int], optional): Width of output, or ``None`` to auto-detect. Defaults to ``None``. - crop (Optional[bool], optional): Crop output to width of terminal. Defaults to True. - soft_wrap (bool, optional): Enable soft wrap mode which disables word wrapping and cropping of text or ``None`` for - Console default. Defaults to ``None``. - new_line_start (bool, False): Insert a new line at the start if the output contains more than one line. Defaults to ``False``. - """ - if not objects: - objects = (NewLine(),) - - if soft_wrap is None: - soft_wrap = self.soft_wrap - if soft_wrap: - if no_wrap is None: - no_wrap = True - if overflow is None: - overflow = "ignore" - crop = False - render_hooks = self._render_hooks[:] - with self: - renderables = self._collect_renderables( - objects, - sep, - end, - justify=justify, - emoji=emoji, - markup=markup, - highlight=highlight, - ) - for hook in render_hooks: - renderables = hook.process_renderables(renderables) - render_options = self.options.update( - justify=justify, - overflow=overflow, - width=min(width, self.width) if width is not None else NO_CHANGE, - height=height, - no_wrap=no_wrap, - markup=markup, - highlight=highlight, - ) - - new_segments: List[Segment] = [] - extend = new_segments.extend - render = self.render - if style is None: - for renderable in renderables: - extend(render(renderable, render_options)) - else: - for renderable in renderables: - extend( - Segment.apply_style( - render(renderable, render_options), self.get_style(style) - ) - ) - if new_line_start: - if ( - len("".join(segment.text for segment in new_segments).splitlines()) - > 1 - ): - new_segments.insert(0, Segment.line()) - if crop: - buffer_extend = self._buffer.extend - for line in Segment.split_and_crop_lines( - new_segments, self.width, pad=False - ): - buffer_extend(line) - else: - self._buffer.extend(new_segments) - - def print_json( - self, - json: Optional[str] = None, - *, - data: Any = None, - indent: Union[None, int, str] = 2, - highlight: bool = True, - skip_keys: bool = False, - ensure_ascii: bool = True, - check_circular: bool = True, - allow_nan: bool = True, - default: Optional[Callable[[Any], Any]] = None, - sort_keys: bool = False, - ) -> None: - """Pretty prints JSON. Output will be valid JSON. - - Args: - json (Optional[str]): A string containing JSON. - data (Any): If json is not supplied, then encode this data. - indent (Union[None, int, str], optional): Number of spaces to indent. Defaults to 2. - highlight (bool, optional): Enable highlighting of output: Defaults to True. - skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False. - ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False. - check_circular (bool, optional): Check for circular references. Defaults to True. - allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True. - default (Callable, optional): A callable that converts values that can not be encoded - in to something that can be JSON encoded. Defaults to None. - sort_keys (bool, optional): Sort dictionary keys. Defaults to False. - """ - from pip._vendor.rich.json import JSON - - if json is None: - json_renderable = JSON.from_data( - data, - indent=indent, - highlight=highlight, - skip_keys=skip_keys, - ensure_ascii=ensure_ascii, - check_circular=check_circular, - allow_nan=allow_nan, - default=default, - sort_keys=sort_keys, - ) - else: - if not isinstance(json, str): - raise TypeError( - f"json must be str. Did you mean print_json(data={json!r}) ?" - ) - json_renderable = JSON( - json, - indent=indent, - highlight=highlight, - skip_keys=skip_keys, - ensure_ascii=ensure_ascii, - check_circular=check_circular, - allow_nan=allow_nan, - default=default, - sort_keys=sort_keys, - ) - self.print(json_renderable, soft_wrap=True) - - def update_screen( - self, - renderable: RenderableType, - *, - region: Optional[Region] = None, - options: Optional[ConsoleOptions] = None, - ) -> None: - """Update the screen at a given offset. - - Args: - renderable (RenderableType): A Rich renderable. - region (Region, optional): Region of screen to update, or None for entire screen. Defaults to None. - x (int, optional): x offset. Defaults to 0. - y (int, optional): y offset. Defaults to 0. - - Raises: - errors.NoAltScreen: If the Console isn't in alt screen mode. - - """ - if not self.is_alt_screen: - raise errors.NoAltScreen("Alt screen must be enabled to call update_screen") - render_options = options or self.options - if region is None: - x = y = 0 - render_options = render_options.update_dimensions( - render_options.max_width, render_options.height or self.height - ) - else: - x, y, width, height = region - render_options = render_options.update_dimensions(width, height) - - lines = self.render_lines(renderable, options=render_options) - self.update_screen_lines(lines, x, y) - - def update_screen_lines( - self, lines: List[List[Segment]], x: int = 0, y: int = 0 - ) -> None: - """Update lines of the screen at a given offset. - - Args: - lines (List[List[Segment]]): Rendered lines (as produced by :meth:`~rich.Console.render_lines`). - x (int, optional): x offset (column no). Defaults to 0. - y (int, optional): y offset (column no). Defaults to 0. - - Raises: - errors.NoAltScreen: If the Console isn't in alt screen mode. - """ - if not self.is_alt_screen: - raise errors.NoAltScreen("Alt screen must be enabled to call update_screen") - screen_update = ScreenUpdate(lines, x, y) - segments = self.render(screen_update) - self._buffer.extend(segments) - self._check_buffer() - - def print_exception( - self, - *, - width: Optional[int] = 100, - extra_lines: int = 3, - theme: Optional[str] = None, - word_wrap: bool = False, - show_locals: bool = False, - suppress: Iterable[Union[str, ModuleType]] = (), - max_frames: int = 100, - ) -> None: - """Prints a rich render of the last exception and traceback. - - Args: - width (Optional[int], optional): Number of characters used to render code. Defaults to 100. - extra_lines (int, optional): Additional lines of code to render. Defaults to 3. - theme (str, optional): Override pygments theme used in traceback - word_wrap (bool, optional): Enable word wrapping of long lines. Defaults to False. - show_locals (bool, optional): Enable display of local variables. Defaults to False. - suppress (Iterable[Union[str, ModuleType]]): Optional sequence of modules or paths to exclude from traceback. - max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. - """ - from .traceback import Traceback - - traceback = Traceback( - width=width, - extra_lines=extra_lines, - theme=theme, - word_wrap=word_wrap, - show_locals=show_locals, - suppress=suppress, - max_frames=max_frames, - ) - self.print(traceback) - - @staticmethod - def _caller_frame_info( - offset: int, - currentframe: Callable[[], Optional[FrameType]] = inspect.currentframe, - ) -> Tuple[str, int, Dict[str, Any]]: - """Get caller frame information. - - Args: - offset (int): the caller offset within the current frame stack. - currentframe (Callable[[], Optional[FrameType]], optional): the callable to use to - retrieve the current frame. Defaults to ``inspect.currentframe``. - - Returns: - Tuple[str, int, Dict[str, Any]]: A tuple containing the filename, the line number and - the dictionary of local variables associated with the caller frame. - - Raises: - RuntimeError: If the stack offset is invalid. - """ - # Ignore the frame of this local helper - offset += 1 - - frame = currentframe() - if frame is not None: - # Use the faster currentframe where implemented - while offset and frame is not None: - frame = frame.f_back - offset -= 1 - assert frame is not None - return frame.f_code.co_filename, frame.f_lineno, frame.f_locals - else: - # Fallback to the slower stack - frame_info = inspect.stack()[offset] - return frame_info.filename, frame_info.lineno, frame_info.frame.f_locals - - def log( - self, - *objects: Any, - sep: str = " ", - end: str = "\n", - style: Optional[Union[str, Style]] = None, - justify: Optional[JustifyMethod] = None, - emoji: Optional[bool] = None, - markup: Optional[bool] = None, - highlight: Optional[bool] = None, - log_locals: bool = False, - _stack_offset: int = 1, - ) -> None: - """Log rich content to the terminal. - - Args: - objects (positional args): Objects to log to the terminal. - sep (str, optional): String to write between print data. Defaults to " ". - end (str, optional): String to write at end of print data. Defaults to "\\\\n". - style (Union[str, Style], optional): A style to apply to output. Defaults to None. - justify (str, optional): One of "left", "right", "center", or "full". Defaults to ``None``. - overflow (str, optional): Overflow method: "crop", "fold", or "ellipsis". Defaults to None. - emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None. - markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None. - highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None. - log_locals (bool, optional): Boolean to enable logging of locals where ``log()`` - was called. Defaults to False. - _stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1. - """ - if not objects: - objects = (NewLine(),) - - render_hooks = self._render_hooks[:] - - with self: - renderables = self._collect_renderables( - objects, - sep, - end, - justify=justify, - emoji=emoji, - markup=markup, - highlight=highlight, - ) - if style is not None: - renderables = [Styled(renderable, style) for renderable in renderables] - - filename, line_no, locals = self._caller_frame_info(_stack_offset) - link_path = None if filename.startswith("<") else os.path.abspath(filename) - path = filename.rpartition(os.sep)[-1] - if log_locals: - locals_map = { - key: value - for key, value in locals.items() - if not key.startswith("__") - } - renderables.append(render_scope(locals_map, title="[i]locals")) - - renderables = [ - self._log_render( - self, - renderables, - log_time=self.get_datetime(), - path=path, - line_no=line_no, - link_path=link_path, - ) - ] - for hook in render_hooks: - renderables = hook.process_renderables(renderables) - new_segments: List[Segment] = [] - extend = new_segments.extend - render = self.render - render_options = self.options - for renderable in renderables: - extend(render(renderable, render_options)) - buffer_extend = self._buffer.extend - for line in Segment.split_and_crop_lines( - new_segments, self.width, pad=False - ): - buffer_extend(line) - - def _check_buffer(self) -> None: - """Check if the buffer may be rendered. Render it if it can (e.g. Console.quiet is False) - Rendering is supported on Windows, Unix and Jupyter environments. For - legacy Windows consoles, the win32 API is called directly. - This method will also record what it renders if recording is enabled via Console.record. - """ - if self.quiet: - del self._buffer[:] - return - with self._lock: - if self.record: - with self._record_buffer_lock: - self._record_buffer.extend(self._buffer[:]) - - if self._buffer_index == 0: - - if self.is_jupyter: # pragma: no cover - from .jupyter import display - - display(self._buffer, self._render_buffer(self._buffer[:])) - del self._buffer[:] - else: - if WINDOWS: - use_legacy_windows_render = False - if self.legacy_windows: - try: - use_legacy_windows_render = ( - self.file.fileno() in _STD_STREAMS_OUTPUT - ) - except (ValueError, io.UnsupportedOperation): - pass - - if use_legacy_windows_render: - from pip._vendor.rich._win32_console import LegacyWindowsTerm - from pip._vendor.rich._windows_renderer import legacy_windows_render - - legacy_windows_render( - self._buffer[:], LegacyWindowsTerm(self.file) - ) - else: - # Either a non-std stream on legacy Windows, or modern Windows. - text = self._render_buffer(self._buffer[:]) - # https://bugs.python.org/issue37871 - write = self.file.write - for line in text.splitlines(True): - try: - write(line) - except UnicodeEncodeError as error: - error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***" - raise - else: - text = self._render_buffer(self._buffer[:]) - try: - self.file.write(text) - except UnicodeEncodeError as error: - error.reason = f"{error.reason}\n*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***" - raise - - self.file.flush() - del self._buffer[:] - - def _render_buffer(self, buffer: Iterable[Segment]) -> str: - """Render buffered output, and clear buffer.""" - output: List[str] = [] - append = output.append - color_system = self._color_system - legacy_windows = self.legacy_windows - not_terminal = not self.is_terminal - if self.no_color and color_system: - buffer = Segment.remove_color(buffer) - for text, style, control in buffer: - if style: - append( - style.render( - text, - color_system=color_system, - legacy_windows=legacy_windows, - ) - ) - elif not (not_terminal and control): - append(text) - - rendered = "".join(output) - return rendered - - def input( - self, - prompt: TextType = "", - *, - markup: bool = True, - emoji: bool = True, - password: bool = False, - stream: Optional[TextIO] = None, - ) -> str: - """Displays a prompt and waits for input from the user. The prompt may contain color / style. - - It works in the same way as Python's builtin :func:`input` function and provides elaborate line editing and history features if Python's builtin :mod:`readline` module is previously loaded. - - Args: - prompt (Union[str, Text]): Text to render in the prompt. - markup (bool, optional): Enable console markup (requires a str prompt). Defaults to True. - emoji (bool, optional): Enable emoji (requires a str prompt). Defaults to True. - password: (bool, optional): Hide typed text. Defaults to False. - stream: (TextIO, optional): Optional file to read input from (rather than stdin). Defaults to None. - - Returns: - str: Text read from stdin. - """ - if prompt: - self.print(prompt, markup=markup, emoji=emoji, end="") - if password: - result = getpass("", stream=stream) - else: - if stream: - result = stream.readline() - else: - result = input() - return result - - def export_text(self, *, clear: bool = True, styles: bool = False) -> str: - """Generate text from console contents (requires record=True argument in constructor). - - Args: - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. - styles (bool, optional): If ``True``, ansi escape codes will be included. ``False`` for plain text. - Defaults to ``False``. - - Returns: - str: String containing console contents. - - """ - assert ( - self.record - ), "To export console contents set record=True in the constructor or instance" - - with self._record_buffer_lock: - if styles: - text = "".join( - (style.render(text) if style else text) - for text, style, _ in self._record_buffer - ) - else: - text = "".join( - segment.text - for segment in self._record_buffer - if not segment.control - ) - if clear: - del self._record_buffer[:] - return text - - def save_text(self, path: str, *, clear: bool = True, styles: bool = False) -> None: - """Generate text from console and save to a given location (requires record=True argument in constructor). - - Args: - path (str): Path to write text files. - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. - styles (bool, optional): If ``True``, ansi style codes will be included. ``False`` for plain text. - Defaults to ``False``. - - """ - text = self.export_text(clear=clear, styles=styles) - with open(path, "wt", encoding="utf-8") as write_file: - write_file.write(text) - - def export_html( - self, - *, - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: Optional[str] = None, - inline_styles: bool = False, - ) -> str: - """Generate HTML from console contents (requires record=True argument in constructor). - - Args: - theme (TerminalTheme, optional): TerminalTheme object containing console colors. - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. - code_format (str, optional): Format string to render HTML. In addition to '{foreground}', - '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``. - inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files - larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag. - Defaults to False. - - Returns: - str: String containing console contents as HTML. - """ - assert ( - self.record - ), "To export console contents set record=True in the constructor or instance" - fragments: List[str] = [] - append = fragments.append - _theme = theme or DEFAULT_TERMINAL_THEME - stylesheet = "" - - render_code_format = CONSOLE_HTML_FORMAT if code_format is None else code_format - - with self._record_buffer_lock: - if inline_styles: - for text, style, _ in Segment.filter_control( - Segment.simplify(self._record_buffer) - ): - text = escape(text) - if style: - rule = style.get_html_style(_theme) - if style.link: - text = f'{text}' - text = f'{text}' if rule else text - append(text) - else: - styles: Dict[str, int] = {} - for text, style, _ in Segment.filter_control( - Segment.simplify(self._record_buffer) - ): - text = escape(text) - if style: - rule = style.get_html_style(_theme) - style_number = styles.setdefault(rule, len(styles) + 1) - if style.link: - text = f'{text}' - else: - text = f'{text}' - append(text) - stylesheet_rules: List[str] = [] - stylesheet_append = stylesheet_rules.append - for style_rule, style_number in styles.items(): - if style_rule: - stylesheet_append(f".r{style_number} {{{style_rule}}}") - stylesheet = "\n".join(stylesheet_rules) - - rendered_code = render_code_format.format( - code="".join(fragments), - stylesheet=stylesheet, - foreground=_theme.foreground_color.hex, - background=_theme.background_color.hex, - ) - if clear: - del self._record_buffer[:] - return rendered_code - - def save_html( - self, - path: str, - *, - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: str = CONSOLE_HTML_FORMAT, - inline_styles: bool = False, - ) -> None: - """Generate HTML from console contents and write to a file (requires record=True argument in constructor). - - Args: - path (str): Path to write html file. - theme (TerminalTheme, optional): TerminalTheme object containing console colors. - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True``. - code_format (str, optional): Format string to render HTML. In addition to '{foreground}', - '{background}', and '{code}', should contain '{stylesheet}' if inline_styles is ``False``. - inline_styles (bool, optional): If ``True`` styles will be inlined in to spans, which makes files - larger but easier to cut and paste markup. If ``False``, styles will be embedded in a style tag. - Defaults to False. - - """ - html = self.export_html( - theme=theme, - clear=clear, - code_format=code_format, - inline_styles=inline_styles, - ) - with open(path, "wt", encoding="utf-8") as write_file: - write_file.write(html) - - def export_svg( - self, - *, - title: str = "Rich", - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: str = CONSOLE_SVG_FORMAT, - ) -> str: - """ - Generate an SVG from the console contents (requires record=True in Console constructor). - - Args: - path (str): The path to write the SVG to. - title (str): The title of the tab in the output image - theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` - code_format (str): Format string used to generate the SVG. Rich will inject a number of variables - into the string in order to form the final SVG output. The default template used and the variables - injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. - """ - - from pip._vendor.rich.cells import cell_len - - style_cache: Dict[Style, str] = {} - - def get_svg_style(style: Style) -> str: - """Convert a Style to CSS rules for SVG.""" - if style in style_cache: - return style_cache[style] - css_rules = [] - color = ( - _theme.foreground_color - if (style.color is None or style.color.is_default) - else style.color.get_truecolor(_theme) - ) - bgcolor = ( - _theme.background_color - if (style.bgcolor is None or style.bgcolor.is_default) - else style.bgcolor.get_truecolor(_theme) - ) - if style.reverse: - color, bgcolor = bgcolor, color - if style.dim: - color = blend_rgb(color, bgcolor, 0.4) - css_rules.append(f"fill: {color.hex}") - if style.bold: - css_rules.append("font-weight: bold") - if style.italic: - css_rules.append("font-style: italic;") - if style.underline: - css_rules.append("text-decoration: underline;") - if style.strike: - css_rules.append("text-decoration: line-through;") - - css = ";".join(css_rules) - style_cache[style] = css - return css - - _theme = theme or SVG_EXPORT_THEME - - width = self.width - char_height = 20 - char_width = char_height * 0.61 - line_height = char_height * 1.22 - - margin_top = 1 - margin_right = 1 - margin_bottom = 1 - margin_left = 1 - - padding_top = 40 - padding_right = 8 - padding_bottom = 8 - padding_left = 8 - - padding_width = padding_left + padding_right - padding_height = padding_top + padding_bottom - margin_width = margin_left + margin_right - margin_height = margin_top + margin_bottom - - text_backgrounds: List[str] = [] - text_group: List[str] = [] - classes: Dict[str, int] = {} - style_no = 1 - - def escape_text(text: str) -> str: - """HTML escape text and replace spaces with nbsp.""" - return escape(text).replace(" ", " ") - - def make_tag( - name: str, content: Optional[str] = None, **attribs: object - ) -> str: - """Make a tag from name, content, and attributes.""" - - def stringify(value: object) -> str: - if isinstance(value, (float)): - return format(value, "g") - return str(value) - - tag_attribs = " ".join( - f'{k.lstrip("_").replace("_", "-")}="{stringify(v)}"' - for k, v in attribs.items() - ) - return ( - f"<{name} {tag_attribs}>{content}" - if content - else f"<{name} {tag_attribs}/>" - ) - - with self._record_buffer_lock: - segments = list(Segment.filter_control(self._record_buffer)) - if clear: - self._record_buffer.clear() - - unique_id = "terminal-" + str( - zlib.adler32( - ("".join(segment.text for segment in segments)).encode( - "utf-8", "ignore" - ) - + title.encode("utf-8", "ignore") - ) - ) - y = 0 - for y, line in enumerate(Segment.split_and_crop_lines(segments, length=width)): - x = 0 - for text, style, _control in line: - style = style or Style() - rules = get_svg_style(style) - if rules not in classes: - classes[rules] = style_no - style_no += 1 - class_name = f"r{classes[rules]}" - - if style.reverse: - has_background = True - background = ( - _theme.foreground_color.hex - if style.color is None - else style.color.get_truecolor(_theme).hex - ) - else: - bgcolor = style.bgcolor - has_background = bgcolor is not None and not bgcolor.is_default - background = ( - _theme.background_color.hex - if style.bgcolor is None - else style.bgcolor.get_truecolor(_theme).hex - ) - - text_length = cell_len(text) - if has_background: - text_backgrounds.append( - make_tag( - "rect", - fill=background, - x=x * char_width, - y=y * line_height + 1.5, - width=char_width * text_length, - height=line_height + 0.25, - shape_rendering="crispEdges", - ) - ) - - if text != " " * len(text): - text_group.append( - make_tag( - "text", - escape_text(text), - _class=f"{unique_id}-{class_name}", - x=x * char_width, - y=y * line_height + char_height, - textLength=char_width * len(text), - clip_path=f"url(#{unique_id}-line-{y})", - ) - ) - x += cell_len(text) - - line_offsets = [line_no * line_height + 1.5 for line_no in range(y)] - lines = "\n".join( - f""" - {make_tag("rect", x=0, y=offset, width=char_width * width, height=line_height + 0.25)} - """ - for line_no, offset in enumerate(line_offsets) - ) - - styles = "\n".join( - f".{unique_id}-r{rule_no} {{ {css} }}" for css, rule_no in classes.items() - ) - backgrounds = "".join(text_backgrounds) - matrix = "".join(text_group) - - terminal_width = ceil(width * char_width + padding_width) - terminal_height = (y + 1) * line_height + padding_height - chrome = make_tag( - "rect", - fill=_theme.background_color.hex, - stroke="rgba(255,255,255,0.35)", - stroke_width="1", - x=margin_left, - y=margin_top, - width=terminal_width, - height=terminal_height, - rx=8, - ) - - title_color = _theme.foreground_color.hex - if title: - chrome += make_tag( - "text", - escape_text(title), - _class=f"{unique_id}-title", - fill=title_color, - text_anchor="middle", - x=terminal_width // 2, - y=margin_top + char_height + 6, - ) - chrome += f""" - - - - - - """ - - svg = code_format.format( - unique_id=unique_id, - char_width=char_width, - char_height=char_height, - line_height=line_height, - terminal_width=char_width * width - 1, - terminal_height=(y + 1) * line_height - 1, - width=terminal_width + margin_width, - height=terminal_height + margin_height, - terminal_x=margin_left + padding_left, - terminal_y=margin_top + padding_top, - styles=styles, - chrome=chrome, - backgrounds=backgrounds, - matrix=matrix, - lines=lines, - ) - return svg - - def save_svg( - self, - path: str, - *, - title: str = "Rich", - theme: Optional[TerminalTheme] = None, - clear: bool = True, - code_format: str = CONSOLE_SVG_FORMAT, - ) -> None: - """Generate an SVG file from the console contents (requires record=True in Console constructor). - - Args: - path (str): The path to write the SVG to. - title (str): The title of the tab in the output image - theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal - clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` - code_format (str): Format string used to generate the SVG. Rich will inject a number of variables - into the string in order to form the final SVG output. The default template used and the variables - injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. - """ - svg = self.export_svg( - title=title, - theme=theme, - clear=clear, - code_format=code_format, - ) - with open(path, "wt", encoding="utf-8") as write_file: - write_file.write(svg) - - -def _svg_hash(svg_main_code: str) -> str: - """Returns a unique hash for the given SVG main code. - - Args: - svg_main_code (str): The content we're going to inject in the SVG envelope. - - Returns: - str: a hash of the given content - """ - return str(zlib.adler32(svg_main_code.encode())) - - -if __name__ == "__main__": # pragma: no cover - console = Console(record=True) - - console.log( - "JSONRPC [i]request[/i]", - 5, - 1.3, - True, - False, - None, - { - "jsonrpc": "2.0", - "method": "subtract", - "params": {"minuend": 42, "subtrahend": 23}, - "id": 3, - }, - ) - - console.log("Hello, World!", "{'a': 1}", repr(console)) - - console.print( - { - "name": None, - "empty": [], - "quiz": { - "sport": { - "answered": True, - "q1": { - "question": "Which one is correct team name in NBA?", - "options": [ - "New York Bulls", - "Los Angeles Kings", - "Golden State Warriors", - "Huston Rocket", - ], - "answer": "Huston Rocket", - }, - }, - "maths": { - "answered": False, - "q1": { - "question": "5 + 7 = ?", - "options": [10, 11, 12, 13], - "answer": 12, - }, - "q2": { - "question": "12 - 8 = ?", - "options": [1, 2, 3, 4], - "answer": 4, - }, - }, - }, - } - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/constrain.py b/.venv/Lib/site-packages/pip/_vendor/rich/constrain.py deleted file mode 100644 index 65fdf56..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/constrain.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Optional, TYPE_CHECKING - -from .jupyter import JupyterMixin -from .measure import Measurement - -if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderableType, RenderResult - - -class Constrain(JupyterMixin): - """Constrain the width of a renderable to a given number of characters. - - Args: - renderable (RenderableType): A renderable object. - width (int, optional): The maximum width (in characters) to render. Defaults to 80. - """ - - def __init__(self, renderable: "RenderableType", width: Optional[int] = 80) -> None: - self.renderable = renderable - self.width = width - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - if self.width is None: - yield self.renderable - else: - child_options = options.update_width(min(self.width, options.max_width)) - yield from console.render(self.renderable, child_options) - - def __rich_measure__( - self, console: "Console", options: "ConsoleOptions" - ) -> "Measurement": - if self.width is not None: - options = options.update_width(self.width) - measurement = Measurement.get(console, options, self.renderable) - return measurement diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/containers.py b/.venv/Lib/site-packages/pip/_vendor/rich/containers.py deleted file mode 100644 index e29cf36..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/containers.py +++ /dev/null @@ -1,167 +0,0 @@ -from itertools import zip_longest -from typing import ( - Iterator, - Iterable, - List, - Optional, - Union, - overload, - TypeVar, - TYPE_CHECKING, -) - -if TYPE_CHECKING: - from .console import ( - Console, - ConsoleOptions, - JustifyMethod, - OverflowMethod, - RenderResult, - RenderableType, - ) - from .text import Text - -from .cells import cell_len -from .measure import Measurement - -T = TypeVar("T") - - -class Renderables: - """A list subclass which renders its contents to the console.""" - - def __init__( - self, renderables: Optional[Iterable["RenderableType"]] = None - ) -> None: - self._renderables: List["RenderableType"] = ( - list(renderables) if renderables is not None else [] - ) - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - """Console render method to insert line-breaks.""" - yield from self._renderables - - def __rich_measure__( - self, console: "Console", options: "ConsoleOptions" - ) -> "Measurement": - dimensions = [ - Measurement.get(console, options, renderable) - for renderable in self._renderables - ] - if not dimensions: - return Measurement(1, 1) - _min = max(dimension.minimum for dimension in dimensions) - _max = max(dimension.maximum for dimension in dimensions) - return Measurement(_min, _max) - - def append(self, renderable: "RenderableType") -> None: - self._renderables.append(renderable) - - def __iter__(self) -> Iterable["RenderableType"]: - return iter(self._renderables) - - -class Lines: - """A list subclass which can render to the console.""" - - def __init__(self, lines: Iterable["Text"] = ()) -> None: - self._lines: List["Text"] = list(lines) - - def __repr__(self) -> str: - return f"Lines({self._lines!r})" - - def __iter__(self) -> Iterator["Text"]: - return iter(self._lines) - - @overload - def __getitem__(self, index: int) -> "Text": - ... - - @overload - def __getitem__(self, index: slice) -> List["Text"]: - ... - - def __getitem__(self, index: Union[slice, int]) -> Union["Text", List["Text"]]: - return self._lines[index] - - def __setitem__(self, index: int, value: "Text") -> "Lines": - self._lines[index] = value - return self - - def __len__(self) -> int: - return self._lines.__len__() - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - """Console render method to insert line-breaks.""" - yield from self._lines - - def append(self, line: "Text") -> None: - self._lines.append(line) - - def extend(self, lines: Iterable["Text"]) -> None: - self._lines.extend(lines) - - def pop(self, index: int = -1) -> "Text": - return self._lines.pop(index) - - def justify( - self, - console: "Console", - width: int, - justify: "JustifyMethod" = "left", - overflow: "OverflowMethod" = "fold", - ) -> None: - """Justify and overflow text to a given width. - - Args: - console (Console): Console instance. - width (int): Number of characters per line. - justify (str, optional): Default justify method for text: "left", "center", "full" or "right". Defaults to "left". - overflow (str, optional): Default overflow for text: "crop", "fold", or "ellipsis". Defaults to "fold". - - """ - from .text import Text - - if justify == "left": - for line in self._lines: - line.truncate(width, overflow=overflow, pad=True) - elif justify == "center": - for line in self._lines: - line.rstrip() - line.truncate(width, overflow=overflow) - line.pad_left((width - cell_len(line.plain)) // 2) - line.pad_right(width - cell_len(line.plain)) - elif justify == "right": - for line in self._lines: - line.rstrip() - line.truncate(width, overflow=overflow) - line.pad_left(width - cell_len(line.plain)) - elif justify == "full": - for line_index, line in enumerate(self._lines): - if line_index == len(self._lines) - 1: - break - words = line.split(" ") - words_size = sum(cell_len(word.plain) for word in words) - num_spaces = len(words) - 1 - spaces = [1 for _ in range(num_spaces)] - index = 0 - if spaces: - while words_size + num_spaces < width: - spaces[len(spaces) - index - 1] += 1 - num_spaces += 1 - index = (index + 1) % len(spaces) - tokens: List[Text] = [] - for index, (word, next_word) in enumerate( - zip_longest(words, words[1:]) - ): - tokens.append(word) - if index < len(spaces): - style = word.get_style_at_offset(console, -1) - next_style = next_word.get_style_at_offset(console, 0) - space_style = style if style == next_style else line.style - tokens.append(Text(" " * spaces[index], style=space_style)) - self[line_index] = Text("").join(tokens) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/control.py b/.venv/Lib/site-packages/pip/_vendor/rich/control.py deleted file mode 100644 index 88fcb92..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/control.py +++ /dev/null @@ -1,225 +0,0 @@ -import sys -import time -from typing import TYPE_CHECKING, Callable, Dict, Iterable, List, Union - -if sys.version_info >= (3, 8): - from typing import Final -else: - from pip._vendor.typing_extensions import Final # pragma: no cover - -from .segment import ControlCode, ControlType, Segment - -if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderResult - -STRIP_CONTROL_CODES: Final = [ - 7, # Bell - 8, # Backspace - 11, # Vertical tab - 12, # Form feed - 13, # Carriage return -] -_CONTROL_STRIP_TRANSLATE: Final = { - _codepoint: None for _codepoint in STRIP_CONTROL_CODES -} - -CONTROL_ESCAPE: Final = { - 7: "\\a", - 8: "\\b", - 11: "\\v", - 12: "\\f", - 13: "\\r", -} - -CONTROL_CODES_FORMAT: Dict[int, Callable[..., str]] = { - ControlType.BELL: lambda: "\x07", - ControlType.CARRIAGE_RETURN: lambda: "\r", - ControlType.HOME: lambda: "\x1b[H", - ControlType.CLEAR: lambda: "\x1b[2J", - ControlType.ENABLE_ALT_SCREEN: lambda: "\x1b[?1049h", - ControlType.DISABLE_ALT_SCREEN: lambda: "\x1b[?1049l", - ControlType.SHOW_CURSOR: lambda: "\x1b[?25h", - ControlType.HIDE_CURSOR: lambda: "\x1b[?25l", - ControlType.CURSOR_UP: lambda param: f"\x1b[{param}A", - ControlType.CURSOR_DOWN: lambda param: f"\x1b[{param}B", - ControlType.CURSOR_FORWARD: lambda param: f"\x1b[{param}C", - ControlType.CURSOR_BACKWARD: lambda param: f"\x1b[{param}D", - ControlType.CURSOR_MOVE_TO_COLUMN: lambda param: f"\x1b[{param+1}G", - ControlType.ERASE_IN_LINE: lambda param: f"\x1b[{param}K", - ControlType.CURSOR_MOVE_TO: lambda x, y: f"\x1b[{y+1};{x+1}H", - ControlType.SET_WINDOW_TITLE: lambda title: f"\x1b]0;{title}\x07", -} - - -class Control: - """A renderable that inserts a control code (non printable but may move cursor). - - Args: - *codes (str): Positional arguments are either a :class:`~rich.segment.ControlType` enum or a - tuple of ControlType and an integer parameter - """ - - __slots__ = ["segment"] - - def __init__(self, *codes: Union[ControlType, ControlCode]) -> None: - control_codes: List[ControlCode] = [ - (code,) if isinstance(code, ControlType) else code for code in codes - ] - _format_map = CONTROL_CODES_FORMAT - rendered_codes = "".join( - _format_map[code](*parameters) for code, *parameters in control_codes - ) - self.segment = Segment(rendered_codes, None, control_codes) - - @classmethod - def bell(cls) -> "Control": - """Ring the 'bell'.""" - return cls(ControlType.BELL) - - @classmethod - def home(cls) -> "Control": - """Move cursor to 'home' position.""" - return cls(ControlType.HOME) - - @classmethod - def move(cls, x: int = 0, y: int = 0) -> "Control": - """Move cursor relative to current position. - - Args: - x (int): X offset. - y (int): Y offset. - - Returns: - ~Control: Control object. - - """ - - def get_codes() -> Iterable[ControlCode]: - control = ControlType - if x: - yield ( - control.CURSOR_FORWARD if x > 0 else control.CURSOR_BACKWARD, - abs(x), - ) - if y: - yield ( - control.CURSOR_DOWN if y > 0 else control.CURSOR_UP, - abs(y), - ) - - control = cls(*get_codes()) - return control - - @classmethod - def move_to_column(cls, x: int, y: int = 0) -> "Control": - """Move to the given column, optionally add offset to row. - - Returns: - x (int): absolute x (column) - y (int): optional y offset (row) - - Returns: - ~Control: Control object. - """ - - return ( - cls( - (ControlType.CURSOR_MOVE_TO_COLUMN, x), - ( - ControlType.CURSOR_DOWN if y > 0 else ControlType.CURSOR_UP, - abs(y), - ), - ) - if y - else cls((ControlType.CURSOR_MOVE_TO_COLUMN, x)) - ) - - @classmethod - def move_to(cls, x: int, y: int) -> "Control": - """Move cursor to absolute position. - - Args: - x (int): x offset (column) - y (int): y offset (row) - - Returns: - ~Control: Control object. - """ - return cls((ControlType.CURSOR_MOVE_TO, x, y)) - - @classmethod - def clear(cls) -> "Control": - """Clear the screen.""" - return cls(ControlType.CLEAR) - - @classmethod - def show_cursor(cls, show: bool) -> "Control": - """Show or hide the cursor.""" - return cls(ControlType.SHOW_CURSOR if show else ControlType.HIDE_CURSOR) - - @classmethod - def alt_screen(cls, enable: bool) -> "Control": - """Enable or disable alt screen.""" - if enable: - return cls(ControlType.ENABLE_ALT_SCREEN, ControlType.HOME) - else: - return cls(ControlType.DISABLE_ALT_SCREEN) - - @classmethod - def title(cls, title: str) -> "Control": - """Set the terminal window title - - Args: - title (str): The new terminal window title - """ - return cls((ControlType.SET_WINDOW_TITLE, title)) - - def __str__(self) -> str: - return self.segment.text - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - if self.segment.text: - yield self.segment - - -def strip_control_codes( - text: str, _translate_table: Dict[int, None] = _CONTROL_STRIP_TRANSLATE -) -> str: - """Remove control codes from text. - - Args: - text (str): A string possibly contain control codes. - - Returns: - str: String with control codes removed. - """ - return text.translate(_translate_table) - - -def escape_control_codes( - text: str, - _translate_table: Dict[int, str] = CONTROL_ESCAPE, -) -> str: - """Replace control codes with their "escaped" equivalent in the given text. - (e.g. "\b" becomes "\\b") - - Args: - text (str): A string possibly containing control codes. - - Returns: - str: String with control codes replaced with their escaped version. - """ - return text.translate(_translate_table) - - -if __name__ == "__main__": # pragma: no cover - from pip._vendor.rich.console import Console - - console = Console() - console.print("Look at the title of your terminal window ^") - # console.print(Control((ControlType.SET_WINDOW_TITLE, "Hello, world!"))) - for i in range(10): - console.set_window_title("🚀 Loading" + "." * i) - time.sleep(0.5) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/default_styles.py b/.venv/Lib/site-packages/pip/_vendor/rich/default_styles.py deleted file mode 100644 index 46e9ea5..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/default_styles.py +++ /dev/null @@ -1,188 +0,0 @@ -from typing import Dict - -from .style import Style - -DEFAULT_STYLES: Dict[str, Style] = { - "none": Style.null(), - "reset": Style( - color="default", - bgcolor="default", - dim=False, - bold=False, - italic=False, - underline=False, - blink=False, - blink2=False, - reverse=False, - conceal=False, - strike=False, - ), - "dim": Style(dim=True), - "bright": Style(dim=False), - "bold": Style(bold=True), - "strong": Style(bold=True), - "code": Style(reverse=True, bold=True), - "italic": Style(italic=True), - "emphasize": Style(italic=True), - "underline": Style(underline=True), - "blink": Style(blink=True), - "blink2": Style(blink2=True), - "reverse": Style(reverse=True), - "strike": Style(strike=True), - "black": Style(color="black"), - "red": Style(color="red"), - "green": Style(color="green"), - "yellow": Style(color="yellow"), - "magenta": Style(color="magenta"), - "cyan": Style(color="cyan"), - "white": Style(color="white"), - "inspect.attr": Style(color="yellow", italic=True), - "inspect.attr.dunder": Style(color="yellow", italic=True, dim=True), - "inspect.callable": Style(bold=True, color="red"), - "inspect.async_def": Style(italic=True, color="bright_cyan"), - "inspect.def": Style(italic=True, color="bright_cyan"), - "inspect.class": Style(italic=True, color="bright_cyan"), - "inspect.error": Style(bold=True, color="red"), - "inspect.equals": Style(), - "inspect.help": Style(color="cyan"), - "inspect.doc": Style(dim=True), - "inspect.value.border": Style(color="green"), - "live.ellipsis": Style(bold=True, color="red"), - "layout.tree.row": Style(dim=False, color="red"), - "layout.tree.column": Style(dim=False, color="blue"), - "logging.keyword": Style(bold=True, color="yellow"), - "logging.level.notset": Style(dim=True), - "logging.level.debug": Style(color="green"), - "logging.level.info": Style(color="blue"), - "logging.level.warning": Style(color="red"), - "logging.level.error": Style(color="red", bold=True), - "logging.level.critical": Style(color="red", bold=True, reverse=True), - "log.level": Style.null(), - "log.time": Style(color="cyan", dim=True), - "log.message": Style.null(), - "log.path": Style(dim=True), - "repr.ellipsis": Style(color="yellow"), - "repr.indent": Style(color="green", dim=True), - "repr.error": Style(color="red", bold=True), - "repr.str": Style(color="green", italic=False, bold=False), - "repr.brace": Style(bold=True), - "repr.comma": Style(bold=True), - "repr.ipv4": Style(bold=True, color="bright_green"), - "repr.ipv6": Style(bold=True, color="bright_green"), - "repr.eui48": Style(bold=True, color="bright_green"), - "repr.eui64": Style(bold=True, color="bright_green"), - "repr.tag_start": Style(bold=True), - "repr.tag_name": Style(color="bright_magenta", bold=True), - "repr.tag_contents": Style(color="default"), - "repr.tag_end": Style(bold=True), - "repr.attrib_name": Style(color="yellow", italic=False), - "repr.attrib_equal": Style(bold=True), - "repr.attrib_value": Style(color="magenta", italic=False), - "repr.number": Style(color="cyan", bold=True, italic=False), - "repr.number_complex": Style(color="cyan", bold=True, italic=False), # same - "repr.bool_true": Style(color="bright_green", italic=True), - "repr.bool_false": Style(color="bright_red", italic=True), - "repr.none": Style(color="magenta", italic=True), - "repr.url": Style(underline=True, color="bright_blue", italic=False, bold=False), - "repr.uuid": Style(color="bright_yellow", bold=False), - "repr.call": Style(color="magenta", bold=True), - "repr.path": Style(color="magenta"), - "repr.filename": Style(color="bright_magenta"), - "rule.line": Style(color="bright_green"), - "rule.text": Style.null(), - "json.brace": Style(bold=True), - "json.bool_true": Style(color="bright_green", italic=True), - "json.bool_false": Style(color="bright_red", italic=True), - "json.null": Style(color="magenta", italic=True), - "json.number": Style(color="cyan", bold=True, italic=False), - "json.str": Style(color="green", italic=False, bold=False), - "json.key": Style(color="blue", bold=True), - "prompt": Style.null(), - "prompt.choices": Style(color="magenta", bold=True), - "prompt.default": Style(color="cyan", bold=True), - "prompt.invalid": Style(color="red"), - "prompt.invalid.choice": Style(color="red"), - "pretty": Style.null(), - "scope.border": Style(color="blue"), - "scope.key": Style(color="yellow", italic=True), - "scope.key.special": Style(color="yellow", italic=True, dim=True), - "scope.equals": Style(color="red"), - "table.header": Style(bold=True), - "table.footer": Style(bold=True), - "table.cell": Style.null(), - "table.title": Style(italic=True), - "table.caption": Style(italic=True, dim=True), - "traceback.error": Style(color="red", italic=True), - "traceback.border.syntax_error": Style(color="bright_red"), - "traceback.border": Style(color="red"), - "traceback.text": Style.null(), - "traceback.title": Style(color="red", bold=True), - "traceback.exc_type": Style(color="bright_red", bold=True), - "traceback.exc_value": Style.null(), - "traceback.offset": Style(color="bright_red", bold=True), - "bar.back": Style(color="grey23"), - "bar.complete": Style(color="rgb(249,38,114)"), - "bar.finished": Style(color="rgb(114,156,31)"), - "bar.pulse": Style(color="rgb(249,38,114)"), - "progress.description": Style.null(), - "progress.filesize": Style(color="green"), - "progress.filesize.total": Style(color="green"), - "progress.download": Style(color="green"), - "progress.elapsed": Style(color="yellow"), - "progress.percentage": Style(color="magenta"), - "progress.remaining": Style(color="cyan"), - "progress.data.speed": Style(color="red"), - "progress.spinner": Style(color="green"), - "status.spinner": Style(color="green"), - "tree": Style(), - "tree.line": Style(), - "markdown.paragraph": Style(), - "markdown.text": Style(), - "markdown.emph": Style(italic=True), - "markdown.strong": Style(bold=True), - "markdown.code": Style(bgcolor="black", color="bright_white"), - "markdown.code_block": Style(dim=True, color="cyan", bgcolor="black"), - "markdown.block_quote": Style(color="magenta"), - "markdown.list": Style(color="cyan"), - "markdown.item": Style(), - "markdown.item.bullet": Style(color="yellow", bold=True), - "markdown.item.number": Style(color="yellow", bold=True), - "markdown.hr": Style(color="yellow"), - "markdown.h1.border": Style(), - "markdown.h1": Style(bold=True), - "markdown.h2": Style(bold=True, underline=True), - "markdown.h3": Style(bold=True), - "markdown.h4": Style(bold=True, dim=True), - "markdown.h5": Style(underline=True), - "markdown.h6": Style(italic=True), - "markdown.h7": Style(italic=True, dim=True), - "markdown.link": Style(color="bright_blue"), - "markdown.link_url": Style(color="blue"), - "iso8601.date": Style(color="blue"), - "iso8601.time": Style(color="magenta"), - "iso8601.timezone": Style(color="yellow"), -} - - -if __name__ == "__main__": # pragma: no cover - import argparse - import io - - from pip._vendor.rich.console import Console - from pip._vendor.rich.table import Table - from pip._vendor.rich.text import Text - - parser = argparse.ArgumentParser() - parser.add_argument("--html", action="store_true", help="Export as HTML table") - args = parser.parse_args() - html: bool = args.html - console = Console(record=True, width=70, file=io.StringIO()) if html else Console() - - table = Table("Name", "Styling") - - for style_name, style in DEFAULT_STYLES.items(): - table.add_row(Text(style_name, style=style), str(style)) - - console.print(table) - if html: - print(console.export_html(inline_styles=True)) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/diagnose.py b/.venv/Lib/site-packages/pip/_vendor/rich/diagnose.py deleted file mode 100644 index ad36183..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/diagnose.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import platform - -from pip._vendor.rich import inspect -from pip._vendor.rich.console import Console, get_windows_console_features -from pip._vendor.rich.panel import Panel -from pip._vendor.rich.pretty import Pretty - - -def report() -> None: # pragma: no cover - """Print a report to the terminal with debugging information""" - console = Console() - inspect(console) - features = get_windows_console_features() - inspect(features) - - env_names = ( - "TERM", - "COLORTERM", - "CLICOLOR", - "NO_COLOR", - "TERM_PROGRAM", - "COLUMNS", - "LINES", - "JUPYTER_COLUMNS", - "JUPYTER_LINES", - "JPY_PARENT_PID", - "VSCODE_VERBOSE_LOGGING", - ) - env = {name: os.getenv(name) for name in env_names} - console.print(Panel.fit((Pretty(env)), title="[b]Environment Variables")) - - console.print(f'platform="{platform.system()}"') - - -if __name__ == "__main__": # pragma: no cover - report() diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/emoji.py b/.venv/Lib/site-packages/pip/_vendor/rich/emoji.py deleted file mode 100644 index 791f046..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/emoji.py +++ /dev/null @@ -1,96 +0,0 @@ -import sys -from typing import TYPE_CHECKING, Optional, Union - -from .jupyter import JupyterMixin -from .segment import Segment -from .style import Style -from ._emoji_codes import EMOJI -from ._emoji_replace import _emoji_replace - -if sys.version_info >= (3, 8): - from typing import Literal -else: - from pip._vendor.typing_extensions import Literal # pragma: no cover - - -if TYPE_CHECKING: - from .console import Console, ConsoleOptions, RenderResult - - -EmojiVariant = Literal["emoji", "text"] - - -class NoEmoji(Exception): - """No emoji by that name.""" - - -class Emoji(JupyterMixin): - __slots__ = ["name", "style", "_char", "variant"] - - VARIANTS = {"text": "\uFE0E", "emoji": "\uFE0F"} - - def __init__( - self, - name: str, - style: Union[str, Style] = "none", - variant: Optional[EmojiVariant] = None, - ) -> None: - """A single emoji character. - - Args: - name (str): Name of emoji. - style (Union[str, Style], optional): Optional style. Defaults to None. - - Raises: - NoEmoji: If the emoji doesn't exist. - """ - self.name = name - self.style = style - self.variant = variant - try: - self._char = EMOJI[name] - except KeyError: - raise NoEmoji(f"No emoji called {name!r}") - if variant is not None: - self._char += self.VARIANTS.get(variant, "") - - @classmethod - def replace(cls, text: str) -> str: - """Replace emoji markup with corresponding unicode characters. - - Args: - text (str): A string with emojis codes, e.g. "Hello :smiley:!" - - Returns: - str: A string with emoji codes replaces with actual emoji. - """ - return _emoji_replace(text) - - def __repr__(self) -> str: - return f"" - - def __str__(self) -> str: - return self._char - - def __rich_console__( - self, console: "Console", options: "ConsoleOptions" - ) -> "RenderResult": - yield Segment(self._char, console.get_style(self.style)) - - -if __name__ == "__main__": # pragma: no cover - import sys - - from pip._vendor.rich.columns import Columns - from pip._vendor.rich.console import Console - - console = Console(record=True) - - columns = Columns( - (f":{name}: {name}" for name in sorted(EMOJI.keys()) if "\u200D" not in name), - column_first=True, - ) - - console.print(columns) - if len(sys.argv) > 1: - console.save_html(sys.argv[1]) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/errors.py b/.venv/Lib/site-packages/pip/_vendor/rich/errors.py deleted file mode 100644 index 0bcbe53..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/errors.py +++ /dev/null @@ -1,34 +0,0 @@ -class ConsoleError(Exception): - """An error in console operation.""" - - -class StyleError(Exception): - """An error in styles.""" - - -class StyleSyntaxError(ConsoleError): - """Style was badly formatted.""" - - -class MissingStyle(StyleError): - """No such style.""" - - -class StyleStackError(ConsoleError): - """Style stack is invalid.""" - - -class NotRenderableError(ConsoleError): - """Object is not renderable.""" - - -class MarkupError(ConsoleError): - """Markup was badly formatted.""" - - -class LiveError(ConsoleError): - """Error related to Live display.""" - - -class NoAltScreen(ConsoleError): - """Alt screen mode was required.""" diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py b/.venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py deleted file mode 100644 index cc69f22..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/file_proxy.py +++ /dev/null @@ -1,54 +0,0 @@ -import io -from typing import IO, TYPE_CHECKING, Any, List - -from .ansi import AnsiDecoder -from .text import Text - -if TYPE_CHECKING: - from .console import Console - - -class FileProxy(io.TextIOBase): - """Wraps a file (e.g. sys.stdout) and redirects writes to a console.""" - - def __init__(self, console: "Console", file: IO[str]) -> None: - self.__console = console - self.__file = file - self.__buffer: List[str] = [] - self.__ansi_decoder = AnsiDecoder() - - @property - def rich_proxied_file(self) -> IO[str]: - """Get proxied file.""" - return self.__file - - def __getattr__(self, name: str) -> Any: - return getattr(self.__file, name) - - def write(self, text: str) -> int: - if not isinstance(text, str): - raise TypeError(f"write() argument must be str, not {type(text).__name__}") - buffer = self.__buffer - lines: List[str] = [] - while text: - line, new_line, text = text.partition("\n") - if new_line: - lines.append("".join(buffer) + line) - del buffer[:] - else: - buffer.append(line) - break - if lines: - console = self.__console - with console: - output = Text("\n").join( - self.__ansi_decoder.decode_line(line) for line in lines - ) - console.print(output) - return len(text) - - def flush(self) -> None: - output = "".join(self.__buffer) - if output: - self.__console.print(output) - del self.__buffer[:] diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/filesize.py b/.venv/Lib/site-packages/pip/_vendor/rich/filesize.py deleted file mode 100644 index 61be475..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/filesize.py +++ /dev/null @@ -1,89 +0,0 @@ -# coding: utf-8 -"""Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2 - -The functions declared in this module should cover the different -usecases needed to generate a string representation of a file size -using several different units. Since there are many standards regarding -file size units, three different functions have been implemented. - -See Also: - * `Wikipedia: Binary prefix `_ - -""" - -__all__ = ["decimal"] - -from typing import Iterable, List, Optional, Tuple - - -def _to_str( - size: int, - suffixes: Iterable[str], - base: int, - *, - precision: Optional[int] = 1, - separator: Optional[str] = " ", -) -> str: - if size == 1: - return "1 byte" - elif size < base: - return "{:,} bytes".format(size) - - for i, suffix in enumerate(suffixes, 2): # noqa: B007 - unit = base**i - if size < unit: - break - return "{:,.{precision}f}{separator}{}".format( - (base * size / unit), - suffix, - precision=precision, - separator=separator, - ) - - -def pick_unit_and_suffix(size: int, suffixes: List[str], base: int) -> Tuple[int, str]: - """Pick a suffix and base for the given size.""" - for i, suffix in enumerate(suffixes): - unit = base**i - if size < unit * base: - break - return unit, suffix - - -def decimal( - size: int, - *, - precision: Optional[int] = 1, - separator: Optional[str] = " ", -) -> str: - """Convert a filesize in to a string (powers of 1000, SI prefixes). - - In this convention, ``1000 B = 1 kB``. - - This is typically the format used to advertise the storage - capacity of USB flash drives and the like (*256 MB* meaning - actually a storage capacity of more than *256 000 000 B*), - or used by **Mac OS X** since v10.6 to report file sizes. - - Arguments: - int (size): A file size. - int (precision): The number of decimal places to include (default = 1). - str (separator): The string to separate the value from the units (default = " "). - - Returns: - `str`: A string containing a abbreviated file size and units. - - Example: - >>> filesize.decimal(30000) - '30.0 kB' - >>> filesize.decimal(30000, precision=2, separator="") - '30.00kB' - - """ - return _to_str( - size, - ("kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"), - 1000, - precision=precision, - separator=separator, - ) diff --git a/.venv/Lib/site-packages/pip/_vendor/rich/highlighter.py b/.venv/Lib/site-packages/pip/_vendor/rich/highlighter.py deleted file mode 100644 index 82293df..0000000 --- a/.venv/Lib/site-packages/pip/_vendor/rich/highlighter.py +++ /dev/null @@ -1,232 +0,0 @@ -import re -from abc import ABC, abstractmethod -from typing import List, Union - -from .text import Span, Text - - -def _combine_regex(*regexes: str) -> str: - """Combine a number of regexes in to a single regex. - - Returns: - str: New regex with all regexes ORed together. - """ - return "|".join(regexes) - - -class Highlighter(ABC): - """Abstract base class for highlighters.""" - - def __call__(self, text: Union[str, Text]) -> Text: - """Highlight a str or Text instance. - - Args: - text (Union[str, ~Text]): Text to highlight. - - Raises: - TypeError: If not called with text or str. - - Returns: - Text: A test instance with highlighting applied. - """ - if isinstance(text, str): - highlight_text = Text(text) - elif isinstance(text, Text): - highlight_text = text.copy() - else: - raise TypeError(f"str or Text instance required, not {text!r}") - self.highlight(highlight_text) - return highlight_text - - @abstractmethod - def highlight(self, text: Text) -> None: - """Apply highlighting in place to text. - - Args: - text (~Text): A text object highlight. - """ - - -class NullHighlighter(Highlighter): - """A highlighter object that doesn't highlight. - - May be used to disable highlighting entirely. - - """ - - def highlight(self, text: Text) -> None: - """Nothing to do""" - - -class RegexHighlighter(Highlighter): - """Applies highlighting from a list of regular expressions.""" - - highlights: List[str] = [] - base_style: str = "" - - def highlight(self, text: Text) -> None: - """Highlight :class:`rich.text.Text` using regular expressions. - - Args: - text (~Text): Text to highlighted. - - """ - - highlight_regex = text.highlight_regex - for re_highlight in self.highlights: - highlight_regex(re_highlight, style_prefix=self.base_style) - - -class ReprHighlighter(RegexHighlighter): - """Highlights the text typically produced from ``__repr__`` methods.""" - - base_style = "repr." - highlights = [ - r"(?P<)(?P[-\w.:|]*)(?P[\w\W]*?)(?P>)", - r'(?P[\w_]{1,50})=(?P"?[\w_]+"?)?', - r"(?P[][{}()])", - _combine_regex( - r"(?P[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})", - r"(?P([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})", - r"(?P(?:[0-9A-Fa-f]{1,2}-){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){7}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){3}[0-9A-Fa-f]{4})", - r"(?P(?:[0-9A-Fa-f]{1,2}-){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{1,2}:){5}[0-9A-Fa-f]{1,2}|(?:[0-9A-Fa-f]{4}\.){2}[0-9A-Fa-f]{4})", - r"(?P[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})", - r"(?P[\w.]*?)\(", - r"\b(?PTrue)\b|\b(?PFalse)\b|\b(?PNone)\b", - r"(?P\.\.\.)", - r"(?P(?(?\B(/[-\w._+]+)*\/)(?P[-\w._+]*)?", - r"(?b?'''.*?(?(file|https|http|ws|wss)://[-0-9a-zA-Z$_+!`(),.?/;:&=%#]*)", - ), - ] - - -class JSONHighlighter(RegexHighlighter): - """Highlights JSON""" - - # Captures the start and end of JSON strings, handling escaped quotes - JSON_STR = r"(?b?\".*?(?[\{\[\(\)\]\}])", - r"\b(?Ptrue)\b|\b(?Pfalse)\b|\b(?Pnull)\b", - r"(?P(? None: - super().highlight(text) - - # Additional work to handle highlighting JSON keys - plain = text.plain - append = text.spans.append - whitespace = self.JSON_WHITESPACE - for match in re.finditer(self.JSON_STR, plain): - start, end = match.span() - cursor = end - while cursor < len(plain): - char = plain[cursor] - cursor += 1 - if char == ":": - append(Span(start, end, "json.key")) - elif char in whitespace: - continue - break - - -class ISO8601Highlighter(RegexHighlighter): - """Highlights the ISO8601 date time strings. - Regex reference: https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch04s07.html - """ - - base_style = "iso8601." - highlights = [ - # - # Dates - # - # Calendar month (e.g. 2008-08). The hyphen is required - r"^(?P[0-9]{4})-(?P1[0-2]|0[1-9])$", - # Calendar date w/o hyphens (e.g. 20080830) - r"^(?P(?P[0-9]{4})(?P1[0-2]|0[1-9])(?P3[01]|0[1-9]|[12][0-9]))$", - # Ordinal date (e.g. 2008-243). The hyphen is optional - r"^(?P(?P[0-9]{4})-?(?P36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9]))$", - # - # Weeks - # - # Week of the year (e.g., 2008-W35). The hyphen is optional - r"^(?P(?P[0-9]{4})-?W(?P5[0-3]|[1-4][0-9]|0[1-9]))$", - # Week date (e.g., 2008-W35-6). The hyphens are optional - r"^(?P(?P[0-9]{4})-?W(?P5[0-3]|[1-4][0-9]|0[1-9])-?(?P[1-7]))$", - # - # Times - # - # Hours and minutes (e.g., 17:21). The colon is optional - r"^(?P

re+gie~DPE1WZW!X-WTP*t8*N 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

l@TIaJQ@x`@gdK1UIc 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

gY^H>Jlwa7W&k$J z&fF=#$_RI2|HLNop&8`9y|liO3UbEraVBw=93R`!*5vp|zT_WRntkchw`5cPVee3p zT%8J~nw}wFPw!q|t`H3pqO=E++Xul7R+|BD+i4h(Frzl6g&2lHZnnS}2vVP=!cI+f z*r{EFF|P$hc8&Mwm)i6^Ew*20sCP9EQDpU?!A4xvsy*3Jpq$6$Mly3`+vg=h12&Qc zfTtx#d5HucIG+vX0A}ahe5aU={(_60e+PbDvytq>>nE^dgqci8yvlRLZ1h{cioMAY z2A_XIlTo>#!5`gM>WUInRH^93kBv6JKY*bh&t1H}d=Xa2_d^<-X3AED$IHNvb=kl&T8b!zJ(}ICdU3m1yd?v(Qk-t#TugF4Y3LZ(2mSDCRYBh^&g! z!(XB*b_3)^h^nX(0{-+PDc~1nM~)MXDaLj}y3aCrd!uGsWGPlFj-^m9FG)yrAl1Z6 zbb#~+nnj!kn3f@yzJQGiF^^`rqQDQLEitnBp!Xmmbm&Bg`UM(ZnWx>v`;{LIG23Cb zLqZC1vT~Xbhp@L9B#B`bjrgv_(#86`7FiKV8JQf#@FVoMY%!ePx0V(2=fu3N;(%i$$CoV@IPMI|RM z=k`E~cO#WMKi#NqT0;;J??(b&{F|WK6;P+d(F3zZ1Q=#B+M><)#6Qd~PJ??i6fU8A zCui>e$K1ETM^#+^CqPJ0!rh3`_#|McK~aNN4Fzk0S-cCo7?mJa(P%Z|qY^byR2~b- zYOc4d*c!{PZ8X(VvA&`|L8Qheq9mx*sI7wf)A*h>s1X$cKJx#5&)j_`At3lK`M}QH zduPs^Ip@roGiP4kt>9TtR{_tqW4%B4)Cc}$jt8RCpcf*4nZ z&r422Ra)F#k}6n&olPPIw%psN_J=bG`i4cSH3v$Z9Oy-ALx06*m=>#GT7-qN!z1Qp zjQ-G+4bwR|bcEOXl&b+Afz!sD{6U!1PKl>WiNbSA$0PpMLwM%TU}E50gO`)#T!Ty>w%NRfxmYdbi#Zz9QVvUkgyB`W zj+j4vfEIHOhcPZ!x8Rv!4Z=d>iHe+JFhaOLW*jZ2IhrQ0AER-tp~5Re**~3y$q(>(IgG5`t$uGLapAHc(`>|9uqnrM?j)svjynp zm@xhdCpD^%M@nr<&?5fmbxfO%lJg_c(Da6{CZHH=m-T+Uy1s{c^gEs19k{3`eP2{q zrT#D_sl$V(L;1Azdpu2$%I-Yi7ynPjeA~4*qT{o0^>D2-<_>_rV=N6u_1Njgk9dp` z1(21V{_iL}tw!eUr~BLZ+~NMK|NIkoG5>d&yH@^FKR&;-sd>AYe`2rsd$m_usTHH+ z%>q2qUh!Hus6b2>J7v^T8I~P}z|K4hzZjMofuZPlv9cqRAA`Yqu%2V71sgct5(m=e z0AFf{Yy)EO2Q^6`GSjQAgH_*^?9V5p+?6>M@bscR={(U?%aI#prM#adF3(a#x9I3> zD2~#2mnU@eA0H4Meb9-bqtBQEh#FCq>!Ay@9^hb;tPu-BP=;9o%Oejo@Yq-lEiTYu z`)YmtJl?|RIhiidX>@_kpbPXox6rMbS zz|MRUzZ@#_grHvn3D|oq9rUA0LZ8P{Fp1AD2RdRg^Ex+%ABuCSz{NRu6-f|b=A&=) zU?!?8u7@3BCRQ_%mbkIq2-L!O2yy#)k+p)uVDNbLaN#k#nu<$+tl;d3l7rs6Luo__ zZ^8x3!GiUu5LmYb0p2%BApK!vp(VyrAR-oDVqE1M@$wbAWl&L$dWr2iAUeU6LvCTe z;hs%7a{(SM9he)I>tIQ#8jYaP_ypge z8c7GWEQ1}1cAZ-8Q-L|Z0)VcEle6SR_(=~3*Z1#hY0>GZ2$0AHOs{>N%RAvjUXZX= zT{Ve=-P6+a6=t>k?wnQzI^c9HY_0KH*SU=9RPX_Zt-#KVoSm5Y&M=$+x0aSt^~K1D zvlAg^m)s#@HXDJRc{P5+9+^JH4DvOJnH&Z_{VS2LW8_stux9>;#1xP1kI)j+(n3If%8GP!MHyzg8b4i0FOy4N<0wYu<9 z$1I_8TxNQwg?h!n3Y`BTleg3MK$OpZ7bVL5XCqG&4TqH}KbszR;Ly`AVKhu0Tum&4 z>17thQVtwCjDD=fmH98z<=aOMEujfSbOHX4ctdMwx(#0@hG zS7ClEspACD3+@T6tuO@9+6@Y-!L(pq2TKrcRmV>FGHl>z;y`Z)O4d{IfLixKxXvKEvc z^3ezsptB0`$6>R>*(FF5Gg#swVSFy!RC_N0Pe*8Z%_7hu%nh1D3 zt*`#=r_EBjzSl}B_SMi{0rlsPir^E>Tmj;OS%TJ8Exm2N(`HHJH{pEd*{N&?+MiNn zj9J!(wd@G%nh=&184GJ0oJg?Vi3FA~Vk~U*>u^OmLX2}~BhaQz3%1S~D+g3{9f?bL zL9eQ=5+g9}cubw$kbH;{C_>CwIcY#u)26DfT;t}S;~8S|fQ{Dm>p_cfsAhNjSH_%4 z1MxFuc$v}BT4sFFRAy8le53jJ?rzFu_s^4(`2Dc(`z>u{9EE;@_h!`%$05nvh*sS& z0kKyR^WShXtg2Ri#)KU-JpT~o; zYFs2zr(=g0`Zs~+4E)lLz-Py43coARP=C5Xj(1=c)xv$zdM_L+^iHno8f;XT3qNv< zz|+SG0VfS075Bl<8Ke9+{4&Y71C;*aEl@S(nfj@~sN#h(^;zID-;Li#)KQ1KdM z9F&Tg){;TQ7Go!wfP-BgHQKljFYQ6jjIVgx232Fh|g!#_mZ z5jaFve1$)}Ee_MWB`_0FCqZfWt)248ZP5C5xJ4#|HsYc*1opNwhS04QJ zhdEmd)Aid__$-WS&O}vp?QK*~lt`8l_2lGR76S*H_C$xUcAkK%pAWP@Qo zDc=ax@tq^+pvmJOLX@_QBq0McAzLso&mKba;5M)$oGvJfLC_$`Icxx9LiI;nMFnd1 zY!qlBY!qlBYy)`0k>Gr4(of6vQ=5KbPsxr^Keg(oPCP;Tt;ZAin?$Bd)B59nNQ@mF z_>49sZ4O~A0VAvvkYhv(psC6B7KF0D1))Cs5km+TrMI}SVX`bXOj#n1L$gG}WSLdy z_oybA2qw?YL!OnBQchY72{x*0M^e!$Ug5_SJ5U12s|YmgBvnb}vR$tKD-D(`xs0evZo0+)E)# z4~))j`}v-<^xw`63o;zb3%IM;HgO!`{Io1D-cm zHj3`nlb^F-J4N7~2XR1cPGuWC4Q+IxBbf`O1K~4!7zi{KIh@q&deqd{aSI4d*1t|- zVUGH*6`dC8hbZZ4+$TSDhbn;5+Q3Hxl9Kj<1~s8)_(x@mXqt}Bbl$EFoCYbzKH-Sw zd-`u25K6D(U|n${kliV*hq>;HsNi|amQ`EBI^6sS!Qxrwr@!S_8}nSUJB>XuJP}gk zl>KUiWBVhpGe_b#L7@v_aK#%_Tbf$I^e=BFCu{jlJ>(OG{*JtoZWCV!T%FP?3?|(+ z3lY&=Z7IAi7t}H*9AXW^e2%qDmJnJi<{}eL9QGry8eP;k)ja+XYkX$1>FYijz}bsM zH6B5!<@uuV$P`pzee-RtajUrHZh0hyhOGM3N%A z@ohX|!B)F`Tc!}$jT_}%9Tij&4hE7S_?^5IHHD!`8c>F3V5xjtF;qVlBY;00AIO0zg5?qf8ziV*g#btMB83%bLcGXod=>#n)hbWR z+7MWc|CV=k>*ZZ-y9B`xEES;!{ruR7?X*Uc4UanuYAtAT-4yOg=?+Ex0gUOx%|xo^T-s9<17OQmvM9VbxY34hWPEq$)>HWY={` zP^*PRI?k|=*a1ohGPoF^8-jQi00eGxJdvEOGx5I2sx3m`wSu!Lmez^_c^R0co`rNyv)Zp?0CdWY(GYFJMdeH6CTJ_z^u6KKvFG{FNAFe?vS_?fW56c zl`bb`WVMLFCCqOLLVcpx78>hOIn13A5a2aPRYg7GRt;$jw%C5Clpo*F?=-eDO;tB^(;FBi z;tH(=0i|fBU1nCZp?%uAE-riu0r4~`xcaLd$U+XC&kh_cal%8Yd{P2}D<)P;7-KSY!8)#;+s5S6#MFVHWXtwLaD2JuVI(iiOva>zZD+$Dh=khL~24b|iM zA2XQOVes#8N)s4-3SLDL#PNA#b5CI(ictWBy%@mP27DT7I1^v0-`+(5)x8@!3sm1} zC>8mk$s9t!R3>J@p$pBHL9MJT?&@j9tsxbyE`6Lj>k6ZX3Lkf#nMf;#r9%f1oSJf< zlpxg<`}p60fkr76=9oVtOXGyz23ZLc5yphjDj>i#xzYCh3q(Q| zA+R%T{O(BGKZltd(YUTiM8lL zxPz~ITpNX#e5)8T8etHjs8jzOD?}88unXi3gZQykDZoDoPuO z>!M8D6qPFMNHdCfa{mZk33W`7G5n&m6N{pI0*FDlbZiZv%!U3crBgeh!^3@D_X9&m z>&w!HyhEcR5=_5m$t6juvc9t$7KvzWnbo31L}#_~x0-Ub)11TL98k%xYbGnp)pw0*8l)E>`BRM_>fL(Ne<) zwJ)?w=tC_vQj7bL0dmKWG!PMhYEoOAa6ppdoDXRs+J3cDE}JJ(b2$P#vl72MA~kMi za-^nkl1R<5@+wYh-gv5~)I5DGYjS+f)+rR8<-o6^f>I>PA)K2}%Cjkw8&zO8feNjP zY8ISLD_t;WW!x^W^_wg(lnvDkmZYh@bH41xx(IdV8jPDPSzT{pwl&aHnHrjkeB<6i zi@EhisrzjR?9Bhc?~c^{Qf6}M?#4M)P^L5FRTz6|F>n1Nm3&wpi|;Jd9fQc+F03T6 z>YBifiuvG21DOQ3e@_zJ6xCSqB!D7miiNz2RJCxNo8|5VC9oo7KE<1Z5>P{_hYlk( zYT1YJ>MpEXM424}WtOu}REzbl+5APen!Q=XhkQ}z>bNw`VjV9gV~TX##dtpAo~rNQ zQhZpth-KwkPt65(&{}ixox07}1SkB;{>c!HCxafuFB)FXs1@%2ChB4~5P46GHa z5MZ>!UK4|fCIq&0=~m!b#6c4FvjEpBUvlvaA(uG2bSv<0{34Y}%Hl{2zN>5JJ7{U- zv>HFu2}GS@9#=n6`yzR#Jk_zafLmSqo7VV&BqoW>Q>N)BG8Fk-`l(evW#I`Upltop zF(Pv1DfSj9>1CQo^{bCWFT-vU`<%>ya#2L#Dghm=Ee8@RJr&*s>1o<1ICe0JZy)&@ zjI-)W@e0j^Xrk2Fl0dp+AU0P&Ih8HfPY!K}m@S5oCB_XZmjLD9?@N#lM4+VVIC!t&%a}XXu+j7pc`r<1StX@G9faA|1 zfQi_uag=%jf3|I$1H}NgAp+ENDW8HeAdQ`ga?QldexMUPz*>PMfWh39OM<#=2Cjkv ze9sg31;qeYAW?mp%dde8=$Z|r)mb{8%lK4@H_8yEs(UbMkdM-h>VMJ-hE+!p!JtTk zRagwqBjIDONP##GfeS)G2BBbsP|!hBIGdj1)3fp8ap4Dd*9P+dT)nZ0eAT`rjEq@? zZdNF;UO)LYTS`e|-L%Lg{j@}XTcT4g)lW70sa`+%_0w`Z!99yIvEN7H95Pyg$C=8i zTP3eJRKwt2;vA|)8=|Cg8X|{k;fBbeTBISC>Qroq7%lFrgLVmIa1Rk;i-h;ZX^Lby z=gY&bQMk_Q)L`gJ!iRKAY@EqTOlUL_NK;}&eR2qz(xop`3eb{@@v3l@JUA_B&A@O= zlI)f*oaT}MrdQ$6AoC-#dJrzRbW zQycPgUOs+iq5k|ueFk&?UZvX zrAM8Gz|On`zvxkoK>27AA>}lFjCvVYJv0<_gcS- z0Rj^BsM%_~cWU+sqPk|w5YsjL6F*0>9%aF6{SC%v{g!tjGWJ&96av&<>yr`H9zQxZ`{=I)4_;)m_WxE?Lsu1fPBFp+HUDp(snOEU}s*A-vn!zDhKLsxdhsS9Za9VOwKgw zi6@9PoGY&)lL@ahu$jkCqy8NpEL3%;nmMLXZ`o%D$k?%O?__)p?;SF>BC5&wG-8^J zkMpyg^6eZU!gmPl%)#Z30{b9a6xiqd7-j92M|#vO@FEBfA~|*<4K`#D$~m3RVstNf)8U8n62+S$Y#)MF91Kx%wh7dY7(6k;(-dTfpz>d&q z&G6po^Z?#FbgD;G)9FscG@X9S&%w;EE{>2wjTj^1+9?-ns+I{= z%ki5aT7I3nMuPAGz6C;UeSq)VID!d1$WihtB3joz%pqdDXl>kS(c%KB&_O$*ftF~x z_UJ+8;=Mz%-yo_<_G`p6$d(oDSCxi@8;>($g%jsN!KOzd-;Rr-;(a{6h>TW=sMggV< z2U9#u?h2EwU^&r4?c<&J43Pd#lE<{WdU?x9DEB6S_(h5BW#RFuvU)~60 zM4}$$r4pc4iQl=x{fA6tQT`xMZ0#6AiE8U%Iq+@^$Fn~fGu+x-6+1Na zD*#1yZS&sbhxp;Coxt>guWdS7inmJ;Mcsn+I;?HbVO^7i3(qSC#2E$ni5&Y%YnaEr z#uZ7^w`16Up;XXL%pIcyN2Wc1mgc`97219ZaalPjO`MD=>7YATd0Y!qi;IE+N8&ST zJ!J@f&N!g|RUYGwKo@{8%b0{!9_@IKl76|@q?3%mYj^-(hZxnl@E3-jb@B{mG5a~i zq<=Wmi;jltNZxurW}V$1oWws%C}1JpLx4_zSF%FDW9eAnGKry}Kq)_Ceb%MY4iy5d z&%&>?!)>}9zR8bKA-i`u+hOd|1EcyJZEf#VJH+yXEubjI7c_xT#}!#RY)oUwDWqIP zI!@rHRyK6LTJJ=HO%x<&GGh7%W>Q(9QNHbS{9duO%xf%o4d3V{Qu+9X;a#bd9Q~IX zjaQKVF6cM9jjKz*Pb5!b-xhyQ-l_LschU*+Syq?q|KSw9x?~t4>f86V^Z30X$cR-X zEqG@59K*QPCEbTaSC>>D#G#{oYCepRHr6Jx&;+rwu;Oc>GgOSOI*}%9P43H>XrOVj zJ&UHHeiNMFGCK-#vk7i?R#sH6WTo}u1unA(3V~G`si4ptO_yWT$RYSxpNx^}d9Kh| zsPzALEfH?7=bs)Ko`j~dr?K4G5aZG^5#tLGz&^%rf)S(Xm}nm}lVcz6f26jL;yUPr5weJObSR*KqtXT7nk-xLq-BCu1izAg&WuIc~ z<MC2?;t6MF57tNKllX@-e zt+3D6_9$#23)Z95kj*&m2fs-8Jq`ix2gh#$zk4-W_hu&N+~C7;t`khVu3z(2gx}5g zlHc)`#=|IaXZa1AZ5-QrWO&gD7O2-=*g`sTcoAuZ9Fq__nlxhoHLQFN)C&d6hx41 zfWsT1Y{aM#=E{OjVuZ>$gqJZ^M2b4W?kMO|wQW=cG!%(rtx#;Biyc2lJ)Z>d1!(1P zu!&SAin@)?s0sD4B{2}KH>wBRNLB^3Tr#Qd7J$O zh_R6oOnQ@zZ+l@f+(-PvjmsZtHz~u{hp#^5EIb^4Aj{Czw|ZN6xa#SIdS5lfsdEQ>kea)F$8J&{2zV&0g*Xvu~mKfHz23{4iMB3r~jj)2o^1x-?g4E1?y1V@{B!^Ewm81;! zT!AMzts|iOKRTRH^npt!sjRaCFYi2t&(p0jAR-qE_0-z+x838#Xo^+adI< zuDZx+FEpaWtF$*kqfx9xVs9*B?f4g7eLoeB1-^l)`7bfR0<9Dzr~l=I3D5~H*P*Q1 zLy2*|;>hMJk)7#pQb4s_nuv#Zp*V2RGhmadlJ)Mz!ln zUTa4G76GhzSOn}PAO%NOg|2-Ka?(pPAA9CbH}j94L*LROECG+G1jrBP zBnj-0edSYOrLcAWdQkE;Jqq8M!G`T*NgQlkL#BiW8)bNq!A9U*#6T&Li~Sx?&=7`y z9jwbx8-CdEcVfunY{cj>ks{O z9zRC==>z@@TE_R&fzQ7Hs}ftEaM@{n|K^^{c<)~ID4FbG1$WehdH?l(O_(F0Y(x3H zhBroXqQ;(uV=-k*mXRwvw2Iif!CF8lTeuL)o-Ty4$qS)u`oi|a+32<7wkM*c{Y9&% z;G;UMZPsDkD+tji%3cYC;k^^iRUy(*V<%ueKKUB(Q__T=S#4m%*XIZ$zCmDTcH>tV zF$~5Y81V@|IvNFgPL}*p7!haEe6A&?SCb}G`guHA0`=s6Fy(HaZcu#EXS>0h!U=1V zkgXXlnEfW6)SsS_)hF@_3uQ$Q%vs^mO0!YMl+@wbRE1a*M#FRLf246yEvjsZH)?~W zc0JpGb7E9GPmWmyO?5Hx|8Eei**;Kpt&XPM3_g&`V6r|0ks@xs1x z&ZVP#jhmkAw;JK{J0g;c@Nf{LqAZ1h{y8;$AS%q}o{fPX=Ml zG;qZ>z&wC6s+WMj6S3&E1|e?!WuKl@u>gx{X5w>^^$=@>HM8}2&-Jtje1Q)uj`9WP zq;(AOR6UpF>3*@|P!CUt!O_p3e*oJ`n1IDRcnh*{&4bsj)v_+61WQOtw-s_*onRA%3-T)RXE)Yp z!coRxhs`AoFt}h~-Q*|Dfs%eA2fWsya7|!0zuZr*Z4zgh#bj)(o{|o0E?-ZCN>QO+J@p=#QmE*0r;XtOZwi zt+H~Q@;8??0#53mZID@5Bu_BJh305v>XPRnyZSl^^^Z41j%T87>i0}z*Ui)wER`y0 z>kT_9Q8fR|;7Lm-Q$5y^MoWN!ka4`E3*T?2DZ4k$;>zMVeyO%|zg;mwLc|UPc2Y{b z+?;7p*Xiz?>a=*lekv z^$xv^@M5yyQlzFazFdd0WCgjKkQx^{wc;&@acy`+Dp?JNa@+9;nN#=*6l1$`>wm>q zkyp+>+gvyzqr*62r#@(o()mbMMXy0r2c3)oT&nDt_sLO&E)NbAJ3YIP6ImeWq(*3r zUdq`(*|&nFL=LB^>nBINoA~7H48c`3@)X;2VjI=2ZmaNGU4>S65!REU7@zeGR@=Uf zSm8f_E07SJ9qkutk-PPL<-nV5mO~yk8-(-cigR@-o}QG9R&Xv-U|~<_dY;V$HG$V) zhOV|O`;Y7ea9pu6)WTdJ>kBnSd^9dpD|lF?0Xy5QV%2i!R+K?(T9FK4`~VE()g7iR zi^Y@fFtt8L$15GC_<9}91T+h@%uxOA&Mq>x`mxmsj}f21c+zgXLrA49j@2)+?dXx& z!-!ildW8s5G#w9sdT*7;vAhyFE}z+PAv!R2Cv5H#=uLDOs%Yq9!}MsVyl-b=vg1478$W^5$A{DFWdwva}?v%|0de= zOG9J%GEV0D6p~nOgg&Lwt^M46W{6A)M5X}(TR(xj!L#{CmFclw51q3^#Yztsx(sE3 z{oC9AzUDOA*n0c>LL9ynI`_@kJaj@q;JYM{HH&62{flXmhu_px<_WzMn@4NP1poJ+ zV#?W{d0g+s=JD&M6hHBQpDu^r)CV}zDdc+}c_PCuDMx#=!!_{Pn=l^9*2%<*^Y#2}5l-ZZxd; zEXNZJQ0dMa*_V5*I(FpP3&&o($O>{XAWS&jfrmAl3olUvni`O~Ed4YW1>z}NKZ)Ui ztDN;yKE8=6a~S(|BHCC_6^2eh zG+DFR9Lh$es4|WHwia?Rcz9JQwKl5_5JgrCcJ-OMUfS5MTf)y_lg2T&^%7BSY?lM$ zh_UUL(sZ}9-TKzCzM*DZ`4yRfLm};giYQv_UciGT8>sBSMu}&WGq}Y?r{Y*7y!neG z5j&tae`V=ZNt&Q0&KRBv$%IK1IF&OCpaQMdVBXk*53x#VrK2f+5=3%Ic)!(0t8V;M*Mh%DePk`a@61YnMbCI$a z0QG>Yo?J};a3DX<+;kv6putG{{1d^HeMT?~R_Hz>9+4ltbu<{?*g7Q>9d?XgkhL3cX6T zv7=g7kGpX^ci=HJd!PuTrU8l7hBXvzy$1vSWWHL9 zR~SxT*}@k2y|lsLpjiOS5lB4yDzUxr+t6L~1~q>$*f|wcJ8O7~V(a3;puxAH`AEQq z&sG9>ooMdvdi>C3HHR*%IkVc(2%X7TOg8F98J>)-YtYXt*sOs#8LqMgac!mT%Xs|Q z;aed-G3fWOr8MqB+C|c zvE_ljXl|@q2dy!dX20UK>y}C&Lu3pFsOiBKb}SeWun!2WVG|3c2lm3T7}@vWkgo&r zbQ}1r_Tke|JYCACL3rx@yst$YIX*ChfA=H6*;5zgWaIbToIIXCH9e<*fj6g^K~YXA z{D|{%%JKVn4%3Ipet)w#PW4MLj%l#flNk``>~jDN?oL3)FG_*SlO@NwJeTx5Pi)ul zbBs@S(+}N7u13$u1$ICI$l({b;dcj6>+5{6UBl0DVP4u~V!-`h!4J%ivey^`+8ART z<~p+n`H%hpm)e|NW^V82TTtJRMRJhppC_!G2`^=O0YKbSiIFUjBb z^@}5d-#qdIt{^=P8Xi5xu1jLfdF(yTKioBWblUU(JoR5|&)cV`gWuMF?4kH+&p|o* zEx8w2O?-CuG@QQg)7Vvhi`jAi>OHjobND@EPsPt^-zg6z=pV;=&>b`%cA$s;Df$pD zMn{(;T3AuKJ88G?J@BVFKgUAJpQTVG4rsfJZ^nz z_u%?d%HySv?b-79+{kUarA_a4emj$JV;4)*l9@(B{{qrMxa{nhz`;@1@+cW9s z^v|`J>)kW?tDUz)9^O4H-tFzz*&K86A$zKR(entwL2kGnWUUQ$SjxF&(Y1tdf{Ibm zdHLX7b>jU5^py#ul!NzB`a0vk(X03D_PqiNsQ1YB9rl0jsrWff!@7S@)jye=Ht~Kz z-GU$fb?P4PUlf1S_~m;jJ?-tSxqi89mlfnr%nxB=L!3sO-as7FJ@K;}&f@RS|F(Nb z`P0k~RmpsKQpHG2^rvy2Gj4?^e`D3JLkjh9`XGt{o^9P*Du>WMBnZGrL(z`9$Eihsrmmt(vepLp}FLS!9+)bc6M~3*90#lAY!^#WFg<-*f#&HgOEy zy?b!n?fN)8KK$G6@!{=u{G6RJ_usXL($CqoGGx!iPd1}mTF3sD^ZUuWI?R#_*SB-O z^DfOFU;`PEtdUJR)lC&s(cn!s_>1n`1ra$Tr zUuUD=UTCJfh*jja(idMWF{?I~o53q`aAmE#3!CJy`N7v|S|!SBx!bYw_#A13?)7F6 z|1I|_g!t@kLZ}e7B5X(4W(Iw$)X~d+=;qC><+Ikr*{>)h+YDY-uI67qB`KzKlt1IF zGtEUCIm!*+lgcAip>j1YFX2hyiGSGt&nRz7&o*0%a&r0J3}&`xV1FeHGd!uWf8B-+ zD~u*r|P7YTvJMJu@0?(rUg%#_Q9)N#oXaT!31$4yZNLhwG+c zY`0cp-0iqhxziX^0#eU%h3hlMoehwzO_Al!bB}AnrlTgo!ny52u4>0Ne=-#-$J-Gg zFHy#JSjv^YPDQUN$L-PWxIG%jMxc~jl#&tnju|q7LnTA)!4lMsWsvL6#`lSc+U`6n z;6()OssM}Cn~+D{J*o>5HopKjQ=7cL8sSWYO$cWrY+Hr&zz8R*EJy>a29(=`a@(*y z3*}A&>=}8c)rp1KY;RbYJ!=iO8|VKL`%K6S-%NGz@UOeaHFc~;ThkIOhv)i<9B$5z z-ZU=KF-FVE7?B)gH_;f!5A))=O}*0_U?i;vPIefmRd2c9Pj=ZpO}>WlWcl zz;0xr*sq`v;I5JZmD9qo@5F|AWFok55!GjC6bKtdv?G9eWL7}vL;zLR#Q@Dunt4YN z-V`7c-jpMnr=z8a>g4+R375Wp!lkdD7&EIF&A9;08rM{~v&S`|eG+w?#zADT3s1_| zjTTK<%C~g+Y<#)Mmy7(lfRmx|$q1&gxW@aw$`KOT~GG=g(S$ zik0f3SxvNNkF{RykJa+=fVg9N6NeD@j zdI?`(G#S-@k^)0KC4|V!*d8n#$gwW?k#O<6kmIsI)REB z162i;1{#u5P$Zp0s7QPbg{wr199KXsqRHqA3JG1cLcWMP39J%~piz>)=RGLPngmjM zX9+gXoU$MlT4hO$e?=N;)y6z$YfQN2Pv2_F?elptRPNQk+eiKd$PZX7TV34?tca?T z$nMow5C5qjef9mE9{OsA)>ZT`$5>#k5CTV6=^mMF4lI-)Vt`Q~z^c2!DyaWb_3$5| zr`)B<701H#B9F9^Lht1A&4tlL*7l~JZUl=RHQ-zbe9^7Wk*((-qRgN^vh^aSgE6o# zp3$_rSb}{TFA)BDIQ@}KpXH?20@1jNEd*zJ{NHcsg=oPOF7NY|a4v4!?t$CedvArUU`$(Ep&Y8ACWn+)HxGgE+M-j^yJIaBK$eyU+2 zXHuoNLQC{Bf5zlIM9XzF8&T>2DlIUc2c+T&Sv2J?wxJ1bXU!r+6I3z+SxAQRsacU& zDyhv$_0YMzWvoT?%>ZFOPY7l9=-JzH`@A$Kry zTQ{_0ZIRw;>|{QOnJbS$J_?s^GD)hO*?AOAL-ZwD|C0nT9RM8jwH;+5ioQz}KB8Wc>KCR+j8eLOy?~SgG)G&j-aHCUC zR%1|&wmL^eCN#XG1|>GU6UYCkh8N0Vw~MJmB51XmTh<%?H_N<2D^$t+^Y;*r6DRF} zbnU~OJ*Dd$0@z%bnl!E{bUb<_Y!<()ZQMHo6B#tpgc9^;%w)+?bVAaX=jHKxz_I-IoBQtcQJ(fWNx7i6ScxlAKAr<;9ANgI(|4y~GFTyz^{u>%% zQR~5-P1C_^`O-}5rM2!6>}{8;ky9uIZXEQ5e>3rau4}VJm1KeKdZbVD&mWOgIV6%;U4-MK zJ33!{aR!`SeiF-i$+TYaTFU#V#&y4o3anoNRk~&RmFn9>RGYXTs^Sq&3dzl6#6cT`Dd{+nG0* z-WGJt1!+g26tV5lyP=26!=oGk&{Sqz)0GA@!;iGmv+=Ch@M#&@+*q9r?U@TC@-POq zW=w2It{Dt6`T^j)H8CUkcP~t^HWTYV?p1X`71)OUYHwN$sQaS;Vg_0F+s}!qdr`FR z-tE@?j+VH(kDa9JektniPuan`>pmlxzECnu4%ZzHl%93hY0Er;RRL8$gMF4H~*2hD+| zW_J_Af->V;xP46Pikw_N=YKUBf;O7BGt2?VinX}1VtwGP$_c==q6Kf?XbLn@3iEL* z1<>;(KkKP_)@4>Txx9w~A=3t+jup_y!Be`+My|KkR6O^U@zl58A)~NE=p5(7} zMb_$ybgY%>#%TG4!O7iaMI$>4t+f@;epP5Z^+Q~p-awr|YtMwD%%sZKyvd)MRnI^P zZculu+YB47qN!uZxL4UflF*=l*PfTsY1-3MFw*Waa>06@Uchq;AQ2EL%EvjgV3eF& zII9o?{xw=M0OMsnjx31^6y>Rl3=V3KlQE{U6i)q_dKmWx^kJ7eX*9dYHJjl_%E0hP zu(MBrE}rV@I7XBK6=z5saow3;%%|OfH#Rl7PTj&2Xpm;A%Ns06_F4lBSIXf@URq!{ z%wtZ<-iTunf*Zobi2*shZ zG0`oj@!D@AqaqF3{KhWd_>D&O2v}$yduWbnUxk-GQ{gt(q^bqk$(3*o&d6QsJ}Vi> ztJ%Nf;tccx=ygo1&;_*IJnbi0+GvuP>8Mx~mSTHx6&~wzQ zuYH|_iR;EIU0!>B7TmCq@>#i?-8>oVW0J%!$usS}y&$L0YW9qL<*P}?oIyh%UvIRz z1Fu#dQ}_=Tk|oHKFL{G+WOGbXPt_V1l2?3;`>UUN)tCIdZ{#zRjg><)+|}#m4#(VT zG9k9|>nrl!Z2ok-FZioY-^ewSYWB^TR5SemSE2vC;yafFl6DJlmM$`A%8&=A3cGfex&E_1?-$_ShRIqFzo z-VXz^yJ9wv)BS}Uh`JwZ6vNNwvdr$6`#v4@0UYDivB4METv6a@{&?Wj;J(-D(|B=U zs=5^)z4mWn+k2$^S82Hhrz&;8BLkB{&(gz#lu`U1!<&wGFxqH>iQfhjza9MQGBS$M zS(zA&BpJDA4~w2<^|Wh8T9lYSQT${4L!zZ){m#ai3aGB2uSwlBhcS+d&GXQK1_AroD2uW)*Q8?jgWh7ZAoC<^p?K<8ucu)_MoF%`=b zCIO)@F!$ze7hz~|D{1vD?oN4*88w%*tNeZRka;b>p%sAcu)4;-Svk+F`nKDs{$IWh zo&qCq=t1WAX45#e8J9y0DSVm&=QU30gfJL27E=Y@!r8dCGqGsRTCW@I0BMqKpnL-y zg013R^jV!M`)>o2s^&K&V+n(eiAl?iIm=BLD>?|g2Ky`MYggAl%-+(i)(*;oSc5~r zE(k0sZ&QYFuuV;Y=Qq{SP%zNaChZ7uG=-)cv z>MM$Z=C?g_k7e$wPeaaaJ#!uO!?NgXaW$smRoq>T741SpjS0B4Q|3Q#?rsxvQD zd=J@7>tMn8gT&HPQm;iloDxHoW|CU@x45!o@LEl-0|WL~&iV|N_QDpYw8i1lB4zbg zUpwXXn1ahuuv68uaA8r8s>YTdS6@#UgQPG9vGtt+TuqX@0OSr&QJ8khjg{d#cd2hG z=n9|{n)7XgVVbr}PcY(7VLZAZEhebAMAx4{G1|YZ00ek`vk;(5EjmV%J2y)0?ep7C zc`1TKrz$5Bn$>5)Be)aw*9df~BnN>wes5Ra2B$;bEDNPM-0!$1g2by8y&wU!$p1L{ zulvT40RPdw$Xj3Ze+x6=y3{UJ$x?fv|K0x+MqxYjpQ4NJQpry7ar9^L*)&apnWukW zmQ>h~tU?D8Y9~0Fecqaw@Aq%h-`B-{ryn$!z8Z5Ire)F%g1(XFN&5db!>4SSFnZJf z0GA`8+4xE?CahyFn@^G^7X$xnr~FG};XD2HqK(=stVd#)a3CNK#0smnrWiN>Or|nk zDiUO_-w(+Ade9;>xwX69v`;F$29$(xa?I7_-2zUdpmapJaD!kS8v)BwXrh>wUY61# zE@nv?gy+kdgnsc+9FzciY%?r4qner?CQ28kTB8C(D9mHZv2HM-wv5M+6dY%KX8r69+D^oJp$0?e__#nuY z%q@I$fHF>mher_Ov6*lKb~=|eC!xeRPoT4~K`(RA^9rE{n8&K4V^U^lI0xnprca@V zizB~aEy;tc%M7GY!*3)ZN|p;&_cN<~(i>N4NxFdk{VR8h}x)j;xMPO@SemRT{KPe4ZCRQz_F>Tg_!LX#?nfNHmw!}Fes$Pz>- zs5i~gxN5RDj6mB7v5nJx!Y4_rnL301?vTX0WUD+@BJ zAL8%Ix;EwtQw|F>T!An7A;X=4=zBVvjOZ&m`em5>T~`7p3|k;^Ni{I|b*;kU65*Lr zQ@@vc{WvOgSVIK*T_@P#izigaD2GJxuZ`;OMPyF82ovvKt67y2FVO4^31b@f2Ia=9 z(0)AZKbZc|moPz=%z|PIre7fQK`B=wSdHuSa5w5S7$I@%qk5U|AotX!-=MUr#wO%S zsu;yqp)Q9V1A+=0EKwPb;iQK#eSAn zkjb%ZbP%I&H*D7WeCxiw>-)lQ^!E+1-y`~11j+G?K`_jyGX6DMHhL7Tj0bToX^b*{ z{5M+0@3}(Dcv$C&GA7K3GIr--#<>8X^GR*3fl3OLy^MNiegmgEjOs`52?}@>6mTmP z@OmSIN*KyG19!;;WwN4^VhiShODCP+l%A7NdbkiMV;lSo{U)q#d50L#$P;xdV-eAW zC3#f0_2Z~+i3V43nlkGmi*eAbfEAo1E;>uN>IkckGTfaZNoUQ^kJYcA!ola%E)TA; z0yVyZi#`bU7Qy-u{}rU!26g4-q*=6|5Vbl#U#(9?={!W5o`Db!AEi!(#&y#ZR{+@P zy+FC!ho*rBzFajHaM(>$lptNUZlyHrfTWAa&dAkE3MZ$n61qVNohv|RHF)h)i@oDd zDXtuYbt0x|pJ}QC=Tps9W_j)7U%nigY(M|}P!iV)pR|gPC*b`fh_C|xdl@bv#(-m) zgt$RB+!JqAOYthSnX1k{sYa4-RSWQr9k$kk8~8Dp{=sKx$!pJ+miz~h4nvP5tnAY* z`N}D1$#MQO$A8qLy?_j(0Y0{I#qI~`nLZy~1sYf9rSX}x{qyh-V&SC0{BeobyFf+`h*Z_$0?*f*!9ja-Zlas6qU6dA00X!( z<~sxja|{Y!;Dx2?RnHddHW(_#QSl^@^NitC0r(L+Kbs93znG}Njk_NE63{R^F|!st z{+o9ke7tDb&FzCQ6_JVxZBp|Ni|k?e5TL{3|337g13!W*E_#Z^RXu!oq8p)?@f-Md z5C@B9Rda!Q`#5O?tY+S<(5TrCH+x@Sp9j))s_!q+iLyCxBYZR|JzR2F1y7t=N)8z- z{h46+pnn-Fn8WMm>gKiEtMn138MS#PxTP^zo&r zY0>mCTVNZwTsi}TG+>H7r6LZqyF`1jY&bT{^Bq` zu>dmo5C)SkzPrzq;1s^&CII2p1$AD~0$UTj93D81KTm9UEdDAQidv+}XR)ZDV6eB?DFO#2pD zsThAuf;5iPb2N+cz@onBA+|ewE`EU;9O`x|$W7|K09M~077;*Av_9}nnJ;%n0N-TF z(?O=~Ni#2PD+TUOBM=lHcEAC}-sCw?%MY!3A_Oicf>Bo_Ak+swA6*}Z&%pwyxqUD$ zKP-qv=#VHvvFjPa{EWc|RdVo&1Eby=6-OcBlhEF(#>1SqDM*k7RtSx6l2#{~vpz!a zm~|*bo1C#tj0G}1d^afij)R^GEG^H>!w20OKR$#xkeY3U!y*gajV*LzQ|@F_<`=%z z&3Tx#MA1TH9E{nNm&CP~g2Y_u1i_r@>yeRa_R?R+w|}Wc)rY9XFM;Rw^p{g>sUeO; z&YZ?fL!y~pd{H`6h=U=^tPVh1a)s2~?_hR#;Ht4^EBr(7n(fG%{8$S^)+) zieBytGbXhfE1S{P|AOP?sKrY*fK#9xmHlTYBr1EQLb$4>V_%X=-G%$+I-n~MU4xWD zzD3PV%DItwK&9zAK1xaZjv&GQ!`g`JLsW457zSTXYW1`6>+=!*~wNOpGbFLO$*?P5t>5KAo|5rq-5dv=Hk8BasJ%I{`6=kceEGkiLY)`9=61P zz6Wu};Q%i`>^_hB zb3HD#=#M!N#L6zy9tK>FBQBpGAh_Taun1<3z&jw5kJVrQ#+RtQN1!n4;6CRaojUQR zb^v$?$w;k;PqAnH!LJ|$c;a#4PH)in#>zJTw!Bq&cx_C2X5tYQ$ARy;Fq*2m##9_q z)itv6KkPE0rv63hsr`=z_c+4tI9Su_+FUjck11a-Jr-YQ16IU5a(KJs5Uf>-=U;p> zbtO^|eGsqL{zD3lf`$Hxq>r(OU>3y8R(S1XPp}vhD4wdW0j6=vGwgkairIaxV2@rO zbvh1pV&gGZEl(ygrD2(807x`>+=h*rYx`C_>L(>F+2&R$u+@7~L?zs~v`aYd`Q4`Fq|@rawBG zK1Lpj6NYkP!dy(UNk0Iu)yG$C1`E(9a7|B(yA3`Ede_#ADQ4GbV=A*L3oUHwerTdW zUz@u6Tu3u|28;%)x&~HeO4lFEoEpT~8e_Csp^->QVJ_~|UMr^7K<~@^=y0?KLAy@{ z4E26yq;KtuzR2%Ge|7%muzH66k8NM5I1pWTPK2o(-^MJhShP`lD<919ON9eV5YTaC zTCzOCK9F95?qAWNP;nxF<#J+gYf>8Ar|!JX?%d>xneg^tQL7KTA4_J!RgtInVNQns z6BpufEfv-y6vVDKhZiK6@~^j{*|7T*M9vpt2d6>dmE@{(Pfbk06_r6_A3wW(v9)>6auYm{7iG$a#CSOmj?)^Fz?l;e7me)ZQQw}1;z5O*r5HGAydHZp(!1LWG4+Q3 z1nZhXzfv3gN=@)9HHZr*#%;59Fou_$-5|L+n0HpY&014*%lwe|3{+KFk7{;~#4A>D zZ>Nez-~iDT+rxLNgYO$NwNuFmc##M5%NK{pz`|8fZm1CEXD4rgTPqji7FE9mE z&C@gy7Ds??K^_48jRTZJYXbeu9R@n5U!Z?+fF2bAik<)6#!7dn#ywl(%4sQry)yzf zt6#8F9I&sS6(+#c2-s^iSiPHQn>slHwplz~+atkOK23s!5wKq)kH+>o4VIoHVylY6 zM7XS9uzz;I=0?EQMZnJ0U@=q_u(wCR4(}JN+X4H^j4%-_i7^C5CQuI@@OL5purAtu>IhaFcB{97wjJ#u!lv!E{%Y_QG?yf0sGqs*kS#G zEpWiXNu;OY0)-K<^EBAK9k9noz&`P9e~lqDRTH5y0`^nn(L|W9!47r6zB4IIgwlS& z{>}k=Xawxj>1}ihfkc7Ml*vELg@^&@C zhurJ{wOAej`w{XOD`zMYV|B^jH+ECFt@6MBQS3lm~O1< zS&&>*Q#PQe=E{M_O5g07X@d}4TQe;M!F4s$1|yhTGc6Ut^)=Jd5Zq8RZ3u!JYo_gm zU|!9%y%Ef>nKl%`f|_aR2o}~%8&-3kQ8U@Kc5qVY92OPXUlY?wilzN`KkI`_?hvSX zXKDu|_en%md8~gSwv&y`4_$t8UzuaaW82B!eMWDEM?78_Z!FrF&u%v|7bKgop#ssX zV_8AU-~S_BFK2ShLs6Zzf&1{AtTqiv#ciqeh?_Q?4AZa_7`?bXEz7hse0Gu#7HZJf z-t9t<)Nb09a2wEG;aofRzhN&rLKnBGahOnPR&7nLJRYN)VjQzF6TM}rY@Xxx^9<}9 z%kWXR6|1AGa1lH%w_icS15>g~S-Pxh-lX3D!UIfrC`-VP*8sm9b_QmaN?Nf6z8w2j zOHmw6oMQFyV(g#VtZM=Q!Fps+8GBJg+3k`Zvizt3FJwK49zO|J)w<(kx-pji&iE?K z^#+1_Yz!w&W!T8(K+A<3$TTe19)&95%qK75!sXY~g9WA=0o=tTQH7QVlc{NvO$ zPSQ|s2ciSO9^*w_ABQ}f(o(~`cZ#79ajDFrjRo-n#QJLi60^q0>f0z~nO7ob`{UyboWj@+fl zQL63%D6?fP7>X@qtPTZ|l@=t@MfOG|jHklui?l$^9hW$N9rxYD9yfx$;BXNr zktD0OUJ6VZrv*xuM8)uri}13Y?OG~p`|u%9gL?!0x)vw_o#6k8!_VI+wHJXZRyLZb z49TV5OPux-i%*zLe;G`H5?$o*63qq}g3LSOpmjE;yn z`N6AraZ1ofGXSTDK3aqd?T9`af6NZ)qfw)Oq(0hk^mgGxd|GmP0sZ6jk$2IK>Z1z> z^{$VgjuJ+xG#{e<*_$tk{A5EHH_xb+?zE*4@_)nIerw6wxiO&|&#cxv>ySfHngzd9g7jm^l)0FLrX~6|T~wlV)|-G1;Ol^CJri zob%th;ycuGJUSAL?X1l~Z0V$ouTCRl&rYM8RQCEy$2b@xcGErq@+Vcqr2UCmeB-Qy z)5i_CoDKMadVs)su_S=QIb*ux3os*=e6>kX+b)4V6ljbrf{Zn&4A|@pr-ELKL#&%0np$oD2jXpBU$3sM1Z}c zg9F6mxH2AFhOH*K?t1KY!bu7!IYSni!VsW2hn1Zs1DE?{d^ihHd!-bXi?*DLcAX2A zjMLp%(imO;F}nU^bp6LThazbbjnM?l4br;qcx z23axnQHu5%>fDjh1?KDG+rv@MN83Xo;!b-oSQ+?LZylT19;k^5q(Qxe9UwVK2g*** z$zkf^lYa(B;%QO0VF%9m@aE*Iqn^Mv6YN0E<<45{2cKxofa7#}&d;&8 zekgWvV}T}hGbZQG(@jv?U)9E$VHa?W9jlUF!UPOFo1?K~lr!)c0-yBP(L@1jC$OxG zY~!CFP&>{txz9f6Td(e4IgE0*9^6uJD8kw2>cCJ(7PsjXHzNhUuE$8pF4-t3mV8JI z0fO=E#hD)Stl?nD96^l|i|D@Sjl z>rDs2XrHn5xaP~y+e@Lt+SKCilo+Aq=Koo1 zc-GzqY;si>kC5)I04ZdBV}yw4v(RR-Od+Xacodk}ed7Qy%mfV%jF9=RouU6ZYj%MC zAO8T{>9mNa|7{IBM2+6*|Nb*l@XpcykR2w(0q}wmD);Q-XZcsM?l+c&n1li!hgP2QN*;(G8lb^ya8UhO8JTD@g zl@Q8ymG+3Rp&gT>cFEV)3^s#}=zbHLLC2{OvDza}eoa!4Zrd0eMljF%oyHu^ zN^u+BmN%~|!PpiGWt;Mj6oq>p5_YMDzehq$?PB9n%@cy<8lH172V;XmODCfuf(z%3yhh`}zSF0p2i?5u1Dp|`8sAKD=Tb{t;#G;_(uoHwz(S7GrR&|2Vd zb3vG!g7VngiE=0CV+wt#CEVk1_7Zj-Z&SZKh{s~})X!0+OMOuwkltT1tfzX2)ep17 z5}HWgRmcgg*d9aQ%Sm6JCz1hM6ozazDg->Y;WTBQz;tJsh_t5q5VEIts<$lcLG^RC z?Htt)dhkb5{ReN~R9|^pnCi28p?d29LiO17`4Rn`PYD-#UkwLvOewFrN{o_vZ6s=>0!rSMT&bYe5fscXaI>y&w4dkEHjlQ~Rd(T|(`Qmsjz!7(f&jKHT5^RU?C9HngZ)!15x zt@rBQU&pXuKan4;Xx@{S5m7Y%p4Wo~v$r@bcoIpW?fX+Sx%dA_7VJHxZx*ciZBG`! zt@hzRIKV|s;rv*%^MVIX8x1W#qlnWVq9i17fFR$holxzgB(Y@az5^ z86FEcE{h(Dd~}%TG>7T_lO3A4C}QCi{C>3fBYJ^%{Evax@Vh?YeY9)`@rz1sfhxW? zCkdNv#S!yY0Wc3vQTtrf4F}s?=M7$$=lLg>7YQYXN&n!fT3cXtzff_u$DXD<_KeQ8 z$*AM26?@>*B2Oeja}c)gRi03SQPVfQuJ{xG8__G5p2*PBp5A<@p+rYgGgtY+ZpwQJTUh^{892^!AlF{S#wjuOY<0%32M-Tfq4jv4k1kX#dBJg$JC<&Aelk8iYObnZj87Z3YIlVGX$S8J{Vxpk zN&icJ6QTcaXYMThpTDPf`rjk`?w|g9?Y|@RpAKl${yRf|*Yoi--5&i1CGaXr|CpsM zi#9G7nSLQ8H15CpTlGCuG!k64y z*xhcd98iKE9nlgb$8 zlc2(9eM~d*%g|;lV8n99NhePnP;rRYo-zxEyEYoGNd+jE^L8@lQDi(Zr7RW~*RPH- zGS0^T(}5R}!Xl-@lOvNB8HELS4UcU}qiLN!tETYcvLee<7Gn88epu)Slo}>jYN(S> zu1RSqGK>56EY!3RJEuKaNPJ}I6?{g-2({|S&=Aj34`)9(^k~e}L7{tNo>D@!F;Dx1 zZjN~x9J)T{X|K@bF;9a+vtph`g-(lk+BZ~)C)y0wOBg=>a5bi|WS#3tf zIeA9LnIP{}kar*k>n@)E8edwjE)5&6=#i1xva`~pxM7jvd{D84PH}}!abQo6;?72K z5pK$b71PqzPQG|-=nTLG`S%Zb5D8N~Ei^&D&I%o?UlWDLFubk8VYh*I5edDIpK$si zp}k{rXNJDVHvml!eI1i%@6g9FPa{L?W1bERJcXh7U{)NZ-kJr8s3+YmFu8QOAQynp! z<&`HMy9B)iSbL8wMI>uEv%b@`_F2F5S!HP+xEnq2=wJGcZ-<2F$B2+_T*!|{Ze^!N%IT;qvL_`M*;57;S-#=8 z7zs@~$Xor`Tt&B3F% zI}+zJXr)EZV}#E`<^cspWVcM3a(x=(po25f%`WxAkm$T(6z7EXM7`&a z$0tkqL%W7gBD}m_;e~rX9qRoL&izG8Aipu|BsuR&=cE{e?gHr|WU|<_Q{H~043eGx ze+cZ%Pw-o{ZD7R_X0Ub(7=tAk*-&@6F#qz_22ueAThP;=WR_q_wwiejj#;o%?#FZF ztFIV&+sV{VKN}3l?wOn2#W)2U8mSZtupF0(`0+J%M+LlX(4;7Aa$3jfn3a|pW4ZCn z2M=aRv1&ga+am1w>#OFsC0Cv;D%FMQXcsQ#irJXqfPr5h6OlmzQ4i!|oMmJ6;5`xv zw)5qkjKj2C^eoAwB~;uv;aruHSSq2bh1&Xxu?nVuF0kS5P25sb3^aI$R~y3FoYKb< zzm&=|aa&J0prUoOVamK6AF|QQ=6RaG7m|VU?kLTdf7m$rrKpLLQWh5JqrUuL9Ml{DoK(3ZRZ*}=FNppLd zrU3f7nXI)Ds6cK^pR~f#500>Mw$(zgbbGE7j59g5_Th%@ia;9beddxhx!&R+F zUcg_4Nt-rx&Bg%~8(0@U2l~x`j+Eu;6ewLW;H0I-J z!%JSl$|fxm=9mq};v~TEV0XSPmkFB5Z*t`eYCJtB+X08FHmUnB4%c(OgUN%0s0++i zZ(bh$97eQ57nr3+us}GY0}rEDG?Tx@nidgi8&~P{>ChC+(K>jh~|#)dFPGEK5#6BxXELd#oqDV;W%5y%3S^w+2TPRy_A3MoZ*;FLswP zwhBqDZIu_ixIfBz2|X%Tr*D1+=Tyx@WUbqkyw+WL3{3b7E{-$dx4tXgrcphYAZ>jU zjO{DOk&sY(P<5 z+T2LMGhyPCtcgo4Xlo%@9et6%hv1YT6uRO-Ytmj5)D2C<=+zZ@rS!4w?5sY{R3C$P zv9@<*)>@U*=wh(4PotN?%05jU9c*R)Iz8sCvvIt6Hf}rCQtcp*&u;Ete+xh&FB0G# zZ3By9?M;D&?D_1i?M<^`5&NRnsO1Ou>{403X}gOLwTCHKBQDKf{^`c4=O9RF z4g=lUAMD!yC) z^kYa5@#=`tTU!5@v=9vYPrmt@yEhzkDf2w9fBpsjjibun;$Jwb{9*sX|Bbvq^#+9; zZe45Zxm}-@GW2SLjKAKVE(>3@V`f%w`U3ynb+7gI{3k_zeeX+4Y|Eg+5~8BQ>9I$r z#~#v#fhUudC02n16YaE5_oiUn-qEUxw=E+PVr?OSSZhxwU%ShfqaYr&HeyljMsU9N znZ0F;{hpt@mlJZ{+f(79F8-s5J|DsG96z4-S}i-0jbl(b2!Xx4XNV^bp$30PoJmVM zE#qwjwDqymCKSor-K6nkT+QJ%I~9cX*nfD-l6OV?tGT+E5-Z~~t1(fW>x5UmgTlop z>tyhj*6;QlxNhOL>fR>1?vj3W&kpLA%>;FaEinJ0@w`VC2SfU}L|HHI4&sR`_+A~= zTf+3a@QrzMppk-30&B^O9?CC%@y{d=1UssOjHL~`qe@&YQ8PYCpuX-C25PkTztoph zI*$vi$yZgeK_Ol6L>La>SyH4NPUh{Z@}k{D1YS5vm zv~UcvndC&i_B$gI$+#C4wp$5o7fnVDH@DDmXx!;D5LMyUT!bGtwJ3SQ053u{vu)yT zZYtCAI4d%N zymh*Ye2;`3U*YJWO+8C|mPr~JWTGu}kFm2xj98ezG(T~z>80}yJDt@?T-T{H*Q~YW zAsBS1;rrwQ{lY(!V2D*+##>Vr>(*3I4!1y&kX|d|r77F$!OF)edwLIV4^H7Fp~$T> zyf;;mVEJ3Lgco{7$E7Nr^Z%}eHNBV4~IKR2k_@hzrmJottVWG0*Nv@3}Wpi#M+ z^55HCV;i8aI*A z&l<2Jh}eC4Hq3cLM*j@scC!9Cp|5XUL-mhtb*o#Cj??&5UP_kiz6@2quFbF$+l>HF z^;VJAivAV3HKc#Kv=bv7Z(W;JZSb_E<72cZBY{3$?wc_3`V9uKt)y&mM_BCXP5-xq z{Tu3|&*(S*fW;{JJW3BX{Gs5REPt?-ck4{wvGws+RjgGi?b)J*sc>45C$#zU+|Agv zIYl}3ZA+M?-1*;>{^R5CO60g`!%LyMTQum{s;cCx9h4Hwa5>BBt-WSoa~3OetGR*uCZ zXOsHYICcm5{hs{%yuGU?KN+gW%NG@OR&DR|$Z661$6}Ny-|%kFqk2T}44r^A;HNAH z({h&JYAWcNm|lOXV(qH`oyy*J9z-0AQ|4qF(CNbP=hK`3=4M==ensvRN&`U+ z&aVi|cr0+{BHzoQ#aHVLF*CpBiu^)2zp#J))3WmW^FFpS@1Ype2}M0Sk39CUKpqW+ zk9{K)K8|yilR}~Jshi#b(C-c|&pLiKsX z((|1Gz$bScF#vy0O$Na7*jIsEC~gxSVo)Lyz@WuYt88n&$PIX)c83gLRVfGWU> z21}ocYp0GU z*KEs(@X`X)&~nQvtb2`0lLDjXQ@pl|NuL!px(k?!YDpK5=O0=#IAP|l zM!4ZWyP9q$R}ywL#v$3E*YIP;4oCpr{B1rj_^3EEn^H- zv4{PR|L}w8f=)cd4}P^eaTy7JWVy|`TVkL8-&ZPgQnyl{Ad)!Ul~@(0#GxuN-6cGt zgxN}vhL<{@R6-R_s1g!wNm!?Z4{#%t__#~hpo9Ze#Ll?9g59nr;d$lQNaMk!5{GyG ziE8{Ml1o8v^jClJ%arm2Db)R_2JlHWcAra#D`96Pn6}Q#yPUek`W%w`t8i44_@T>t zf|@;3%`SEc`zzrGB=pP!@HxZ`YW_78>Zy)+dDE>jp4DpiPXUZQRdnHcrFnUeDEFtS z$FKP(zkHfso*>D;oN@L{Aj9t&wX5!ERX5IEm<%xGyqC3kPxGgN_7BC${d)?d`V!Ut zs|K=O|>cm+SdDz!u0 z$0~J(cmwYcuews1`|c3YsrjnX43QH5pcc4Fw0HhD^!Q$b>{KJ344kHKL6Sc!Z>LiuYsPkx6DqIm#Q1b!XV^Ul z&E=Zj+8<{W`gf@ns-`&#?y7=MGE%y|@JaTdQ=Uv0L{)f}_;jl(=3kwi61aW^W2$9i zLp@n9miWKC-%X<&nmB`&45s?)zdH?G7Jj))`ry_#IV>|^4OsDULB)owscWU#_>nY) zUHodNZSilNP)FB86IYZd%43xT>0?{F1NlT~kG2F zUQ9ofqONn!7_H;mx?aNv&{&YH^2)~~y+>y_XiVZ)pixGQLd7n|Q@DzacQJ!h*c13T z*$i9ITx40A^Oa>>(?-Y}Ur@dU(X|BXMm3w@sWp6E)2$gF=arFL(RD65gJ@;^nj8a& zS8Y{A+bP@_?cy)W#UFiA;}AomKkq z-Lv?V{J-HJ>}5iAHz4CiT6qIQ|1PVX{7~r1I7y~=xPz>f@k?jG%jT7z>y9m1?5D>b z^mn*7&j~cf(Y~e=-qfqc7V-Dwu_gRHZS2u=%3~*wJt0!xJ}c7u@cg@aJ_S4@ zAjvw0J3T&U2GLnWHxjKVeXZx?<@W+}0aGc#w%6qj{lOE$BdiFexp-r^3=RqJ*(eE> zk-s%V9sdefZU1TzU98o` z<`M%PMc^E!FSVd)nFY_sknoxp^5IARcejf>^D zShb6(4xlBfgave4(7aB;xaJwkGOnr20!Rg!)b%N%1R6Po zK9#rXl`UBSaH3vGLeC;vs-f=CxGNf&abndLGgEgV1RGsU-J@ZBh7D5yp=J?lcg-92 zAz9Y$^wss`c#IK(F3Q9@wc8GW<=^lVjD?HcGP?LdEv{K2BW6VYEzg+%pX4V4$&cKD zH!HNzL+H(C9 z^7-@DY_a|c2mL#jYN`jCkCd#XkF4{V`d3-<&zn$#)Eykl$g5SCg(d{$*#$3P)D7Usw}~J;XuDv$zAix-Y06mUdio$F1(*Sq3@0NtEgW;+Lia+ zE91LP=yT`&83{S#2r9FPKjRJ2yb&sob{teliJ7suZj%!Mo~ z^mbH z@6e2}^TG!K6EZWW&xEYC-)EW=g5-QmPfo`@{Md7Sgg0H;v~}xDkg!v{(=?M@V+?k2 zWLf9mcHsW(ELj(EgdY8&v|u;#M+ZH-Rd)O)o z9BEA*qozbvdWrY|HFX4kGInA6Jcafpl#vi7!CfyLR{tWe08|Z105i^$^$l*E`If_( zQxs<4%$I`nV+aS(Gz@b-u03Im;n1Ngor!QCO~r`H3;c&p-2X$g-|64JVxaaj8DbVc zT>Ppw3mNCtVWLbzZ)NNW-Z#pz-Sa%y)TgmBy8S%grr zJRpPu$x&WG-uE=)c4oZ`hz*t?fln(gUo&kI(VWUP+hK4OmUsrT}s(eRFVp^kse7`J})CbptuC&bpw z=~$DGbpA%w#MeWY7t&@W-a***c?!TW_9w+mfS?qbgo%MD%eJ&VnL zhE7eHa8RscPDcm&kAKjym#QYe9J*YN0FbWN(m*8srTyp^2i?IQu!Y3hed*zm=^no1 zKXZG~!}|bHRqWqA+qJ6ms9xPI!z%B>Lx5JNYSdx!TkNU)TXQ)&n*1^nbGN0p`2?W` z9R^O%{TExvFP1g76;MyvrYFkkdbbVL_wp0RR0`bp-$b#jg(=&S2g(q?&~4i+1PGgM#HuHGpISA zNrC-6KPNe;unnqhIk|emHZYVTis>Kc!3L=MRbx27Zy}{J^eApz#wG{4ks0&ba}c4~ z;iy2YLZ6-PlSLw`=1C3n+3h~LR~}e}ax0CSvZ3%84n)HdUhE2uL*Iq2E9q^S2TAcR z$-T#on6}Tj%8;;e_N*z5=YOrNanUKOqan=%?4Ua0&Gh|K+urMCe<6fr$(g&pqMD2M zR|-n@GJI2)euzaiBtLWyIPNCU|HEt3kSEx2N8@B>r0vQ$=H_k!!};h=_F_`rexJ^w z#CvWv%$WbB495A~qo~gB1^Ns^?Ll1mkf7=A_U5nfzcj0s`L32w!wuHcg2TTv8hY-T zpVXPR)0$|ew0D|X`A&8#p=2vnu9YVs{NcGq`D(ZZe?mEP;3oy3;hJgCDZ|QUBR5_Y zu#v;SVj6$HBx{I2yd@x^9M|{`=^=j1DjSMLRRneSWo(H%96PxtyRH3$wxUl{b6|%I z8LlI?I=VCXlAAGG6aU0)?M$cW^VIBm{`Onf=cvq2hO6hLN2rI>JJqx4u&l9P=jxds z8Lp0I)p5D2M_P7M0e@Go&MuV9z?BbmHGSpK^;!CYMgBd5Q|XSemQ-vo2j;@bLE z?MT`h7E$Q+LK?=RM9fyJPi5?cA`?iSAtCWk{zSZ}SRG7Z8B0^aCOyG~u<7NUX?;Ku zj4GcDi2N0>EfpYV41t49hSD8Fz@j){2!Htn{&!a9op&fRGM?Y>Rw;jMFv6(JgdH;I zM}N`%ZoGS96&!XXSeOtM{n(S%1^jDRBbpQ0Dm3}}5wI8a4 z-TG(TjdYoemw~aV`^o*!I|HO$_|E}%F~gsi>OLLY`4A*H*U^c;@RxZ>(Dyrm9lk&P z>pS>E)n^_w{GYutE$9=HP@hxbq2Lo**(}*?{i@tr+!`8d$h3VohTyIf_|coh1MJZa z#Ax9Xv2F#!+_OZSQ#_ z7-gUR^CV$G+TMFPGok&n9riaZGkig}*jz$3mL_9&@tcdR(DbgV*aQAwYw$OTj!AxT zq=XMeW0Wd=SoSk(UqVZvJ?cYNQMAQ^4u3W2ZdZy`U2LuYm$#+OaJ!nMN|Jk@Wx4@6 ziF5V|B~`KWCKSsbOc7!TPZYoBpFBCxZ_W)ZK0*D72i2QS9f8lC_`&3X%@uXT`7A?a z#A2a6ksOlX6USX(>}DNUXMTLb_&@9KheW=768BICt#|p8|ESxXUAeXU!)Mm&CTBMg zpgbO{C)z?3pP7##HTmN+cj`Hnh4$!AWi7PN{oEGXP(xr`bii4uW$_U86x;WS(K^1? zn87O_8P^>V-IU+$P?FajkI%R3j$npP7*v#@6*~i5uqF1Dqf!8NTE_9+z}#*<3I#F+ zgT(*-uIZ>)fDi~^%&Slrjh^@|b#jWPpkUJ(GDRvl;7kP-UCvYQ4_3kaGxc3X_m529 z6-n6}c`fwZUq}b~-w( zE>&_hQ2>ZH&(d%4lG*`6XDY3kj{Z5YG;er0)sD1}R z8!c$kQKVzkUnyoBx-4Rl#B?^%shThYrZ5`-@egv$RpNj!`*I@H=TNj;xjoN1Gn(!f z-gj4&*IdD|ZR*4hYtg@eb$qS->-J zrGRIk%z$&U>%97w*`Muc+`r^%`cuajn+w4PpwtWsl%uc-2qrQV{f%;O4p8o#lK~Vo z89Dwt@k%x9CNruNJQyqsa5Tq#7V)``UXdDRLS;rxj3Ss?Ye{oL6Xm`?Rw{@d+*cmO;xCTVziFWscSuIi)N2EgM#0J@ydqz-b5!=gK4V%FW zzlXtlUN`wA?i9@3z=kZUXtD)+EYc3}xxZ0Os*%jZiW;}#IS=35>Bn!(^lE#C4v<4q zMY__SxCxdw@O?dJ|28ST*4V^^pHENBSiZIHq+e8+vn0Jf3|zizZMyt}e2=#Ll8o{V zn^As7M)@iI%4fX?!O+jcLrLoFSnm_)FxZZdD{5GTHgaM(7fG(#zxii*IrxbBJhDsu zo>{WXCw?5rF3)QUy@KDKZL$k?$4~Ng_hFze9Kk~-tQ&u3>m1ZPVDxoMNVUiLu1-}DG&~|@{ zdX(Jnr|nNmcwcIx9@J(E{O>Enf+<8A>pkP>NNN@(G~*9JlN%d z)pg%3&Z*S})%HEIrgGM`!JOPJvBWEQ?iGWzE~x5(+djY>9MTalug!{1ElNyLRk7z* zAA`f6_$m2r1N7X}j7^7OH=BIaa6h$c@wA}BzmK0Y_d!qnYZ_*J3nzr9$IqTHUaROe zy!I*s!AKmnE?c4*S_~qUtqf8BE~{ddD3jMn{COa{3D`vll4N_|j1W8g(+&;xNi#x= zuTZ=3p!w83i4^rNX@1cAXZma#1Hm?HWAKW8!qtQSxm&dZ%IDm2jo=@jP6ual1huFL z!qfSD39P$j3Z6=_wX36YKBsU5q;?XoTM@=&G(I^Ltzs3#x_P_EhI7Y<>MRzYJ|hyd z%f@oGIYFe;!^zu5sejNAMf@$eF1rRLVEsIV{s7dJSCN9+Qf+~v1 zVCUkK1#dIhNo`oOn)CAN$zm%u*YVQ3RQE`Syd-I(issgKsdZG0)G#GB^{C4DP7^BQ zwG;CFi=Q+^^e-T^3tY@YCOWPP$SQ*o{cbsoXc`nf=k()p{0m!&O(r%=v9lCAh@;QB z99#)>uAC;^|BlLKGO#%$46d)7m6Rc%H2;HZ-Tu%F@zXYSL)qu0%DYSSIkmebmHN}? z2J`Aa`27s~{&clV()=`Ogl~_Fn!R6gl8@?ga!9`}W%1*I%4czccb{+*@EW1Sn(XS5 z`lR9Zzn{n#(u{QfyKuPu@9+EWe}`*-#~nw}{@%m2|345TwfEP@7T*7VZ@BUIYd_N! z_~KV1;-zVQUOP17bO82_fa#TTNJCU#2jUZS6QBaHAf*D(jt5a;?rf-BIKKM}?Emp! zrl;ML%ZJWSW})-K5O(H*f%g1gQ&-~6RWub%C23N~H_zee~(i{tFfc-t1ZRi)Q4`C)G0nAbxBKd9@i0>>juXUc`RAPAL=Z zrX)kHwz>+RLjYoee%EZP=m259>X+Wrvj-gs(8yEK;Lw=AOF zSNURrQ@a}nZa49K(C+lj8t;Mq+AXHtp6{^ysZpWB0E4X!8Vxy>+U5ZIj#K&$_;(9! z6RkO8UpGU6+eDYS&mvMw+$Z8edK2vx&Yun{#yug4Kk*7#Qps_;usv%@Kny`zxH+F} z^6PMId)1MjIuYZM(6t@;9U;5>QBnFz;#~o{l|TAdml}QEZa*^RMhnFeCYKRlb3$9@ zS`svbaXU2J4a7mz0snQzYdRV}SXqrLR9*F0UDP6n6@Hfw|ErIPxN7HD#a{9c;G1PdnWaL9RCv%y8ucQE z)Xp!0Dt=02b^q9#PO&hb(+52V`lil3)+XNTOdtS>j8IXe-8_7`%=2T z3%5Xh=V#Qn&lad}uZ;Shy!^wD_p!?Kcoz>?U)Ji$`l)T<;#cQt5lF9F-5^DJ-GUZ7 zNzgx*Dt)?h2x+U84xB%w?W5q;g=)@ma^_cZ3hy^Zco4XWqY{feG|pQyj-1y_kLMyXL?zAt5kg2T^4;u z3TmfdHwCw2$rEjDWcjR7d8bUSv#L0D%s6KZ24WX%W#JIXg+7CP|-RupXGV;IlrlB#mN1fvUvD=^+CNK>EfC8O;&mo7-ji4B z2I=2&kw$3py-vkwANtZ!SiU-Em|}%V)eR3lR?QD7I;-x0&ae|A%|F?`Z90qzJ!9y6 z96pAwhWp&3m^)gCcr#!^j?rD_f)`- zw{x}UJ_$sLzw!r@f)z!iWB%t*IXa10z`)|>cq=R%Y|H;v6w`W$&#p!d3oJWkuwD&gB{dxFalL0>RxCzJjq zN78ZsG&5D$fM@1S`Q`V4iaF)F9uEM&&}c%u%@;>`n!+UayozFamFIH8a=~&pJhNZV zS+|y{b4e_1H@_>`uv3cbu>8f&p+&(s2OT{kP>dBvF|RQd%ZkH+W}IhW8P{YW8P{y! zD3;}ifg@f~5;JIE8W*NErcm>VF@>9fn9V?nXw9fHDFJ+Wx>g+HQ~+0a#JanjPeZVY z743W)e)Bq0E0r=Bd#wPy*K)erPFK77t}po82Y(N^3hcqTCWZWO-v`3S7?I&Z&%X;teY%c_Zr(lS8ayeIp>GV&Hn{mvH&d{`O)X=ZZbj z@t-l19|+aUHj>W#xpz*cmHD@l6@JH6_-F0#T!jZySI^lLX~`}!DOqG+SLEGvkwaBP z@2Q-hT_i7AB*zuGK3!xYMTUlKYni%)LOlD_4kK2b-0|R1;l)Rx!7iOIr2|&@LE;%o z+|wm)B-57_--&oyMvTN~a{hta6rF?0IyUx|@q}{;i(;1+$EF?}JHBM~)X%DeT7G&S z%wQ*HT#uUQq>O|aN;oDX;bK6-{{E-3&j#m8k>M3CJ`%71 z1b``Ay4HzoJ2Fx0j( z;*z9Gl#U}D0!h--@hGeF$b;Q^WXb=sP_*+q0lnx!IOt_sX9`6}dmed+73DnAM0=e_ z9-MI=Ijt2YZI|b_dlL~xWDzPhDq*KA;&GBp@ZLr}84<)Z-5Ffw`D^>|kWLcd zA${eC;64GDf^9T*BnCVePN?DUT@&h*8UXR*G{kD}68RvHuE>xhvp?+M>37n8A*nAM zx_;Y(CePCEyvqje-$EEFcCCeq2Wc$WpAUDQbqh9nBR)?%{)}PHlMcCVwC7nrEf_`n zk5>=V{%uX8ZGTkuw8szE{vrL^&s60uesz`!O1C=Xak{`hnh~`AGE>894Sp{i)4zyE zG^Hl7Ljyj@F*$!~fbH?r81L>V2%a-70_H-QYE>*u$@uN-^O0dXciIF+omRW z{$&v_*d|NW=$zGNY8wYWV7vGW8Rr{zl*qRCt~2e3L$$*l+ZDK@pXKheh}0JM$(n;P z?49%pF1D65`dmG5pD)D}KiS!@!JZu$lWISeMaN*g{ToC*FB(J6H%C?i-MMdZ@u zL;>BH5^u9hSzLb}xEmWd4%!mkSaVE9Hzbd#8{a*HZd^fRO8n-a8#M!WqbA*rQ1q87 zzWER-&+H0E=+>1tsx^r^d_e`3aY)l&jfVvFK&iwxr{K8wo5>?P5mXkBof5yzjcWD+ zDRHjFXU$$em-&S|vU^cYuQ3G`UpWa~FCFN7>D2G^JzvT?x(oDC4%27-OoDVkXtrph z4VcSBgM(W4i7jw3x2p9y#uE-pJpFveWNC@_yS_e396wCQc#eSg{9Qo6ALGMcdV4^? zYASs;KAU9pQFTg9!fGD*4OX>dOJC@j7*zMz?Ll=9^5Ito)qRRl38e~&uZ})JjOb|- zCIO6A`w5%lDY|MiCAezS&gr1I2d9G-XksoK=yY(0PEH3u30FkJDTbDPK)4a*P4a-S z=qL^dj~wKH@F(*}K7l{}$(ESFZycR5fuJSw-szCqR|e_ed$l9);QH&gL-kKa`!$Gjr&$SGY6ZsN53AftZ8ISM{%^ z?#I-(32ms2qJKWb$XyBkm9DmSm!t;$T9ULc&Avu6XGwqgDx-d{a8X7Tr2|(n(0(v; z5l`1w*Jdbh-JOt8@!E3+?AsvnWx75_{o=uCCijDZs~LI!sL#u&uY3#C_l1o5-aLE2 zVUDVQuN{;g?~Mc2w{^~UJr-!>l{yhYY~@FBzI)QgKce&9&qub*`R-poBW{s-zFYP+ zmWIq@sV#TD8=bcq=ezB%kLY~&p+9QZlspQTR$kU6FCYGqpYOI@w4EfFwc=m?uhgm0 zo$pqEadhXq4YZNg-DUFMfzN|a&dR^_&UdRP8}Vr-{GIOnD9(4E-R&bf-))<+)y{XT zN^JEO%SxZ`-ZrxjNxJh{8E$;U=ew(p-4f@!V?Ntwkp7=e)(AKIe7Ei<=qxzjt+np~ z&Ud%__EtUL9nXV^Y(0hVgPrd_G#%D3;Q8(cm-j#4z2z7G56^d3{VPjBc z?zHo4VG*GG@3+{(GLrM%uFD{;%{!VM_QOv8=IvK{nNRj`=N6foO zQEu%)@+5*LP>LMPDCJ3nFV0Fbm5sK$>)A$c4g}srs!v~)E#ME>-`&;UUwnB4{k;v8 z&Z@AF{}oS4q@@3q8FQ2VSFl13{H6SNdD34>+ONvoA1Bj_Q~9Q}CezY>Rf>KUq-_De zD#<|%dmrQj^im_%-(OQXjQ;$5t}Y~-ws3&?h&{s5&S%kT`j;J-nv(4MNgzoP{v}HM zz53Ost+d3?#jj41HaW1-<*knvxEGqaB?L|-4vyvBwzrlN>B1{!>FjcK^csH8_1`^l zTg-N3gTt#HE&M>q8e;m#zOG0=cug(D{8PAWf8Y~vVw>8k=XBtaWx zuGVhKC0lgYW)f=ees`*OcpHme=?jSla^cr?0lyHq@pvsHGAzaXk2P&8eYOt!Xlc0x z=2f%4P1$nim(WN%UudkQMq^m(EHDKnB9W^0&-e*fL}MEgH}F$3Z8~Xpa>^U{)tG_) ze12IZwg`JPd!4$mSz`~N?Ekdi&_fN^JEcnY?U96&-F`#T;R6}|D zvDjI4j;r9VkzHks;E&Kh{*i5SO4mCi(+GuN{v}^l7i2NjLX>koA+B`JW-k?Mc5@V% zdpRR%FF4rcxkfMVYumH#<3ly#Rg@8ykaWb4K2Q>d{dpfqaTJTTFeq?X(Q_u`LB zvT_!-2cE$?0Oi*k)#ldj!`SmRmjRB)oC6TA@bo>LM_^%VXghQXAWQUE%#5W6SEM6w33*q zP9u5vL>9WpP>!^0ttoe~hgQI9L1E zJ}D_~(OPqq2WVxX9{AYHx{`5e5?YBlV0Xm(YB=bZoTdCWprr1gx8&bi>6 zb1ylM*xE?z`QJYKf-^4q_SwX`Bdzn!slDXPv(Kp|=9~4TCZmGYxFT%5iJ5Fgr>gy% zj@V8UKUWUS=TD(Pb+WJSg%AJC)8B+uUp77`)L?73_to(%z%6c2)&5x-<=tx+45}}o zO`O@z3@&E|Tj)cVNd|NqNv8!#2di&>A16cs6ys)srK$P>T9@Cuw=@n|lki*??K%qy z;i=)*+`jmwhbJV$y7y1lR8&(Yr)TT{W4mJZn`pGA=TvsQG^uLG>9V@(rXjzols7~w zx+-%!;RS0W9UTRc+y|;kalX?Yj+Ay)cC;5oav!P8ZRe+PkjCtyn28_wne{F;1mjNlK~kht5yWg1Xh8%9!=Dvb3|Zw7rr}*Kgbu zS<^23tPCB~9uTBJBZ;W!`|u^x;|r$;wH;&O8m|yr0-#sV8 zkY3^gr;m3NAZoXR++5rXli%NcXFXQX)=AR(sD8+;-4A!6!hab!u2t4}oA07+>j)F$ zwGEDcQcKU3o~GpaQeGL>7iQfPb?<~<<`IwQ{r)xU48)R8ALo|>)ayt}m}y9?S&2b^ z{2MqjP4?jIphrEER!8?CkIuSJryN8096_HUoUB*fvWKu}m?2bhB#!~+Sx;u^S3fu| z(69byiuzS>T!9`>8+He7V_wJ)3C##@yP|>Ni6Fc>t4LSU3X+*K;l#03j6pJBtUMyss;(lV1rs89kPk zGE6Nm_!b5bCmp9MpZDL5OA0g41&&<&s7DSH*xDE-oPU*;SHpxp&`b45 z52mkI2=(;WY{?X*JJH#ECcL{$GCZUB!oj4|qs#PhyAZVpf@s3E`~0DtS> zAA|1>H%Ad%)2bXsfEIWy3cP|ZsgP&C`P)7%l-uibN&*x988Zyju)x5I(sq|3Uz%$G zYe20JDOU;|gsyTPaL?z~_tv$EDFBggyJ_2KNE@9>7WhL{d;^J9Y=VoSViP<*cmzMP z)!*OpTo&Dp&}7CyPL#V_i4J7WNmP&1kI(zZ(t~*3ONlh|dW`R$qtXnZjW`V8I)Xk7 z;H2&QGJsI@1DOIjCiRcTEF%0nQPE$+S&*$`R;!iNivay6y2K93!_%<-Fh#=L?f=xTm$?+FIB}?zTxA;!|Vr-XL5_J7A5@j~e3PmI8GI!TO8u;Cz zFQmrl@A3mD=eP)P{NPFk%ZoP+-`zQ*SA`{hy0!;z>|fiX?+aYk zH)bAlFgL{acpk>cylhM_sGvkp;dy3`?r8XM3JqUNMX^QOCb?)^8DZiX!84o1t^9h5 z#nmSHd>?+(cft9-Y7xd>A~|zdBF5G?AkxGN$3nmNrz{rwv8e%{f4GAByn?f~@5|>G zlQJZqPqM$OuW;nWocVR`+vc~H?|X#cWE7`EEPMi54g6V)azw~bjwl$)S#CkIHa$48 zzK|`|8Qh}%vPh*o65AU`i1}5q3%G89$vz$ti}|>;q;YCl*&lMt|B!3eVJk%AwL6qG z^w#G2??mYrPp1`B5Y5Me-~R_GGUHcgFyj}JVa898Wzt#t0^*_gUaVQ1ZWLS&OB_dMf@k} zLEugQEOIT+by%4=66OJ9!?5v`r|_tlI^s6+eRQ3Hb80o8-R^USK<{AgIhZ#Y%)`xH z4)kS!zC>;V1bViFJ4*;ntfoVZ=FVJp&?p9p>ED`YWAFt-Hx3(ZcgT+V*2d8AvWCg+ zZKh^*^h)Jk9lgQ@Q5Q5QfZng`H3}7Y{C#f_g&LDzZ-~OJo1IwA2I+iOkCP7DrsgA# ztchy1)jyP+!B^aIn7H*2FBl|G;VoK%#P`Pox|Pl2=xq|gZ>gV_)4((A#`oiS%Xcu& z_!Y?-yI40XP!(;290@I|J^DQ*{K3xc4LeqB^BNG4Ds#)lP<=n>auZ*LqEQ zzBuAdU*KNF_18&d6yte$Z@Qm_kN&gz1?gDuIi;EX*B>}ZKr74yG4VLRG0+6nNY_A{fw ziLc}*-l8F}Ge?1Un*q$bxdnJcyk;%tu>J)KVoe(XNzaj>p+qY5LXnP_##Y6SVguS_ zOH}OS{QSz^)l?=y3w)8>=YMapKw^b~giZM?2dmfa!FN4y*>H+r61)M-n4{g+pp}or zJZnCXMKB(__^tK5^)~0M&wm4x0~bPFKs3{k9T(q9!Ns=)PM*TeOp&m^{Er9mm-qF6 zr1&$wS--P8`sZ)#S!qNiIPgI7^e}&B>g}1GCh!OKb|tsWD|H(Lc3{XOO4nTRZnQzm z;V%8$`|Wf;8}2uVm~=!=fvem$34lZx=s(HMk`pBXuIk7Y5?|eumpz}NewzkYicLk| zY~NxNC>*1V|M|PN$w{1|gw$Tj_{WK}w8rWa#K+tPn6-)Ff-5UpEZV%>MWd~R>@sb3 zXqp%;^B0!zC_w^qha{o;J5I*5OYStCZ13f*%Gbhaz9jYFsP;m(=(TD?6bwA#<^3p6 z*`h)yw@bp2R}x>2yG;0EJ0$;TzTXaAY8kcq;)&qAbrlUr@2As?IiY9U#Leb}YSz3? z#2A{urRi`?Epf}+z=Bsv$mYL!sW{Ee!d`M2j&^j9t;+4DE1JS8C*)LlD}<#zlX&Ve z9QnqdG6*(!UOEu06HDl6FQ*>OqW7S5C8Qc}HflQZX4M*Mc$5h)Uq5z2ZtdRA!B_li zT(P`ld`@j?q`qUkKYya`Sw1O6>=fL8@oGjSiKapi`XVX)giCvSd-g`_5ON$%04?m$%8~))ZvPwd?rù|uoQKO$O7~zTI80Z7sRbLZzopR_;gwhzV|w< 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 z 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�QStknyBAN;Sa;IvMGxDCxQ{z0$9E{Ojc}^ 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